Skip to content

Commit b03745b

Browse files
amrshennawimeta-codesync[bot]
authored andcommitted
Revert D109607577: Relax AArch64 b/bl with branch stubs
Differential Revision: D109607577 Original commit changeset: e8c0f43980a9 Original Phabricator Diff: D109607577 fbshipit-source-id: 22aa51cd3f37b8e35a38e7370465d008fca6ac91
1 parent 6fa7d42 commit b03745b

9 files changed

Lines changed: 136 additions & 1028 deletions

File tree

cinderx/Jit/code_allocator.cpp

Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -335,59 +335,48 @@ void CodeAllocatorCinder::ensureSplitSpace(
335335
}
336336

337337
AllocateResult CodeAllocatorCinder::addSplitCode(asmjit::CodeHolder* code) {
338+
// Compute how much space each section type needs.
338339
size_t hot_size = 0;
339340
size_t cold_size = 0;
340-
341-
for (;;) {
342-
// Compute how much space each section type needs.
343-
hot_size = 0;
344-
cold_size = 0;
345-
for (asmjit::Section* section : code->sections()) {
346-
CodeSection cs = codeSectionFromName(section->name());
347-
if (cs == CodeSection::kCold) {
348-
cold_size += section->realSize();
349-
} else {
350-
hot_size += section->realSize();
351-
}
341+
for (asmjit::Section* section : code->sections()) {
342+
CodeSection cs = codeSectionFromName(section->name());
343+
if (cs == CodeSection::kCold) {
344+
cold_size += section->realSize();
345+
} else {
346+
hot_size += section->realSize();
352347
}
348+
}
353349

354-
// Ensure we have enough space for both hot and cold code.
350+
// Ensure we have enough space for both hot and cold code.
355351
#if defined(__aarch64__)
356-
// On ARM64, branch displacements are limited (±128MB for B/BL, ±1MB for
357-
// B.cond). Allocate hot and cold from a single contiguous region so
358-
// cross-section jumps are always in range.
359-
ensureSplitSpace(hot_size, cold_size);
352+
// On ARM64, branch displacements are limited (±128MB for B/BL, ±1MB for
353+
// B.cond). Allocate hot and cold from a single contiguous region so
354+
// cross-section jumps are always in range.
355+
ensureSplitSpace(hot_size, cold_size);
360356
#else
361-
// On x86-64, RIP-relative addressing has a ±2GB range which is large enough
362-
// that independent allocations are unlikely to exceed it in practice.
363-
ensureSpace(hot_alloc_, hot_alloc_free_, hot_size, true);
364-
ensureSpace(
365-
cold_alloc_,
366-
cold_alloc_free_,
367-
cold_size,
368-
getConfig().cold_code_huge_pages);
357+
// On x86-64, RIP-relative addressing has a ±2GB range which is large enough
358+
// that independent allocations are unlikely to exceed it in practice.
359+
ensureSpace(hot_alloc_, hot_alloc_free_, hot_size, true);
360+
ensureSpace(
361+
cold_alloc_,
362+
cold_alloc_free_,
363+
cold_size,
364+
getConfig().cold_code_huge_pages);
369365
#endif
370366

371-
// Fix up offsets for each code section before resolving links.
372-
// All offsets are relative to the hot allocation base so that asmjit can
373-
// resolve cross-section jumps correctly.
374-
size_t hot_offset = 0;
375-
size_t cold_offset = static_cast<size_t>(cold_alloc_ - hot_alloc_);
376-
for (asmjit::Section* section : code->sections()) {
377-
CodeSection cs = codeSectionFromName(section->name());
378-
if (cs == CodeSection::kCold) {
379-
section->setOffset(cold_offset);
380-
cold_offset += section->realSize();
381-
} else {
382-
section->setOffset(hot_offset);
383-
hot_offset += section->realSize();
384-
}
385-
}
386-
387-
bool changed = false;
388-
PROPAGATE_ERROR(code->ensureBranchStubIslands(&changed));
389-
if (!changed) {
390-
break;
367+
// Fix up offsets for each code section before resolving links.
368+
// All offsets are relative to the hot allocation base so that asmjit can
369+
// resolve cross-section jumps correctly.
370+
size_t hot_offset = 0;
371+
size_t cold_offset = static_cast<size_t>(cold_alloc_ - hot_alloc_);
372+
for (asmjit::Section* section : code->sections()) {
373+
CodeSection cs = codeSectionFromName(section->name());
374+
if (cs == CodeSection::kCold) {
375+
section->setOffset(cold_offset);
376+
cold_offset += section->realSize();
377+
} else {
378+
section->setOffset(hot_offset);
379+
hot_offset += section->realSize();
391380
}
392381
}
393382

cinderx/Jit/codegen/autogen.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,8 +1708,8 @@ void translateCall(Environ* env, const Instruction* instr) {
17081708
auto input = instr->getInput(0);
17091709

17101710
if (input->isImm()) {
1711-
// Use bl(imm) so asmjit can pick the final encoding at relocation time:
1712-
// direct bl if within ±128MB, or a branch to an out-of-line stub.
1711+
// Use bl(imm) which leverages asmjit's relaxation to pick the optimal
1712+
// encoding: direct bl if within ±128MB, or ldr+blr via address table.
17131713
as->bl(static_cast<uint64_t>(input->getConstant()));
17141714
} else if (input->isReg()) {
17151715
as->blr(AT::getGp(input));

cinderx/Jit/codegen/code_section.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ const char* codeSectionName(CodeSection section) {
1616
}
1717

1818
CodeSection codeSectionFromName(const char* name) {
19-
if (strcmp(name, ".text") == 0 || strcmp(name, ".addrtab") == 0 ||
20-
strcmp(name, ".a64stubs") == 0) {
19+
if (strcmp(name, ".text") == 0 || strcmp(name, ".addrtab") == 0) {
2120
return CodeSection::kHot;
2221
}
2322
if (strcmp(name, ".coldtext") == 0) {

cinderx/RuntimeTests/branch_relaxation_test.cpp

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,6 @@ using namespace cinderx::jit::codegen;
1515

1616
namespace {
1717

18-
extern "C" uint64_t branchRelaxationReturn42() {
19-
return 42;
20-
}
21-
22-
extern "C" uint64_t branchRelaxationAddOne(uint64_t value) {
23-
return value + 1;
24-
}
25-
2618
class BranchRelaxationTest : public ::testing::Test {
2719
public:
2820
void SetUp() override {
@@ -83,40 +75,6 @@ TEST_F(BranchRelaxationTest, InRangeCondBranch) {
8375
EXPECT_EQ(func(1), 42u);
8476
}
8577

86-
TEST_F(BranchRelaxationTest, AbsoluteCallThroughCodeAllocator) {
87-
asmjit::CodeHolder code;
88-
code.init(code_allocator_->asmJitEnvironment());
89-
arch::Builder as(&code);
90-
91-
as.stp(arch::fp, arch::lr, asmjit::a64::ptr_pre(asmjit::a64::sp, -16));
92-
as.mov(arch::fp, asmjit::a64::sp);
93-
as.bl(reinterpret_cast<uint64_t>(branchRelaxationAddOne));
94-
as.add(asmjit::a64::x0, asmjit::a64::x0, 1);
95-
as.mov(asmjit::a64::sp, arch::fp);
96-
as.ldp(arch::fp, arch::lr, asmjit::a64::ptr_post(asmjit::a64::sp, 16));
97-
as.ret(arch::lr);
98-
99-
void* fn = compileBuilder(as, code);
100-
ASSERT_NE(fn, nullptr);
101-
102-
auto func = reinterpret_cast<uint64_t (*)(uint64_t)>(fn);
103-
EXPECT_EQ(func(41), 43u);
104-
}
105-
106-
TEST_F(BranchRelaxationTest, AbsoluteBranchThroughCodeAllocator) {
107-
asmjit::CodeHolder code;
108-
code.init(code_allocator_->asmJitEnvironment());
109-
arch::Builder as(&code);
110-
111-
as.b(reinterpret_cast<uint64_t>(branchRelaxationReturn42));
112-
113-
void* fn = compileBuilder(as, code);
114-
ASSERT_NE(fn, nullptr);
115-
116-
auto func = reinterpret_cast<uint64_t (*)()>(fn);
117-
EXPECT_EQ(func(), 42u);
118-
}
119-
12078
// tbz has a 14-bit signed immediate (+/-32KB).
12179
TEST_F(BranchRelaxationTest, OutOfRangeTbzIsRelaxed) {
12280
asmjit::CodeHolder code;

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,7 @@ Error Assembler::_emit(InstId instId, const Operand_& o0, const Operand_& o1, co
815815
// is valid. All options that require special handling (including invalid
816816
// instruction) are handled by the next branch.
817817
// Check for < 8 bytes remaining because branch relaxation (cbz/cbnz,
818-
// tbz/tbnz, b.cond, adr) may emit two 32-bit words (8 bytes).
818+
// tbz/tbnz, b.cond, bl/b, adr) may emit two 32-bit words (8 bytes).
819819
InstOptions options = InstOptions(instId - 1 >= Inst::_kIdCount - 1) | InstOptions((size_t)(_bufferEnd - writer.cursor()) < 8) | instOptions() | forcedInstOptions();
820820

821821
CondCode instCC = BaseInst::extractARMCondCode(instId);
@@ -854,7 +854,7 @@ Error Assembler::_emit(InstId instId, const Operand_& o0, const Operand_& o1, co
854854
goto InvalidInstruction;
855855

856856
// Grow request, happens rarely. Reserve 8 bytes because branch relaxation
857-
// (cbz/cbnz, tbz/tbnz, b.cond, adr) may emit two 32-bit words.
857+
// (cbz/cbnz, tbz/tbnz, b.cond, bl/b, adr) may emit two 32-bit words.
858858
err = writer.ensureSpace(this, 8);
859859
if (ASMJIT_UNLIKELY(err))
860860
goto Failed;
@@ -5207,9 +5207,9 @@ Error Assembler::_emit(InstId instId, const Operand_& o0, const Operand_& o1, co
52075207

52085208
size_t codeOffset = writer.offsetFrom(_bufferData);
52095209

5210-
// For b(imm) and bl(imm), use a branch stub to handle out-of-range
5211-
// targets. At emit time we reserve one branch instruction, and during
5212-
// relocation we choose either the target or the stub as its destination.
5210+
// For b(imm) and bl(imm), use address table mechanism to handle out-of-range
5211+
// targets. At emit time we reserve 8 bytes, and during relocation we choose
5212+
// the optimal encoding based on the actual displacement.
52135213
if ((instId == Inst::kIdBl || instId == Inst::kIdB) &&
52145214
offsetFormat.immBitCount() == 26 &&
52155215
(baseAddress == Globals::kNoBaseAddress || _section->id() != 0))
@@ -5235,7 +5235,7 @@ Error Assembler::_emit(InstId instId, const Operand_& o0, const Operand_& o1, co
52355235
pc &= ~uint64_t(4096 - 1);
52365236

52375237
// For b(imm) and bl(imm) when base address is known, try the direct
5238-
// encoding first. If it doesn't fit, use the branch stub mechanism.
5238+
// encoding first. If it doesn't fit, use the address table mechanism.
52395239
if ((instId == Inst::kIdBl || instId == Inst::kIdB) && offsetFormat.immBitCount() == 26) {
52405240
int64_t displacement = int64_t(targetOffset - pc);
52415241
int64_t dispImm = displacement >> 2;
@@ -5281,7 +5281,7 @@ Error Assembler::_emit(InstId instId, const Operand_& o0, const Operand_& o1, co
52815281
}
52825282

52835283
// --------------------------------------------------------------------------
5284-
// [EmitOp - b/bl Relaxation via Branch Stub]
5284+
// [EmitOp - b/bl Relaxation via Address Table]
52855285
// --------------------------------------------------------------------------
52865286

52875287
EmitOp_BranchReloc:
@@ -5295,21 +5295,21 @@ Error Assembler::_emit(InstId instId, const Operand_& o0, const Operand_& o1, co
52955295
if (err)
52965296
goto Failed;
52975297

5298-
size_t codeOffset = writer.offsetFrom(_bufferData);
52995298
uint64_t targetOffset = rmRel->as<Imm>().valueAs<uint64_t>();
5300-
err = _code->addAddressToA64BranchStubTable(targetOffset);
5299+
err = _code->addAddressToAddressTable(targetOffset);
53015300
if (err)
53025301
goto Failed;
53035302

5303+
size_t codeOffset = writer.offsetFrom(_bufferData);
53045304
re->_sourceSectionId = _section->id();
53055305
re->_sourceOffset = codeOffset;
53065306
re->_format = offsetFormat;
53075307
re->_payload = targetOffset;
53085308

5309-
// Emit a 32-bit branch placeholder. During relocation this is patched to
5310-
// either `b/bl target` or `b/bl stub`, where the stub loads the full target
5311-
// address and branches to it.
5312-
writer.emit32uLE(opcode.get());
5309+
// Emit two 32-bit words (placeholder: nop; nop). These will be patched
5310+
// during relocation to either `b/bl target; nop` or `ldr x16, [pc+off]; br/blr x16`.
5311+
writer.emit32uLE(0xD503201F); // NOP
5312+
writer.emit32uLE(0xD503201F); // NOP
53135313
goto EmitDone;
53145314
}
53155315

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ static uint32_t estimateNodeSize(BaseNode* node, uint32_t currentOffset) noexcep
9696
return 8;
9797
if (realId == Inst::kIdLdr && op.isMem() && op.as<Mem>().hasBaseLabel())
9898
return 8;
99+
if ((realId == Inst::kIdB || realId == Inst::kIdBl) && op.isImm())
100+
return 8;
99101
if (realId == Inst::kIdMov && op.isImm() && op.as<Imm>().valueAs<uint64_t>() > 0xFFFF)
100102
return 16;
101103
}
@@ -147,10 +149,9 @@ Error Builder::relaxBranches() {
147149
ZoneVector<uint32_t> labelSections;
148150
ASMJIT_PROPAGATE(labelSections.resize(&_allocator, labelCount));
149151

150-
auto updateNodePositions = [&]() noexcept {
152+
for (;;) {
151153
uint32_t sectionId = 0;
152154
uint32_t offset = 0;
153-
154155
memset(labelSections.data(), 0, labelCount * sizeof(uint32_t));
155156

156157
for (BaseNode* node = firstNode(); node; node = node->next()) {
@@ -170,13 +171,9 @@ Error Builder::relaxBranches() {
170171

171172
offset += size;
172173
}
173-
};
174-
175-
for (;;) {
176-
updateNodePositions();
177174

178175
bool changed = false;
179-
uint32_t sectionId = 0;
176+
sectionId = 0;
180177

181178
for (BaseNode* node = firstNode(); node; node = node->next()) {
182179
if (node->isSection()) {

0 commit comments

Comments
 (0)