OpenJDK 17的C1和C2編譯器實現中,方法返回前插入安全點(Safepoint Poll)的機制

OpenJDK 17 JIT編譯器堆棧分析-CSDN博客

在OpenJDK 17的C1和C2編譯器實現中,方法返回前插入安全點(Safepoint Poll)的機制主要涉及以下關鍵步驟,結合源代碼進行分析:

1.?安全點輪詢樁(Safepoint Poll Stub)的生成

  • C1編譯器
    通過C1SafepointPollStub::emit_code()生成樁代碼:

    cpp

    void C1SafepointPollStub::emit_code(LIR_Assembler* ce) {__ bind(_entry);InternalAddress safepoint_pc(ce->masm()->pc() - ce->masm()->offset() + safepoint_offset());// ... 保存安全點PC到線程對象address stub = SharedRuntime::polling_page_return_handler_blob()->entry_point();__ jump(RuntimeAddress(stub)); // 跳轉到VM的安全點處理例程
    }
    • 計算安全點PC地址(safepoint_pc)并保存到線程對象的saved_exception_pc_offset位置。

    • 跳轉到VM預生成的polling_page_return_handler_blob入口點處理安全點。

  • C2編譯器
    通過C2SafepointPollStubTable::emit_stub_impl()生成樁代碼:

    cpp

    void C2SafepointPollStubTable::emit_stub_impl(...) {__ bind(entry->_stub_label);InternalAddress safepoint_pc(...); // 計算安全點PC// ... 保存PC到線程對象__ jump(callback_addr); // 跳轉到VM的安全點處理例程
    }
    • 邏輯與C1類似,但通過C2SafepointPollStubTable管理多個樁代碼。

2.?在方法返回前插入樁調用

  • 代碼生成階段PhaseOutput::fill_buffer()):
    編譯器遍歷基本塊(Block)生成機器碼時,在返回指令(Return)前插入安全點檢查:

    cpp

    for (uint i = 0; i < nblocks; i++) {Block* block = C->cfg()->get_block(i);for (uint j = 0; j < last_inst; j++) {Node* n = block->get_node(j);if (n->is_MachSafePoint()) { // 安全點節點(含返回前檢查)non_safepoints.observe_safepoint(...); // 記錄安全點位置Process_OopMap_Node(...);             // 生成OopMap}// ... 正常發射指令}
    }
    safepoint_poll_table()->emit(*cb); // 發射安全點樁代碼
    • 當遇到MachSafePoint節點(代表安全點,包括方法返回前的檢查點)時:

      • 記錄安全點的位置(non_safepoints.observe_safepoint())。

      • 生成OopMap(用于GC時定位寄存器/棧幀中的對象)。

    • 最終通過safepoint_poll_table()->emit()將樁代碼寫入代碼緩沖區。

3.?樁代碼與返回指令的關聯

  • 在方法返回指令(如ret之前,編譯器生成一條跳轉指令到樁代碼標簽(_stub_label):

    cpp

    // 在fill_buffer()中處理分支指令時:
    else if (mach->is_MachBranch()) {mach->as_MachBranch()->label_set(&blk_labels[target_block], target_block);
    }
  • 樁代碼標簽由C2SafepointPollStubTable管理,每個樁對應一個唯一的標簽和偏移量。

4.?運行時處理

  • 當線程執行到跳轉指令時,會跳轉到樁代碼:

    • 樁代碼保存當前PC(用于安全點恢復)。

    • 跳轉到VM的公共處理例程polling_page_return_handler_blob

  • VM例程檢查安全點狀態:

    • 無需安全點:恢復執行,繼續方法返回。

    • 需要安全點:掛起線程,等待所有線程到達安全點后再恢復。

關鍵設計要點

  1. 共享樁代碼
    C2使用C2SafepointPollStubTable管理多個樁,避免重復代碼。每個返回點共享相同的處理邏輯,僅PC地址不同。

  2. 延遲綁定
    樁代碼地址在最終生成機器碼時確定(PhaseOutput::fill_buffer()末尾的safepoint_poll_table()->emit())。

  3. 平臺適配

    • #ifdef _LP64區分64/32位實現:64位直接用r15_thread寄存器,32位需臨時保存rcx/rdx

    • 使用rscratch1(平臺無關的臨時寄存器)保證可移植性。

示例流程

假設方法返回前的機器碼邏輯:

text

0x01: ...          // 方法正常代碼
0x0F: jmp <stub>   // 編譯器插入的跳轉(到樁代碼)
0x10: ret           // 返回指令(跳轉后執行)

樁代碼執行:

asm

stub:lea rscratch1, [safepoint_pc]  // 計算安全點PCmov [r15_thread + saved_exception_pc], rscratch1 // 保存PCjmp VM_handler                 // 跳轉到VM公共處理例程

這樣確保線程在返回前必經過安全點檢查,且能準確報告PC位置供VM處理。


總結:C1/C2通過在方法返回指令前插入跳轉到共享樁代碼的指令,由樁代碼觸發VM的安全點檢查機制。這既減少了代碼膨脹,又保證了安全點的精確性。

##源碼

void C1SafepointPollStub::emit_code(LIR_Assembler* ce) {__ bind(_entry);InternalAddress safepoint_pc(ce->masm()->pc() - ce->masm()->offset() + safepoint_offset());
#ifdef _LP64__ lea(rscratch1, safepoint_pc);__ movptr(Address(r15_thread, JavaThread::saved_exception_pc_offset()), rscratch1);
#elseconst Register tmp1 = rcx;const Register tmp2 = rdx;__ push(tmp1);__ push(tmp2);__ lea(tmp1, safepoint_pc);__ get_thread(tmp2);__ movptr(Address(tmp2, JavaThread::saved_exception_pc_offset()), tmp1);__ pop(tmp2);__ pop(tmp1);
#endif /* _LP64 */assert(SharedRuntime::polling_page_return_handler_blob() != NULL,"polling page return stub not created yet");address stub = SharedRuntime::polling_page_return_handler_blob()->entry_point();__ jump(RuntimeAddress(stub));
}#define __ masm.
void C2SafepointPollStubTable::emit_stub_impl(MacroAssembler& masm, C2SafepointPollStub* entry) const {assert(SharedRuntime::polling_page_return_handler_blob() != NULL,"polling page return stub not created yet");address stub = SharedRuntime::polling_page_return_handler_blob()->entry_point();RuntimeAddress callback_addr(stub);__ bind(entry->_stub_label);InternalAddress safepoint_pc(masm.pc() - masm.offset() + entry->_safepoint_offset);
#ifdef _LP64__ lea(rscratch1, safepoint_pc);__ movptr(Address(r15_thread, JavaThread::saved_exception_pc_offset()), rscratch1);
#elseconst Register tmp1 = rcx;const Register tmp2 = rdx;__ push(tmp1);__ push(tmp2);__ lea(tmp1, safepoint_pc);__ get_thread(tmp2);__ movptr(Address(tmp2, JavaThread::saved_exception_pc_offset()), tmp1);__ pop(tmp2);__ pop(tmp1);
#endif__ jump(callback_addr);
}
#undef __void emit_stub_impl(MacroAssembler& masm, C2SafepointPollStub* entry) const;// The selection logic below relieves the need to add dummy files to unsupported platforms.template <bool enabled>typename EnableIf<enabled>::typeselect_emit_stub(MacroAssembler& masm, C2SafepointPollStub* entry) const {emit_stub_impl(masm, entry);}void emit_stub(MacroAssembler& masm, C2SafepointPollStub* entry) const {select_emit_stub<VM_Version::supports_stack_watermark_barrier()>(masm, entry);}void C2SafepointPollStubTable::emit(CodeBuffer& cb) {MacroAssembler masm(&cb);for (int i = _safepoints.length() - 1; i >= 0; i--) {// Make sure there is enough space in the code bufferif (cb.insts()->maybe_expand_to_ensure_remaining(PhaseOutput::MAX_inst_size) && cb.blob() == NULL) {ciEnv::current()->record_failure("CodeCache is full");return;}C2SafepointPollStub* entry = _safepoints.at(i);emit_stub(masm, entry);}
}//------------------------------fill_buffer------------------------------------
void PhaseOutput::fill_buffer(CodeBuffer* cb, uint* blk_starts) {// blk_starts[] contains offsets calculated during short branches processing,// offsets should not be increased during following steps.// Compute the size of first NumberOfLoopInstrToAlign instructions at head// of a loop. It is used to determine the padding for loop alignment.Compile::TracePhase tp("fill buffer", &timers[_t_fillBuffer]);compute_loop_first_inst_sizes();// Create oopmap set._oop_map_set = new OopMapSet();// !!!!! This preserves old handling of oopmaps for nowC->debug_info()->set_oopmaps(_oop_map_set);uint nblocks  = C->cfg()->number_of_blocks();// Count and start of implicit null check instructionsuint inct_cnt = 0;uint* inct_starts = NEW_RESOURCE_ARRAY(uint, nblocks+1);// Count and start of callsuint* call_returns = NEW_RESOURCE_ARRAY(uint, nblocks+1);uint  return_offset = 0;int nop_size = (new MachNopNode())->size(C->regalloc());int previous_offset = 0;int current_offset  = 0;int last_call_offset = -1;int last_avoid_back_to_back_offset = -1;
#ifdef ASSERTuint* jmp_target = NEW_RESOURCE_ARRAY(uint,nblocks);uint* jmp_offset = NEW_RESOURCE_ARRAY(uint,nblocks);uint* jmp_size   = NEW_RESOURCE_ARRAY(uint,nblocks);uint* jmp_rule   = NEW_RESOURCE_ARRAY(uint,nblocks);
#endif// Create an array of unused labels, one for each basic block, if printing is enabled
#if defined(SUPPORT_OPTO_ASSEMBLY)int* node_offsets      = NULL;uint node_offset_limit = C->unique();if (C->print_assembly()) {node_offsets = NEW_RESOURCE_ARRAY(int, node_offset_limit);}if (node_offsets != NULL) {// We need to initialize. Unused array elements may contain garbage and mess up PrintOptoAssembly.memset(node_offsets, 0, node_offset_limit*sizeof(int));}
#endifNonSafepointEmitter non_safepoints(C);  // emit non-safepoints lazily// Emit the constant table.if (C->has_mach_constant_base_node()) {if (!constant_table().emit(*cb)) {C->record_failure("consts section overflow");return;}}// Create an array of labels, one for each basic blockLabel* blk_labels = NEW_RESOURCE_ARRAY(Label, nblocks+1);for (uint i = 0; i <= nblocks; i++) {blk_labels[i].init();}// Now fill in the code bufferNode* delay_slot = NULL;for (uint i = 0; i < nblocks; i++) {Block* block = C->cfg()->get_block(i);_block = block;Node* head = block->head();// If this block needs to start aligned (i.e, can be reached other// than by falling-thru from the previous block), then force the// start of a new bundle.if (Pipeline::requires_bundling() && starts_bundle(head)) {cb->flush_bundle(true);}#ifdef ASSERTif (!block->is_connector()) {stringStream st;block->dump_head(C->cfg(), &st);MacroAssembler(cb).block_comment(st.as_string());}jmp_target[i] = 0;jmp_offset[i] = 0;jmp_size[i]   = 0;jmp_rule[i]   = 0;
#endifint blk_offset = current_offset;// Define the label at the beginning of the basic blockMacroAssembler(cb).bind(blk_labels[block->_pre_order]);uint last_inst = block->number_of_nodes();// Emit block normally, except for last instruction.// Emit means "dump code bits into code buffer".for (uint j = 0; j<last_inst; j++) {_index = j;// Get the nodeNode* n = block->get_node(j);// See if delay slots are supportedif (valid_bundle_info(n) && node_bundling(n)->used_in_unconditional_delay()) {assert(delay_slot == NULL, "no use of delay slot node");assert(n->size(C->regalloc()) == Pipeline::instr_unit_size(), "delay slot instruction wrong size");delay_slot = n;continue;}// If this starts a new instruction group, then flush the current one// (but allow split bundles)if (Pipeline::requires_bundling() && starts_bundle(n))cb->flush_bundle(false);// Special handling for SafePoint/Call Nodesbool is_mcall = false;if (n->is_Mach()) {MachNode *mach = n->as_Mach();is_mcall = n->is_MachCall();bool is_sfn = n->is_MachSafePoint();// If this requires all previous instructions be flushed, then do soif (is_sfn || is_mcall || mach->alignment_required() != 1) {cb->flush_bundle(true);current_offset = cb->insts_size();}// A padding may be needed again since a previous instruction// could be moved to delay slot.// align the instruction if necessaryint padding = mach->compute_padding(current_offset);// Make sure safepoint node for polling is distinct from a call's// return by adding a nop if needed.if (is_sfn && !is_mcall && padding == 0 && current_offset == last_call_offset) {padding = nop_size;}if (padding == 0 && mach->avoid_back_to_back(MachNode::AVOID_BEFORE) &&current_offset == last_avoid_back_to_back_offset) {// Avoid back to back some instructions.padding = nop_size;}if (padding > 0) {assert((padding % nop_size) == 0, "padding is not a multiple of NOP size");int nops_cnt = padding / nop_size;MachNode *nop = new MachNopNode(nops_cnt);block->insert_node(nop, j++);last_inst++;C->cfg()->map_node_to_block(nop, block);// Ensure enough space.cb->insts()->maybe_expand_to_ensure_remaining(MAX_inst_size);if ((cb->blob() == NULL) || (!CompileBroker::should_compile_new_jobs())) {C->record_failure("CodeCache is full");return;}nop->emit(*cb, C->regalloc());cb->flush_bundle(true);current_offset = cb->insts_size();}bool observe_safepoint = is_sfn;// Remember the start of the last call in a basic blockif (is_mcall) {MachCallNode *mcall = mach->as_MachCall();// This destination address is NOT PC-relativemcall->method_set((intptr_t)mcall->entry_point());// Save the return addresscall_returns[block->_pre_order] = current_offset + mcall->ret_addr_offset();observe_safepoint = mcall->guaranteed_safepoint();}// sfn will be valid whenever mcall is valid now because of inheritanceif (observe_safepoint) {// Handle special safepoint nodes for synchronizationif (!is_mcall) {MachSafePointNode *sfn = mach->as_MachSafePoint();// !!!!! Stubs only need an oopmap right now, so bail outif (sfn->jvms()->method() == NULL) {// Write the oopmap directly to the code blob??!!continue;}} // End synchronizationnon_safepoints.observe_safepoint(mach->as_MachSafePoint()->jvms(),current_offset);Process_OopMap_Node(mach, current_offset);} // End if safepoint// If this is a null check, then add the start of the previous instruction to the listelse if( mach->is_MachNullCheck() ) {inct_starts[inct_cnt++] = previous_offset;}// If this is a branch, then fill in the label with the target BB's labelelse if (mach->is_MachBranch()) {// This requires the TRUE branch target be in succs[0]uint block_num = block->non_connector_successor(0)->_pre_order;// Try to replace long branch if delay slot is not used,// it is mostly for back branches since forward branch's// distance is not updated yet.bool delay_slot_is_used = valid_bundle_info(n) &&C->output()->node_bundling(n)->use_unconditional_delay();if (!delay_slot_is_used && mach->may_be_short_branch()) {assert(delay_slot == NULL, "not expecting delay slot node");int br_size = n->size(C->regalloc());int offset = blk_starts[block_num] - current_offset;if (block_num >= i) {// Current and following block's offset are not// finalized yet, adjust distance by the difference// between calculated and final offsets of current block.offset -= (blk_starts[i] - blk_offset);}// In the following code a nop could be inserted before// the branch which will increase the backward distance.bool needs_padding = (current_offset == last_avoid_back_to_back_offset);if (needs_padding && offset <= 0)offset -= nop_size;if (C->matcher()->is_short_branch_offset(mach->rule(), br_size, offset)) {// We've got a winner.  Replace this branch.MachNode* replacement = mach->as_MachBranch()->short_branch_version();// Update the jmp_size.int new_size = replacement->size(C->regalloc());assert((br_size - new_size) >= (int)nop_size, "short_branch size should be smaller");// Insert padding between avoid_back_to_back branches.if (needs_padding && replacement->avoid_back_to_back(MachNode::AVOID_BEFORE)) {MachNode *nop = new MachNopNode();block->insert_node(nop, j++);C->cfg()->map_node_to_block(nop, block);last_inst++;nop->emit(*cb, C->regalloc());cb->flush_bundle(true);current_offset = cb->insts_size();}
#ifdef ASSERTjmp_target[i] = block_num;jmp_offset[i] = current_offset - blk_offset;jmp_size[i]   = new_size;jmp_rule[i]   = mach->rule();
#endifblock->map_node(replacement, j);mach->subsume_by(replacement, C);n    = replacement;mach = replacement;}}mach->as_MachBranch()->label_set( &blk_labels[block_num], block_num );} else if (mach->ideal_Opcode() == Op_Jump) {for (uint h = 0; h < block->_num_succs; h++) {Block* succs_block = block->_succs[h];for (uint j = 1; j < succs_block->num_preds(); j++) {Node* jpn = succs_block->pred(j);if (jpn->is_JumpProj() && jpn->in(0) == mach) {uint block_num = succs_block->non_connector()->_pre_order;Label *blkLabel = &blk_labels[block_num];mach->add_case_label(jpn->as_JumpProj()->proj_no(), blkLabel);}}}}
#ifdef ASSERT// Check that oop-store precedes the card-markelse if (mach->ideal_Opcode() == Op_StoreCM) {uint storeCM_idx = j;int count = 0;for (uint prec = mach->req(); prec < mach->len(); prec++) {Node *oop_store = mach->in(prec);  // Precedence edgeif (oop_store == NULL) continue;count++;uint i4;for (i4 = 0; i4 < last_inst; ++i4) {if (block->get_node(i4) == oop_store) {break;}}// Note: This test can provide a false failure if other precedence// edges have been added to the storeCMNode.assert(i4 == last_inst || i4 < storeCM_idx, "CM card-mark executes before oop-store");}assert(count > 0, "storeCM expects at least one precedence edge");}
#endifelse if (!n->is_Proj()) {// Remember the beginning of the previous instruction, in case// it's followed by a flag-kill and a null-check.  Happens on// Intel all the time, with add-to-memory kind of opcodes.previous_offset = current_offset;}// Not an else-if!// If this is a trap based cmp then add its offset to the list.if (mach->is_TrapBasedCheckNode()) {inct_starts[inct_cnt++] = current_offset;}}// Verify that there is sufficient space remainingcb->insts()->maybe_expand_to_ensure_remaining(MAX_inst_size);if ((cb->blob() == NULL) || (!CompileBroker::should_compile_new_jobs())) {C->record_failure("CodeCache is full");return;}// Save the offset for the listing
#if defined(SUPPORT_OPTO_ASSEMBLY)if ((node_offsets != NULL) && (n->_idx < node_offset_limit)) {node_offsets[n->_idx] = cb->insts_size();}
#endifassert(!C->failing(), "Should not reach here if failing.");// "Normal" instruction caseDEBUG_ONLY(uint instr_offset = cb->insts_size());n->emit(*cb, C->regalloc());current_offset = cb->insts_size();// Above we only verified that there is enough space in the instruction section.// However, the instruction may emit stubs that cause code buffer expansion.// Bail out here if expansion failed due to a lack of code cache space.if (C->failing()) {return;}assert(!is_mcall || (call_returns[block->_pre_order] <= (uint)current_offset),"ret_addr_offset() not within emitted code");#ifdef ASSERTuint n_size = n->size(C->regalloc());if (n_size < (current_offset-instr_offset)) {MachNode* mach = n->as_Mach();n->dump();mach->dump_format(C->regalloc(), tty);tty->print_cr(" n_size (%d), current_offset (%d), instr_offset (%d)", n_size, current_offset, instr_offset);Disassembler::decode(cb->insts_begin() + instr_offset, cb->insts_begin() + current_offset + 1, tty);tty->print_cr(" ------------------- ");BufferBlob* blob = this->scratch_buffer_blob();address blob_begin = blob->content_begin();Disassembler::decode(blob_begin, blob_begin + n_size + 1, tty);assert(false, "wrong size of mach node");}
#endifnon_safepoints.observe_instruction(n, current_offset);// mcall is last "call" that can be a safepoint// record it so we can see if a poll will directly follow it// in which case we'll need a pad to make the PcDesc sites unique// see  5010568. This can be slightly inaccurate but conservative// in the case that return address is not actually at current_offset.// This is a small price to pay.if (is_mcall) {last_call_offset = current_offset;}if (n->is_Mach() && n->as_Mach()->avoid_back_to_back(MachNode::AVOID_AFTER)) {// Avoid back to back some instructions.last_avoid_back_to_back_offset = current_offset;}// See if this instruction has a delay slotif (valid_bundle_info(n) && node_bundling(n)->use_unconditional_delay()) {guarantee(delay_slot != NULL, "expecting delay slot node");// Back up 1 instructioncb->set_insts_end(cb->insts_end() - Pipeline::instr_unit_size());// Save the offset for the listing
#if defined(SUPPORT_OPTO_ASSEMBLY)if ((node_offsets != NULL) && (delay_slot->_idx < node_offset_limit)) {node_offsets[delay_slot->_idx] = cb->insts_size();}
#endif// Support a SafePoint in the delay slotif (delay_slot->is_MachSafePoint()) {MachNode *mach = delay_slot->as_Mach();// !!!!! Stubs only need an oopmap right now, so bail outif (!mach->is_MachCall() && mach->as_MachSafePoint()->jvms()->method() == NULL) {// Write the oopmap directly to the code blob??!!delay_slot = NULL;continue;}int adjusted_offset = current_offset - Pipeline::instr_unit_size();non_safepoints.observe_safepoint(mach->as_MachSafePoint()->jvms(),adjusted_offset);// Generate an OopMap entryProcess_OopMap_Node(mach, adjusted_offset);}// Insert the delay slot instructiondelay_slot->emit(*cb, C->regalloc());// Don't reuse itdelay_slot = NULL;}} // End for all instructions in block// If the next block is the top of a loop, pad this block out to align// the loop top a little. Helps prevent pipe stalls at loop back branches.if (i < nblocks-1) {Block *nb = C->cfg()->get_block(i + 1);int padding = nb->alignment_padding(current_offset);if( padding > 0 ) {MachNode *nop = new MachNopNode(padding / nop_size);block->insert_node(nop, block->number_of_nodes());C->cfg()->map_node_to_block(nop, block);nop->emit(*cb, C->regalloc());current_offset = cb->insts_size();}}// Verify that the distance for generated before forward// short branches is still valid.guarantee((int)(blk_starts[i+1] - blk_starts[i]) >= (current_offset - blk_offset), "shouldn't increase block size");// Save new block start offsetblk_starts[i] = blk_offset;} // End of for all blocksblk_starts[nblocks] = current_offset;non_safepoints.flush_at_end();// Offset too large?if (C->failing())  return;// Define a pseudo-label at the end of the codeMacroAssembler(cb).bind( blk_labels[nblocks] );// Compute the size of the first block_first_block_size = blk_labels[1].loc_pos() - blk_labels[0].loc_pos();#ifdef ASSERTfor (uint i = 0; i < nblocks; i++) { // For all blocksif (jmp_target[i] != 0) {int br_size = jmp_size[i];int offset = blk_starts[jmp_target[i]]-(blk_starts[i] + jmp_offset[i]);if (!C->matcher()->is_short_branch_offset(jmp_rule[i], br_size, offset)) {tty->print_cr("target (%d) - jmp_offset(%d) = offset (%d), jump_size(%d), jmp_block B%d, target_block B%d", blk_starts[jmp_target[i]], blk_starts[i] + jmp_offset[i], offset, br_size, i, jmp_target[i]);assert(false, "Displacement too large for short jmp");}}}
#endifBarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();bs->emit_stubs(*cb);if (C->failing())  return;// Fill in stubs for calling the runtime from safepoint polls.safepoint_poll_table()->emit(*cb);if (C->failing())  return;#ifndef PRODUCT// Information on the size of the method, without the extraneous codeScheduling::increment_method_size(cb->insts_size());
#endif// ------------------// Fill in exception table entries.FillExceptionTables(inct_cnt, call_returns, inct_starts, blk_labels);// Only java methods have exception handlers and deopt handlers// class HandlerImpl is platform-specific and defined in the *.ad files.if (C->method()) {// Emit the exception handler code._code_offsets.set_value(CodeOffsets::Exceptions, HandlerImpl::emit_exception_handler(*cb));if (C->failing()) {return; // CodeBuffer::expand failed}// Emit the deopt handler code._code_offsets.set_value(CodeOffsets::Deopt, HandlerImpl::emit_deopt_handler(*cb));// Emit the MethodHandle deopt handler code (if required).if (C->has_method_handle_invokes() && !C->failing()) {// We can use the same code as for the normal deopt handler, we// just need a different entry point address._code_offsets.set_value(CodeOffsets::DeoptMH, HandlerImpl::emit_deopt_handler(*cb));}}// One last check for failed CodeBuffer::expand:if ((cb->blob() == NULL) || (!CompileBroker::should_compile_new_jobs())) {C->record_failure("CodeCache is full");return;}#if defined(SUPPORT_ABSTRACT_ASSEMBLY) || defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_OPTO_ASSEMBLY)if (C->print_assembly()) {tty->cr();tty->print_cr("============================= C2-compiled nmethod ==============================");}
#endif#if defined(SUPPORT_OPTO_ASSEMBLY)// Dump the assembly code, including basic-block numbersif (C->print_assembly()) {ttyLocker ttyl;  // keep the following output all in one blockif (!VMThread::should_terminate()) {  // test this under the tty lock// This output goes directly to the tty, not the compiler log.// To enable tools to match it up with the compilation activity,// be sure to tag this tty output with the compile ID.if (xtty != NULL) {xtty->head("opto_assembly compile_id='%d'%s", C->compile_id(),C->is_osr_compilation() ? " compile_kind='osr'" : "");}if (C->method() != NULL) {tty->print_cr("----------------------- MetaData before Compile_id = %d ------------------------", C->compile_id());C->method()->print_metadata();} else if (C->stub_name() != NULL) {tty->print_cr("----------------------------- RuntimeStub %s -------------------------------", C->stub_name());}tty->cr();tty->print_cr("------------------------ OptoAssembly for Compile_id = %d -----------------------", C->compile_id());dump_asm(node_offsets, node_offset_limit);tty->print_cr("--------------------------------------------------------------------------------");if (xtty != NULL) {// print_metadata and dump_asm above may safepoint which makes us loose the ttylock.// Retake lock too make sure the end tag is coherent, and that xmlStream->pop_tag is done// thread safettyLocker ttyl2;xtty->tail("opto_assembly");}}}
#endif
}  

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/93319.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/93319.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/93319.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【論文筆記】STORYWRITER: A Multi-Agent Framework for Long Story Generation

論文信息 論文標題&#xff1a;StoryWriter: A Multi-Agent Framework for Long Story Generation 論文作者&#xff1a;Haotian Xia, Hao Peng et al. (Tsinghua University) 論文鏈接&#xff1a;https://arxiv.org/abs/2506.16445 代碼鏈接&#xff1a;https://github.com/…

Cohere 開發企業級大型語言模型(LLM)

Cohere 是一家專注于開發企業級大型語言模型&#xff08;LLM&#xff09;的公司。該公司推出了一系列名為 “Command” 的模型&#xff0c;其中最強大的 “Command A” 于今年三月首次亮相 Cohere 還提供嵌入模型&#xff0c;這是一種將文件轉化為神經網絡可以理解的緊湊數值形…

Rust Web框架Axum學習指南之入門初體驗

一、準備階段 確保已經安裝 rust&#xff0c;開發環境使用 vscode 或者 rustrover 都可以。接著就可以創建項目&#xff0c;通過編輯器創建或者命令行創建都可以&#xff1a; cargo new axum-admin二、添加依賴 添加依賴如下&#xff1a; [package] name "axum-admin&quo…

autofit.js: 自動調整HTML元素大小的JavaScript庫

&#x1f90d; 前端開發工程師、技術日更博主、已過CET6 &#x1f368; 阿珊和她的貓_CSDN博客專家、23年度博客之星前端領域TOP1 &#x1f560; 牛客高級專題作者、打造專欄《前端面試必備》 、《2024面試高頻手撕題》、《前端求職突破計劃》 &#x1f35a; 藍橋云課簽約作者、…

RocketMQ 命名服務器(NameServer)詳解

&#x1f680; RocketMQ 命名服務器&#xff08;NameServer&#xff09;詳解 NameServer 是 RocketMQ 架構中的輕量級路由發現服務&#xff0c;它不參與消息的收發&#xff0c;但承擔著整個集群的“地址簿”和“導航系統”的關鍵角色。 理解 NameServer 的設計與工作原理&#…

代碼隨想錄算法訓練營四十三天|圖論part01

深度優先搜索&#xff08;dfs&#xff09;理論基礎 dfs就是可一個方向去搜直到盡頭再換方向&#xff0c;換方向的過程就涉及到了回溯。 代碼框架 因為dfs搜索可一個方向&#xff0c;并需要回溯&#xff0c;所以用遞歸的方式來實現是最方便的。 先來回顧一下回溯法的代碼框架…

飛算JavaAI金融風控場景實踐:從實時監測到智能決策的全鏈路安全防護

目錄一、金融風控核心場景的技術突破1.1 實時交易風險監測系統1.1.1 高并發交易數據處理1.2 智能反欺詐系統架構1.2.1 多維度欺詐風險識別1.3 動態風控規則引擎1.3.1 風控規則動態管理二、金融風控系統效能升級實踐2.1 風控模型迭代加速機制2.1.1 自動化特征工程結語&#xff1…

Vue 組件二次封裝透傳slots、refs、attrs、listeners

最近寫了一個開源項目&#xff0c;有些地方需要二次封裝&#xff0c;需要透傳一些數據&#xff0c;需要注意的是ref&#xff0c;我這里使用倆種方式間接傳遞ref&#xff0c;具體如下&#xff1a; 使用&#xff1a; import VideoPlayer from ./index.jsVue.use(VideoPlayer)inde…

介紹大根堆小根堆

文章目錄一、核心定義與結構特性示例&#xff08;以“數組存儲堆”為例&#xff09;二、堆的兩個核心操作1. 插入操作&#xff08;以小根堆為例&#xff09;2. 刪除極值操作&#xff08;以小根堆為例&#xff0c;刪除根節點的最小值&#xff09;三、小根堆 vs 大根堆&#xff1…

【Html網頁模板】賽博朋克數據分析大屏網頁

目錄專欄導讀? 項目概述&#x1f3a8; 設計理念&#x1f6e0;? 技術架構核心技術棧設計模式&#x1f3af; 核心功能1. 視覺效果系統&#x1f308; 色彩體系2. 數據可視化模塊&#x1f4ca; 主圖表系統&#x1f4c8; 性能監控面板3. 實時數據流系統? 數據流動畫&#x1f4ca;…

【經典上穿突破】副圖/選股指標,雙均線交叉原理,對價格波動反應靈敏,適合捕捉短期啟動點

【經典上穿突破】副圖/選股指標&#xff0c;雙均線交叉原理&#xff0c;對價格波動反應靈敏&#xff0c;適合捕捉短期啟動點 這是一款結合短線與中線信號的趨勢跟蹤指標&#xff0c;通過雙均線交叉原理捕捉股價突破時機&#xff0c;適用于個股分析和盤中選股。 核心功能模塊&…

RK3568 NPU RKNN(四):RKNN-ToolKit2性能和內存評估

文章目錄1、前言2、目標3、完整的測試程序4、運行測試程序5、程序拆解6、總結1、前言 本文僅記錄本人學習過程&#xff0c;不具備教學指導意義。 2、目標 使用野火提供的示例程序&#xff0c;體驗 RKNN-ToolKit2 在PC端使用連板推理&#xff0c;進行性能和內存評估。 3、完…

ASP.NET 上傳文件安全檢測方案

一、前端初步過濾&#xff08;防誤操作&#xff09;<!-- HTML部分 --><input type"file" id"fileUpload" accept".jpg,.png,.pdf,.docx" /><button onclick"validateFile()">上傳</button><script>func…

Nacos Server 3.0.x安裝教程

前言 注&#xff1a; 1.Nacos Server 3.0.x 要求 JDK版本不低于17。 2.Nacos 2.2.0 及以上版本需要 Java 11 或更高版本。 3.Java 8&#xff0c;需要下載 Nacos 2.1.x 及以下版本 JDK17安裝 JDK官方下載地址&#xff1a;Oracle官網JDK下載地址 JDK17&#xff1a;JDK17下載地…

【數據庫干貨】六大范式速記

1NF、2NF、3NF、BCNF、4NF、5NF都是數據庫設計中的范式&#xff08;Normalization&#xff09;&#xff0c;用于確保數據庫中的數據結構盡可能地減少冗余&#xff0c;避免更新異常、插入異常、刪除異常等問題&#xff0c;從而提高數據的存儲效率和一致性。 本篇文章簡單講解下各…

Java開發主流框架搭配詳解及學習路線指南

文章目錄一、前言&#x1f517;二、主流Java框架搭配2.1 Spring Boot MyBatis-Plus Spring Cloud2.2 Spring Boot Spring Data JPA Spring Cloud2.3 Quarkus/Vert.x (響應式編程棧)三、技術選型建議四、Java學習路線指南階段1&#xff1a;Java基礎 (4-6周)階段2&#xff1a…

flutter-使用device_info_plus獲取手機設備信息完整指南

文章目錄1. 概述2. 安裝與配置3. 基本使用方法3.1. 創建實例3.2. 區分平臺獲取信息4. 詳細信息獲取4.1. Android 設備信息4.2. iOS 設備信息4.3. Web 瀏覽器信息4.4. Windows 設備信息5. 實戰示例6. 注意事項6.1. 權限問題6.2. 隱私保護6.3. 平臺差異處理6.4. 性能考慮7. 常見問…

Java 時間處理 API 全解析:從 JDK7 到 JDK8 的演進

個人主頁-愛因斯晨 友友們&#xff0c;互三咯~ 目錄 個人主頁-愛因斯晨 ?編輯 前言 一、JDK7 時間處理基石 ——Date 類 &#xff08;一&#xff09;Date 類基本功能 &#xff08;二&#xff09;Date 類的局限性 二、格式化時間好幫手 ——SimpleDateFormat 類 &#…

duiLib 實現鼠標拖動標題欄時,窗口跟著拖動

1、布局文件&#xff0c;窗口需設置可拖動的標題欄區域&#xff1a;2、HandleMessage函數中&#xff0c;處理WM_LBUTTONDOWN消息&#xff0c;判斷鼠標在標題欄&#xff0c;讓系統處理窗口移動。代碼片段如下&#xff1a;else if (uMsg WM_LBUTTONDOWN) {// 獲取鼠標點擊坐標PO…

圖解嵌入式硬件知識庫體系

構建一個嵌入式硬件知識庫體系需要涵蓋嵌入式系統設計、開發和應用的各個方面,內容全面且系統化,適合不同層次的用戶。本文是一個結構化的嵌入式硬件知識庫體系,包含主要內容模塊及其詳細說明。 @startmindmap * 嵌入式硬件知識庫體系 ** 1. 嵌入式系統基礎 *** 概述與定義 …