Avoid massive template instantiation in CodeAssembler

Instead of instantiating each function multiple times, just call out to
a common function, passing the variadic number of arguments in an
initializer list.

R=tebbi@chromium.org

Bug: v8:7754
Change-Id: Idb2d77cef7cf8e590de6aa3cea02c0e0773da45f
Reviewed-on: https://chromium-review.googlesource.com/1101689
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53785}
This commit is contained in:
Clemens Hammacher 2018-06-18 11:58:09 +02:00 committed by Commit Bot
parent f67e424d7c
commit 38be8e067a
2 changed files with 120 additions and 144 deletions

View File

@ -24,23 +24,6 @@
#include "src/utils.h" #include "src/utils.h"
#include "src/zone/zone.h" #include "src/zone/zone.h"
#define REPEAT_0_TO_1(V, T0, T) V(T0) V(T0, T)
#define REPEAT_0_TO_2(V, T0, T) REPEAT_0_TO_1(V, T0, T) V(T0, T, T)
#define REPEAT_0_TO_3(V, T0, T) REPEAT_0_TO_2(V, T0, T) V(T0, T, T, T)
#define REPEAT_0_TO_4(V, T0, T) REPEAT_0_TO_3(V, T0, T) V(T0, T, T, T, T)
#define REPEAT_0_TO_5(V, T0, T) REPEAT_0_TO_4(V, T0, T) V(T0, T, T, T, T, T)
#define REPEAT_0_TO_6(V, T0, T) REPEAT_0_TO_5(V, T0, T) V(T0, T, T, T, T, T, T)
#define REPEAT_0_TO_7(V, T0, T) \
REPEAT_0_TO_6(V, T0, T) V(T0, T, T, T, T, T, T, T)
#define REPEAT_0_TO_8(V, T0, T) \
REPEAT_0_TO_7(V, T0, T) V(T0, T, T, T, T, T, T, T, T)
#define REPEAT_0_TO_9(V, T0, T) \
REPEAT_0_TO_8(V, T0, T) V(T0, T, T, T, T, T, T, T, T, T)
#define REPEAT_0_TO_10(V, T0, T) \
REPEAT_0_TO_9(V, T0, T) V(T0, T, T, T, T, T, T, T, T, T, T)
#define REPEAT_0_TO_11(V, T0, T) \
REPEAT_0_TO_10(V, T0, T) V(T0, T, T, T, T, T, T, T, T, T, T, T)
namespace v8 { namespace v8 {
namespace internal { namespace internal {
@ -1066,11 +1049,30 @@ void CodeAssembler::GotoIfException(Node* node, Label* if_exception,
Bind(&success); Bind(&success);
} }
template <class... TArgs> namespace {
TNode<Object> CodeAssembler::CallRuntimeImpl(Runtime::FunctionId function, template <size_t kMaxSize>
TNode<Object> context, class NodeArray {
TArgs... args) { public:
int argc = static_cast<int>(sizeof...(args)); void Add(Node* node) {
DCHECK_GT(kMaxSize, size());
*ptr_++ = node;
}
Node* const* data() const { return arr_; }
int size() const { return static_cast<int>(ptr_ - arr_); }
private:
Node* arr_[kMaxSize];
Node** ptr_ = arr_;
};
} // namespace
TNode<Object> CodeAssembler::CallRuntimeImpl(
Runtime::FunctionId function, TNode<Object> context,
std::initializer_list<TNode<Object>> args) {
constexpr size_t kMaxNumArgs = 6;
DCHECK_GE(kMaxNumArgs, args.size());
int argc = static_cast<int>(args.size());
auto call_descriptor = Linkage::GetRuntimeCallDescriptor( auto call_descriptor = Linkage::GetRuntimeCallDescriptor(
zone(), function, argc, Operator::kNoProperties, zone(), function, argc, Operator::kNoProperties,
CallDescriptor::kNoFlags); CallDescriptor::kNoFlags);
@ -1081,87 +1083,51 @@ TNode<Object> CodeAssembler::CallRuntimeImpl(Runtime::FunctionId function,
Node* ref = ExternalConstant(ExternalReference::Create(function)); Node* ref = ExternalConstant(ExternalReference::Create(function));
Node* arity = Int32Constant(argc); Node* arity = Int32Constant(argc);
Node* nodes[] = {centry, implicit_cast<TNode<Object>>(args)..., ref, arity, NodeArray<kMaxNumArgs + 4> inputs;
context}; inputs.Add(centry);
for (auto arg : args) inputs.Add(arg);
inputs.Add(ref);
inputs.Add(arity);
inputs.Add(context);
CallPrologue(); CallPrologue();
Node* return_value = Node* return_value =
raw_assembler()->CallN(call_descriptor, arraysize(nodes), nodes); raw_assembler()->CallN(call_descriptor, inputs.size(), inputs.data());
CallEpilogue(); CallEpilogue();
return UncheckedCast<Object>(return_value); return UncheckedCast<Object>(return_value);
} }
// Instantiate CallRuntime() for argument counts used by CSA-generated code void CodeAssembler::TailCallRuntimeImpl(
#define INSTANTIATE(...) \ Runtime::FunctionId function, TNode<Int32T> arity, TNode<Object> context,
template V8_EXPORT_PRIVATE TNode<Object> CodeAssembler::CallRuntimeImpl( \ std::initializer_list<TNode<Object>> args) {
Runtime::FunctionId, __VA_ARGS__);
REPEAT_0_TO_6(INSTANTIATE, TNode<Object>, SloppyTNode<Object>)
#undef INSTANTIATE
template <class... TArgs>
void CodeAssembler::TailCallRuntimeImpl(Runtime::FunctionId function,
TNode<Int32T> arity,
TNode<Object> context, TArgs... args) {
int result_size = Runtime::FunctionForId(function)->result_size; int result_size = Runtime::FunctionForId(function)->result_size;
TNode<Code> centry = TNode<Code> centry =
HeapConstant(CodeFactory::RuntimeCEntry(isolate(), result_size)); HeapConstant(CodeFactory::RuntimeCEntry(isolate(), result_size));
return TailCallRuntimeWithCEntryImpl(function, arity, centry, context, return TailCallRuntimeWithCEntryImpl(function, arity, centry, context, args);
implicit_cast<TNode<Object>>(args)...);
} }
// Instantiate TailCallRuntime() for argument counts used by CSA-generated code void CodeAssembler::TailCallRuntimeWithCEntryImpl(
#define INSTANTIATE(...) \ Runtime::FunctionId function, TNode<Int32T> arity, TNode<Code> centry,
template V8_EXPORT_PRIVATE void CodeAssembler::TailCallRuntimeImpl( \ TNode<Object> context, std::initializer_list<TNode<Object>> args) {
Runtime::FunctionId, TNode<Int32T>, __VA_ARGS__); constexpr size_t kMaxNumArgs = 6;
REPEAT_0_TO_6(INSTANTIATE, TNode<Object>, SloppyTNode<Object>) DCHECK_GE(kMaxNumArgs, args.size());
#undef INSTANTIATE int argc = static_cast<int>(args.size());
template <class... TArgs>
void CodeAssembler::TailCallRuntimeWithCEntryImpl(Runtime::FunctionId function,
TNode<Int32T> arity,
TNode<Code> centry,
TNode<Object> context,
TArgs... args) {
int argc = static_cast<int>(sizeof...(args));
auto call_descriptor = Linkage::GetRuntimeCallDescriptor( auto call_descriptor = Linkage::GetRuntimeCallDescriptor(
zone(), function, argc, Operator::kNoProperties, zone(), function, argc, Operator::kNoProperties,
CallDescriptor::kNoFlags); CallDescriptor::kNoFlags);
Node* ref = ExternalConstant(ExternalReference::Create(function)); Node* ref = ExternalConstant(ExternalReference::Create(function));
Node* nodes[] = {centry, args..., ref, arity, context}; NodeArray<kMaxNumArgs + 4> inputs;
inputs.Add(centry);
for (auto arg : args) inputs.Add(arg);
inputs.Add(ref);
inputs.Add(arity);
inputs.Add(context);
raw_assembler()->TailCallN(call_descriptor, arraysize(nodes), nodes); raw_assembler()->TailCallN(call_descriptor, inputs.size(), inputs.data());
} }
// Instantiate TailCallRuntimeWithCEntryImpl() for argument counts used by
// CSA-generated code.
#define INSTANTIATE(...) \
template V8_EXPORT_PRIVATE void \
CodeAssembler::TailCallRuntimeWithCEntryImpl( \
Runtime::FunctionId, TNode<Int32T>, TNode<Code>, __VA_ARGS__);
REPEAT_0_TO_6(INSTANTIATE, TNode<Object>, SloppyTNode<Object>)
#undef INSTANTIATE
template <class... TArgs>
Node* CodeAssembler::CallStubR(const CallInterfaceDescriptor& descriptor,
size_t result_size, SloppyTNode<Code> target,
SloppyTNode<Object> context, TArgs... args) {
Node* nodes[] = {target, args..., context};
int input_count = arraysize(nodes);
if (context == nullptr) --input_count;
return CallStubN(descriptor, result_size, input_count, nodes,
context != nullptr);
}
// Instantiate CallStubR() for argument counts used by CSA-generated code.
#define INSTANTIATE(...) \
template V8_EXPORT_PRIVATE Node* CodeAssembler::CallStubR( \
const CallInterfaceDescriptor& descriptor, size_t, SloppyTNode<Code>, \
__VA_ARGS__);
REPEAT_0_TO_10(INSTANTIATE, SloppyTNode<Object>, Node*)
#undef INSTANTIATE
Node* CodeAssembler::CallStubN(const CallInterfaceDescriptor& descriptor, Node* CodeAssembler::CallStubN(const CallInterfaceDescriptor& descriptor,
size_t result_size, int input_count, size_t result_size, int input_count,
Node* const* inputs, bool pass_context) { Node* const* inputs, bool pass_context) {
@ -1186,55 +1152,66 @@ Node* CodeAssembler::CallStubN(const CallInterfaceDescriptor& descriptor,
return return_value; return return_value;
} }
template <class... TArgs>
void CodeAssembler::TailCallStubImpl(const CallInterfaceDescriptor& descriptor, void CodeAssembler::TailCallStubImpl(const CallInterfaceDescriptor& descriptor,
TNode<Code> target, TNode<Object> context, TNode<Code> target, TNode<Object> context,
TArgs... args) { std::initializer_list<Node*> args) {
DCHECK_EQ(descriptor.GetParameterCount(), sizeof...(args)); constexpr size_t kMaxNumArgs = 11;
DCHECK_GE(kMaxNumArgs, args.size());
DCHECK_EQ(descriptor.GetParameterCount(), args.size());
size_t result_size = 1; size_t result_size = 1;
auto call_descriptor = Linkage::GetStubCallDescriptor( auto call_descriptor = Linkage::GetStubCallDescriptor(
isolate(), zone(), descriptor, descriptor.GetStackParameterCount(), isolate(), zone(), descriptor, descriptor.GetStackParameterCount(),
CallDescriptor::kNoFlags, Operator::kNoProperties, CallDescriptor::kNoFlags, Operator::kNoProperties,
MachineType::AnyTagged(), result_size); MachineType::AnyTagged(), result_size);
Node* nodes[] = {target, args..., context}; NodeArray<kMaxNumArgs + 2> inputs;
CHECK_EQ(descriptor.GetParameterCount() + 2, arraysize(nodes)); inputs.Add(target);
raw_assembler()->TailCallN(call_descriptor, arraysize(nodes), nodes); for (auto arg : args) inputs.Add(arg);
inputs.Add(context);
raw_assembler()->TailCallN(call_descriptor, inputs.size(), inputs.data());
} }
// Instantiate TailCallStub() for argument counts used by CSA-generated code Node* CodeAssembler::CallStubRImpl(const CallInterfaceDescriptor& descriptor,
#define INSTANTIATE(...) \ size_t result_size, SloppyTNode<Code> target,
template V8_EXPORT_PRIVATE void CodeAssembler::TailCallStubImpl( \ SloppyTNode<Object> context,
const CallInterfaceDescriptor& descriptor, TNode<Code>, __VA_ARGS__); std::initializer_list<Node*> args) {
REPEAT_0_TO_11(INSTANTIATE, TNode<Object>, Node*) constexpr size_t kMaxNumArgs = 10;
#undef INSTANTIATE DCHECK_GE(kMaxNumArgs, args.size());
template <class... TArgs> NodeArray<kMaxNumArgs + 2> inputs;
Node* CodeAssembler::TailCallStubThenBytecodeDispatch( inputs.Add(target);
for (auto arg : args) inputs.Add(arg);
if (context) inputs.Add(context);
return CallStubN(descriptor, result_size, inputs.size(), inputs.data(),
context != nullptr);
}
Node* CodeAssembler::TailCallStubThenBytecodeDispatchImpl(
const CallInterfaceDescriptor& descriptor, Node* target, Node* context, const CallInterfaceDescriptor& descriptor, Node* target, Node* context,
TArgs... args) { std::initializer_list<Node*> args) {
DCHECK_LE(descriptor.GetParameterCount(), sizeof...(args)); constexpr size_t kMaxNumArgs = 6;
DCHECK_GE(kMaxNumArgs, args.size());
DCHECK_LE(descriptor.GetParameterCount(), args.size());
int argc = static_cast<int>(args.size());
// Extra arguments not mentioned in the descriptor are passed on the stack. // Extra arguments not mentioned in the descriptor are passed on the stack.
int stack_parameter_count = int stack_parameter_count = argc - descriptor.GetRegisterParameterCount();
sizeof...(args) - descriptor.GetRegisterParameterCount();
DCHECK_LE(descriptor.GetStackParameterCount(), stack_parameter_count); DCHECK_LE(descriptor.GetStackParameterCount(), stack_parameter_count);
auto call_descriptor = Linkage::GetStubCallDescriptor( auto call_descriptor = Linkage::GetStubCallDescriptor(
isolate(), zone(), descriptor, stack_parameter_count, isolate(), zone(), descriptor, stack_parameter_count,
CallDescriptor::kNoFlags, Operator::kNoProperties, CallDescriptor::kNoFlags, Operator::kNoProperties,
MachineType::AnyTagged(), 0); MachineType::AnyTagged(), 0);
Node* nodes[] = {target, args..., context}; NodeArray<kMaxNumArgs + 2> inputs;
return raw_assembler()->TailCallN(call_descriptor, arraysize(nodes), nodes); inputs.Add(target);
} for (auto arg : args) inputs.Add(arg);
inputs.Add(context);
// Instantiate TailCallStubThenBytecodeDispatch() for argument counts used by return raw_assembler()->TailCallN(call_descriptor, inputs.size(),
// CSA-generated code inputs.data());
#define INSTANTIATE(...) \ }
template V8_EXPORT_PRIVATE Node* \
CodeAssembler::TailCallStubThenBytecodeDispatch( \
const CallInterfaceDescriptor&, Node*, Node*, Node*, __VA_ARGS__);
REPEAT_0_TO_6(INSTANTIATE, Node*, Node*)
#undef INSTANTIATE
template <class... TArgs> template <class... TArgs>
Node* CodeAssembler::TailCallBytecodeDispatch( Node* CodeAssembler::TailCallBytecodeDispatch(
@ -1726,15 +1703,3 @@ Smi* CheckObjectType(Object* value, Smi* type, String* location) {
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
#undef REPEAT_0_TO_1
#undef REPEAT_0_TO_2
#undef REPEAT_0_TO_3
#undef REPEAT_0_TO_4
#undef REPEAT_0_TO_5
#undef REPEAT_0_TO_6
#undef REPEAT_0_TO_7
#undef REPEAT_0_TO_8
#undef REPEAT_0_TO_9
#undef REPEAT_0_TO_10
#undef REPEAT_0_TO_11

View File

@ -983,7 +983,7 @@ class V8_EXPORT_PRIVATE CodeAssembler {
TNode<Object> CallRuntime(Runtime::FunctionId function, TNode<Object> CallRuntime(Runtime::FunctionId function,
SloppyTNode<Object> context, TArgs... args) { SloppyTNode<Object> context, TArgs... args) {
return CallRuntimeImpl(function, context, return CallRuntimeImpl(function, context,
implicit_cast<SloppyTNode<Object>>(args)...); {implicit_cast<SloppyTNode<Object>>(args)...});
} }
template <class... TArgs> template <class... TArgs>
@ -992,14 +992,14 @@ class V8_EXPORT_PRIVATE CodeAssembler {
int argc = static_cast<int>(sizeof...(args)); int argc = static_cast<int>(sizeof...(args));
TNode<Int32T> arity = Int32Constant(argc); TNode<Int32T> arity = Int32Constant(argc);
return TailCallRuntimeImpl(function, arity, context, return TailCallRuntimeImpl(function, arity, context,
implicit_cast<SloppyTNode<Object>>(args)...); {implicit_cast<SloppyTNode<Object>>(args)...});
} }
template <class... TArgs> template <class... TArgs>
void TailCallRuntime(Runtime::FunctionId function, TNode<Int32T> arity, void TailCallRuntime(Runtime::FunctionId function, TNode<Int32T> arity,
SloppyTNode<Object> context, TArgs... args) { SloppyTNode<Object> context, TArgs... args) {
return TailCallRuntimeImpl(function, arity, context, return TailCallRuntimeImpl(function, arity, context,
implicit_cast<SloppyTNode<Object>>(args)...); {implicit_cast<SloppyTNode<Object>>(args)...});
} }
template <class... TArgs> template <class... TArgs>
@ -1010,7 +1010,7 @@ class V8_EXPORT_PRIVATE CodeAssembler {
TNode<Int32T> arity = Int32Constant(argc); TNode<Int32T> arity = Int32Constant(argc);
return TailCallRuntimeWithCEntryImpl( return TailCallRuntimeWithCEntryImpl(
function, arity, centry, context, function, arity, centry, context,
implicit_cast<SloppyTNode<Object>>(args)...); {implicit_cast<SloppyTNode<Object>>(args)...});
} }
// //
@ -1021,22 +1021,22 @@ class V8_EXPORT_PRIVATE CodeAssembler {
TNode<T> CallStub(Callable const& callable, SloppyTNode<Object> context, TNode<T> CallStub(Callable const& callable, SloppyTNode<Object> context,
TArgs... args) { TArgs... args) {
TNode<Code> target = HeapConstant(callable.code()); TNode<Code> target = HeapConstant(callable.code());
return CallStub<T>(callable.descriptor(), target, context, return CallStub<T>(callable.descriptor(), target, context, args...);
implicit_cast<Node*>(args)...);
} }
template <class T = Object, class... TArgs> template <class T = Object, class... TArgs>
TNode<T> CallStub(const CallInterfaceDescriptor& descriptor, TNode<T> CallStub(const CallInterfaceDescriptor& descriptor,
SloppyTNode<Code> target, SloppyTNode<Object> context, SloppyTNode<Code> target, SloppyTNode<Object> context,
TArgs... args) { TArgs... args) {
return UncheckedCast<T>(CallStubR(descriptor, 1, target, context, return UncheckedCast<T>(CallStubR(descriptor, 1, target, context, args...));
implicit_cast<Node*>(args)...));
} }
template <class... TArgs> template <class... TArgs>
Node* CallStubR(const CallInterfaceDescriptor& descriptor, size_t result_size, Node* CallStubR(const CallInterfaceDescriptor& descriptor, size_t result_size,
SloppyTNode<Code> target, SloppyTNode<Object> context, SloppyTNode<Code> target, SloppyTNode<Object> context,
TArgs... args); TArgs... args) {
return CallStubRImpl(descriptor, result_size, target, context, {args...});
}
Node* CallStubN(const CallInterfaceDescriptor& descriptor, size_t result_size, Node* CallStubN(const CallInterfaceDescriptor& descriptor, size_t result_size,
int input_count, Node* const* inputs, int input_count, Node* const* inputs,
@ -1053,8 +1053,7 @@ class V8_EXPORT_PRIVATE CodeAssembler {
void TailCallStub(const CallInterfaceDescriptor& descriptor, void TailCallStub(const CallInterfaceDescriptor& descriptor,
SloppyTNode<Code> target, SloppyTNode<Object> context, SloppyTNode<Code> target, SloppyTNode<Object> context,
TArgs... args) { TArgs... args) {
return TailCallStubImpl(descriptor, target, context, return TailCallStubImpl(descriptor, target, context, {args...});
implicit_cast<Node*>(args)...);
} }
template <class... TArgs> template <class... TArgs>
@ -1063,8 +1062,11 @@ class V8_EXPORT_PRIVATE CodeAssembler {
template <class... TArgs> template <class... TArgs>
Node* TailCallStubThenBytecodeDispatch( Node* TailCallStubThenBytecodeDispatch(
const CallInterfaceDescriptor& descriptor, Node* context, Node* target, const CallInterfaceDescriptor& descriptor, Node* target, Node* context,
TArgs... args); TArgs... args) {
return TailCallStubThenBytecodeDispatchImpl(descriptor, target, context,
{args...});
}
// Tailcalls to the given code object with JSCall linkage. The JS arguments // Tailcalls to the given code object with JSCall linkage. The JS arguments
// (including receiver) are supposed to be already on the stack. // (including receiver) are supposed to be already on the stack.
@ -1186,23 +1188,32 @@ class V8_EXPORT_PRIVATE CodeAssembler {
PoisoningMitigationLevel poisoning_level() const; PoisoningMitigationLevel poisoning_level() const;
private: private:
template <class... TArgs>
TNode<Object> CallRuntimeImpl(Runtime::FunctionId function, TNode<Object> CallRuntimeImpl(Runtime::FunctionId function,
TNode<Object> context, TArgs... args); TNode<Object> context,
std::initializer_list<TNode<Object>> args);
template <class... TArgs>
void TailCallRuntimeImpl(Runtime::FunctionId function, TNode<Int32T> arity, void TailCallRuntimeImpl(Runtime::FunctionId function, TNode<Int32T> arity,
TNode<Object> context, TArgs... args); TNode<Object> context,
std::initializer_list<TNode<Object>> args);
template <class... TArgs>
void TailCallRuntimeWithCEntryImpl(Runtime::FunctionId function, void TailCallRuntimeWithCEntryImpl(Runtime::FunctionId function,
TNode<Int32T> arity, TNode<Code> centry, TNode<Int32T> arity, TNode<Code> centry,
TNode<Object> context, TArgs... args); TNode<Object> context,
std::initializer_list<TNode<Object>> args);
template <class... TArgs>
void TailCallStubImpl(const CallInterfaceDescriptor& descriptor, void TailCallStubImpl(const CallInterfaceDescriptor& descriptor,
TNode<Code> target, TNode<Object> context, TNode<Code> target, TNode<Object> context,
TArgs... args); std::initializer_list<Node*> args);
Node* TailCallStubThenBytecodeDispatchImpl(
const CallInterfaceDescriptor& descriptor, Node* target, Node* context,
std::initializer_list<Node*> args);
Node* CallStubRImpl(const CallInterfaceDescriptor& descriptor,
size_t result_size, SloppyTNode<Code> target,
SloppyTNode<Object> context,
std::initializer_list<Node*> args);
// These two don't have definitions and are here only for catching use cases // These two don't have definitions and are here only for catching use cases
// where the cast is not necessary. // where the cast is not necessary.
TNode<Int32T> Signed(TNode<Int32T> x); TNode<Int32T> Signed(TNode<Int32T> x);