как использовать сборку, чтобы получить результат функции __stdcall, которая возвращает float

У меня есть процедура сборки, которая в общем случае вызывает функцию, которая использует stdcall конвенция и вернуть float, Эта функция используется фреймворком для выставления stdcall функции для скриптового языка.

Вот функция, использующая встроенную сборку GNU, которая компилируется в MinGW 4.3, Win32:

inline uint64_t stdcall_invoke_return_float(int args_size_bytes,
const char * args_ptr,
void * func_ptr)
{
uint64_t result;
assert(
0 == args_size_bytes % 4
|| !"argument size must be a multiple of 4 bytes");
#if defined(__GNUC__)
asm
(
/* INPUT PARAMS:  %0 is the address where top of FP stack to be stored
*                %1 is the number of BYTES to push onto the stack, */
/*                   and during the copy loop it is the address of */
/*                   the next word to push */
/*                %2 is the base address of the array */
/*                %3 is the address of the function to call */
"testl %1, %1    # If zero argument bytes given, skip \n\t""je    2f        # right to the function call.        \n\t""addl  %2, %1\n""1:\n\t""subl  $4, %1    # Push arguments onto the stack in   \n\t""pushl (%1)      # reverse order. Keep looping while  \n\t""cmp   %2, %1    # addr to push (%1) > base addr (%2) \n\t""jg    1b        # Callee cleans up b/c __stdcall.    \n""2:\n\t""call  * %3      # Callee will leave result in ST0    \n\t""fsts  %0        # Copy 32-bit float from ST0->result": "=m" (result)
: "r" (args_size_bytes), "r" (args_ptr), "mr" (func_ptr)
: "%eax", "%edx", "%ecx" /* eax, ecx, edx are caller-save */, "cc");
#else
#pragma error "Replacement for inline assembler required"#endif
return result;
}

Это всего лишь небольшой клей, чтобы упростить написание тестовых случаев:

template<typename FuncPtr, typename ArgType>
float float_invoke(FuncPtr f, int nargs, ArgType * args)
{
uint64_t result = stdcall_invoke_return_float(
nargs * sizeof(ArgType),
reinterpret_cast<const char *>(args),
reinterpret_cast<void *>(f)
);
return *reinterpret_cast<float *>(&result);
}

Теперь у меня есть несколько тестов, которые вызывают эту функцию:

__stdcall float TestReturn1_0Float()
{ return 1.0f; }

__stdcall float TestFloat(float a)
{ return a; }

__stdcall float TestSum2Floats(float a, float b)
{ return a + b; }

static const float args[2] = { 10.0f, -1.0f };

assert_equals(1.0f, float_invoke(TestReturn1_0Float, 0, args)); // test 1
assert_equals(10.0f, float_invoke(TestFloat, 1, args));         // test 2
assert_equals(-1.0f, float_invoke(TestFloat, 1, args + 1));     // test 3
assert_equals(9.0f, float_invoke(TestSumTwoFloats, 2, args));   // test 4

Случайно, тест 3 дает мне вывод мусора вместо возврата -1.0.

Мне интересно, если я

  • не в состоянии сохранить некоторое состояние до call инструкция?
  • испортить какое-то состояние с fsts инструкция?
  • принципиально недоразумение, как получить float значение из stdcall функция, которая возвращает float????

Вся помощь высоко ценится.

2

Решение

Не имея машины с Windows, я не могу полностью проверить это; в Linux следующее возвращает код возврата функции с плавающей точкой:

extern float something(int);

#include
#include

int main(int argc, char **argv)
{
int val = atoi(argv[1]);
float ret;

asm("pushl %1\n\t""call * %2\n\t""addl $4, %%esp": "=t"(ret)
: "r"(val), "r"(something)
: "%eax", "%ecx", "%edx", "memory", "cc");

printf("something(%d) == %f\n", val, ret);
return 0;
}

Ключ является использование "=t"(ret) ограничение — это получает вершина стека с плавающей запятой, увидеть Машинные ограничения (из руководства gcc). Если Windows stdcall возвращается float результаты в ST(0) а также, это должно работать, не нужно fld/fst как компилятор может сделать это для вас, если это необходимо.

Вам также необходимо указать memory а также cc Clobbers при вызове функций из встроенной сборки.

1

Другие решения

Вы разрешаете ссылку на память для указателя функции, GCC может построить ссылку относительно указателя стека при неверном предположении, что встроенная сборка не изменит его.

1

По вопросам рекламы [email protected]