Skip to content

Commit 8803001

Browse files
kddnewtonmeta-codesync[bot]
authored andcommitted
Avoid redundant AArch64 load_addr instructions
Summary: AArch64 `load_addr` always reserved an eight-byte relocation slot so it could expand to `adrp+add`. For page-aligned absolute targets, every relocation outcome needs only one instruction: `adr` for a nearby target, `adrp` for a target in page range, or a literal `ldr` for a farther target. The old layout therefore retained a redundant NOP or emitted `add #0`. Emit a four-byte placeholder when the absolute target has a zero page offset. Update the Builder size estimate before branch relaxation, and make relocation patch only the first word for single-instruction `adr`, `adrp`, and literal `ldr` encodings. Non-page-aligned targets continue reserving two words and emitting `adrp+add` when necessary; label-based ADR relocation retains its fixed slot because final alignment is not known at emission time. Extend the AArch64 patching coverage for the one-instruction `adr`, `adrp`, and literal `ldr` paths. Add a Builder boundary test proving the smaller `load_addr` size is included correctly when deciding whether a nearby `tbz` requires relaxation. Reviewed By: alexmalyshev Differential Revision: D114745354 fbshipit-source-id: 47398b94fdc51e203668040c85a9d50c505ceffa
1 parent c836d75 commit 8803001

4 files changed

Lines changed: 58 additions & 29 deletions

File tree

cinderx/ThirdParty/asmjit/src/asmjit/arm/a64assembler.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,9 +1297,10 @@ Error Assembler::_emit(InstId instId, const Operand_& o0, const Operand_& o1, co
12971297
re->_sourceOffset = codeOffset;
12981298
re->_payload = targetAddress;
12991299

1300-
// Emit adr Rd, #0 (encodes Rd in bits [4:0]) + NOP placeholder.
1300+
// A page-aligned target never needs the second instruction.
13011301
writer.emit32uLE(opcode.get());
1302-
writer.emit32uLE(0xD503201Fu); // NOP
1302+
if ((targetAddress & 0xFFFu) != 0)
1303+
writer.emit32uLE(0xD503201Fu); // NOP
13031304
goto EmitDone;
13041305
}
13051306

cinderx/ThirdParty/asmjit/src/asmjit/arm/a64builder.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,14 @@ static uint32_t estimateNodeSize(BaseNode* node, uint32_t currentOffset) noexcep
9292
for (uint32_t i = 0; i < inst->opCount(); i++) {
9393
const Operand_& op = inst->op(i);
9494

95-
if (realId == Inst::kIdAdr && (op.isLabel() || op.isImm()))
96-
return 8;
95+
if (realId == Inst::kIdAdr) {
96+
if (op.isLabel())
97+
return 8;
98+
if (op.isImm() &&
99+
op.as<Imm>().predicate() == Predicate::kAbsoluteAddress &&
100+
(op.as<Imm>().valueAs<uint64_t>() & 0xFFFu) != 0)
101+
return 8;
102+
}
97103
if (realId == Inst::kIdLdr && op.isMem() && op.as<Mem>().hasBaseLabel())
98104
return 8;
99105
if (realId == Inst::kIdMov && op.isImm() && op.as<Imm>().valueAs<uint64_t>() > 0xFFFF)

cinderx/ThirdParty/asmjit/src/asmjit/core/codeholder.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,10 +1195,10 @@ size_t CodeHolder::codeSize() const noexcept {
11951195
return size_t(offset);
11961196
}
11971197

1198-
// Tries to encode a PC-relative address load into the 8-byte slot at
1199-
// `buffer + offset` using `adr Rd, target` (±1MB) or `adrp Rd, page;
1200-
// add Rd, Rd, #off` (±4GB). Returns true on success, false if the
1201-
// displacement is too large for either encoding.
1198+
// Tries to encode a PC-relative address load at `buffer + offset` using
1199+
// `adr Rd, target` (±1MB) or `adrp Rd, page` with an optional
1200+
// `add Rd, Rd, #off` (±4GB). Returns true on success, false if the displacement
1201+
// is too large for either encoding.
12021202
static bool tryEncodeAdrOrAdrpAdd(uint8_t* buffer, size_t offset, uint64_t targetAddress, uint64_t pc, uint32_t rd) noexcept {
12031203
int64_t displacement = int64_t(targetAddress - pc);
12041204

@@ -1207,7 +1207,6 @@ static bool tryEncodeAdrOrAdrpAdd(uint8_t* buffer, size_t offset, uint64_t targe
12071207
uint32_t immHi = (uint32_t(displacement) >> 2) & 0x7FFFFu;
12081208
uint32_t adrOpcode = 0x10000000u | (immLo << 29) | (immHi << 5) | rd;
12091209
Support::writeU32uLE(buffer + offset, adrOpcode);
1210-
Support::writeU32uLE(buffer + offset + 4, 0xD503201Fu); // NOP
12111210
return true;
12121211
}
12131212

@@ -1217,9 +1216,11 @@ static bool tryEncodeAdrOrAdrpAdd(uint8_t* buffer, size_t offset, uint64_t targe
12171216
uint32_t immLo = uint32_t(pageDelta) & 3u;
12181217
uint32_t immHi = (uint32_t(pageDelta) >> 2) & 0x7FFFFu;
12191218
uint32_t adrpOpcode = 0x90000000u | (immLo << 29) | (immHi << 5) | rd;
1220-
uint32_t addOpcode = 0x91000000u | (pageOffset << 10) | (rd << 5) | rd;
12211219
Support::writeU32uLE(buffer + offset, adrpOpcode);
1222-
Support::writeU32uLE(buffer + offset + 4, addOpcode);
1220+
if (pageOffset != 0) {
1221+
uint32_t addOpcode = 0x91000000u | (pageOffset << 10) | (rd << 5) | rd;
1222+
Support::writeU32uLE(buffer + offset + 4, addOpcode);
1223+
}
12231224
return true;
12241225
}
12251226

@@ -1474,8 +1475,8 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept {
14741475
}
14751476

14761477
case RelocType::kA64AdrAbsEntry: {
1477-
// AArch64: absolute address payload. Relaxes to `adr` (±1MB),
1478-
// `adrp+add` (±4GB), or `ldr Rd, [pc+off]` from the address table.
1478+
// AArch64: absolute address payload. Page-aligned targets reserve one
1479+
// instruction; other targets reserve two in case `adrp+add` is needed.
14791480
uint64_t targetAddress = re->payload();
14801481
uint64_t pc = baseAddress + sectionOffset + sourceOffset;
14811482
uint32_t rd = Support::readU32uLE(buffer + sourceOffset) & 0x1Fu;
@@ -1502,7 +1503,6 @@ Error CodeHolder::relocateToBase(uint64_t baseAddress) noexcept {
15021503

15031504
uint32_t ldrOpcode = 0x58000000u | ((uint32_t(ldrImm19) & 0x7FFFFu) << 5) | rd;
15041505
Support::writeU32uLE(buffer + sourceOffset, ldrOpcode);
1505-
Support::writeU32uLE(buffer + sourceOffset + 4, 0xD503201Fu); // NOP
15061506

15071507
Support::writeU64uLE(addressTableEntryData + atEntryIndex, targetAddress);
15081508
}

cinderx/ThirdParty/asmjit/test/asmjit_test_patching_a64.cpp

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -952,17 +952,15 @@ UNIT(a64_load_addr_reloc_ldr) {
952952
const Section* text = code.textSection();
953953
const uint8_t* buf = text->data();
954954
uint32_t instr0 = readU32LE(buf);
955-
uint32_t instr1 = readU32LE(buf + 4);
956955

957956
// Should be ldr x4, [pc+off]: opcode 0x58000000 | (imm19 << 5) | 4
958-
// The address table is right after the text section. Text = 8 bytes,
959-
// so address table is at offset 8. ldr displacement = 8 - 0 = 8.
957+
// The address table is aligned to offset 8. ldr displacement = 8 - 0 = 8.
960958
// imm19 = 8 / 4 = 2
961959
// ldr x4, [pc+8]: 0x58000000 | (2 << 5) | 4 = 0x58000044
960+
EXPECT_EQ(text->bufferSize(), 4u)
961+
.message("Expected a single ldr instruction");
962962
EXPECT_EQ(instr0, 0x58000044u)
963963
.message("Expected ldr x4, [pc+off], got: 0x%08X", instr0);
964-
EXPECT_EQ(instr1, 0xD503201Fu)
965-
.message("Expected NOP, got: 0x%08X", instr1);
966964
}
967965

968966
// Test load_addr with negative displacement (target address < PC).
@@ -983,15 +981,14 @@ UNIT(a64_load_addr_reloc_adr_negative) {
983981
const Section* text = code.textSection();
984982
const uint8_t* buf = text->data();
985983
uint32_t instr0 = readU32LE(buf);
986-
uint32_t instr1 = readU32LE(buf + 4);
987984

988985
// displacement = -128 = 0xFFFFFF80 (as uint32)
989986
// immLo = 0xFFFFFF80 & 3 = 0, immHi = (0xFFFFFF80 >> 2) & 0x7FFFF = 0x7FFE0
990987
// adr x5: 0x10000000 | (0 << 29) | (0x7FFE0 << 5) | 5 = 0x10FFFC05
988+
EXPECT_EQ(text->bufferSize(), 4u)
989+
.message("Expected a single adr instruction");
991990
EXPECT_EQ(instr0, 0x10FFFC05u)
992991
.message("Expected adr x5, #-128, got: 0x%08X", instr0);
993-
EXPECT_EQ(instr1, 0xD503201Fu)
994-
.message("Expected NOP, got: 0x%08X", instr1);
995992
}
996993

997994
// Test load_addr with a non-zero base address that still fits in adr range.
@@ -1107,7 +1104,7 @@ UNIT(a64_load_addr_reloc_adr_max_negative) {
11071104
.message("Expected adr x7 with displacement -0x100000, got: 0x%08X", instr0);
11081105
}
11091106

1110-
// Test load_addr just past adr range should use adrp+add.
1107+
// Test load_addr just past adr range should use adrp without a zero add.
11111108
UNIT(a64_load_addr_reloc_adr_to_adrp_boundary) {
11121109
CodeHolder code;
11131110
a64::Assembler as;
@@ -1126,14 +1123,39 @@ UNIT(a64_load_addr_reloc_adr_to_adrp_boundary) {
11261123
const uint8_t* buf = text->data();
11271124
uint32_t instr0 = readU32LE(buf);
11281125

1129-
uint32_t instr1 = readU32LE(buf + 4);
1130-
1131-
// Should be adrp+add, not adr, since 0x100000 = 2^20 exceeds 21-bit signed max.
1126+
// Should be adrp, not adr, since 0x100000 = 2^20 exceeds 21-bit signed max.
11321127
// pageDelta = (0x100000 >> 12) - 0 = 0x100, pageOffset = 0
11331128
// adrp x8: 0x90000000 | (0x40 << 5) | 8 = 0x90000808
1134-
// add x8, x8, #0: 0x91000000 | (0 << 10) | (8 << 5) | 8 = 0x91000108
1129+
EXPECT_EQ(text->bufferSize(), 4u)
1130+
.message("Expected a single adrp instruction");
11351131
EXPECT_EQ(instr0, 0x90000808u)
11361132
.message("Expected adrp x8 (just past adr range), got: 0x%08X", instr0);
1137-
EXPECT_EQ(instr1, 0x91000108u)
1138-
.message("Expected add x8, x8, #0, got: 0x%08X", instr1);
1133+
}
1134+
1135+
// Test that Builder accounts for a page-aligned load_addr as one instruction
1136+
// when deciding whether a branch needs relaxation.
1137+
UNIT(a64_load_addr_builder_single_instruction_size) {
1138+
CodeHolder code;
1139+
Environment env(Arch::kAArch64);
1140+
code.init(env, 0);
1141+
1142+
a64::Builder as(&code);
1143+
Label target = as.newLabel();
1144+
as.tbz(a64::x0, 0, target);
1145+
as.load_addr(a64::x1, uint64_t(0x200000));
1146+
for (uint32_t i = 0; i < 8189; i++)
1147+
as.nop();
1148+
as.bind(target);
1149+
as.nop();
1150+
1151+
EXPECT_EQ(as.finalize(), kErrorOk);
1152+
EXPECT_EQ(code.flatten(), kErrorOk);
1153+
EXPECT_EQ(code.relocateToBase(0), kErrorOk);
1154+
1155+
const Section* text = code.textSection();
1156+
const uint8_t* buf = text->data();
1157+
EXPECT_EQ(text->bufferSize(), 32768u)
1158+
.message("Expected tbz and load_addr to remain single instructions");
1159+
EXPECT_EQ(Support::readU32uLE(buf + 4), 0x90001001u)
1160+
.message("Expected page-aligned load_addr immediately after tbz");
11391161
}

0 commit comments

Comments
 (0)