Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 11 additions & 28 deletions src/hotspot/cpu/aarch64/aarch64.ad
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//
// Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
// Copyright (c) 2014, 2024, Red Hat, Inc. All rights reserved.
// Copyright 2025 Arm Limited and/or its affiliates.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
// This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -1194,15 +1195,10 @@ class HandlerImpl {

public:

static int emit_exception_handler(C2_MacroAssembler *masm);
static int emit_deopt_handler(C2_MacroAssembler* masm);

static uint size_exception_handler() {
return MacroAssembler::far_codestub_branch_size();
}

static uint size_deopt_handler() {
// count one adr and one far branch instruction
// count one branch instruction and one far call instruction sequence
return NativeInstruction::instruction_size + MacroAssembler::far_codestub_branch_size();
}
};
Expand Down Expand Up @@ -2261,25 +2257,6 @@ uint MachUEPNode::size(PhaseRegAlloc* ra_) const

//=============================================================================

// Emit exception handler code.
int HandlerImpl::emit_exception_handler(C2_MacroAssembler* masm)
{
// mov rscratch1 #exception_blob_entry_point
// br rscratch1
// Note that the code buffer's insts_mark is always relative to insts.
// That's why we must use the macroassembler to generate a handler.
address base = __ start_a_stub(size_exception_handler());
if (base == nullptr) {
ciEnv::current()->record_failure("CodeCache is full");
return 0; // CodeBuffer::expand failed
}
int offset = __ offset();
__ far_jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point()));
assert(__ offset() - offset <= (int) size_exception_handler(), "overflow");
__ end_a_stub();
return offset;
}

// Emit deopt handler code.
int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm)
{
Expand All @@ -2290,14 +2267,20 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm)
ciEnv::current()->record_failure("CodeCache is full");
return 0; // CodeBuffer::expand failed
}

int offset = __ offset();
Label start;
__ bind(start);
__ far_call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));

__ adr(lr, __ pc());
__ far_jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
int entry_offset = __ offset();
__ b(start);

assert(__ offset() - offset == (int) size_deopt_handler(), "overflow");
assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size,
"out of bounds read in post-call NOP check");
__ end_a_stub();
return offset;
return entry_offset;
}

// REQUIRED MATCHER CODE
Expand Down
14 changes: 11 additions & 3 deletions src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,12 +449,20 @@ int LIR_Assembler::emit_deopt_handler() {

int offset = code_offset();

__ adr(lr, pc());
__ far_jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
Label start;
__ bind(start);

__ far_call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));

int entry_offset = __ offset();
__ b(start);

guarantee(code_offset() - offset <= deopt_handler_size(), "overflow");
assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size,
"out of bounds read in post-call NOP check");
__ end_a_stub();

return offset;
return entry_offset;
}

void LIR_Assembler::add_debug_info_for_branch(address adr, CodeEmitInfo* info) {
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ friend class ArrayCopyStub;
// CompiledDirectCall::to_trampoline_stub_size()
_call_stub_size = 13 * NativeInstruction::instruction_size,
_exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(175),
_deopt_handler_size = 7 * NativeInstruction::instruction_size
_deopt_handler_size = 4 * NativeInstruction::instruction_size
};

public:
Expand Down
6 changes: 0 additions & 6 deletions src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,12 +394,6 @@ void NativePostCallNop::make_deopt() {
NativeDeoptInstruction::insert(addr_at(0));
}

#ifdef ASSERT
static bool is_movk_to_zr(uint32_t insn) {
return ((insn & 0xffe0001f) == 0xf280001f);
}
#endif

bool NativePostCallNop::patch(int32_t oopmap_slot, int32_t cb_offset) {
if (((oopmap_slot & 0xff) != oopmap_slot) || ((cb_offset & 0xffffff) != cb_offset)) {
return false; // cannot encode
Expand Down
29 changes: 23 additions & 6 deletions src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,14 +526,31 @@ inline NativeLdSt* NativeLdSt_at(address addr) {
// can store an offset from the initial nop to the nmethod.

class NativePostCallNop: public NativeInstruction {
private:
static bool is_movk_to_zr(uint32_t insn) {
return ((insn & 0xffe0001f) == 0xf280001f);
}

public:
enum AArch64_specific_constants {
// The two parts should be checked separately to prevent out of bounds access in case
// the return address points to the deopt handler stub code entry point which could be
// at the end of page.
first_check_size = 4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
first_check_size = 4
first_check_size = instruction_size

};

bool check() const {
uint64_t insns = *(uint64_t*)addr_at(0);
// Check for two instructions: nop; movk zr, xx
// These instructions only ever appear together in a post-call
// NOP, so it's unnecessary to check that the third instruction is
// a MOVK as well.
return (insns & 0xffe0001fffffffff) == 0xf280001fd503201f;
// Check the first instruction is NOP.
if (is_nop()) {
uint32_t insn = *(uint32_t*)addr_at(first_check_size);
// Check next instruction is MOVK zr, xx.
// These instructions only ever appear together in a post-call
// NOP, so it's unnecessary to check that the third instruction is
// a MOVK as well.
return is_movk_to_zr(insn);
}

return false;
}

bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const {
Expand Down
2 changes: 0 additions & 2 deletions src/hotspot/cpu/aarch64/runtime_aarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,6 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() {

//------------------------------generate_exception_blob---------------------------
// creates exception blob at the end
// Using exception blob, this code is jumped from a compiled method.
// (see emit_exception_handler in aarch64.ad file)
//
// Given an exception pc at a call we call into the runtime for the
// handler in this method. This handler might merely restore state
Expand Down
49 changes: 16 additions & 33 deletions src/hotspot/cpu/arm/arm.ad
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,8 @@ class HandlerImpl {

public:

static int emit_exception_handler(C2_MacroAssembler *masm);
static int emit_deopt_handler(C2_MacroAssembler* masm);

static uint size_exception_handler() {
return ( 3 * 4 );
}


static uint size_deopt_handler() {
return ( 9 * 4 );
}
Expand Down Expand Up @@ -876,26 +870,6 @@ uint MachUEPNode::size(PhaseRegAlloc *ra_) const {

//=============================================================================

// Emit exception handler code.
int HandlerImpl::emit_exception_handler(C2_MacroAssembler* masm) {
address base = __ start_a_stub(size_exception_handler());
if (base == nullptr) {
ciEnv::current()->record_failure("CodeCache is full");
return 0; // CodeBuffer::expand failed
}

int offset = __ offset();

// OK to trash LR, because exception blob will kill it
__ jump(OptoRuntime::exception_blob()->entry_point(), relocInfo::runtime_call_type, LR_tmp);

assert(__ offset() - offset <= (int) size_exception_handler(), "overflow");

__ end_a_stub();

return offset;
}

int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) {
// Can't use any of the current frame's registers as we may have deopted
// at a poll and everything can be live.
Expand All @@ -906,19 +880,28 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) {
}

int offset = __ offset();
address deopt_pc = __ pc();

__ sub(SP, SP, wordSize); // make room for saved PC
__ push(LR); // save LR that may be live when we get here
__ mov_relative_address(LR, deopt_pc);
__ str(LR, Address(SP, wordSize)); // save deopt PC
__ pop(LR); // restore LR
Label start;
__ bind(start);

__ jump(SharedRuntime::deopt_blob()->unpack(), relocInfo::runtime_call_type, noreg);

int entry_offset = __ offset();
address deopt_pc = __ pc();
// Preserve R0 and reserve space for the address of the entry point
__ push(RegisterSet(R0) | RegisterSet(R1));
// Store the entry point address
__ mov_relative_address(R0, deopt_pc);
__ str(R0, Address(SP, wordSize));
__ pop(R0); // restore R0
__ b(start);

assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow");
assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size,
"out of bounds read in post-call NOP check");

__ end_a_stub();
return offset;
return entry_offset;
}

bool Matcher::match_rule_supported(int opcode) {
Expand Down
12 changes: 10 additions & 2 deletions src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,14 +272,22 @@ int LIR_Assembler::emit_deopt_handler() {

int offset = code_offset();

Label start;
__ bind(start);

__ jump(SharedRuntime::deopt_blob()->unpack(), relocInfo::runtime_call_type, noreg);

int entry_offset = __ offset();
__ mov_relative_address(LR, __ pc());
__ push(LR); // stub expects LR to be saved
__ jump(SharedRuntime::deopt_blob()->unpack(), relocInfo::runtime_call_type, noreg);
__ b(start);

assert(code_offset() - offset <= deopt_handler_size(), "overflow");
assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size,
"out of bounds read in post-call NOP check");
__ end_a_stub();

return offset;
return entry_offset;
}


Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/cpu/arm/c1_LIRAssembler_arm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
enum {
_call_stub_size = 16,
_exception_handler_size = PRODUCT_ONLY(68) NOT_PRODUCT(68+60),
_deopt_handler_size = 16
_deopt_handler_size = 20
};

public:
Expand Down
7 changes: 7 additions & 0 deletions src/hotspot/cpu/arm/nativeInst_arm_32.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,13 @@ inline NativeCall* nativeCall_before(address return_address) {

class NativePostCallNop: public NativeInstruction {
public:
enum arm_specific_constants {
// If the check is adjusted to read beyond size of the instruction sequence at the deopt
// handler stub code entry point, it has to happen in two stages - to prevent out of bounds
// access in case the return address points to the entry point which could be at
// the end of page.
first_check_size = 4
};
bool check() const { return is_nop(); }
bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const { return false; }
bool patch(int32_t oopmap_slot, int32_t cb_offset) { return false; }
Expand Down
2 changes: 0 additions & 2 deletions src/hotspot/cpu/arm/runtime_arm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,6 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() {

//------------------------------ generate_exception_blob ---------------------------
// creates exception blob at the end
// Using exception blob, this code is jumped from a compiled method.
// (see emit_exception_handler in sparc.ad file)
//
// Given an exception pc at a call we call into the runtime for the
// handler in this method. This handler might merely restore state
Expand Down
9 changes: 8 additions & 1 deletion src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,12 +264,19 @@ int LIR_Assembler::emit_deopt_handler() {
}

int offset = code_offset();
Label start;

__ bind(start);
__ bl64_patchable(SharedRuntime::deopt_blob()->unpack(), relocInfo::runtime_call_type);
int entry_offset = __ offset();
__ b(start);

guarantee(code_offset() - offset <= deopt_handler_size(), "overflow");
assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size,
"out of bounds read in post-call NOP check");
__ end_a_stub();

return offset;
return entry_offset;
}


Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ enum {
_static_call_stub_size = 4 * BytesPerInstWord + MacroAssembler::b64_patchable_size, // or smaller
_call_stub_size = _static_call_stub_size + MacroAssembler::trampoline_stub_size, // or smaller
_exception_handler_size = MacroAssembler::b64_patchable_size, // or smaller
_deopt_handler_size = MacroAssembler::bl64_patchable_size
_deopt_handler_size = MacroAssembler::bl64_patchable_size + BytesPerInstWord
};

// '_static_call_stub_size' is only used on ppc (see LIR_Assembler::emit_static_call_stub()
Expand Down
10 changes: 8 additions & 2 deletions src/hotspot/cpu/ppc/nativeInst_ppc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ class NativeInstruction {
friend class Relocation;

public:
bool is_post_call_nop() const { return MacroAssembler::is_post_call_nop(long_at(0)); }

bool is_jump() const { return Assembler::is_b(long_at(0)); } // See NativeGeneralJump.

bool is_sigtrap_ic_miss_check() {
Expand Down Expand Up @@ -531,6 +529,14 @@ class NativePostCallNop: public NativeInstruction {
};

public:
enum ppc_specific_constants {
// If the check is adjusted to read beyond size of the instruction at the deopt handler stub
// code entry point, it has to happen in two stages - to prevent out of bounds access in case
// the return address points to the entry point which could be at the end of page.
first_check_size = 4
};

bool is_post_call_nop() const { return MacroAssembler::is_post_call_nop(long_at(0)); }
bool check() const { return is_post_call_nop(); }
bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const {
uint32_t instr_bits = long_at(0);
Expand Down
Loading