8369211: AArch64: Devirtualize class RelocActions

Reviewed-by: adinn, asmehra
This commit is contained in:
Andrew Haley 2025-10-21 14:27:02 +00:00
parent b77b9103c3
commit 9a88d7f468
2 changed files with 81 additions and 120 deletions

View File

@ -148,56 +148,34 @@ extern "C" void disnm(intptr_t p);
// strictly should be 64 bit movz #imm16<<0
// 110___10100 (i.e. requires insn[31:21] == 11010010100)
//
class RelocActions {
protected:
typedef int (*reloc_insn)(address insn_addr, address &target);
virtual reloc_insn adrpMem() = 0;
virtual reloc_insn adrpAdd() = 0;
virtual reloc_insn adrpMovk() = 0;
static uint32_t insn_at(address insn_addr, int n) {
return ((uint32_t*)insn_addr)[n];
}
const address _insn_addr;
const uint32_t _insn;
static uint32_t insn_at(address insn_addr, int n) {
return ((uint32_t*)insn_addr)[n];
}
uint32_t insn_at(int n) const {
return insn_at(_insn_addr, n);
}
template<typename T>
class RelocActions : public AllStatic {
public:
RelocActions(address insn_addr) : _insn_addr(insn_addr), _insn(insn_at(insn_addr, 0)) {}
RelocActions(address insn_addr, uint32_t insn)
: _insn_addr(insn_addr), _insn(insn) {}
virtual int unconditionalBranch(address insn_addr, address &target) = 0;
virtual int conditionalBranch(address insn_addr, address &target) = 0;
virtual int testAndBranch(address insn_addr, address &target) = 0;
virtual int loadStore(address insn_addr, address &target) = 0;
virtual int adr(address insn_addr, address &target) = 0;
virtual int adrp(address insn_addr, address &target, reloc_insn inner) = 0;
virtual int immediate(address insn_addr, address &target) = 0;
virtual void verify(address insn_addr, address &target) = 0;
int ALWAYSINLINE run(address insn_addr, address &target) {
static int ALWAYSINLINE run(address insn_addr, address &target) {
int instructions = 1;
uint32_t insn = insn_at(insn_addr, 0);
uint32_t dispatch = Instruction_aarch64::extract(_insn, 30, 25);
uint32_t dispatch = Instruction_aarch64::extract(insn, 30, 25);
switch(dispatch) {
case 0b001010:
case 0b001011: {
instructions = unconditionalBranch(insn_addr, target);
instructions = T::unconditionalBranch(insn_addr, target);
break;
}
case 0b101010: // Conditional branch (immediate)
case 0b011010: { // Compare & branch (immediate)
instructions = conditionalBranch(insn_addr, target);
break;
instructions = T::conditionalBranch(insn_addr, target);
break;
}
case 0b011011: {
instructions = testAndBranch(insn_addr, target);
instructions = T::testAndBranch(insn_addr, target);
break;
}
case 0b001100:
@ -209,9 +187,9 @@ public:
case 0b111100:
case 0b111110: {
// load/store
if ((Instruction_aarch64::extract(_insn, 29, 24) & 0b111011) == 0b011000) {
if ((Instruction_aarch64::extract(insn, 29, 24) & 0b111011) == 0b011000) {
// Load register (literal)
instructions = loadStore(insn_addr, target);
instructions = T::loadStore(insn_addr, target);
break;
} else {
// nothing to do
@ -224,27 +202,27 @@ public:
case 0b101000:
case 0b111000: {
// adr/adrp
assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be");
int shift = Instruction_aarch64::extract(_insn, 31, 31);
assert(Instruction_aarch64::extract(insn, 28, 24) == 0b10000, "must be");
int shift = Instruction_aarch64::extract(insn, 31, 31);
if (shift) {
uint32_t insn2 = insn_at(1);
uint32_t insn2 = insn_at(insn_addr, 1);
if (Instruction_aarch64::extract(insn2, 29, 24) == 0b111001 &&
Instruction_aarch64::extract(_insn, 4, 0) ==
Instruction_aarch64::extract(insn, 4, 0) ==
Instruction_aarch64::extract(insn2, 9, 5)) {
instructions = adrp(insn_addr, target, adrpMem());
instructions = T::adrp(insn_addr, target, T::adrpMem);
} else if (Instruction_aarch64::extract(insn2, 31, 22) == 0b1001000100 &&
Instruction_aarch64::extract(_insn, 4, 0) ==
Instruction_aarch64::extract(insn, 4, 0) ==
Instruction_aarch64::extract(insn2, 4, 0)) {
instructions = adrp(insn_addr, target, adrpAdd());
instructions = T::adrp(insn_addr, target, T::adrpAdd);
} else if (Instruction_aarch64::extract(insn2, 31, 21) == 0b11110010110 &&
Instruction_aarch64::extract(_insn, 4, 0) ==
Instruction_aarch64::extract(insn, 4, 0) ==
Instruction_aarch64::extract(insn2, 4, 0)) {
instructions = adrp(insn_addr, target, adrpMovk());
instructions = T::adrp(insn_addr, target, T::adrpMovk);
} else {
ShouldNotReachHere();
}
} else {
instructions = adr(insn_addr, target);
instructions = T::adr(insn_addr, target);
}
break;
}
@ -252,7 +230,7 @@ public:
case 0b011001:
case 0b101001:
case 0b111001: {
instructions = immediate(insn_addr, target);
instructions = T::immediate(insn_addr, target);
break;
}
default: {
@ -260,42 +238,36 @@ public:
}
}
verify(insn_addr, target);
T::verify(insn_addr, target);
return instructions * NativeInstruction::instruction_size;
}
};
class Patcher : public RelocActions {
virtual reloc_insn adrpMem() { return &Patcher::adrpMem_impl; }
virtual reloc_insn adrpAdd() { return &Patcher::adrpAdd_impl; }
virtual reloc_insn adrpMovk() { return &Patcher::adrpMovk_impl; }
class Patcher : public AllStatic {
public:
Patcher(address insn_addr) : RelocActions(insn_addr) {}
virtual int unconditionalBranch(address insn_addr, address &target) {
static int unconditionalBranch(address insn_addr, address &target) {
intptr_t offset = (target - insn_addr) >> 2;
Instruction_aarch64::spatch(insn_addr, 25, 0, offset);
return 1;
}
virtual int conditionalBranch(address insn_addr, address &target) {
static int conditionalBranch(address insn_addr, address &target) {
intptr_t offset = (target - insn_addr) >> 2;
Instruction_aarch64::spatch(insn_addr, 23, 5, offset);
return 1;
}
virtual int testAndBranch(address insn_addr, address &target) {
static int testAndBranch(address insn_addr, address &target) {
intptr_t offset = (target - insn_addr) >> 2;
Instruction_aarch64::spatch(insn_addr, 18, 5, offset);
return 1;
}
virtual int loadStore(address insn_addr, address &target) {
static int loadStore(address insn_addr, address &target) {
intptr_t offset = (target - insn_addr) >> 2;
Instruction_aarch64::spatch(insn_addr, 23, 5, offset);
return 1;
}
virtual int adr(address insn_addr, address &target) {
static int adr(address insn_addr, address &target) {
#ifdef ASSERT
assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be");
assert(Instruction_aarch64::extract(insn_at(insn_addr, 0), 28, 24) == 0b10000, "must be");
#endif
// PC-rel. addressing
ptrdiff_t offset = target - insn_addr;
@ -305,17 +277,18 @@ public:
Instruction_aarch64::patch(insn_addr, 30, 29, offset_lo);
return 1;
}
virtual int adrp(address insn_addr, address &target, reloc_insn inner) {
template<typename U>
static int adrp(address insn_addr, address &target, U inner) {
int instructions = 1;
#ifdef ASSERT
assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be");
assert(Instruction_aarch64::extract(insn_at(insn_addr, 0), 28, 24) == 0b10000, "must be");
#endif
ptrdiff_t offset = target - insn_addr;
instructions = 2;
precond(inner != nullptr);
// Give the inner reloc a chance to modify the target.
address adjusted_target = target;
instructions = (*inner)(insn_addr, adjusted_target);
instructions = inner(insn_addr, adjusted_target);
uintptr_t pc_page = (uintptr_t)insn_addr >> 12;
uintptr_t adr_page = (uintptr_t)adjusted_target >> 12;
offset = adr_page - pc_page;
@ -325,7 +298,7 @@ public:
Instruction_aarch64::patch(insn_addr, 30, 29, offset_lo);
return instructions;
}
static int adrpMem_impl(address insn_addr, address &target) {
static int adrpMem(address insn_addr, address &target) {
uintptr_t dest = (uintptr_t)target;
int offset_lo = dest & 0xfff;
uint32_t insn2 = insn_at(insn_addr, 1);
@ -334,21 +307,21 @@ public:
guarantee(((dest >> size) << size) == dest, "misaligned target");
return 2;
}
static int adrpAdd_impl(address insn_addr, address &target) {
static int adrpAdd(address insn_addr, address &target) {
uintptr_t dest = (uintptr_t)target;
int offset_lo = dest & 0xfff;
Instruction_aarch64::patch(insn_addr + sizeof (uint32_t), 21, 10, offset_lo);
return 2;
}
static int adrpMovk_impl(address insn_addr, address &target) {
static int adrpMovk(address insn_addr, address &target) {
uintptr_t dest = uintptr_t(target);
Instruction_aarch64::patch(insn_addr + sizeof (uint32_t), 20, 5, (uintptr_t)target >> 32);
dest = (dest & 0xffffffffULL) | (uintptr_t(insn_addr) & 0xffff00000000ULL);
target = address(dest);
return 2;
}
virtual int immediate(address insn_addr, address &target) {
assert(Instruction_aarch64::extract(_insn, 31, 21) == 0b11010010100, "must be");
static int immediate(address insn_addr, address &target) {
assert(Instruction_aarch64::extract(insn_at(insn_addr, 0), 31, 21) == 0b11010010100, "must be");
uint64_t dest = (uint64_t)target;
// Move wide constant
assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch");
@ -358,7 +331,7 @@ public:
Instruction_aarch64::patch(insn_addr+8, 20, 5, (dest >>= 16) & 0xffff);
return 3;
}
virtual void verify(address insn_addr, address &target) {
static void verify(address insn_addr, address &target) {
#ifdef ASSERT
address address_is = MacroAssembler::target_addr_for_insn(insn_addr);
if (!(address_is == target)) {
@ -392,56 +365,54 @@ static bool offset_for(uint32_t insn1, uint32_t insn2, ptrdiff_t &byte_offset) {
return false;
}
class AArch64Decoder : public RelocActions {
virtual reloc_insn adrpMem() { return &AArch64Decoder::adrpMem_impl; }
virtual reloc_insn adrpAdd() { return &AArch64Decoder::adrpAdd_impl; }
virtual reloc_insn adrpMovk() { return &AArch64Decoder::adrpMovk_impl; }
class AArch64Decoder : public AllStatic {
public:
AArch64Decoder(address insn_addr, uint32_t insn) : RelocActions(insn_addr, insn) {}
virtual int loadStore(address insn_addr, address &target) {
intptr_t offset = Instruction_aarch64::sextract(_insn, 23, 5);
static int loadStore(address insn_addr, address &target) {
intptr_t offset = Instruction_aarch64::sextract(insn_at(insn_addr, 0), 23, 5);
target = insn_addr + (offset << 2);
return 1;
}
virtual int unconditionalBranch(address insn_addr, address &target) {
intptr_t offset = Instruction_aarch64::sextract(_insn, 25, 0);
static int unconditionalBranch(address insn_addr, address &target) {
intptr_t offset = Instruction_aarch64::sextract(insn_at(insn_addr, 0), 25, 0);
target = insn_addr + (offset << 2);
return 1;
}
virtual int conditionalBranch(address insn_addr, address &target) {
intptr_t offset = Instruction_aarch64::sextract(_insn, 23, 5);
static int conditionalBranch(address insn_addr, address &target) {
intptr_t offset = Instruction_aarch64::sextract(insn_at(insn_addr, 0), 23, 5);
target = address(((uint64_t)insn_addr + (offset << 2)));
return 1;
}
virtual int testAndBranch(address insn_addr, address &target) {
intptr_t offset = Instruction_aarch64::sextract(_insn, 18, 5);
static int testAndBranch(address insn_addr, address &target) {
intptr_t offset = Instruction_aarch64::sextract(insn_at(insn_addr, 0), 18, 5);
target = address(((uint64_t)insn_addr + (offset << 2)));
return 1;
}
virtual int adr(address insn_addr, address &target) {
static int adr(address insn_addr, address &target) {
// PC-rel. addressing
intptr_t offset = Instruction_aarch64::extract(_insn, 30, 29);
offset |= Instruction_aarch64::sextract(_insn, 23, 5) << 2;
uint32_t insn = insn_at(insn_addr, 0);
intptr_t offset = Instruction_aarch64::extract(insn, 30, 29);
offset |= Instruction_aarch64::sextract(insn, 23, 5) << 2;
target = address((uint64_t)insn_addr + offset);
return 1;
}
virtual int adrp(address insn_addr, address &target, reloc_insn inner) {
assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be");
intptr_t offset = Instruction_aarch64::extract(_insn, 30, 29);
offset |= Instruction_aarch64::sextract(_insn, 23, 5) << 2;
template<typename U>
static int adrp(address insn_addr, address &target, U inner) {
uint32_t insn = insn_at(insn_addr, 0);
assert(Instruction_aarch64::extract(insn, 28, 24) == 0b10000, "must be");
intptr_t offset = Instruction_aarch64::extract(insn, 30, 29);
offset |= Instruction_aarch64::sextract(insn, 23, 5) << 2;
int shift = 12;
offset <<= shift;
uint64_t target_page = ((uint64_t)insn_addr) + offset;
target_page &= ((uint64_t)-1) << shift;
uint32_t insn2 = insn_at(1);
uint32_t insn2 = insn_at(insn_addr, 1);
target = address(target_page);
precond(inner != nullptr);
(*inner)(insn_addr, target);
inner(insn_addr, target);
return 2;
}
static int adrpMem_impl(address insn_addr, address &target) {
static int adrpMem(address insn_addr, address &target) {
uint32_t insn2 = insn_at(insn_addr, 1);
// Load/store register (unsigned immediate)
ptrdiff_t byte_offset = Instruction_aarch64::extract(insn2, 21, 10);
@ -450,14 +421,14 @@ public:
target += byte_offset;
return 2;
}
static int adrpAdd_impl(address insn_addr, address &target) {
static int adrpAdd(address insn_addr, address &target) {
uint32_t insn2 = insn_at(insn_addr, 1);
// add (immediate)
ptrdiff_t byte_offset = Instruction_aarch64::extract(insn2, 21, 10);
target += byte_offset;
return 2;
}
static int adrpMovk_impl(address insn_addr, address &target) {
static int adrpMovk(address insn_addr, address &target) {
uint32_t insn2 = insn_at(insn_addr, 1);
uint64_t dest = uint64_t(target);
dest = (dest & 0xffff0000ffffffff) |
@ -476,35 +447,33 @@ public:
return 2;
}
}
virtual int immediate(address insn_addr, address &target) {
static int immediate(address insn_addr, address &target) {
uint32_t *insns = (uint32_t *)insn_addr;
assert(Instruction_aarch64::extract(_insn, 31, 21) == 0b11010010100, "must be");
assert(Instruction_aarch64::extract(insns[0], 31, 21) == 0b11010010100, "must be");
// Move wide constant: movz, movk, movk. See movptr().
assert(nativeInstruction_at(insns+1)->is_movk(), "wrong insns in patch");
assert(nativeInstruction_at(insns+2)->is_movk(), "wrong insns in patch");
target = address(uint64_t(Instruction_aarch64::extract(_insn, 20, 5))
+ (uint64_t(Instruction_aarch64::extract(insns[1], 20, 5)) << 16)
+ (uint64_t(Instruction_aarch64::extract(insns[2], 20, 5)) << 32));
target = address(uint64_t(Instruction_aarch64::extract(insns[0], 20, 5))
+ (uint64_t(Instruction_aarch64::extract(insns[1], 20, 5)) << 16)
+ (uint64_t(Instruction_aarch64::extract(insns[2], 20, 5)) << 32));
assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch");
assert(nativeInstruction_at(insn_addr+8)->is_movk(), "wrong insns in patch");
return 3;
}
virtual void verify(address insn_addr, address &target) {
static void verify(address insn_addr, address &target) {
}
};
address MacroAssembler::target_addr_for_insn(address insn_addr, uint32_t insn) {
AArch64Decoder decoder(insn_addr, insn);
address MacroAssembler::target_addr_for_insn(address insn_addr) {
address target;
decoder.run(insn_addr, target);
RelocActions<AArch64Decoder>::run(insn_addr, target);
return target;
}
// Patch any kind of instruction; there may be several instructions.
// Return the total length (in bytes) of the instructions.
int MacroAssembler::pd_patch_instruction_size(address insn_addr, address target) {
Patcher patcher(insn_addr);
return patcher.run(insn_addr, target);
return RelocActions<Patcher>::run(insn_addr, target);
}
int MacroAssembler::patch_oop(address insn_addr, address o) {
@ -546,11 +515,11 @@ int MacroAssembler::patch_narrow_klass(address insn_addr, narrowKlass n) {
return 2 * NativeInstruction::instruction_size;
}
address MacroAssembler::target_addr_for_insn_or_null(address insn_addr, unsigned insn) {
if (NativeInstruction::is_ldrw_to_zr(address(&insn))) {
address MacroAssembler::target_addr_for_insn_or_null(address insn_addr) {
if (NativeInstruction::is_ldrw_to_zr(insn_addr)) {
return nullptr;
}
return MacroAssembler::target_addr_for_insn(insn_addr, insn);
return MacroAssembler::target_addr_for_insn(insn_addr);
}
void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool in_nmethod, Register tmp) {

View File

@ -676,16 +676,8 @@ public:
static bool needs_explicit_null_check(intptr_t offset);
static bool uses_implicit_null_check(void* address);
static address target_addr_for_insn(address insn_addr, unsigned insn);
static address target_addr_for_insn_or_null(address insn_addr, unsigned insn);
static address target_addr_for_insn(address insn_addr) {
unsigned insn = *(unsigned*)insn_addr;
return target_addr_for_insn(insn_addr, insn);
}
static address target_addr_for_insn_or_null(address insn_addr) {
unsigned insn = *(unsigned*)insn_addr;
return target_addr_for_insn_or_null(insn_addr, insn);
}
static address target_addr_for_insn(address insn_addr);
static address target_addr_for_insn_or_null(address insn_addr);
// Required platform-specific helpers for Label::patch_instructions.
// They _shadow_ the declarations in AbstractAssembler, which are undefined.