| 1 | /* |
| 2 | * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * 1. Redistributions of source code must retain the above copyright |
| 8 | * notice, this list of conditions and the following disclaimer. |
| 9 | * 2. Redistributions in binary form must reproduce the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer in the |
| 11 | * documentation and/or other materials provided with the distribution. |
| 12 | * |
| 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| 14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| 17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | #include "config.h" |
| 27 | #include "JIT.h" |
| 28 | |
| 29 | // This probably does not belong here; adding here for now as a quick Windows build fix. |
| 30 | #if ENABLE(ASSEMBLER) && CPU(X86) && !OS(MAC_OS_X) |
| 31 | #include "MacroAssembler.h" |
| 32 | JSC::MacroAssemblerX86Common::SSE2CheckState JSC::MacroAssemblerX86Common::s_sse2CheckState = NotCheckedSSE2; |
| 33 | #endif |
| 34 | |
| 35 | #if ENABLE(JIT) |
| 36 | |
| 37 | #include "CodeBlock.h" |
| 38 | #include "Interpreter.h" |
| 39 | #include "JITInlineMethods.h" |
| 40 | #include "JITStubCall.h" |
| 41 | #include "JSArray.h" |
| 42 | #include "JSFunction.h" |
| 43 | #include "LinkBuffer.h" |
| 44 | #include "RepatchBuffer.h" |
| 45 | #include "ResultType.h" |
| 46 | #include "SamplingTool.h" |
| 47 | |
| 48 | #ifndef NDEBUG |
| 49 | #include <stdio.h> |
| 50 | #endif |
| 51 | |
| 52 | using namespace std; |
| 53 | |
| 54 | namespace JSC { |
| 55 | |
| 56 | void ctiPatchNearCallByReturnAddress(CodeBlock* codeblock, ReturnAddressPtr returnAddress, MacroAssemblerCodePtr newCalleeFunction) |
| 57 | { |
| 58 | RepatchBuffer repatchBuffer(codeblock); |
| 59 | repatchBuffer.relinkNearCallerToTrampoline(returnAddress, newCalleeFunction); |
| 60 | } |
| 61 | |
| 62 | void ctiPatchCallByReturnAddress(CodeBlock* codeblock, ReturnAddressPtr returnAddress, MacroAssemblerCodePtr newCalleeFunction) |
| 63 | { |
| 64 | RepatchBuffer repatchBuffer(codeblock); |
| 65 | repatchBuffer.relinkCallerToTrampoline(returnAddress, newCalleeFunction); |
| 66 | } |
| 67 | |
| 68 | void ctiPatchCallByReturnAddress(CodeBlock* codeblock, ReturnAddressPtr returnAddress, FunctionPtr newCalleeFunction) |
| 69 | { |
| 70 | RepatchBuffer repatchBuffer(codeblock); |
| 71 | repatchBuffer.relinkCallerToFunction(returnAddress, function: newCalleeFunction); |
| 72 | } |
| 73 | |
| 74 | JIT::JIT(JSGlobalData* globalData, CodeBlock* codeBlock) |
| 75 | : m_interpreter(globalData->interpreter) |
| 76 | , m_globalData(globalData) |
| 77 | , m_codeBlock(codeBlock) |
| 78 | , m_labels(codeBlock ? codeBlock->instructions().size() : 0) |
| 79 | , m_propertyAccessCompilationInfo(codeBlock ? codeBlock->numberOfStructureStubInfos() : 0) |
| 80 | , m_callStructureStubCompilationInfo(codeBlock ? codeBlock->numberOfCallLinkInfos() : 0) |
| 81 | , m_bytecodeIndex((unsigned)-1) |
| 82 | #if USE(JSVALUE32_64) |
| 83 | , m_jumpTargetIndex(0) |
| 84 | , m_mappedBytecodeIndex((unsigned)-1) |
| 85 | , m_mappedVirtualRegisterIndex((unsigned)-1) |
| 86 | , m_mappedTag((RegisterID)-1) |
| 87 | , m_mappedPayload((RegisterID)-1) |
| 88 | #else |
| 89 | , m_lastResultBytecodeRegister(std::numeric_limits<int>::max()) |
| 90 | , m_jumpTargetsPosition(0) |
| 91 | #endif |
| 92 | { |
| 93 | } |
| 94 | |
| 95 | #if USE(JSVALUE32_64) |
| 96 | void JIT::emitTimeoutCheck() |
| 97 | { |
| 98 | Jump skipTimeout = branchSub32(NonZero, Imm32(1), timeoutCheckRegister); |
| 99 | JITStubCall stubCall(this, cti_timeout_check); |
| 100 | stubCall.addArgument(regT1, regT0); // save last result registers. |
| 101 | stubCall.call(timeoutCheckRegister); |
| 102 | stubCall.getArgument(0, regT1, regT0); // reload last result registers. |
| 103 | skipTimeout.link(this); |
| 104 | } |
| 105 | #else |
| 106 | void JIT::emitTimeoutCheck() |
| 107 | { |
| 108 | Jump skipTimeout = branchSub32(cond: NonZero, imm: Imm32(1), dest: timeoutCheckRegister); |
| 109 | JITStubCall(this, cti_timeout_check).call(dst: timeoutCheckRegister); |
| 110 | skipTimeout.link(masm: this); |
| 111 | |
| 112 | killLastResultRegister(); |
| 113 | } |
| 114 | #endif |
| 115 | |
| 116 | #define NEXT_OPCODE(name) \ |
| 117 | m_bytecodeIndex += OPCODE_LENGTH(name); \ |
| 118 | break; |
| 119 | |
| 120 | #if USE(JSVALUE32_64) |
| 121 | #define DEFINE_BINARY_OP(name) \ |
| 122 | case name: { \ |
| 123 | JITStubCall stubCall(this, cti_##name); \ |
| 124 | stubCall.addArgument(currentInstruction[2].u.operand); \ |
| 125 | stubCall.addArgument(currentInstruction[3].u.operand); \ |
| 126 | stubCall.call(currentInstruction[1].u.operand); \ |
| 127 | NEXT_OPCODE(name); \ |
| 128 | } |
| 129 | |
| 130 | #define DEFINE_UNARY_OP(name) \ |
| 131 | case name: { \ |
| 132 | JITStubCall stubCall(this, cti_##name); \ |
| 133 | stubCall.addArgument(currentInstruction[2].u.operand); \ |
| 134 | stubCall.call(currentInstruction[1].u.operand); \ |
| 135 | NEXT_OPCODE(name); \ |
| 136 | } |
| 137 | |
| 138 | #else // USE(JSVALUE32_64) |
| 139 | |
| 140 | #define DEFINE_BINARY_OP(name) \ |
| 141 | case name: { \ |
| 142 | JITStubCall stubCall(this, cti_##name); \ |
| 143 | stubCall.addArgument(currentInstruction[2].u.operand, regT2); \ |
| 144 | stubCall.addArgument(currentInstruction[3].u.operand, regT2); \ |
| 145 | stubCall.call(currentInstruction[1].u.operand); \ |
| 146 | NEXT_OPCODE(name); \ |
| 147 | } |
| 148 | |
| 149 | #define DEFINE_UNARY_OP(name) \ |
| 150 | case name: { \ |
| 151 | JITStubCall stubCall(this, cti_##name); \ |
| 152 | stubCall.addArgument(currentInstruction[2].u.operand, regT2); \ |
| 153 | stubCall.call(currentInstruction[1].u.operand); \ |
| 154 | NEXT_OPCODE(name); \ |
| 155 | } |
| 156 | #endif // USE(JSVALUE32_64) |
| 157 | |
| 158 | #define DEFINE_OP(name) \ |
| 159 | case name: { \ |
| 160 | emit_##name(currentInstruction); \ |
| 161 | NEXT_OPCODE(name); \ |
| 162 | } |
| 163 | |
| 164 | #define DEFINE_SLOWCASE_OP(name) \ |
| 165 | case name: { \ |
| 166 | emitSlow_##name(currentInstruction, iter); \ |
| 167 | NEXT_OPCODE(name); \ |
| 168 | } |
| 169 | |
| 170 | void JIT::privateCompileMainPass() |
| 171 | { |
| 172 | Instruction* instructionsBegin = m_codeBlock->instructions().begin(); |
| 173 | unsigned instructionCount = m_codeBlock->instructions().size(); |
| 174 | |
| 175 | m_propertyAccessInstructionIndex = 0; |
| 176 | m_globalResolveInfoIndex = 0; |
| 177 | m_callLinkInfoIndex = 0; |
| 178 | |
| 179 | for (m_bytecodeIndex = 0; m_bytecodeIndex < instructionCount; ) { |
| 180 | Instruction* currentInstruction = instructionsBegin + m_bytecodeIndex; |
| 181 | ASSERT_WITH_MESSAGE(m_interpreter->isOpcode(currentInstruction->u.opcode), "privateCompileMainPass gone bad @ %d" , m_bytecodeIndex); |
| 182 | |
| 183 | #if ENABLE(OPCODE_SAMPLING) |
| 184 | if (m_bytecodeIndex > 0) // Avoid the overhead of sampling op_enter twice. |
| 185 | sampleInstruction(currentInstruction); |
| 186 | #endif |
| 187 | |
| 188 | #if !USE(JSVALUE32_64) |
| 189 | if (m_labels[m_bytecodeIndex].isUsed()) |
| 190 | killLastResultRegister(); |
| 191 | #endif |
| 192 | |
| 193 | m_labels[m_bytecodeIndex] = label(); |
| 194 | |
| 195 | switch (m_interpreter->getOpcodeID(opcode: currentInstruction->u.opcode)) { |
| 196 | DEFINE_BINARY_OP(op_del_by_val) |
| 197 | #if USE(JSVALUE32) |
| 198 | DEFINE_BINARY_OP(op_div) |
| 199 | #endif |
| 200 | DEFINE_BINARY_OP(op_in) |
| 201 | DEFINE_BINARY_OP(op_less) |
| 202 | DEFINE_BINARY_OP(op_lesseq) |
| 203 | DEFINE_BINARY_OP(op_urshift) |
| 204 | DEFINE_UNARY_OP(op_is_boolean) |
| 205 | DEFINE_UNARY_OP(op_is_function) |
| 206 | DEFINE_UNARY_OP(op_is_number) |
| 207 | DEFINE_UNARY_OP(op_is_object) |
| 208 | DEFINE_UNARY_OP(op_is_string) |
| 209 | DEFINE_UNARY_OP(op_is_undefined) |
| 210 | #if !USE(JSVALUE32_64) |
| 211 | DEFINE_UNARY_OP(op_negate) |
| 212 | #endif |
| 213 | DEFINE_UNARY_OP(op_typeof) |
| 214 | |
| 215 | DEFINE_OP(op_add) |
| 216 | DEFINE_OP(op_bitand) |
| 217 | DEFINE_OP(op_bitnot) |
| 218 | DEFINE_OP(op_bitor) |
| 219 | DEFINE_OP(op_bitxor) |
| 220 | DEFINE_OP(op_call) |
| 221 | DEFINE_OP(op_call_eval) |
| 222 | DEFINE_OP(op_call_varargs) |
| 223 | DEFINE_OP(op_catch) |
| 224 | DEFINE_OP(op_construct) |
| 225 | DEFINE_OP(op_construct_verify) |
| 226 | DEFINE_OP(op_convert_this) |
| 227 | DEFINE_OP(op_init_arguments) |
| 228 | DEFINE_OP(op_create_arguments) |
| 229 | DEFINE_OP(op_debug) |
| 230 | DEFINE_OP(op_del_by_id) |
| 231 | #if !USE(JSVALUE32) |
| 232 | DEFINE_OP(op_div) |
| 233 | #endif |
| 234 | DEFINE_OP(op_end) |
| 235 | DEFINE_OP(op_enter) |
| 236 | DEFINE_OP(op_enter_with_activation) |
| 237 | DEFINE_OP(op_eq) |
| 238 | DEFINE_OP(op_eq_null) |
| 239 | DEFINE_OP(op_get_by_id) |
| 240 | DEFINE_OP(op_get_by_val) |
| 241 | DEFINE_OP(op_get_by_pname) |
| 242 | DEFINE_OP(op_get_global_var) |
| 243 | DEFINE_OP(op_get_pnames) |
| 244 | DEFINE_OP(op_get_scoped_var) |
| 245 | DEFINE_OP(op_instanceof) |
| 246 | DEFINE_OP(op_jeq_null) |
| 247 | DEFINE_OP(op_jfalse) |
| 248 | DEFINE_OP(op_jmp) |
| 249 | DEFINE_OP(op_jmp_scopes) |
| 250 | DEFINE_OP(op_jneq_null) |
| 251 | DEFINE_OP(op_jneq_ptr) |
| 252 | DEFINE_OP(op_jnless) |
| 253 | DEFINE_OP(op_jless) |
| 254 | DEFINE_OP(op_jnlesseq) |
| 255 | DEFINE_OP(op_jsr) |
| 256 | DEFINE_OP(op_jtrue) |
| 257 | DEFINE_OP(op_load_varargs) |
| 258 | DEFINE_OP(op_loop) |
| 259 | DEFINE_OP(op_loop_if_less) |
| 260 | DEFINE_OP(op_loop_if_lesseq) |
| 261 | DEFINE_OP(op_loop_if_true) |
| 262 | DEFINE_OP(op_loop_if_false) |
| 263 | DEFINE_OP(op_lshift) |
| 264 | DEFINE_OP(op_method_check) |
| 265 | DEFINE_OP(op_mod) |
| 266 | DEFINE_OP(op_mov) |
| 267 | DEFINE_OP(op_mul) |
| 268 | #if USE(JSVALUE32_64) |
| 269 | DEFINE_OP(op_negate) |
| 270 | #endif |
| 271 | DEFINE_OP(op_neq) |
| 272 | DEFINE_OP(op_neq_null) |
| 273 | DEFINE_OP(op_new_array) |
| 274 | DEFINE_OP(op_new_error) |
| 275 | DEFINE_OP(op_new_func) |
| 276 | DEFINE_OP(op_new_func_exp) |
| 277 | DEFINE_OP(op_new_object) |
| 278 | DEFINE_OP(op_new_regexp) |
| 279 | DEFINE_OP(op_next_pname) |
| 280 | DEFINE_OP(op_not) |
| 281 | DEFINE_OP(op_nstricteq) |
| 282 | DEFINE_OP(op_pop_scope) |
| 283 | DEFINE_OP(op_post_dec) |
| 284 | DEFINE_OP(op_post_inc) |
| 285 | DEFINE_OP(op_pre_dec) |
| 286 | DEFINE_OP(op_pre_inc) |
| 287 | DEFINE_OP(op_profile_did_call) |
| 288 | DEFINE_OP(op_profile_will_call) |
| 289 | DEFINE_OP(op_push_new_scope) |
| 290 | DEFINE_OP(op_push_scope) |
| 291 | DEFINE_OP(op_put_by_id) |
| 292 | DEFINE_OP(op_put_by_index) |
| 293 | DEFINE_OP(op_put_by_val) |
| 294 | DEFINE_OP(op_put_getter) |
| 295 | DEFINE_OP(op_put_global_var) |
| 296 | DEFINE_OP(op_put_scoped_var) |
| 297 | DEFINE_OP(op_put_setter) |
| 298 | DEFINE_OP(op_resolve) |
| 299 | DEFINE_OP(op_resolve_base) |
| 300 | DEFINE_OP(op_resolve_global) |
| 301 | DEFINE_OP(op_resolve_skip) |
| 302 | DEFINE_OP(op_resolve_with_base) |
| 303 | DEFINE_OP(op_ret) |
| 304 | DEFINE_OP(op_rshift) |
| 305 | DEFINE_OP(op_sret) |
| 306 | DEFINE_OP(op_strcat) |
| 307 | DEFINE_OP(op_stricteq) |
| 308 | DEFINE_OP(op_sub) |
| 309 | DEFINE_OP(op_switch_char) |
| 310 | DEFINE_OP(op_switch_imm) |
| 311 | DEFINE_OP(op_switch_string) |
| 312 | DEFINE_OP(op_tear_off_activation) |
| 313 | DEFINE_OP(op_tear_off_arguments) |
| 314 | DEFINE_OP(op_throw) |
| 315 | DEFINE_OP(op_to_jsnumber) |
| 316 | DEFINE_OP(op_to_primitive) |
| 317 | |
| 318 | case op_get_array_length: |
| 319 | case op_get_by_id_chain: |
| 320 | case op_get_by_id_generic: |
| 321 | case op_get_by_id_proto: |
| 322 | case op_get_by_id_proto_list: |
| 323 | case op_get_by_id_self: |
| 324 | case op_get_by_id_self_list: |
| 325 | case op_get_string_length: |
| 326 | case op_put_by_id_generic: |
| 327 | case op_put_by_id_replace: |
| 328 | case op_put_by_id_transition: |
| 329 | ASSERT_NOT_REACHED(); |
| 330 | } |
| 331 | } |
| 332 | |
| 333 | ASSERT(m_propertyAccessInstructionIndex == m_codeBlock->numberOfStructureStubInfos()); |
| 334 | ASSERT(m_callLinkInfoIndex == m_codeBlock->numberOfCallLinkInfos()); |
| 335 | |
| 336 | #ifndef NDEBUG |
| 337 | // Reset this, in order to guard its use with ASSERTs. |
| 338 | m_bytecodeIndex = (unsigned)-1; |
| 339 | #endif |
| 340 | } |
| 341 | |
| 342 | |
| 343 | void JIT::privateCompileLinkPass() |
| 344 | { |
| 345 | unsigned jmpTableCount = m_jmpTable.size(); |
| 346 | for (unsigned i = 0; i < jmpTableCount; ++i) |
| 347 | m_jmpTable[i].from.linkTo(label: m_labels[m_jmpTable[i].toBytecodeIndex], masm: this); |
| 348 | m_jmpTable.clear(); |
| 349 | } |
| 350 | |
| 351 | void JIT::privateCompileSlowCases() |
| 352 | { |
| 353 | Instruction* instructionsBegin = m_codeBlock->instructions().begin(); |
| 354 | |
| 355 | m_propertyAccessInstructionIndex = 0; |
| 356 | #if USE(JSVALUE32_64) |
| 357 | m_globalResolveInfoIndex = 0; |
| 358 | #endif |
| 359 | m_callLinkInfoIndex = 0; |
| 360 | |
| 361 | for (Vector<SlowCaseEntry>::iterator iter = m_slowCases.begin(); iter != m_slowCases.end();) { |
| 362 | #if !USE(JSVALUE32_64) |
| 363 | killLastResultRegister(); |
| 364 | #endif |
| 365 | |
| 366 | m_bytecodeIndex = iter->to; |
| 367 | #ifndef NDEBUG |
| 368 | unsigned firstTo = m_bytecodeIndex; |
| 369 | #endif |
| 370 | Instruction* currentInstruction = instructionsBegin + m_bytecodeIndex; |
| 371 | |
| 372 | switch (m_interpreter->getOpcodeID(opcode: currentInstruction->u.opcode)) { |
| 373 | DEFINE_SLOWCASE_OP(op_add) |
| 374 | DEFINE_SLOWCASE_OP(op_bitand) |
| 375 | DEFINE_SLOWCASE_OP(op_bitnot) |
| 376 | DEFINE_SLOWCASE_OP(op_bitor) |
| 377 | DEFINE_SLOWCASE_OP(op_bitxor) |
| 378 | DEFINE_SLOWCASE_OP(op_call) |
| 379 | DEFINE_SLOWCASE_OP(op_call_eval) |
| 380 | DEFINE_SLOWCASE_OP(op_call_varargs) |
| 381 | DEFINE_SLOWCASE_OP(op_construct) |
| 382 | DEFINE_SLOWCASE_OP(op_construct_verify) |
| 383 | DEFINE_SLOWCASE_OP(op_convert_this) |
| 384 | #if !USE(JSVALUE32) |
| 385 | DEFINE_SLOWCASE_OP(op_div) |
| 386 | #endif |
| 387 | DEFINE_SLOWCASE_OP(op_eq) |
| 388 | DEFINE_SLOWCASE_OP(op_get_by_id) |
| 389 | DEFINE_SLOWCASE_OP(op_get_by_val) |
| 390 | DEFINE_SLOWCASE_OP(op_get_by_pname) |
| 391 | DEFINE_SLOWCASE_OP(op_instanceof) |
| 392 | DEFINE_SLOWCASE_OP(op_jfalse) |
| 393 | DEFINE_SLOWCASE_OP(op_jnless) |
| 394 | DEFINE_SLOWCASE_OP(op_jless) |
| 395 | DEFINE_SLOWCASE_OP(op_jnlesseq) |
| 396 | DEFINE_SLOWCASE_OP(op_jtrue) |
| 397 | DEFINE_SLOWCASE_OP(op_loop_if_less) |
| 398 | DEFINE_SLOWCASE_OP(op_loop_if_lesseq) |
| 399 | DEFINE_SLOWCASE_OP(op_loop_if_true) |
| 400 | DEFINE_SLOWCASE_OP(op_loop_if_false) |
| 401 | DEFINE_SLOWCASE_OP(op_lshift) |
| 402 | DEFINE_SLOWCASE_OP(op_method_check) |
| 403 | DEFINE_SLOWCASE_OP(op_mod) |
| 404 | DEFINE_SLOWCASE_OP(op_mul) |
| 405 | #if USE(JSVALUE32_64) |
| 406 | DEFINE_SLOWCASE_OP(op_negate) |
| 407 | #endif |
| 408 | DEFINE_SLOWCASE_OP(op_neq) |
| 409 | DEFINE_SLOWCASE_OP(op_not) |
| 410 | DEFINE_SLOWCASE_OP(op_nstricteq) |
| 411 | DEFINE_SLOWCASE_OP(op_post_dec) |
| 412 | DEFINE_SLOWCASE_OP(op_post_inc) |
| 413 | DEFINE_SLOWCASE_OP(op_pre_dec) |
| 414 | DEFINE_SLOWCASE_OP(op_pre_inc) |
| 415 | DEFINE_SLOWCASE_OP(op_put_by_id) |
| 416 | DEFINE_SLOWCASE_OP(op_put_by_val) |
| 417 | #if USE(JSVALUE32_64) |
| 418 | DEFINE_SLOWCASE_OP(op_resolve_global) |
| 419 | #endif |
| 420 | DEFINE_SLOWCASE_OP(op_rshift) |
| 421 | DEFINE_SLOWCASE_OP(op_stricteq) |
| 422 | DEFINE_SLOWCASE_OP(op_sub) |
| 423 | DEFINE_SLOWCASE_OP(op_to_jsnumber) |
| 424 | DEFINE_SLOWCASE_OP(op_to_primitive) |
| 425 | default: |
| 426 | ASSERT_NOT_REACHED(); |
| 427 | } |
| 428 | |
| 429 | ASSERT_WITH_MESSAGE(iter == m_slowCases.end() || firstTo != iter->to,"Not enough jumps linked in slow case codegen." ); |
| 430 | ASSERT_WITH_MESSAGE(firstTo == (iter - 1)->to, "Too many jumps linked in slow case codegen." ); |
| 431 | |
| 432 | emitJumpSlowToHot(jump: jump(), relativeOffset: 0); |
| 433 | } |
| 434 | |
| 435 | #if ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) |
| 436 | ASSERT(m_propertyAccessInstructionIndex == m_codeBlock->numberOfStructureStubInfos()); |
| 437 | #endif |
| 438 | ASSERT(m_callLinkInfoIndex == m_codeBlock->numberOfCallLinkInfos()); |
| 439 | |
| 440 | #ifndef NDEBUG |
| 441 | // Reset this, in order to guard its use with ASSERTs. |
| 442 | m_bytecodeIndex = (unsigned)-1; |
| 443 | #endif |
| 444 | } |
| 445 | |
| 446 | JITCode JIT::privateCompile() |
| 447 | { |
| 448 | sampleCodeBlock(m_codeBlock); |
| 449 | #if ENABLE(OPCODE_SAMPLING) |
| 450 | sampleInstruction(m_codeBlock->instructions().begin()); |
| 451 | #endif |
| 452 | |
| 453 | // Could use a pop_m, but would need to offset the following instruction if so. |
| 454 | preserveReturnAddressAfterCall(reg: regT2); |
| 455 | emitPutToCallFrameHeader(from: regT2, entry: RegisterFile::ReturnPC); |
| 456 | |
| 457 | Jump slowRegisterFileCheck; |
| 458 | Label afterRegisterFileCheck; |
| 459 | if (m_codeBlock->codeType() == FunctionCode) { |
| 460 | // In the case of a fast linked call, we do not set this up in the caller. |
| 461 | emitPutImmediateToCallFrameHeader(value: m_codeBlock, entry: RegisterFile::CodeBlock); |
| 462 | |
| 463 | peek(dest: regT0, OBJECT_OFFSETOF(JITStackFrame, registerFile) / sizeof (void*)); |
| 464 | addPtr(imm: Imm32(m_codeBlock->m_numCalleeRegisters * sizeof(Register)), src: callFrameRegister, dest: regT1); |
| 465 | |
| 466 | slowRegisterFileCheck = branchPtr(cond: Above, left: regT1, right: Address(regT0, OBJECT_OFFSETOF(RegisterFile, m_end))); |
| 467 | afterRegisterFileCheck = label(); |
| 468 | } |
| 469 | |
| 470 | privateCompileMainPass(); |
| 471 | privateCompileLinkPass(); |
| 472 | privateCompileSlowCases(); |
| 473 | |
| 474 | if (m_codeBlock->codeType() == FunctionCode) { |
| 475 | slowRegisterFileCheck.link(masm: this); |
| 476 | m_bytecodeIndex = 0; |
| 477 | JITStubCall(this, cti_register_file_check).call(); |
| 478 | #ifndef NDEBUG |
| 479 | m_bytecodeIndex = (unsigned)-1; // Reset this, in order to guard its use with ASSERTs. |
| 480 | #endif |
| 481 | jump(target: afterRegisterFileCheck); |
| 482 | } |
| 483 | |
| 484 | ASSERT(m_jmpTable.isEmpty()); |
| 485 | |
| 486 | LinkBuffer patchBuffer(this, m_globalData->executableAllocator.poolForSize(n: m_assembler.size())); |
| 487 | |
| 488 | // Translate vPC offsets into addresses in JIT generated code, for switch tables. |
| 489 | for (unsigned i = 0; i < m_switches.size(); ++i) { |
| 490 | SwitchRecord record = m_switches[i]; |
| 491 | unsigned bytecodeIndex = record.bytecodeIndex; |
| 492 | |
| 493 | if (record.type != SwitchRecord::String) { |
| 494 | ASSERT(record.type == SwitchRecord::Immediate || record.type == SwitchRecord::Character); |
| 495 | ASSERT(record.jumpTable.simpleJumpTable->branchOffsets.size() == record.jumpTable.simpleJumpTable->ctiOffsets.size()); |
| 496 | |
| 497 | record.jumpTable.simpleJumpTable->ctiDefault = patchBuffer.locationOf(label: m_labels[bytecodeIndex + record.defaultOffset]); |
| 498 | |
| 499 | for (unsigned j = 0; j < record.jumpTable.simpleJumpTable->branchOffsets.size(); ++j) { |
| 500 | unsigned offset = record.jumpTable.simpleJumpTable->branchOffsets[j]; |
| 501 | record.jumpTable.simpleJumpTable->ctiOffsets[j] = offset ? patchBuffer.locationOf(label: m_labels[bytecodeIndex + offset]) : record.jumpTable.simpleJumpTable->ctiDefault; |
| 502 | } |
| 503 | } else { |
| 504 | ASSERT(record.type == SwitchRecord::String); |
| 505 | |
| 506 | record.jumpTable.stringJumpTable->ctiDefault = patchBuffer.locationOf(label: m_labels[bytecodeIndex + record.defaultOffset]); |
| 507 | |
| 508 | StringJumpTable::StringOffsetTable::iterator end = record.jumpTable.stringJumpTable->offsetTable.end(); |
| 509 | for (StringJumpTable::StringOffsetTable::iterator it = record.jumpTable.stringJumpTable->offsetTable.begin(); it != end; ++it) { |
| 510 | unsigned offset = it->second.branchOffset; |
| 511 | it->second.ctiOffset = offset ? patchBuffer.locationOf(label: m_labels[bytecodeIndex + offset]) : record.jumpTable.stringJumpTable->ctiDefault; |
| 512 | } |
| 513 | } |
| 514 | } |
| 515 | |
| 516 | for (size_t i = 0; i < m_codeBlock->numberOfExceptionHandlers(); ++i) { |
| 517 | HandlerInfo& handler = m_codeBlock->exceptionHandler(index: i); |
| 518 | handler.nativeCode = patchBuffer.locationOf(label: m_labels[handler.target]); |
| 519 | } |
| 520 | |
| 521 | for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) { |
| 522 | if (iter->to) |
| 523 | patchBuffer.link(call: iter->from, function: FunctionPtr(iter->to)); |
| 524 | } |
| 525 | |
| 526 | if (m_codeBlock->hasExceptionInfo()) { |
| 527 | m_codeBlock->callReturnIndexVector().reserveCapacity(newCapacity: m_calls.size()); |
| 528 | for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) |
| 529 | m_codeBlock->callReturnIndexVector().append(val: CallReturnOffsetToBytecodeIndex(patchBuffer.returnAddressOffset(call: iter->from), iter->bytecodeIndex)); |
| 530 | } |
| 531 | |
| 532 | // Link absolute addresses for jsr |
| 533 | for (Vector<JSRInfo>::iterator iter = m_jsrSites.begin(); iter != m_jsrSites.end(); ++iter) |
| 534 | patchBuffer.patch(label: iter->storeLocation, value: patchBuffer.locationOf(label: iter->target).executableAddress()); |
| 535 | |
| 536 | #if ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) |
| 537 | for (unsigned i = 0; i < m_codeBlock->numberOfStructureStubInfos(); ++i) { |
| 538 | StructureStubInfo& info = m_codeBlock->structureStubInfo(index: i); |
| 539 | info.callReturnLocation = patchBuffer.locationOf(call: m_propertyAccessCompilationInfo[i].callReturnLocation); |
| 540 | info.hotPathBegin = patchBuffer.locationOf(label: m_propertyAccessCompilationInfo[i].hotPathBegin); |
| 541 | } |
| 542 | #endif |
| 543 | #if ENABLE(JIT_OPTIMIZE_CALL) |
| 544 | for (unsigned i = 0; i < m_codeBlock->numberOfCallLinkInfos(); ++i) { |
| 545 | CallLinkInfo& info = m_codeBlock->callLinkInfo(index: i); |
| 546 | info.ownerCodeBlock = m_codeBlock; |
| 547 | info.callReturnLocation = patchBuffer.locationOfNearCall(call: m_callStructureStubCompilationInfo[i].callReturnLocation); |
| 548 | info.hotPathBegin = patchBuffer.locationOf(label: m_callStructureStubCompilationInfo[i].hotPathBegin); |
| 549 | info.hotPathOther = patchBuffer.locationOfNearCall(call: m_callStructureStubCompilationInfo[i].hotPathOther); |
| 550 | } |
| 551 | #endif |
| 552 | unsigned methodCallCount = m_methodCallCompilationInfo.size(); |
| 553 | m_codeBlock->addMethodCallLinkInfos(n: methodCallCount); |
| 554 | for (unsigned i = 0; i < methodCallCount; ++i) { |
| 555 | MethodCallLinkInfo& info = m_codeBlock->methodCallLinkInfo(index: i); |
| 556 | info.structureLabel = patchBuffer.locationOf(label: m_methodCallCompilationInfo[i].structureToCompare); |
| 557 | info.callReturnLocation = m_codeBlock->structureStubInfo(index: m_methodCallCompilationInfo[i].propertyAccessIndex).callReturnLocation; |
| 558 | } |
| 559 | |
| 560 | return patchBuffer.finalizeCode(); |
| 561 | } |
| 562 | |
| 563 | #if !USE(JSVALUE32_64) |
| 564 | void JIT::emitGetVariableObjectRegister(RegisterID variableObject, int index, RegisterID dst) |
| 565 | { |
| 566 | loadPtr(address: Address(variableObject, OBJECT_OFFSETOF(JSVariableObject, d)), dest: dst); |
| 567 | loadPtr(address: Address(dst, OBJECT_OFFSETOF(JSVariableObject::JSVariableObjectData, registers)), dest: dst); |
| 568 | loadPtr(address: Address(dst, index * sizeof(Register)), dest: dst); |
| 569 | } |
| 570 | |
| 571 | void JIT::emitPutVariableObjectRegister(RegisterID src, RegisterID variableObject, int index) |
| 572 | { |
| 573 | loadPtr(address: Address(variableObject, OBJECT_OFFSETOF(JSVariableObject, d)), dest: variableObject); |
| 574 | loadPtr(address: Address(variableObject, OBJECT_OFFSETOF(JSVariableObject::JSVariableObjectData, registers)), dest: variableObject); |
| 575 | storePtr(src, address: Address(variableObject, index * sizeof(Register))); |
| 576 | } |
| 577 | #endif |
| 578 | |
| 579 | #if ENABLE(JIT_OPTIMIZE_CALL) |
| 580 | void JIT::unlinkCall(CallLinkInfo* callLinkInfo) |
| 581 | { |
| 582 | // When the JSFunction is deleted the pointer embedded in the instruction stream will no longer be valid |
| 583 | // (and, if a new JSFunction happened to be constructed at the same location, we could get a false positive |
| 584 | // match). Reset the check so it no longer matches. |
| 585 | RepatchBuffer repatchBuffer(callLinkInfo->ownerCodeBlock.get()); |
| 586 | #if USE(JSVALUE32_64) |
| 587 | repatchBuffer.repatch(callLinkInfo->hotPathBegin, 0); |
| 588 | #else |
| 589 | repatchBuffer.repatch(dataLabelPtr: callLinkInfo->hotPathBegin, value: JSValue::encode(value: JSValue())); |
| 590 | #endif |
| 591 | } |
| 592 | |
| 593 | void JIT::linkCall(JSFunction* callee, CodeBlock* callerCodeBlock, CodeBlock* calleeCodeBlock, JITCode& code, CallLinkInfo* callLinkInfo, int callerArgCount, JSGlobalData* globalData) |
| 594 | { |
| 595 | RepatchBuffer repatchBuffer(callerCodeBlock); |
| 596 | |
| 597 | // Currently we only link calls with the exact number of arguments. |
| 598 | // If this is a native call calleeCodeBlock is null so the number of parameters is unimportant |
| 599 | if (!calleeCodeBlock || (callerArgCount == calleeCodeBlock->m_numParameters)) { |
| 600 | ASSERT(!callLinkInfo->isLinked()); |
| 601 | |
| 602 | if (calleeCodeBlock) |
| 603 | calleeCodeBlock->addCaller(caller: callLinkInfo); |
| 604 | |
| 605 | repatchBuffer.repatch(dataLabelPtr: callLinkInfo->hotPathBegin, value: callee); |
| 606 | repatchBuffer.relink(nearCall: callLinkInfo->hotPathOther, destination: code.addressForCall()); |
| 607 | } |
| 608 | |
| 609 | // patch the call so we do not continue to try to link. |
| 610 | repatchBuffer.relink(nearCall: callLinkInfo->callReturnLocation, destination: globalData->jitStubs.ctiVirtualCall()); |
| 611 | } |
| 612 | #endif // ENABLE(JIT_OPTIMIZE_CALL) |
| 613 | |
| 614 | } // namespace JSC |
| 615 | |
| 616 | #endif // ENABLE(JIT) |
| 617 | |