Skip to content

Commit 488ef6b

Browse files
alexmalyshevfacebook-github-bot
authored andcommitted
Move CodeAllocator into the ModuleState
Summary: Requires adding a new ICodeAllocator interface, and then every user can access it via the module state. Tweaked the interface to not use an outparam for addCode(), it now returns a struct of the address and the error code. Reviewed By: DinoV Differential Revision: D78522984 fbshipit-source-id: e7972f7eb6c5ff9446a3fbb738159f5abf758701
1 parent 8457ba1 commit 488ef6b

10 files changed

Lines changed: 157 additions & 107 deletions

File tree

Jit/code_allocator.cpp

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "cinderx/Jit/code_allocator.h"
44

5+
#include "cinderx/Common/log.h"
56
#include "cinderx/Jit/config.h"
67
#include "cinderx/Jit/threaded_compile.h"
78

@@ -16,6 +17,11 @@ using codegen::codeSectionFromName;
1617

1718
namespace {
1819

20+
#define PROPAGATE_ERROR(EXPR) \
21+
if (asmjit::Error err = (EXPR); err != asmjit::kErrorOk) { \
22+
return AllocateResult{nullptr, err}; \
23+
}
24+
1925
// 2MiB to match Linux's huge-page size.
2026
constexpr size_t kAllocSize = 1024 * 1024 * 2;
2127

@@ -51,26 +57,24 @@ bool setHugePages([[maybe_unused]] void* ptr, [[maybe_unused]] size_t size) {
5157

5258
} // namespace
5359

54-
CodeAllocator* CodeAllocator::s_global_code_allocator_ = nullptr;
55-
56-
CodeAllocator::~CodeAllocator() {}
57-
58-
void CodeAllocator::makeGlobalCodeAllocator() {
59-
JIT_CHECK(
60-
s_global_code_allocator_ == nullptr, "Global allocator already set");
60+
ICodeAllocator* CodeAllocator::make() {
6161
if (getConfig().multiple_code_sections) {
62-
s_global_code_allocator_ = new MultipleSectionCodeAllocator;
62+
return new MultipleSectionCodeAllocator{};
6363
} else if (getConfig().use_huge_pages) {
64-
s_global_code_allocator_ = new CodeAllocatorCinder;
65-
} else {
66-
s_global_code_allocator_ = new CodeAllocator;
64+
return new CodeAllocatorCinder{};
6765
}
66+
return new CodeAllocator{};
6867
}
6968

70-
void CodeAllocator::freeGlobalCodeAllocator() {
71-
JIT_CHECK(s_global_code_allocator_ != nullptr, "Global allocator not set");
72-
delete s_global_code_allocator_;
73-
s_global_code_allocator_ = nullptr;
69+
AllocateResult CodeAllocator::addCode(asmjit::CodeHolder* code) {
70+
void* addr = nullptr;
71+
asmjit::Error error = runtime_.add(&addr, code);
72+
73+
if (addr != nullptr && error == asmjit::kErrorOk) {
74+
used_bytes_.fetch_add(code->codeSize(), std::memory_order_relaxed);
75+
}
76+
77+
return AllocateResult{addr, error};
7478
}
7579

7680
bool CodeAllocator::contains(const void* ptr) const {
@@ -81,22 +85,26 @@ bool CodeAllocator::contains(const void* ptr) const {
8185
asmjit::kErrorOk;
8286
}
8387

88+
size_t CodeAllocator::usedBytes() const {
89+
return used_bytes_.load(std::memory_order_relaxed);
90+
}
91+
92+
const asmjit::Environment& CodeAllocator::asmJitEnvironment() const {
93+
return runtime_.environment();
94+
}
95+
8496
CodeAllocatorCinder::~CodeAllocatorCinder() {
8597
for (std::span<uint8_t> alloc : allocations_) {
8698
JIT_CHECK(
8799
munmap(alloc.data(), alloc.size()) == 0, "Freeing code memory failed");
88100
}
89101
}
90102

91-
asmjit::Error CodeAllocatorCinder::addCode(
92-
void** dst,
93-
asmjit::CodeHolder* code) noexcept {
103+
AllocateResult CodeAllocatorCinder::addCode(asmjit::CodeHolder* code) {
94104
ThreadedCompileSerialize guard;
95105

96-
*dst = nullptr;
97-
98-
ASMJIT_PROPAGATE(code->flatten());
99-
ASMJIT_PROPAGATE(code->resolveUnresolvedLinks());
106+
PROPAGATE_ERROR(code->flatten());
107+
PROPAGATE_ERROR(code->resolveUnresolvedLinks());
100108

101109
size_t max_code_size = code->codeSize();
102110
size_t alloc_size = ((max_code_size / kAllocSize) + 1) * kAllocSize;
@@ -114,7 +122,7 @@ asmjit::Error CodeAllocatorCinder::addCode(
114122
current_alloc_free_ = alloc_size;
115123
}
116124

117-
ASMJIT_PROPAGATE(code->relocateToBase(uintptr_t(current_alloc_)));
125+
PROPAGATE_ERROR(code->relocateToBase(uintptr_t(current_alloc_)));
118126

119127
size_t actual_code_size = code->codeSize();
120128
JIT_CHECK(actual_code_size <= max_code_size, "Code grew during relocation");
@@ -136,13 +144,13 @@ asmjit::Error CodeAllocatorCinder::addCode(
136144
}
137145
}
138146

139-
*dst = current_alloc_;
147+
void* addr = current_alloc_;
140148

141149
current_alloc_ += actual_code_size;
142150
current_alloc_free_ -= actual_code_size;
143151
used_bytes_ += actual_code_size;
144152

145-
return asmjit::kErrorOk;
153+
return AllocateResult{addr, asmjit::kErrorOk};
146154
}
147155

148156
bool CodeAllocatorCinder::contains(const void* ptr) const {
@@ -196,15 +204,12 @@ void MultipleSectionCodeAllocator::createSlabs() noexcept {
196204
code_sections_[CodeSection::kCold] = region;
197205
}
198206

199-
asmjit::Error MultipleSectionCodeAllocator::addCode(
200-
void** dst,
201-
asmjit::CodeHolder* code) noexcept {
207+
AllocateResult MultipleSectionCodeAllocator::addCode(asmjit::CodeHolder* code) {
202208
ThreadedCompileSerialize guard;
203209

204210
if (code_sections_.empty()) {
205211
createSlabs();
206212
}
207-
*dst = nullptr;
208213

209214
size_t potential_code_size = code->codeSize();
210215
used_bytes_ += potential_code_size;
@@ -216,8 +221,11 @@ asmjit::Error MultipleSectionCodeAllocator::addCode(
216221
JIT_LOG(
217222
"Not enough memory to split code across sections, falling back to "
218223
"normal allocation.");
219-
return runtime_.add(dst, code);
224+
void* addr = nullptr;
225+
asmjit::Error err = runtime_.add(&addr, code);
226+
return AllocateResult{addr, err};
220227
}
228+
221229
// Fix up the offsets for each code section before resolving links.
222230
// Both the `.text` and `.addrtab` sections are written to the hot section,
223231
// and we need to resolve offsets between them properly.
@@ -242,16 +250,16 @@ asmjit::Error MultipleSectionCodeAllocator::addCode(
242250

243251
// Assuming that the offsets are set properly, relocating all code to be
244252
// relative to the start of the hot code will ensure jumps are correct.
245-
ASMJIT_PROPAGATE(code->resolveUnresolvedLinks());
246-
ASMJIT_PROPAGATE(
253+
PROPAGATE_ERROR(code->resolveUnresolvedLinks());
254+
PROPAGATE_ERROR(
247255
code->relocateToBase(uintptr_t(code_sections_[CodeSection::kHot])));
248256

249257
// We assume that the hot section of the code is non-empty. This would be
250258
// incorrect for a completely cold function.
251259
JIT_CHECK(
252260
code->textSection()->realSize() > 0,
253261
"Every function must have a non-empty hot section.");
254-
*dst = code_sections_[CodeSection::kHot];
262+
void* addr = code_sections_[CodeSection::kHot];
255263

256264
for (asmjit::Section* section : code->_sections) {
257265
size_t buffer_size = section->bufferSize();
@@ -265,7 +273,7 @@ asmjit::Error MultipleSectionCodeAllocator::addCode(
265273
code_sections_[code_section] += buffer_size;
266274
}
267275

268-
return asmjit::kErrorOk;
276+
return AllocateResult{addr, asmjit::kErrorOk};
269277
}
270278

271279
bool MultipleSectionCodeAllocator::contains(const void* ptr) const {

Jit/code_allocator.h

Lines changed: 13 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
#pragma once
44

5-
#include "cinderx/Common/log.h"
5+
#include "cinderx/Jit/code_allocator_iface.h"
66
#include "cinderx/Jit/codegen/code_section.h"
77

88
#include <asmjit/asmjit.h>
99

1010
#include <atomic>
11-
#include <memory>
1211
#include <span>
12+
#include <unordered_map>
1313
#include <vector>
1414

1515
namespace jit {
@@ -27,59 +27,29 @@ namespace jit {
2727
like accommodate memory pools with different allocation characteristics, or
2828
have multiple threads which might compile independently.
2929
*/
30-
class CodeAllocator {
30+
class CodeAllocator : public ICodeAllocator {
3131
public:
32-
virtual ~CodeAllocator();
33-
34-
// Get the global code allocator for this process.
35-
static CodeAllocator* get() {
36-
JIT_CHECK(exists(), "No global code allocator");
37-
return s_global_code_allocator_;
38-
}
39-
40-
// Check if the global code allocator has been created.
41-
static bool exists() {
42-
return s_global_code_allocator_ != nullptr;
43-
}
32+
~CodeAllocator() override = default;
4433

4534
// To be called once by JIT initialization after enough configuration has been
4635
// loaded to determine which global code allocator type to use.
47-
static void makeGlobalCodeAllocator();
48-
49-
static void freeGlobalCodeAllocator();
50-
51-
size_t usedBytes() const {
52-
return used_bytes_;
53-
}
54-
55-
const asmjit::Environment& asmJitEnvironment() const {
56-
return runtime_.environment();
57-
}
36+
static ICodeAllocator* make();
5837

59-
virtual asmjit::Error addCode(void** dst, asmjit::CodeHolder* code) noexcept {
60-
used_bytes_ += code->codeSize();
61-
return runtime_.add(dst, code);
62-
}
63-
64-
// Check if a pointer is located within this allocator's memory.
65-
virtual bool contains(const void* ptr) const;
38+
AllocateResult addCode(asmjit::CodeHolder* code) override;
39+
bool contains(const void* ptr) const override;
40+
size_t usedBytes() const override;
41+
const asmjit::Environment& asmJitEnvironment() const override;
6642

6743
protected:
6844
asmjit::JitRuntime runtime_;
6945
std::atomic<size_t> used_bytes_{0};
70-
71-
private:
72-
static CodeAllocator* s_global_code_allocator_;
7346
};
7447

7548
// A code allocator which tries to allocate all code on huge pages.
7649
class CodeAllocatorCinder : public CodeAllocator {
7750
public:
7851
~CodeAllocatorCinder() override;
7952

80-
asmjit::Error addCode(void** dst, asmjit::CodeHolder* code) noexcept override;
81-
bool contains(const void* ptr) const override;
82-
8353
size_t lostBytes() const {
8454
return lost_bytes_;
8555
}
@@ -92,6 +62,9 @@ class CodeAllocatorCinder : public CodeAllocator {
9262
return huge_allocs_;
9363
}
9464

65+
AllocateResult addCode(asmjit::CodeHolder* code) override;
66+
bool contains(const void* ptr) const override;
67+
9568
private:
9669
// List of chunks allocated for use in deallocation
9770
std::vector<std::span<uint8_t>> allocations_;
@@ -114,7 +87,7 @@ class MultipleSectionCodeAllocator : public CodeAllocator {
11487
public:
11588
~MultipleSectionCodeAllocator() override;
11689

117-
asmjit::Error addCode(void** dst, asmjit::CodeHolder* code) noexcept override;
90+
AllocateResult addCode(asmjit::CodeHolder* code) override;
11891
bool contains(const void* ptr) const override;
11992

12093
private:

Jit/code_allocator_iface.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) Meta Platforms, Inc. and affiliates.
2+
3+
#pragma once
4+
5+
#include <asmjit/asmjit.h>
6+
7+
#include <cstddef>
8+
9+
namespace jit {
10+
11+
// Resulting address and status code after calling ICodeAllocator::addCode().
12+
struct AllocateResult {
13+
void* addr{nullptr};
14+
asmjit::Error error{asmjit::kErrorNotInitialized};
15+
};
16+
17+
// Interface for allocating memory for compiled code generated by the JIT.
18+
class ICodeAllocator {
19+
public:
20+
virtual ~ICodeAllocator() = default;
21+
22+
// Add a compiled code object to the allocator. Get its new address on
23+
// success, or nullptr and an error on failure.
24+
virtual AllocateResult addCode(asmjit::CodeHolder* code) = 0;
25+
26+
// Check if a pointer is located within this allocator's memory.
27+
virtual bool contains(const void* ptr) const = 0;
28+
29+
// Get the number of bytes of memory used by the allocator.
30+
virtual size_t usedBytes() const = 0;
31+
32+
// Get the asmjit environment used by this allocator.
33+
virtual const asmjit::Environment& asmJitEnvironment() const = 0;
34+
};
35+
36+
} // namespace jit

Jit/codegen/gen_asm.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
#include "cinderx/Common/py-portability.h"
2121
#include "cinderx/Common/util.h"
2222
#include "cinderx/Interpreter/interpreter.h"
23-
#include "cinderx/Jit/code_allocator.h"
2423
#include "cinderx/Jit/codegen/autogen.h"
2524
#include "cinderx/Jit/codegen/code_section.h"
2625
#include "cinderx/Jit/codegen/gen_asm_utils.h"
@@ -303,17 +302,16 @@ void* finalizeCode(asmjit::x86::Builder& builder, std::string_view name) {
303302
DebugUtils::errorAsString(err))};
304303
}
305304

306-
void* result = nullptr;
307-
if (auto err = CodeAllocator::get()->addCode(&result, builder.code());
308-
err != kErrorOk) {
305+
ICodeAllocator* code_allocator = cinderx::getModuleState()->codeAllocator();
306+
AllocateResult result = code_allocator->addCode(builder.code());
307+
if (result.error != kErrorOk) {
309308
throw std::runtime_error{fmt::format(
310309
"Failed to add generated code for {} to asmjit runtime, got error code "
311310
"{}",
312311
name,
313-
DebugUtils::errorAsString(err))};
312+
DebugUtils::errorAsString(result.error))};
314313
}
315-
316-
return result;
314+
return result.addr;
317315
}
318316

319317
// Generate the final stage trampoline that is responsible for finishing
@@ -323,7 +321,8 @@ void* generateDeoptTrampoline(bool generator_mode) {
323321
generator_mode ? "deopt_trampoline_generators" : "deopt_trampoline";
324322

325323
CodeHolder code;
326-
ASM_CHECK(code.init(CodeAllocator::get()->asmJitEnvironment()), name);
324+
ICodeAllocator* code_allocator = cinderx::getModuleState()->codeAllocator();
325+
ASM_CHECK(code.init(code_allocator->asmJitEnvironment()), name);
327326
x86::Builder a(&code);
328327
Annotations annot;
329328

@@ -511,7 +510,8 @@ void* generateDeoptTrampoline(bool generator_mode) {
511510

512511
void* generateFailedDeferredCompileTrampoline() {
513512
CodeHolder code;
514-
code.init(CodeAllocator::get()->asmJitEnvironment());
513+
ICodeAllocator* code_allocator = cinderx::getModuleState()->codeAllocator();
514+
code.init(code_allocator->asmJitEnvironment());
515515
x86::Builder a(&code);
516516
Annotations annot;
517517

@@ -660,7 +660,8 @@ void* NativeGenerator::getVectorcallEntry() {
660660
JIT_CHECK(as_ == nullptr, "x86::Builder should not have been initialized.");
661661

662662
CodeHolder code;
663-
code.init(CodeAllocator::get()->asmJitEnvironment());
663+
ICodeAllocator* code_allocator = cinderx::getModuleState()->codeAllocator();
664+
code.init(code_allocator->asmJitEnvironment());
664665
ThrowableErrorHandler eh;
665666
code.setErrorHandler(&eh);
666667

0 commit comments

Comments
 (0)