Предположим простой частичный сценарий оценки:
#include <vector>
/* may be known at runtime */
int someConstant();
/* can be partially evaluated */
double foo(std::vector<double> args) {
return args[someConstant()] * someConstant();
}
Скажем так someConstant()
известен и не изменяется во время выполнения (например, предоставляется пользователем один раз) и может быть заменен соответствующим литералом int. Если foo
является частью горячего пути, я ожидаю значительного улучшения производительности:
/* partially evaluated, someConstant() == 2 */
double foo(std::vector<double> args) {
return args[2] * 2;
}
Мое текущее решение этой проблемы заключается в том, чтобы генерировать LLVM IR во время выполнения, потому что я знаю структуру частично оцененного кода (поэтому мне не понадобится частичный оценщик общего назначения).
Поэтому я хочу написать функцию foo_ir
который генерирует ИК-код, который делает то же самое, что и foo
, но не звонит someConstant()
потому что это известно во время выполнения.
Достаточно просто, не правда ли? Тем не менее, когда я смотрю на сгенерированный IR для кода выше:
; Function Attrs: uwtable
define double @_Z3fooSt6vectorIdSaIdEE(%"class.std::vector"* %args) #0 {
%1 = call i32 @_Z12someConstantv()
%2 = sext i32 %1 to i64
%3 = call double* @_ZNSt6vectorIdSaIdEEixEm(%"class.std::vector"* %args, i64 %2)
%4 = load double* %3
%5 = call i32 @_Z12someConstantv()
%6 = sitofp i32 %5 to double
%7 = fmul double %4, %6
ret double %7
}
; Function Attrs: nounwind uwtable
define linkonce_odr double* @_ZNSt6vectorIdSaIdEEixEm(%"class.std::vector"* %this, i64 %__n) #1 align 2 {
%1 = alloca %"class.std::vector"*, align 8
%2 = alloca i64, align 8
store %"class.std::vector"* %this, %"class.std::vector"** %1, align 8
store i64 %__n, i64* %2, align 8
%3 = load %"class.std::vector"** %1
%4 = bitcast %"class.std::vector"* %3 to %"struct.std::_Vector_base"*
%5 = getelementptr inbounds %"struct.std::_Vector_base"* %4, i32 0, i32 0
%6 = getelementptr inbounds %"struct.std::_Vector_base<double, std::allocator<double> >::_Vector_impl"* %5, i32 0, i32 0
%7 = load double** %6, align 8
%8 = load i64* %2, align 8
%9 = getelementptr inbounds double* %7, i64 %8
ret double* %9
}
Я вижу, что []
был включен из определения STL (функция @_ZNSt6vectorIdSaIdEEixEm
) — справедливо. Проблема в том, что это может быть также функция-член или даже прямой доступ к данным, я просто не могу предположить, что расположение данных везде одинаково, поэтому во время разработки я не знаю конкретных std::vector
макет хост-машины.
Есть ли способ использовать метапрограммирование C ++ для получения необходимой информации во время компиляции? то есть есть ли способ попросить llvm предоставить IR для std::vector
«s []
метод?
В качестве бонуса: я бы предпочел не применять компиляцию библиотеки с помощью clang, вместо этого LLVM должен зависеть от времени выполнения, поэтому просто вызов clang во время компиляции (даже если я не знаю, как это сделать) — это вторая -лучшее решение.
Отвечая на мой собственный вопрос:
Хотя у меня до сих пор нет решения для общего случая (например, std::map
), существует простое решение для std::vector
:
В соответствии с стандарт C ++, следующее верно для функции-члена data()
Возвращает прямой указатель на массив памяти, используемый внутри
вектор для хранения принадлежащих ему элементов.Поскольку элементы в векторе гарантированно хранятся в
смежные места хранения в том же порядке, как представлено
вектор, полученный указатель может быть смещен для доступа к любому элементу в
массив.
Так на самом деле, макет уровня объекта std::vector
фиксируется стандартом.
Других решений пока нет …