/** * Generates monitor exit. * The code should not contain safepoints. * * @param[in] ss buffer to put the assembly code to * @param[in] input_param1 register should point to the lockword in object header. * If input_param1 == ecx it reduce one register mov. * The code use and do not restore eax, ecx registers. * @return 0 if success in eax register */ char* gen_monitor_exit_helper(char *ss, const R_Opnd & input_param1) { if (&input_param1 != &ecx_opnd) { ss = mov(ss, ecx_opnd, input_param1); } #ifdef ASM_MONITOR_HELPER ss = mov(ss, eax_opnd, M_Base_Opnd(ecx_reg, 0)); // mov eax,dword[ecx] ss = test(ss, eax_opnd, Imm_Opnd(0x80000000), size_32); // test eax,0x80000000 ss = branch8(ss, Condition_NZ, Imm_Opnd(size_8, 0)); // jnz fat char *fat = ((char *)ss) - 1; ss = mov(ss, eax_opnd, M_Base_Opnd(ecx_reg, 1), size_8); // mov al, byte[ecx+1] ss = alu(ss, sub_opc, eax_opnd, Imm_Opnd(size_8,0x8),size_8); // sub al, 0x8 ss = branch8(ss, Condition_C, Imm_Opnd(size_8, 0)); // jc zero_rec char *zero_rec = ((char *)ss) - 1; ss = mov(ss, M_Base_Opnd(ecx_reg, 1), eax_opnd, size_8); // mov byte[ecx+1],al ss = ret(ss, Imm_Opnd(4)); // ret 4 signed offset = (signed)ss - (signed)zero_rec - 1; //zero_rec: *zero_rec = (char)offset; ss = mov(ss, M_Base_Opnd(ecx_reg, 2), Imm_Opnd(size_16, 0), size_16);// mov word[ecx+2],0 ss = ret(ss, Imm_Opnd(4)); // ret 4 offset = (signed)ss - (signed)fat - 1; //fat: *fat = (char)offset; #endif ss = push(ss, ecx_opnd); ss = call(ss, (char *)hythread_thin_monitor_exit); ss = alu(ss, add_opc, esp_opnd, Imm_Opnd(4)); // pop parameters return ss; }
static char* get_reg(char* ss, const R_Opnd & dst, Reg_No base, int64 offset, bool check_null = false, bool preserve_flags = false) { char* patch_offset = NULL; ss = mov(ss, dst, M_Base_Opnd(base, (I_32)offset)); if (check_null) { if (preserve_flags) *ss++ = (char)0x9C; // PUSHFD ss = test(ss, dst, dst); ss = branch8(ss, Condition_Z, Imm_Opnd(size_8, 0)); patch_offset = ((char*)ss) - 1; // Store location for jump patch } ss = mov(ss, dst, M_Base_Opnd(dst.reg_no(), 0)); if (check_null) { // Patch conditional jump POINTER_SIZE_SINT offset = (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)patch_offset - 1; assert(offset >= -128 && offset < 127); *patch_offset = (char)offset; if (preserve_flags) *ss++ = (char)0x9D; // POPFD } return ss; }
void JIT::emit_op_ret_object_or_this(Instruction* currentInstruction) { unsigned result = currentInstruction[1].u.operand; unsigned thisReg = currentInstruction[2].u.operand; emitLoad(result, regT1, regT0); Jump notJSCell = branch32(NotEqual, regT1, TrustedImm32(JSValue::CellTag)); loadPtr(Address(regT0, JSCell::structureOffset()), regT2); Jump notObject = branch8(NotEqual, Address(regT2, Structure::typeInfoTypeOffset()), TrustedImm32(ObjectType)); emitGetFromCallFrameHeaderPtr(RegisterFile::ReturnPC, regT2); emitGetFromCallFrameHeaderPtr(RegisterFile::CallerFrame, callFrameRegister); restoreReturnAddressBeforeReturn(regT2); ret(); notJSCell.link(this); notObject.link(this); emitLoad(thisReg, regT1, regT0); emitGetFromCallFrameHeaderPtr(RegisterFile::ReturnPC, regT2); emitGetFromCallFrameHeaderPtr(RegisterFile::CallerFrame, callFrameRegister); restoreReturnAddressBeforeReturn(regT2); ret(); }
void JIT::emit_op_ret_object_or_this(Instruction* currentInstruction) { unsigned result = currentInstruction[1].u.operand; unsigned thisReg = currentInstruction[2].u.operand; // We could JIT generate the deref, only calling out to C when the refcount hits zero. if (m_codeBlock->needsFullScopeChain()) JITStubCall(this, cti_op_ret_scopeChain).call(); emitLoad(result, regT1, regT0); Jump notJSCell = branch32(NotEqual, regT1, Imm32(JSValue::CellTag)); loadPtr(Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), regT2); Jump notObject = branch8(NotEqual, Address(regT2, OBJECT_OFFSETOF(Structure, m_typeInfo) + OBJECT_OFFSETOF(TypeInfo, m_type)), Imm32(ObjectType)); emitGetFromCallFrameHeaderPtr(RegisterFile::ReturnPC, regT2); emitGetFromCallFrameHeaderPtr(RegisterFile::CallerFrame, callFrameRegister); restoreReturnAddressBeforeReturn(regT2); ret(); notJSCell.link(this); notObject.link(this); emitLoad(thisReg, regT1, regT0); emitGetFromCallFrameHeaderPtr(RegisterFile::ReturnPC, regT2); emitGetFromCallFrameHeaderPtr(RegisterFile::CallerFrame, callFrameRegister); restoreReturnAddressBeforeReturn(regT2); ret(); }
static char* gen_invoke_common_managed_func(char* stub) { // Defines stack alignment on managed function enter. const I_32 STACK_ALIGNMENT = MANAGED_STACK_ALIGNMENT; const I_32 STACK_ALIGNMENT_MASK = ~(STACK_ALIGNMENT - 1); const char * LOOP_BEGIN = "loop_begin"; const char * LOOP_END = "loop_end"; // [ebp + 8] - args // [ebp + 12] - size // [ebp + 16] - func const I_32 STACK_ARGS_OFFSET = 8; const I_32 STACK_NARGS_OFFSET = 12; const I_32 STACK_FUNC_OFFSET = 16; const I_32 STACK_CALLEE_SAVED_OFFSET = -12; tl::MemoryPool pool; LilCguLabelAddresses labels(&pool, stub); // Initialize ebp-based stack frame. stub = push(stub, ebp_opnd); stub = mov(stub, ebp_opnd, esp_opnd); // Preserve callee-saved registers. stub = push(stub, ebx_opnd); stub = push(stub, esi_opnd); stub = push(stub, edi_opnd); // Load an array of arguments ('args') and its size from the stack. stub = mov(stub, eax_opnd, M_Base_Opnd(ebp_reg, STACK_ARGS_OFFSET)); stub = mov(stub, ecx_opnd, M_Base_Opnd(ebp_reg, STACK_NARGS_OFFSET)); // Align memory stack. stub = lea(stub, ebx_opnd, M_Index_Opnd(n_reg, ecx_reg, 4, 4)); stub = mov(stub, esi_opnd, ebx_opnd); stub = neg(stub, esi_opnd); stub = alu(stub, add_opc, esi_opnd, esp_opnd); stub = alu(stub, and_opc, esi_opnd, Imm_Opnd(size_32, STACK_ALIGNMENT_MASK)); stub = alu(stub, add_opc, ebx_opnd, esi_opnd); stub = mov(stub, esp_opnd, ebx_opnd); // Load a pointer to the last argument of 'args' array. stub = lea(stub, eax_opnd, M_Index_Opnd(eax_reg, ecx_reg, -4, 4)); stub = alu(stub, sub_opc, eax_opnd, esp_opnd); stub = alu(stub, or_opc, ecx_opnd, ecx_opnd); stub = branch8(stub, Condition_Z, Imm_Opnd(size_8, 0)); labels.add_patch_to_label(LOOP_END, stub - 1, LPT_Rel8); // LOOP_BEGIN: // Push inputs on the stack. labels.define_label(LOOP_BEGIN, stub, false); stub = push(stub, M_Index_Opnd(esp_reg, eax_reg, 0, 1)); stub = loop(stub, Imm_Opnd(size_8, 0)); labels.add_patch_to_label(LOOP_BEGIN, stub - 1, LPT_Rel8); // LOOP_END: labels.define_label(LOOP_END, stub, false); // Call target function. stub = mov(stub, eax_opnd, M_Base_Opnd(ebp_reg, STACK_FUNC_OFFSET)); stub = call(stub, eax_opnd); // Restore callee-saved registers from the stack. stub = lea(stub, esp_opnd, M_Base_Opnd(ebp_reg, STACK_CALLEE_SAVED_OFFSET)); stub = pop(stub, edi_opnd); stub = pop(stub, esi_opnd); stub = pop(stub, ebx_opnd); // Leave current frame. stub = pop(stub, ebp_opnd); return stub; }
static transfer_control_stub_type gen_transfer_control_stub() { static transfer_control_stub_type addr = NULL; if (addr) { return addr; } const int STUB_SIZE = 255; char * stub = (char *)malloc_fixed_code_for_jit(STUB_SIZE, DEFAULT_CODE_ALIGNMENT, CODE_BLOCK_HEAT_COLD, CAA_Allocate); char * ss = stub; #ifndef NDEBUG memset(stub, 0xcc /*int 3*/, STUB_SIZE); #endif // // ************* LOW LEVEL DEPENDENCY! *************** // This code sequence must be atomic. The "atomicity" effect is achieved by // changing the rsp at the very end of the sequence. // rdx holds the pointer to the stack iterator #if defined (PLATFORM_POSIX) // RDI holds 1st parameter on Linux ss = mov(ss, rdx_opnd, rdi_opnd); #else // RCX holds 1st parameter on Windows ss = mov(ss, rdx_opnd, rcx_opnd); #endif // Restore general registers ss = get_reg(ss, rbp_opnd, rdx_reg, CONTEXT_OFFSET(p_rbp), false); ss = get_reg(ss, rbx_opnd, rdx_reg, CONTEXT_OFFSET(p_rbx), true); ss = get_reg(ss, r12_opnd, rdx_reg, CONTEXT_OFFSET(p_r12), true); ss = get_reg(ss, r13_opnd, rdx_reg, CONTEXT_OFFSET(p_r13), true); ss = get_reg(ss, r14_opnd, rdx_reg, CONTEXT_OFFSET(p_r14), true); ss = get_reg(ss, r15_opnd, rdx_reg, CONTEXT_OFFSET(p_r15), true); ss = get_reg(ss, rsi_opnd, rdx_reg, CONTEXT_OFFSET(p_rsi), true); ss = get_reg(ss, rdi_opnd, rdx_reg, CONTEXT_OFFSET(p_rdi), true); ss = get_reg(ss, r8_opnd, rdx_reg, CONTEXT_OFFSET(p_r8), true); ss = get_reg(ss, r9_opnd, rdx_reg, CONTEXT_OFFSET(p_r9), true); ss = get_reg(ss, r10_opnd, rdx_reg, CONTEXT_OFFSET(p_r10), true); ss = get_reg(ss, r11_opnd, rdx_reg, CONTEXT_OFFSET(p_r11), true); // Get the new RSP M_Base_Opnd saved_rsp(rdx_reg, CONTEXT_OFFSET(rsp)); ss = mov(ss, rax_opnd, saved_rsp); // Store it over return address for future use ss = mov(ss, M_Base_Opnd(rsp_reg, 0), rax_opnd); // Get the new RIP ss = get_reg(ss, rcx_opnd, rdx_reg, CONTEXT_OFFSET(p_rip), false); // Store RIP to [<new RSP> - 136] to preserve 128 bytes under RSP // which are 'reserved' on Linux ss = mov(ss, M_Base_Opnd(rax_reg, -136), rcx_opnd); ss = get_reg(ss, rax_opnd, rdx_reg, CONTEXT_OFFSET(p_rax), true); // Restore processor flags ss = movzx(ss, rcx_opnd, M_Base_Opnd(rdx_reg, CONTEXT_OFFSET(eflags)), size_16); ss = test(ss, rcx_opnd, rcx_opnd); ss = branch8(ss, Condition_Z, Imm_Opnd(size_8, 0)); char* patch_offset = ((char*)ss) - 1; // Store location for jump patch *ss++ = (char)0x9C; // PUSHFQ M_Base_Opnd sflags(rsp_reg, 0); ss = alu(ss, and_opc, sflags, Imm_Opnd(size_32,FLG_CLEAR_MASK), size_32); ss = alu(ss, and_opc, rcx_opnd, Imm_Opnd(size_32,FLG_SET_MASK), size_32); ss = alu(ss, or_opc, sflags, rcx_opnd, size_32); *ss++ = (char)0x9D; // POPFQ // Patch conditional jump POINTER_SIZE_SINT offset = (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)patch_offset - 1; *patch_offset = (char)offset; ss = get_reg(ss, rcx_opnd, rdx_reg, CONTEXT_OFFSET(p_rcx), true, true); ss = get_reg(ss, rdx_opnd, rdx_reg, CONTEXT_OFFSET(p_rdx), true, true); // Setup stack pointer to previously saved value ss = mov(ss, rsp_opnd, M_Base_Opnd(rsp_reg, 0)); // Jump to address stored to [<new RSP> - 136] ss = jump(ss, M_Base_Opnd(rsp_reg, -136)); addr = (transfer_control_stub_type)stub; assert(ss-stub <= STUB_SIZE); /* The following code will be generated: mov rdx,rcx mov rbp,qword ptr [rdx+10h] mov rbp,qword ptr [rbp] mov rbx,qword ptr [rdx+20h] test rbx,rbx je __label1__ mov rbx,qword ptr [rbx] __label1__ ; .... The same for r12,r13,r14,r15,rsi,rdi,r8,r9,r10 mov r11,qword ptr [rdx+88h] test r11,r11 je __label11__ mov r11,qword ptr [r11] __label11__ mov rax,qword ptr [rdx+8] mov qword ptr [rsp],rax mov rcx,qword ptr [rdx+18h] mov rcx,qword ptr [rcx] mov qword ptr [rax-88h],rcx mov rax,qword ptr [rdx+48h] test rax,rax je __label12__ mov rax,qword ptr [rax] __label12__ movzx rcx,word ptr [rdx+90h] test rcx,rcx je __label13__ pushfq and dword ptr [rsp], 0x003F7202 and ecx, 0x00000CD5 or dword ptr [esp], ecx popfq __label13__ mov rcx,qword ptr [rdx+50h] pushfq test rcx,rcx je __label14__ mov rcx,qword ptr [rcx] __label14__ popfq mov rdx,qword ptr [rdx+58h] pushfq test rdx,rdx je __label15__ mov rdx,qword ptr [rdx] __label15__ popfq mov rsp,qword ptr [rsp] jmp qword ptr [rsp-88h] */ DUMP_STUB(stub, "getaddress__transfer_control", ss-stub); return addr; }
/** * Generates fast path of monitor enter * the code should not contains safepoint. * * @param[in] ss buffer to put the assembly code to * @param[in] input_param1 register which should point to the object lockword. * If input_param1 == ecx it reduces one register mov. * the code use and do not restore ecx, edx, eax registers * * @return 0 if success in eax register */ char* gen_monitorenter_fast_path_helper(char *ss, const R_Opnd & input_param1) { if (&input_param1 != &ecx_opnd) { ss = mov(ss, ecx_opnd, input_param1); } #ifdef ASM_MONITOR_HELPER //get self_id ss = gen_hythread_self_helper(ss); ss = mov(ss, edx_opnd, M_Base_Opnd(eax_reg, hythread_get_thread_id_offset())); // mov edx,dword [eax+off] ss = mov(ss, eax_opnd, M_Base_Opnd(ecx_reg, 2), size_16); // mov ax,word[ecx+2] ss = alu(ss, cmp_opc, edx_opnd, eax_opnd, size_16); // cmp dx,ax ss = branch8(ss, Condition_NZ, Imm_Opnd(size_8, 0)); // jnz check_zero char *check_zero = ((char *)ss) - 1; //; ax==dx it's safe to do inc ss = mov(ss, eax_opnd, M_Base_Opnd(ecx_reg, 1), size_8); // mov al, byte[ecx+1] //rec_inc: ss = alu(ss, add_opc, eax_opnd, Imm_Opnd(size_8, 0x8), size_8); // add al,0x8 ss = branch8(ss, Condition_C, Imm_Opnd(size_8, 0)); // jc failed char *failed1 = ((char *)ss) - 1; ss = mov(ss, M_Base_Opnd(ecx_reg, 1), eax_opnd, size_8); // mov byte[ecx+1],al ss = ret(ss, Imm_Opnd(4)); // ret 4 signed offset = (signed)ss - (signed)check_zero - 1; *check_zero = (char)offset; //check_zero: ss = test(ss, eax_opnd, eax_opnd, size_16); // test ax,ax ss = branch8(ss, Condition_NZ, Imm_Opnd(size_8, 0)); // jnz failed char *failed2 = ((char *)ss) - 1; ss = prefix(ss, lock_prefix); //; here ax==0. ss = cmpxchg(ss, M_Base_Opnd(ecx_reg, 2), edx_opnd, size_16); // lock cmpxchg16 [ecx+2],dx ss = branch8(ss, Condition_NZ, Imm_Opnd(size_8, 0)); // jnz failed char *failed3 = ((char *)ss) - 1; #ifdef LOCK_RESERVATION ss = mov(ss, eax_opnd, M_Base_Opnd(ecx_reg, 1), size_8); // mov al, byte[ecx+1] ss = test(ss, eax_opnd, Imm_Opnd(size_8, 0x4), size_8); // test al,0x4 ss = branch8(ss, Condition_NZ, Imm_Opnd(size_8, 0)); // jnz finish char *finish = ((char *)ss) - 1; ss = alu(ss, add_opc, eax_opnd, Imm_Opnd(size_8, 0x8), size_8); // add al,0x8 ss = mov(ss, M_Base_Opnd(ecx_reg, 1), eax_opnd, size_8); // mov byte[ecx+1],al offset = (signed)ss - (signed)finish - 1; *finish = (char)offset; //finish: #endif ss = ret(ss, Imm_Opnd(4)); // ret 4 offset = (signed)ss - (signed)failed1 - 1; *failed1 = (char)offset; //failed: offset = (signed)ss - (signed)failed2 - 1; *failed2 = (char)offset; offset = (signed)ss - (signed)failed3 - 1; *failed3 = (char)offset; #endif //ASM_MONITOR_HELPER // the second attempt to lock monitor ss = push(ss, ecx_opnd); ss = call(ss, (char *)hythread_thin_monitor_try_enter); ss = alu(ss, add_opc, esp_opnd, Imm_Opnd(4)); // pop parameters return ss; }