Skip to content

Commit 546b0d3

Browse files
committed
Remove mallocs for our lambda passing
This commit works by adding a SmallFunction class that behaves like std::function, but allocates its data inline rather than through a separate allocation. It probably could have also worked by taking a custom allocator and using the new RegionAllocator. It adds a bit more restrictions than std::function does (the types caught by the closure have to be more "trivial" than std::function supports), so some of this change is marking some types as trivial, or copying data into a trivial format from things that aren't (ex SmallVector).
1 parent a496261 commit 546b0d3

File tree

4 files changed

+81
-22
lines changed

4 files changed

+81
-22
lines changed

src/asm_writing/rewriter.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -816,12 +816,30 @@ RewriterVar* Rewriter::call(bool has_side_effects, void* func_addr, const Rewrit
816816
type = ActionType::MUTATION;
817817
else
818818
type = ActionType::NORMAL;
819-
addAction([=]() { this->_call(result, has_side_effects, func_addr, args, args_xmm); }, uses, type);
819+
820+
// It's not nice to pass llvm::SmallVectors through a closure, especially with our SmallFunction
821+
// optimization, so just regionAlloc them and copy the data in:
822+
RewriterVar** _args = (RewriterVar**)this->regionAlloc(sizeof(RewriterVar*) * args.size());
823+
memcpy(_args, args.begin(), sizeof(RewriterVar*) * args.size());
824+
RewriterVar** _args_xmm = (RewriterVar**)this->regionAlloc(sizeof(RewriterVar*) * args_xmm.size());
825+
memcpy(_args_xmm, args_xmm.begin(), sizeof(RewriterVar*) * args_xmm.size());
826+
827+
int args_size = args.size();
828+
assert(args_xmm.size() <= 0x7fff);
829+
// Hack: pack this into a short to make sure it fits in the closure
830+
short xmm_args_size = args_xmm.size();
831+
832+
// Hack: explicitly order the closure arguments so they pad nicer
833+
addAction([args_size, xmm_args_size, has_side_effects, this, result, func_addr, _args, _args_xmm]() {
834+
this->_call(result, has_side_effects, func_addr, llvm::ArrayRef<RewriterVar*>(_args, args_size),
835+
llvm::ArrayRef<RewriterVar*>(_args_xmm, xmm_args_size));
836+
}, uses, type);
837+
820838
return result;
821839
}
822840

823-
void Rewriter::_setupCall(bool has_side_effects, const RewriterVar::SmallVector& args,
824-
const RewriterVar::SmallVector& args_xmm) {
841+
void Rewriter::_setupCall(bool has_side_effects, llvm::ArrayRef<RewriterVar*> args,
842+
llvm::ArrayRef<RewriterVar*> args_xmm) {
825843
if (has_side_effects)
826844
assert(done_guarding);
827845

@@ -966,8 +984,8 @@ void Rewriter::_setupCall(bool has_side_effects, const RewriterVar::SmallVector&
966984
#endif
967985
}
968986

969-
void Rewriter::_call(RewriterVar* result, bool has_side_effects, void* func_addr, const RewriterVar::SmallVector& args,
970-
const RewriterVar::SmallVector& args_xmm) {
987+
void Rewriter::_call(RewriterVar* result, bool has_side_effects, void* func_addr, llvm::ArrayRef<RewriterVar*> args,
988+
llvm::ArrayRef<RewriterVar*> args_xmm) {
971989
assembler->comment("_call");
972990

973991
// RewriterVarUsage scratch = createNewVar(Location::any());

src/asm_writing/rewriter.h

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,9 @@ struct Location {
6969
int32_t _data;
7070
};
7171

72-
constexpr Location() : type(Uninitialized), _data(-1) {}
73-
constexpr Location(const Location& r) : type(r.type), _data(r._data) {}
74-
Location operator=(const Location& r) {
75-
type = r.type;
76-
_data = r._data;
77-
return *this;
78-
}
72+
constexpr Location() noexcept : type(Uninitialized), _data(-1) {}
73+
constexpr Location(const Location& r) = default;
74+
Location& operator=(const Location& r) = default;
7975

8076
constexpr Location(LocationType type, int32_t data) : type(type), _data(data) {}
8177

@@ -283,11 +279,50 @@ class RewriterVar {
283279
friend class JitFragmentWriter;
284280
};
285281

282+
// A utility class that is similar to std::function, but stores any closure data inline rather
283+
// than in a separate allocation. It's similar to SmallVector, but will just fail to compile if
284+
// you try to put more bytes in than you allocated.
285+
// Currently, it only works for functions with the signature "void()"
286+
template <int N = 24> class SmallFunction {
287+
private:
288+
void (*func)(void*);
289+
char data[N];
290+
291+
template <typename Functor> struct Caller {
292+
static void call(void* data) { (*(Functor*)data)(); }
293+
};
294+
295+
public:
296+
template <typename Functor> SmallFunction(Functor&& f) noexcept {
297+
static_assert(std::has_trivial_copy_constructor<typename std::remove_reference<Functor>::type>::value,
298+
"SmallFunction currently only works with simple types");
299+
static_assert(std::is_trivially_destructible<typename std::remove_reference<Functor>::type>::value,
300+
"SmallFunction currently only works with simple types");
301+
static_assert(sizeof(Functor) <= sizeof(data), "Please increase N");
302+
new (data) typename std::remove_reference<Functor>::type(std::forward<Functor>(f));
303+
func = Caller<Functor>::call;
304+
}
305+
306+
SmallFunction() = default;
307+
SmallFunction(const SmallFunction<N>& rhs) = default;
308+
SmallFunction(SmallFunction<N>&& rhs) = default;
309+
SmallFunction& operator=(const SmallFunction<N>& rhs) = default;
310+
SmallFunction& operator=(SmallFunction<N>&& rhs) = default;
311+
312+
void operator()() { func(data); }
313+
};
314+
286315
class RewriterAction {
287316
public:
288-
std::function<void()> action;
317+
SmallFunction<48> action;
318+
319+
template <typename F> RewriterAction(F&& action) : action(std::forward<F>(action)) {}
289320

290-
RewriterAction(std::function<void()> f) : action(std::move(f)) {}
321+
RewriterAction() = default;
322+
RewriterAction(const RewriterAction& rhs) = default;
323+
RewriterAction(RewriterAction&& rhs) = default;
324+
RewriterAction& operator=(const RewriterAction& rhs) = default;
325+
RewriterAction& operator=(RewriterAction&& rhs) = default;
291326
};
292327

293328
enum class ActionType { NORMAL, GUARD, MUTATION };
@@ -449,10 +484,9 @@ class Rewriter : public ICSlotRewrite::CommitHook {
449484

450485
void _trap();
451486
void _loadConst(RewriterVar* result, int64_t val);
452-
void _setupCall(bool has_side_effects, const RewriterVar::SmallVector& args,
453-
const RewriterVar::SmallVector& args_xmm);
454-
void _call(RewriterVar* result, bool has_side_effects, void* func_addr, const RewriterVar::SmallVector& args,
455-
const RewriterVar::SmallVector& args_xmm);
487+
void _setupCall(bool has_side_effects, llvm::ArrayRef<RewriterVar*> args, llvm::ArrayRef<RewriterVar*> args_xmm);
488+
void _call(RewriterVar* result, bool has_side_effects, void* func_addr, llvm::ArrayRef<RewriterVar*> args,
489+
llvm::ArrayRef<RewriterVar*> args_xmm);
456490
void _add(RewriterVar* result, RewriterVar* a, int64_t b, Location dest);
457491
int _allocate(RewriterVar* result, int n);
458492
void _allocateAndCopy(RewriterVar* result, RewriterVar* array, int n);

src/codegen/baseline_jit.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -668,8 +668,15 @@ RewriterVar* JitFragmentWriter::emitPPCall(void* func_addr, llvm::ArrayRef<Rewri
668668
RewriterVar::SmallVector args_vec(args.begin(), args.end());
669669
#if ENABLE_BASELINEJIT_ICS
670670
RewriterVar* result = createNewVar();
671-
addAction([=]() { this->_emitPPCall(result, func_addr, args_vec, num_slots, slot_size); }, args,
672-
ActionType::NORMAL);
671+
672+
int args_size = args.size();
673+
RewriterVar** _args = (RewriterVar**)regionAlloc(sizeof(RewriterVar*) * args_size);
674+
memcpy(_args, args.begin(), sizeof(RewriterVar*) * args_size);
675+
676+
addAction([=]() {
677+
this->_emitPPCall(result, func_addr, llvm::ArrayRef<RewriterVar*>(_args, args_size), num_slots, slot_size);
678+
}, args, ActionType::NORMAL);
679+
673680
if (type_recorder) {
674681
RewriterVar* type_recorder_var = imm(type_recorder);
675682
RewriterVar* obj_cls_var = result->getAttr(offsetof(Box, cls));
@@ -813,7 +820,7 @@ void JitFragmentWriter::_emitOSRPoint(RewriterVar* result, RewriterVar* node_var
813820
assertConsistent();
814821
}
815822

816-
void JitFragmentWriter::_emitPPCall(RewriterVar* result, void* func_addr, const RewriterVar::SmallVector& args,
823+
void JitFragmentWriter::_emitPPCall(RewriterVar* result, void* func_addr, llvm::ArrayRef<RewriterVar*> args,
817824
int num_slots, int slot_size) {
818825
assembler::Register r = allocReg(assembler::R11);
819826

src/codegen/baseline_jit.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ class JitFragmentWriter : public Rewriter {
294294
void _emitGetLocal(RewriterVar* val_var, const char* name);
295295
void _emitJump(CFGBlock* b, RewriterVar* block_next, int& size_of_exit_to_interp);
296296
void _emitOSRPoint(RewriterVar* result, RewriterVar* node_var);
297-
void _emitPPCall(RewriterVar* result, void* func_addr, const RewriterVar::SmallVector& args, int num_slots,
297+
void _emitPPCall(RewriterVar* result, void* func_addr, llvm::ArrayRef<RewriterVar*> args, int num_slots,
298298
int slot_size);
299299
void _emitRecordType(RewriterVar* type_recorder_var, RewriterVar* obj_cls_var);
300300
void _emitReturn(RewriterVar* v);

0 commit comments

Comments
 (0)