일반적인 방법으로 stdcall
규칙을 사용하는 것으로 알려진 함수를 호출하고 float
을 반환하는 어셈블리 루틴이 있습니다. 이 함수는 마샬링 프레임 워크에서 stdcall
함수를 스크립팅 언어에 표시하는 데 사용됩니다.어셈블리를 사용하여 부동 소수점을 반환하는 __stdcall 함수의 결과를 얻는 방법
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);
}
:
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;
}
이 쉽게 테스트 케이스를 작성할 수 있도록 약간의 접착제가 그냥 : 여기에 배경
는 Win32에서는 MinGW 4.3 컴파일 GNU 인라인 어셈블리를 사용하여 함수의
이제이 함수를 호출하는 몇 가지 테스트 사례가 있습니다.
__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
???
모두 도움을 주시면 대단히 감사하겠습니다.
'= t' 제약 조건과 일관되게 작동하는 것 같습니다. 나는 여전히 * fsts 명령어를 사용하는 이유가 불일치하게 작동한다는 것을 알고 싶다. 또한, 왜 메모리 clobber가 필요합니까? – 0xbe5077ed
메모리 "clobber"는 사실 _barrier_입니다. 이것은 컴파일러가 인라인'asm()'블록을 이러한 방식으로 명령한다는 것을 의미하며 나머지 명령어들은 모든로드/저장이 완료되기 전에 완료되고 그 후에 다시 수행됩니다.'cc'와 마찬가지로, 조건 코드에 대해서도 이전의 비교 결과를 나타냅니다 (asm 블록 앞에'cmp'를 쓰고 그 결과를 테스트하지는 않습니다). –