Я пытаюсь сгенерировать вызов функции используя AsmJit
к которому я передаю char*
, это char*
сам по себе извлекается из другого вызова функции. Я опробовал это:
typedef
const char* getStr();
const char* getStrImpl() {
return "hello pie";
}
void use_str_impl(int id, const char* c_str) {
// do stuff...
}
int main() {
JitRuntime rt;
CodeHolder code;
code.init(rt.getCodeInfo());
X86Compiler c(&code);
auto jitted_func = c.addFunc(FuncSignature0<const char*>(code.getCodeInfo().getCdeclCallConv()));
auto err = c.getLastError();
auto call = c.call((uint64_t) fooFuncImpl, FuncSignature0<intptr_t>());
X86Gpd res(call->getRet().getId());
auto call2 = c.call((uint64_t) send_input, FuncSignature2<void, int, intptr_t>());
err = !call2->setArg(0, Imm(42));
err = !call2->setArg(1, res);
c.ret();
c.endFunc();
err = c.finalize();
if(err) return 0;
size_t size = code.getCodeSize();
VMemMgr vm;
void* p = vm.alloc(size);
if (!p) return 0;
code.relocate(p);
auto fun = (entrypoint*) p;
fun();
}
Оказывается, это не генерирует никаких инструкций для второго параметра или второго вызова setArg
, Я также пытался использовать .newIntPtr
и используя инструкции перемещения, чтобы переместить результат вызова на место. Но это породило dec
и добавить инструкции, которые не имели смысла для меня и моего небольшого опыта сборки. Как правильно делать такие вещи?
Кстати, я использую следующую ветку AsmJit.
Я сделал несколько исправлений к вашему образцу с некоторыми комментариями.
JitRuntime rt;
size_t size = code.getCodeSize();
VMemMgr vm;
....
void* p = vm.alloc(size);
if (!p) return 0;
code.relocate(p);
auto fun = (entrypoint*) p;
Вы использовали JitRuntime просто для настройки параметров CodeHolder, но затем избежали этого и самостоятельно распределили память для функции. Хотя это правильный вариант использования, это не то, что делает большинство людей. Использование времени выполнения add()
достаточно в большинстве случаев.
X86Gpd res(call->getRet().getId());
call
узлу в этой точке не назначен регистр возврата, поэтому он будет возвращать неверный идентификатор. Если вам нужно создать виртуальный регистр, вы всегда должны вызывать компилятор newSomething()
, Компилятор AsmJit предоставляет API для проверки этого случая во время выполнения, если вы не уверены:
// Would print 0
printf("%d", (int)c.isVirtRegValid(call->getRet().getId()));
Решение состоит в том, чтобы создать новый виртуальный регистр и присвоить его возвращаемому значению функции. Для присвоения возвращаемого значения требуется индекс (например, назначение аргумента), причина в том, что некоторые функции могут возвращать несколько значений (например, 64-битное значение в 32-битном режиме), при этом в большинстве случаев достаточно использовать 0 в качестве индекса.
X86Gp reg = c.newIntPtr("reg");
call->setRet(0, reg);
Вы можете проверить getRet()
функциональность:
X86Gp reg = c.newIntPtr("reg");
assert(call->getRet(0).isNone());
call->setRet(0, reg);
assert(call->getRet(0) == reg);
#include <stdio.h>
#include <asmjit/asmjit.h>
const char* func_a() {
printf("func_a(): Called\n");
return "hello pie";
}
void func_b(int id, const char* c_str) {
printf("func_b(%d, %s): Called\n", id, c_str);
}
int main() {
using namespace asmjit;
JitRuntime rt;
CodeHolder code;
code.init(rt.getCodeInfo());
X86Compiler c(&code);
X86Gp reg = c.newIntPtr("reg");
// Compilation step...
c.addFunc(FuncSignature0<void>(code.getCodeInfo().getCdeclCallConv()));
auto call_a = c.call((uint64_t)func_a, FuncSignature0<intptr_t>());
call_a->setRet(0, reg);
auto call_b = c.call((uint64_t)func_b, FuncSignature2<void, int, intptr_t>());
call_b->setArg(0, Imm(42));
call_b->setArg(1, reg);
c.ret();
c.endFunc();
// Finalize does the following:
// - allocates virtual registers
// - inserts prolog / epilog
// - assembles to CodeHolder
auto err = c.finalize();
if (err) {
printf("COMPILER FAILED: %s\b", DebugUtils::errorAsString(err));
return 1;
}
typedef void (*EntryPoint)(void);
EntryPoint entry;
// Adds function to the runtime. Should be freed by rt.release().
// Function is valid until the runtime is valid if not released.
err = rt.add(&entry, &code);
if (err) {
printf("RUNTIME FAILED: %s\b", DebugUtils::errorAsString(err));
return 1;
}
entry();
return 0;
}
Других решений пока нет …