Skip to content

Commit 4ede630

Browse files
committed
[cpyrt] Take element type of std::initializer_list args from the class
Almost every constructor of a standard container has a std::initializer_list<T> argument, and each of them resolved a type from its spelling, which declares new code: - GetPriority() resolved the argument name through GetScope(), which deliberately maps std::initializer_list<T> to std::vector<T>, a mapping the ranking of overloads relies on. This declared a new trampoline variable on every call. - CreateConverter() and the InitializerListConverter resolved the element type T from a substring of the type name. Take T from the template arguments of the std::initializer_list class instead, and instantiate std::vector<T> from it directly for the ranking. Names like std::vector<std::initializer_list<int>>, where the list is not the argument class itself, still go through GetScope(). Add interop wrappers for Cpp::GetClassTemplateInstantiationArgs() and Cpp::GetUnderlyingType(). For example: import time import cppjit cppjit.cppdef("#include <set>\n#include <list>\n#include <vector>\n#include <utility>\n") types = ["int", "long", "short", "char", "float", "double", "unsigned int", "unsigned long"] pairs = [f"std::pair<{a}, {b}>" for a, b in [("int", "int"), ("long", "double"), ("double", "double"), ("short", "short"), ("float", "float"), ("long", "long"), ("unsigned int", "unsigned long"), ("char", "char")]] classes = ([cppjit.gbl.std.set[t] for t in types] + [cppjit.gbl.std.list[t] for t in types] + [cppjit.gbl.std.vector[p] for p in pairs]) start = time.perf_counter() for cls in classes: cls() # first construction of each class print(f"first construction of {len(classes)} container classes: {1000 * (time.perf_counter() - start):.0f} ms") This takes about 645 ms instead of 690 ms (median of 3 trials, Linux x86_64, Python 3.14, CppInterOp/LLVM 22.1.8). The savings grow with the number of distinct container instantiations created for the first time.
1 parent c834f24 commit 4ede630

5 files changed

Lines changed: 69 additions & 8 deletions

File tree

‎src/cpyrt/CPPMethod.cxx‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,29 @@ int cpyrt::CPPMethod::GetPriority() {
525525

526526
// prefer more derived classes
527527
const std::string& clean_name = TypeManip::clean_type(aname, false);
528-
interop::TCppScope_t scope = interop::GetScope(clean_name);
528+
interop::TCppScope_t scope;
529+
if (clean_name.find('<') != std::string::npos &&
530+
clean_name.find("initializer_list") != std::string::npos) {
531+
// GetScope() deliberately resolves std::initializer_list<T> as
532+
// std::vector<T>, which the ranking relies on; but resolving the
533+
// spelling declares a new trampoline on every call. Take the element
534+
// type from the argument class and instantiate std::vector<T> from
535+
// it directly. Names where the list is not the argument class itself
536+
// (e.g. std::vector<std::initializer_list<int>>) still go through
537+
// GetScope().
538+
interop::TCppScope_t list =
539+
interop::GetScopeFromType(interop::GetUnderlyingType(
540+
interop::GetMethodArgType(fMethod, iarg)));
541+
std::vector<Cpp::TemplateArgInfo> args;
542+
if (list && interop::GetName(list) == "initializer_list")
543+
interop::GetClassTemplateInstantiationArgs(list, args);
544+
scope = args.size() == 1
545+
? interop::InstantiateTemplate(
546+
interop::GetNamed("vector", interop::GetNamed("std")),
547+
args.data(), args.size())
548+
: interop::GetScope(clean_name);
549+
} else
550+
scope = interop::GetScope(clean_name);
529551
if (scope)
530552
priority += static_cast<int>(interop::GetNumBasesLongestBranch(scope));
531553

‎src/cpyrt/Converters.cxx‎

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3230,9 +3230,23 @@ struct faux_initlist {
32303230
cpyrt::InitializerListConverter::InitializerListConverter(
32313231
interop::TCppScope_t klass, std::string const& value_type)
32323232

3233+
: InitializerListConverter{klass, value_type,
3234+
interop::GetType(value_type, true)} {}
3235+
3236+
cpyrt::InitializerListConverter::InitializerListConverter(
3237+
interop::TCppScope_t klass, std::string const& value_type,
3238+
interop::TCppType_t value_ctype)
3239+
32333240
: InstanceConverter{klass}, fValueTypeName{value_type},
3234-
fValueType{interop::GetScope(value_type)},
3235-
fValueSize{interop::SizeOfType(interop::GetType(value_type, true))} {}
3241+
fValueType{interop::IsClassType(value_ctype)
3242+
? interop::GetScopeFromType(value_ctype)
3243+
: nullptr},
3244+
fValueSize{0} {
3245+
// like GetScope(), make sure a class template instance is defined
3246+
if (fValueType)
3247+
interop::IsComplete(fValueType);
3248+
fValueSize = interop::SizeOfType(value_ctype);
3249+
}
32363250

32373251
cpyrt::InitializerListConverter::~InitializerListConverter() {
32383252
for (Converter* converter : fConverters) {
@@ -3781,14 +3795,21 @@ cppjit::cpyrt::CreateConverter(interop::TCppType_t type, cdims_t dims) {
37813795

37823796
//-- special case: initializer list
37833797
if (realTypeStr.compare(0, 21, "std::initializer_list") == 0) {
3784-
// get the type of the list and create a converter (TODO: get hold of
3785-
// value_type?)
3798+
// get the type of the list and create a converter
37863799
auto pos = realTypeStr.find('<');
37873800
std::string value_type =
37883801
realTypeStr.substr(pos + 1, realTypeStr.size() - pos - 2);
3802+
// take the element type from the class, as resolving its spelling
3803+
// declares new code
3804+
interop::TCppScope_t list = interop::GetScopeFromType(realType);
3805+
std::vector<Cpp::TemplateArgInfo> args;
3806+
interop::GetClassTemplateInstantiationArgs(list, args);
3807+
interop::TCppType_t value_ctype = args.size() == 1
3808+
? interop::TCppType_t(args[0].m_Type)
3809+
: interop::GetType(value_type, true);
37893810
Converter* cnv = nullptr;
37903811
bool use_byte_cnv = false;
3791-
if (cpd == "" && interop::GetScope(value_type)) {
3812+
if (cpd == "" && interop::IsClassType(value_ctype)) {
37923813
// initializer list of object values does not work as the target is raw
37933814
// memory; simply use byte copies
37943815

@@ -3797,8 +3818,7 @@ cppjit::cpyrt::CreateConverter(interop::TCppType_t type, cdims_t dims) {
37973818
} else
37983819
cnv = CreateConverter(value_type);
37993820
if (cnv || use_byte_cnv)
3800-
return new InitializerListConverter(interop::GetScopeFromType(realType),
3801-
value_type);
3821+
return new InitializerListConverter(list, value_type, value_ctype);
38023822
}
38033823

38043824
//-- still nothing? use a generalized converter

‎src/cpyrt/DeclareConverters.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,9 @@ class InitializerListConverter : public InstanceConverter {
515515
public:
516516
InitializerListConverter(interop::TCppScope_t klass,
517517
std::string const& value_type);
518+
InitializerListConverter(interop::TCppScope_t klass,
519+
std::string const& value_type,
520+
interop::TCppType_t value_ctype);
518521
InitializerListConverter(const InitializerListConverter&) = delete;
519522
InitializerListConverter& operator=(const InitializerListConverter&) = delete;
520523
virtual ~InitializerListConverter();

‎src/interop/cppjit_interop.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ std::string ResolveName(const std::string& cppitem_name);
5757
RPY_EXPORTED
5858
TCppType_t ResolveType(TCppType_t cppitem_name);
5959
RPY_EXPORTED
60+
TCppType_t GetUnderlyingType(TCppType_t type);
61+
RPY_EXPORTED
6062
TCppType_t ResolveEnumReferenceType(TCppType_t type);
6163
RPY_EXPORTED
6264
TCppType_t ResolveEnumPointerType(TCppType_t type);
@@ -388,6 +390,9 @@ long long GetEnumDataValue(TCppScope_t scope);
388390
RPY_EXPORTED
389391
TCppScope_t InstantiateTemplate(TCppScope_t tmpl, Cpp::TemplateArgInfo* args,
390392
size_t args_size);
393+
RPY_EXPORTED
394+
void GetClassTemplateInstantiationArgs(TCppScope_t templ_instance,
395+
std::vector<Cpp::TemplateArgInfo>& args);
391396

392397
RPY_EXPORTED
393398
void DumpScope(TCppScope_t scope);

‎src/interop/interop_wrapper.cxx‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,11 @@ interop::TCppScope_t interop::GetScopeFromType(TCppType_t type) {
720720
return Cpp::GetScopeFromType(type);
721721
}
722722

723+
interop::TCppType_t interop::GetUnderlyingType(TCppType_t type) {
724+
std::lock_guard<std::recursive_mutex> Lock(InterOpMutex);
725+
return Cpp::GetUnderlyingType(type);
726+
}
727+
723728
interop::TCppType_t interop::GetTypeFromScope(TCppScope_t klass) {
724729
std::lock_guard<std::recursive_mutex> Lock(InterOpMutex);
725730
return Cpp::GetTypeFromScope(klass);
@@ -1776,6 +1781,12 @@ interop::TCppScope_t interop::InstantiateTemplate(TCppScope_t tmpl,
17761781
/*instantiate_body=*/false);
17771782
}
17781783

1784+
void interop::GetClassTemplateInstantiationArgs(
1785+
TCppScope_t templ_instance, std::vector<Cpp::TemplateArgInfo>& args) {
1786+
std::lock_guard<std::recursive_mutex> Lock(InterOpMutex);
1787+
Cpp::GetClassTemplateInstantiationArgs(templ_instance, args);
1788+
}
1789+
17791790
void interop::DumpScope(TCppScope_t scope) {
17801791
std::lock_guard<std::recursive_mutex> Lock(InterOpMutex);
17811792
Cpp::DumpScope(scope);

0 commit comments

Comments
 (0)