8289743: AArch64: Clean up patching logic

Reviewed-by: adinn, ngasson
This commit is contained in:
Andrew Haley 2022-07-20 13:00:12 +00:00
parent 011958d30b
commit 1c055076e0
6 changed files with 452 additions and 182 deletions

View File

@ -155,10 +155,6 @@ void Address::lea(MacroAssembler *as, Register r) const {
}
}
void Assembler::adrp(Register reg1, const Address &dest, uint64_t &byte_offset) {
ShouldNotReachHere();
}
#undef __
#define starti Instruction_aarch64 current_insn(this);
@ -189,7 +185,7 @@ void Assembler::adrp(Register reg1, const Address &dest, uint64_t &byte_offset)
offset >>= 2;
starti;
f(1, 31), f(offset_lo, 30, 29), f(0b10000, 28, 24), sf(offset, 23, 5);
rf(Rd, 0);
zrf(Rd, 0);
}
// An "all-purpose" add/subtract immediate, per ARM documentation:

View File

@ -220,7 +220,7 @@ public:
return extend(uval, msb - lsb);
}
static void patch(address a, int msb, int lsb, uint64_t val) {
static ALWAYSINLINE void patch(address a, int msb, int lsb, uint64_t val) {
int nbits = msb - lsb + 1;
guarantee(val < (1ULL << nbits), "Field too big for insn");
assert_cond(msb >= lsb);
@ -718,7 +718,7 @@ public:
wrap_label(Rd, L, &Assembler::_adrp);
}
void adrp(Register Rd, const Address &dest, uint64_t &offset);
void adrp(Register Rd, const Address &dest, uint64_t &offset) = delete;
#undef INSN

View File

@ -2690,7 +2690,7 @@ void LIR_Assembler::emit_updatecrc32(LIR_OpUpdateCRC32* op) {
assert_different_registers(val, crc, res);
uint64_t offset;
__ adrp(res, ExternalAddress(StubRoutines::crc_table_addr()), offset);
if (offset) __ add(res, res, offset);
__ add(res, res, offset);
__ mvnw(crc, crc); // ~crc
__ update_byte_crc32(crc, val, res);

View File

@ -170,7 +170,12 @@ void InterpreterMacroAssembler::get_unsigned_2_byte_index_at_bcp(
void InterpreterMacroAssembler::get_dispatch() {
uint64_t offset;
adrp(rdispatch, ExternalAddress((address)Interpreter::dispatch_table()), offset);
lea(rdispatch, Address(rdispatch, offset));
// Use add() here after ARDP, rather than lea().
// lea() does not generate anything if its offset is zero.
// However, relocs expect to find either an ADD or a load/store
// insn after an ADRP. add() always generates an ADD insn, even
// for add(Rn, Rn, 0).
add(rdispatch, rdispatch, offset);
}
void InterpreterMacroAssembler::get_cache_index_at_bcp(Register index,

View File

@ -73,102 +73,435 @@
#define STOP(str) stop(str);
#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
// 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 branch, address target) {
int instructions = 1;
assert((uint64_t)target < (1ull << 48), "48-bit overflow in address constant");
intptr_t offset = (target - branch) >> 2;
unsigned insn = *(unsigned*)branch;
if ((Instruction_aarch64::extract(insn, 29, 24) & 0b111011) == 0b011000) {
// Load register (literal)
Instruction_aarch64::spatch(branch, 23, 5, offset);
} else if (Instruction_aarch64::extract(insn, 30, 26) == 0b00101) {
// Unconditional branch (immediate)
Instruction_aarch64::spatch(branch, 25, 0, offset);
} else if (Instruction_aarch64::extract(insn, 31, 25) == 0b0101010) {
// Conditional branch (immediate)
Instruction_aarch64::spatch(branch, 23, 5, offset);
} else if (Instruction_aarch64::extract(insn, 30, 25) == 0b011010) {
// Compare & branch (immediate)
Instruction_aarch64::spatch(branch, 23, 5, offset);
} else if (Instruction_aarch64::extract(insn, 30, 25) == 0b011011) {
// Test & branch (immediate)
Instruction_aarch64::spatch(branch, 18, 5, offset);
} else if (Instruction_aarch64::extract(insn, 28, 24) == 0b10000) {
// PC-rel. addressing
offset = target-branch;
int shift = Instruction_aarch64::extract(insn, 31, 31);
if (shift) {
uint64_t dest = (uint64_t)target;
uint64_t pc_page = (uint64_t)branch >> 12;
uint64_t adr_page = (uint64_t)target >> 12;
unsigned offset_lo = dest & 0xfff;
offset = adr_page - pc_page;
#ifdef ASSERT
extern "C" void disnm(intptr_t p);
#endif
// Target-dependent relocation processing
//
// Instruction sequences whose target may need to be retrieved or
// patched are distinguished by their leading instruction, sorting
// them into three main instruction groups and related subgroups.
//
// 1) Branch, Exception and System (insn count = 1)
// 1a) Unconditional branch (immediate):
// b/bl imm19
// 1b) Compare & branch (immediate):
// cbz/cbnz Rt imm19
// 1c) Test & branch (immediate):
// tbz/tbnz Rt imm14
// 1d) Conditional branch (immediate):
// b.cond imm19
//
// 2) Loads and Stores (insn count = 1)
// 2a) Load register literal:
// ldr Rt imm19
//
// 3) Data Processing Immediate (insn count = 2 or 3)
// 3a) PC-rel. addressing
// adr/adrp Rx imm21; ldr/str Ry Rx #imm12
// adr/adrp Rx imm21; add Ry Rx #imm12
// adr/adrp Rx imm21; movk Rx #imm16<<32; ldr/str Ry, [Rx, #offset_in_page]
// adr/adrp Rx imm21
// adr/adrp Rx imm21; movk Rx #imm16<<32
// adr/adrp Rx imm21; movk Rx #imm16<<32; add Ry, Rx, #offset_in_page
// The latter form can only happen when the target is an
// ExternalAddress, and (by definition) ExternalAddresses don't
// move. Because of that property, there is never any need to
// patch the last of the three instructions. However,
// MacroAssembler::target_addr_for_insn takes all three
// instructions into account and returns the correct address.
// 3b) Move wide (immediate)
// movz Rx #imm16; movk Rx #imm16 << 16; movk Rx #imm16 << 32;
//
// A switch on a subset of the instruction's bits provides an
// efficient dispatch to these subcases.
//
// insn[28:26] -> main group ('x' == don't care)
// 00x -> UNALLOCATED
// 100 -> Data Processing Immediate
// 101 -> Branch, Exception and System
// x1x -> Loads and Stores
//
// insn[30:25] -> subgroup ('_' == group, 'x' == don't care).
// n.b. in some cases extra bits need to be checked to verify the
// instruction is as expected
//
// 1) ... xx101x Branch, Exception and System
// 1a) 00___x Unconditional branch (immediate)
// 1b) 01___0 Compare & branch (immediate)
// 1c) 01___1 Test & branch (immediate)
// 1d) 10___0 Conditional branch (immediate)
// other Should not happen
//
// 2) ... xxx1x0 Loads and Stores
// 2a) xx1__00 Load/Store register (insn[28] == 1 && insn[24] == 0)
// 2aa) x01__00 Load register literal (i.e. requires insn[29] == 0)
// strictly should be 64 bit non-FP/SIMD i.e.
// 0101_000 (i.e. requires insn[31:24] == 01011000)
//
// 3) ... xx100x Data Processing Immediate
// 3a) xx___00 PC-rel. addressing (n.b. requires insn[24] == 0)
// 3b) xx___101 Move wide (immediate) (n.b. requires insn[24:23] == 01)
// 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);
// We handle 4 types of PC relative addressing
// 1 - adrp Rx, target_page
// ldr/str Ry, [Rx, #offset_in_page]
// 2 - adrp Rx, target_page
// add Ry, Rx, #offset_in_page
// 3 - adrp Rx, target_page (page aligned reloc, offset == 0)
// movk Rx, #imm16<<32
// 4 - adrp Rx, target_page (page aligned reloc, offset == 0)
// In the first 3 cases we must check that Rx is the same in the adrp and the
// subsequent ldr/str, add or movk instruction. Otherwise we could accidentally end
// up treating a type 4 relocation as a type 1, 2 or 3 just because it happened
// to be followed by a random unrelated ldr/str, add or movk instruction.
//
unsigned insn2 = ((unsigned*)branch)[1];
if (Instruction_aarch64::extract(insn2, 29, 24) == 0b111001 &&
Instruction_aarch64::extract(insn, 4, 0) ==
Instruction_aarch64::extract(insn2, 9, 5)) {
// Load/store register (unsigned immediate)
unsigned size = Instruction_aarch64::extract(insn2, 31, 30);
Instruction_aarch64::patch(branch + sizeof (unsigned),
21, 10, offset_lo >> size);
guarantee(((dest >> size) << size) == dest, "misaligned target");
instructions = 2;
} else if (Instruction_aarch64::extract(insn2, 31, 22) == 0b1001000100 &&
Instruction_aarch64::extract(insn, 4, 0) ==
Instruction_aarch64::extract(insn2, 4, 0)) {
// add (immediate)
Instruction_aarch64::patch(branch + sizeof (unsigned),
21, 10, offset_lo);
instructions = 2;
} else if (Instruction_aarch64::extract(insn2, 31, 21) == 0b11110010110 &&
Instruction_aarch64::extract(insn, 4, 0) ==
virtual reloc_insn adrpMem() = 0;
virtual reloc_insn adrpAdd() = 0;
virtual reloc_insn adrpMovk() = 0;
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);
}
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) {
int instructions = 1;
uint32_t dispatch = Instruction_aarch64::extract(_insn, 30, 25);
switch(dispatch) {
case 0b001010:
case 0b001011: {
instructions = unconditionalBranch(insn_addr, target);
break;
}
case 0b101010: // Conditional branch (immediate)
case 0b011010: { // Compare & branch (immediate)
instructions = conditionalBranch(insn_addr, target);
break;
}
case 0b011011: {
instructions = testAndBranch(insn_addr, target);
break;
}
case 0b001100:
case 0b001110:
case 0b011100:
case 0b011110:
case 0b101100:
case 0b101110:
case 0b111100:
case 0b111110: {
// load/store
if ((Instruction_aarch64::extract(_insn, 29, 24) & 0b111011) == 0b011000) {
// Load register (literal)
instructions = loadStore(insn_addr, target);
break;
} else {
// nothing to do
assert(target == 0, "did not expect to relocate target for polling page load");
}
break;
}
case 0b001000:
case 0b011000:
case 0b101000:
case 0b111000: {
// adr/adrp
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);
if (Instruction_aarch64::extract(insn2, 29, 24) == 0b111001 &&
Instruction_aarch64::extract(_insn, 4, 0) ==
Instruction_aarch64::extract(insn2, 9, 5)) {
instructions = adrp(insn_addr, target, adrpMem());
} else if (Instruction_aarch64::extract(insn2, 31, 22) == 0b1001000100 &&
Instruction_aarch64::extract(_insn, 4, 0) ==
Instruction_aarch64::extract(insn2, 4, 0)) {
// movk #imm16<<32
Instruction_aarch64::patch(branch + 4, 20, 5, (uint64_t)target >> 32);
uintptr_t dest = ((uintptr_t)target & 0xffffffffULL) | ((uintptr_t)branch & 0xffff00000000ULL);
uintptr_t pc_page = (uintptr_t)branch >> 12;
uintptr_t adr_page = (uintptr_t)dest >> 12;
offset = adr_page - pc_page;
instructions = 2;
instructions = adrp(insn_addr, target, adrpAdd());
} else if (Instruction_aarch64::extract(insn2, 31, 21) == 0b11110010110 &&
Instruction_aarch64::extract(_insn, 4, 0) ==
Instruction_aarch64::extract(insn2, 4, 0)) {
instructions = adrp(insn_addr, target, adrpMovk());
} else {
ShouldNotReachHere();
}
} else {
instructions = adr(insn_addr, target);
}
break;
}
case 0b001001:
case 0b011001:
case 0b101001:
case 0b111001: {
instructions = immediate(insn_addr, target);
break;
}
default: {
ShouldNotReachHere();
}
}
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; }
public:
Patcher(address insn_addr) : RelocActions(insn_addr) {}
virtual 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) {
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) {
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) {
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) {
#ifdef ASSERT
assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be");
#endif
// PC-rel. addressing
ptrdiff_t offset = target - insn_addr;
int offset_lo = offset & 3;
offset >>= 2;
Instruction_aarch64::spatch(branch, 23, 5, offset);
Instruction_aarch64::patch(branch, 30, 29, offset_lo);
} else if (Instruction_aarch64::extract(insn, 31, 21) == 0b11010010100) {
Instruction_aarch64::spatch(insn_addr, 23, 5, offset);
Instruction_aarch64::patch(insn_addr, 30, 29, offset_lo);
return 1;
}
virtual int adrp(address insn_addr, address &target, reloc_insn inner) {
int instructions = 1;
#ifdef ASSERT
assert(Instruction_aarch64::extract(_insn, 28, 24) == 0b10000, "must be");
#endif
ptrdiff_t offset = target - insn_addr;
instructions = 2;
precond(inner != nullptr);
uintptr_t dest = (uintptr_t)target;
uintptr_t pc_page = (uintptr_t)insn_addr >> 12;
uintptr_t adr_page = (uintptr_t)target >> 12;
offset = adr_page - pc_page;
instructions = (*inner)(insn_addr, target);
// Now we extract the lower 21 bits of the signed offset field for
// the ADRP.
offset = offset << (64-21) >> (64-21);
int offset_lo = offset & 3;
offset >>= 2;
Instruction_aarch64::spatch(insn_addr, 23, 5, offset);
Instruction_aarch64::patch(insn_addr, 30, 29, offset_lo);
return instructions;
}
static int adrpMem_impl(address insn_addr, address &target) {
uintptr_t dest = (uintptr_t)target;
int offset_lo = dest & 0xfff;
uint32_t insn2 = insn_at(insn_addr, 1);
uint32_t size = Instruction_aarch64::extract(insn2, 31, 30);
Instruction_aarch64::patch(insn_addr + sizeof (uint32_t), 21, 10, offset_lo >> size);
guarantee(((dest >> size) << size) == dest, "misaligned target");
return 2;
}
static int adrpAdd_impl(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) {
uintptr_t dest = (uintptr_t)target;
Instruction_aarch64::patch(insn_addr + sizeof (uint32_t), 20, 5, (uintptr_t)target >> 32);
return 2;
}
virtual int immediate(address insn_addr, address &target) {
assert(Instruction_aarch64::extract(_insn, 31, 21) == 0b11010010100, "must be");
uint64_t dest = (uint64_t)target;
// Move wide constant
assert(nativeInstruction_at(branch+4)->is_movk(), "wrong insns in patch");
assert(nativeInstruction_at(branch+8)->is_movk(), "wrong insns in patch");
Instruction_aarch64::patch(branch, 20, 5, dest & 0xffff);
Instruction_aarch64::patch(branch+4, 20, 5, (dest >>= 16) & 0xffff);
Instruction_aarch64::patch(branch+8, 20, 5, (dest >>= 16) & 0xffff);
assert(target_addr_for_insn(branch) == target, "should be");
instructions = 3;
} else if (NativeInstruction::is_ldrw_to_zr(address(&insn))) {
// nothing to do
assert(target == 0, "did not expect to relocate target for polling page load");
} else {
ShouldNotReachHere();
assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch");
assert(nativeInstruction_at(insn_addr+8)->is_movk(), "wrong insns in patch");
Instruction_aarch64::patch(insn_addr, 20, 5, dest & 0xffff);
Instruction_aarch64::patch(insn_addr+4, 20, 5, (dest >>= 16) & 0xffff);
Instruction_aarch64::patch(insn_addr+8, 20, 5, (dest >>= 16) & 0xffff);
return 3;
}
return instructions * NativeInstruction::instruction_size;
virtual void verify(address insn_addr, address &target) {
#ifdef ASSERT
address address_is = MacroAssembler::target_addr_for_insn(insn_addr);
if (!(address_is == target)) {
tty->print_cr("%p at %p should be %p", address_is, insn_addr, target);
disnm((intptr_t)insn_addr);
assert(address_is == target, "should be");
}
#endif
}
};
// If insn1 and insn2 use the same register to form an address, either
// by an offsetted LDR or a simple ADD, return the offset. If the
// second instruction is an LDR, the offset may be scaled.
static bool offset_for(uint32_t insn1, uint32_t insn2, ptrdiff_t &byte_offset) {
if (Instruction_aarch64::extract(insn2, 29, 24) == 0b111001 &&
Instruction_aarch64::extract(insn1, 4, 0) ==
Instruction_aarch64::extract(insn2, 9, 5)) {
// Load/store register (unsigned immediate)
byte_offset = Instruction_aarch64::extract(insn2, 21, 10);
uint32_t size = Instruction_aarch64::extract(insn2, 31, 30);
byte_offset <<= size;
return true;
} else if (Instruction_aarch64::extract(insn2, 31, 22) == 0b1001000100 &&
Instruction_aarch64::extract(insn1, 4, 0) ==
Instruction_aarch64::extract(insn2, 4, 0)) {
// add (immediate)
byte_offset = Instruction_aarch64::extract(insn2, 21, 10);
return true;
}
return false;
}
class Decoder : public RelocActions {
virtual reloc_insn adrpMem() { return &Decoder::adrpMem_impl; }
virtual reloc_insn adrpAdd() { return &Decoder::adrpAdd_impl; }
virtual reloc_insn adrpMovk() { return &Decoder::adrpMovk_impl; }
public:
Decoder(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);
target = insn_addr + (offset << 2);
return 1;
}
virtual int unconditionalBranch(address insn_addr, address &target) {
intptr_t offset = Instruction_aarch64::sextract(_insn, 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);
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);
target = address(((uint64_t)insn_addr + (offset << 2)));
return 1;
}
virtual 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;
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;
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);
target = address(target_page);
precond(inner != nullptr);
(*inner)(insn_addr, target);
return 2;
}
static int adrpMem_impl(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);
uint32_t size = Instruction_aarch64::extract(insn2, 31, 30);
byte_offset <<= size;
target += byte_offset;
return 2;
}
static int adrpAdd_impl(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) {
uint32_t insn2 = insn_at(insn_addr, 1);
uint64_t dest = uint64_t(target);
dest = (dest & 0xffffffff) |
((uint64_t)Instruction_aarch64::extract(insn2, 20, 5) << 32);
target = address(dest);
// We know the destination 4k page. Maybe we have a third
// instruction.
uint32_t insn = insn_at(insn_addr, 0);
uint32_t insn3 = insn_at(insn_addr, 2);
ptrdiff_t byte_offset;
if (offset_for(insn, insn3, byte_offset)) {
target += byte_offset;
return 3;
} else {
return 2;
}
}
virtual int immediate(address insn_addr, address &target) {
uint32_t *insns = (uint32_t *)insn_addr;
assert(Instruction_aarch64::extract(_insn, 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));
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) {
}
};
address MacroAssembler::target_addr_for_insn(address insn_addr, uint32_t insn) {
Decoder decoder(insn_addr, insn);
address target;
decoder.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);
}
int MacroAssembler::patch_oop(address insn_addr, address o) {
@ -210,90 +543,9 @@ int MacroAssembler::patch_narrow_klass(address insn_addr, narrowKlass n) {
return 2 * NativeInstruction::instruction_size;
}
address MacroAssembler::target_addr_for_insn(address insn_addr, unsigned insn) {
intptr_t offset = 0;
if ((Instruction_aarch64::extract(insn, 29, 24) & 0b011011) == 0b00011000) {
// Load register (literal)
offset = Instruction_aarch64::sextract(insn, 23, 5);
return address(((uint64_t)insn_addr + (offset << 2)));
} else if (Instruction_aarch64::extract(insn, 30, 26) == 0b00101) {
// Unconditional branch (immediate)
offset = Instruction_aarch64::sextract(insn, 25, 0);
} else if (Instruction_aarch64::extract(insn, 31, 25) == 0b0101010) {
// Conditional branch (immediate)
offset = Instruction_aarch64::sextract(insn, 23, 5);
} else if (Instruction_aarch64::extract(insn, 30, 25) == 0b011010) {
// Compare & branch (immediate)
offset = Instruction_aarch64::sextract(insn, 23, 5);
} else if (Instruction_aarch64::extract(insn, 30, 25) == 0b011011) {
// Test & branch (immediate)
offset = Instruction_aarch64::sextract(insn, 18, 5);
} else if (Instruction_aarch64::extract(insn, 28, 24) == 0b10000) {
// PC-rel. addressing
offset = Instruction_aarch64::extract(insn, 30, 29);
offset |= Instruction_aarch64::sextract(insn, 23, 5) << 2;
int shift = Instruction_aarch64::extract(insn, 31, 31) ? 12 : 0;
if (shift) {
offset <<= shift;
uint64_t target_page = ((uint64_t)insn_addr) + offset;
target_page &= ((uint64_t)-1) << shift;
// Return the target address for the following sequences
// 1 - adrp Rx, target_page
// ldr/str Ry, [Rx, #offset_in_page]
// 2 - adrp Rx, target_page
// add Ry, Rx, #offset_in_page
// 3 - adrp Rx, target_page (page aligned reloc, offset == 0)
// movk Rx, #imm12<<32
// 4 - adrp Rx, target_page (page aligned reloc, offset == 0)
//
// In the first two cases we check that the register is the same and
// return the target_page + the offset within the page.
// Otherwise we assume it is a page aligned relocation and return
// the target page only.
//
unsigned insn2 = ((unsigned*)insn_addr)[1];
if (Instruction_aarch64::extract(insn2, 29, 24) == 0b111001 &&
Instruction_aarch64::extract(insn, 4, 0) ==
Instruction_aarch64::extract(insn2, 9, 5)) {
// Load/store register (unsigned immediate)
unsigned int byte_offset = Instruction_aarch64::extract(insn2, 21, 10);
unsigned int size = Instruction_aarch64::extract(insn2, 31, 30);
return address(target_page + (byte_offset << size));
} else if (Instruction_aarch64::extract(insn2, 31, 22) == 0b1001000100 &&
Instruction_aarch64::extract(insn, 4, 0) ==
Instruction_aarch64::extract(insn2, 4, 0)) {
// add (immediate)
unsigned int byte_offset = Instruction_aarch64::extract(insn2, 21, 10);
return address(target_page + byte_offset);
} else {
if (Instruction_aarch64::extract(insn2, 31, 21) == 0b11110010110 &&
Instruction_aarch64::extract(insn, 4, 0) ==
Instruction_aarch64::extract(insn2, 4, 0)) {
target_page = (target_page & 0xffffffff) |
((uint64_t)Instruction_aarch64::extract(insn2, 20, 5) << 32);
}
return (address)target_page;
}
} else {
ShouldNotReachHere();
}
} else if (Instruction_aarch64::extract(insn, 31, 23) == 0b110100101) {
uint32_t *insns = (uint32_t *)insn_addr;
// 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");
return 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));
} else {
ShouldNotReachHere();
}
return address(((uint64_t)insn_addr + (offset << 2)));
}
address MacroAssembler::target_addr_for_insn_or_null(address insn_addr, unsigned insn) {
if (NativeInstruction::is_ldrw_to_zr(address(&insn))) {
return 0;
return nullptr;
}
return MacroAssembler::target_addr_for_insn(insn_addr, insn);
}
@ -3436,7 +3688,6 @@ void MacroAssembler::kernel_crc32(Register crc, Register buf, Register len,
Register table0, Register table1, Register table2, Register table3,
Register tmp, Register tmp2, Register tmp3) {
Label L_by16, L_by16_loop, L_by4, L_by4_loop, L_by1, L_by1_loop, L_exit;
uint64_t offset;
if (UseCRC32) {
kernel_crc32_using_crc32(crc, buf, len, table0, table1, table2, table3);
@ -3445,8 +3696,11 @@ void MacroAssembler::kernel_crc32(Register crc, Register buf, Register len,
mvnw(crc, crc);
adrp(table0, ExternalAddress(StubRoutines::crc_table_addr()), offset);
if (offset) add(table0, table0, offset);
{
uint64_t offset;
adrp(table0, ExternalAddress(StubRoutines::crc_table_addr()), offset);
add(table0, table0, offset);
}
add(table1, table0, 1*256*sizeof(juint));
add(table2, table0, 2*256*sizeof(juint));
add(table3, table0, 3*256*sizeof(juint));

View File

@ -6949,6 +6949,22 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
#ifdef ASSERT
{
// Stress relocs for adrp() by trying to reach a page beyond
// the range of a simple ADRP instruction.
ExternalAddress longWayAway(__ pc() - (1ll << 34));
if (! __ is_valid_AArch64_address(longWayAway.target())) {
longWayAway = ExternalAddress(__ pc() + (1ll << 34));
}
if (__ is_valid_AArch64_address(longWayAway.target())) {
uint64_t offset;
__ adrp(rscratch1, longWayAway, offset);
__ add(rscratch1, rscratch1, offset);
}
}
#endif // ASSERT
// check for pending exceptions
#ifdef ASSERT
Label L;
@ -6959,7 +6975,6 @@ class StubGenerator: public StubCodeGenerator {
#endif // ASSERT
__ far_jump(RuntimeAddress(StubRoutines::forward_exception_entry()));
// codeBlob framesize is in words (not VMRegImpl::slot_size)
RuntimeStub* stub =
RuntimeStub::new_runtime_stub(name,