Я разрабатываю компилятор, проект которого выглядит следующим образом:
↓ . Take a line of Python code;
. Convert Python code to an IR (Intermediate Language);
. Convert the intermediate language to assembly;
. Optimization (After)
. Use AsmJIT to convert assembly code to machine code;
. Execution at runtime in memory.
. Next line
↑
И решил использовать AsmJIT для сборки кода. Однако теперь у меня длинный ассемблерный код, и проблема заключается в «преобразовании» этого ассемблерного кода в вызовы функций AsmJIT, которые выглядят так:
a.mov(eax, dword_ptr(esp, 4)); // Load the destination pointer.
a.mov(ecx, dword_ptr(esp, 8)); // Load the first source pointer.
a.mov(edx, dword_ptr(esp, 12)); // Load the second source pointer.
a.movups(xmm0, ptr(ecx)); // Load 4 floats from [ecx] to XMM0.
a.movups(xmm1, ptr(edx)); // Load 4 floats from [edx] to XMM1.
a.addps(xmm0, xmm1); // Add 4 floats in XMM1 to XMM0.
a.movups(ptr(eax), xmm0); // Store the result to [eax].
a.ret(); // Return from function.
Учитывая значительный размер моего кода, выполнить это «преобразование» вручную крайне сложно. Можно ли использовать строки с ассемблерным кодом, как мне действовать?
Вы правы, что в AsmJIT нет «IR», но моя проблема заключается в том, чтобы поместить ассемблерный код, содержащий 229 строк, в эти «вызовы функций», которые запрашивает AsmJIT, если это единственный способ, которым я собираюсь это сделать. И я уже отредактировал вопрос.
Если я правильно понимаю вопрос, у вас есть конвейер, и вы хотели бы использовать AsmJit для кодирования кода, который является выходными данными конвейера (выходными данными является машинный код, который вы хотите собрать).
Это возможно с помощью AsmJit, и вот пример:
#include <asmjit/x86.h>
#include <stdio.h>
using namespace asmjit;
class MyErrorHandler : public ErrorHandler {
public:
void handleError(Error err,
const char* message,
BaseEmitter* origin) override {
printf("AsmJit error: %s\n", message);
}
};
static x86::Gp createGp32(uint32_t id) noexcept { return x86::gpd(id); }
static x86::Gp createGp64(uint32_t id) noexcept { return x86::gpq(id); }
static x86::Vec createXmm(uint32_t id) noexcept { return x86::xmm(id); }
int main() {
MyErrorHandler eh;
FileLogger logger(stdout);
JitRuntime rt;
CodeHolder code;
code.init(rt.environment());
code.setErrorHandler(&eh);
code.setLogger(&logger);
x86::Assembler a(&code);
// An example of converting instruction name to AsmJit's instruction id.
const char* instructionName = "vcvtsi2sd";
InstId instructionId =
InstAPI::stringToInstId(a.arch(), instructionName, strlen(instructionName));
// An example of creating operands dynamically, encodes 'vcvtsi2sd xmm0, xmm1, rbx'.
a.emit(instructionId, createXmm(0), createXmm(1), createGp64(3));
return 0;
}
По сути, если вы используете emit()
, вы можете создать целую инструкцию для динамической сборки на основе результатов вашего собственного конвейера. Этот подход может быть полностью управляемым таблицей (преобразование вашего кода операции IR в одну инструкцию) или перевод может быть более сложным (преобразование IR более высокого уровня в 1 или более инструкций).
Проверьте InstId (идентификатор инструкции) и операнд (все операнды наследуются от операнда, поэтому вы можете использовать операнд в своих структурах, и тогда это может быть что угодно - метка, регистр, непосредственное значение или адрес памяти).
Если у вас есть текстовое представление в качестве вывода, вы можете использовать AsmTK:
По сути, это синтаксический анализатор, который использует API инструкций AsmJit для преобразования текстового ввода в форму, которую понимает AsmJit.
Спасибо, я не видел AsmTK, теперь можно напрямую использовать строки.
Я думаю, вам следует переработать свой ответ, поскольку неясно, что вы пытаетесь сделать. В AsmJit нет IR, он понимает только ассемблер + множество вспомогательных инструментов, таких как компилятор (для распределения регистров), которые вы можете использовать, чтобы облегчить себе жизнь. Вам всегда придется выполнять перевод из вашего IR/AST/чего угодно в asmjit. Если у вас есть таблицы перевода (например, у вас есть 10 операций, которые будут сопоставлены с 10 инструкциями), вы можете воспользоваться идентификаторами инструкций AsmJit и использовать директиву assembler->emit(instructionId, операнды...), но все зависит от вашего проекта.