gpt4 book ai didi

assembly - 使用手写汇编调用 native 代码

转载 作者:行者123 更新时间:2023-12-03 21:50:41 25 4
gpt4 key购买 nike

我正在尝试从托管程序集中调用 native 函数。我已经在预编译库上完成了这项工作,并且一切顺利。目前我正在构建自己的库,但无法完成这项工作。

native DLL源如下:

#define DERM_SIMD_EXPORT        __declspec(dllexport)

#define DERM_SIMD_API __cdecl

extern "C" {

DERM_SIMD_EXPORT void DERM_SIMD_API Matrix4x4_Multiply_SSE(float *result, float *left, float *right);

}

void DERM_SIMD_API Matrix4x4_Multiply_SSE(float *result, float *left, float *right) {
__asm {
....
}
}

此后我们有加载库并从函数指针创建委托(delegate)的托管代码。

public unsafe class Simd
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MatrixMultiplyDelegate(float* result, float* left, float* right);

public static MatrixMultiplyDelegate MatrixMultiply;

public static void LoadSimdExtensions()
{
string assemblyPath = "Derm.Simd.dll";

IntPtr address = GetProcAddress.GetAddress(assemblyPath, "Matrix4x4_Multiply_SSE");

if (address != IntPtr.Zero) {
MatrixMultiply = (MatrixMultiplyDelegate)Marshal.GetDelegateForFunctionPointer(address, typeof(MatrixMultiplyDelegate));
}
}
}

使用上面的源代码运行没有错误(获得了函数指针,并且实际创建了委托(delegate)。

当我调用委托(delegate)时出现问题:它被执行(我也可以调试它!),但在函数退出时,托管应用程序引发 System.ExecutionEngineException(当它没有无一异常(exception)地退出)。

实际问题是函数实现:它包含一个带有 SSE 指令的 asm block ;如果我删除 asm block ,代码将完美运行。

我怀疑我遗漏了一些注册表保存/恢复程序集,但我对此一无所知。

奇怪的是,如果我将调用约定更改为 __stdcall,则调试版本“似乎”可以工作,而发布版本的行为就好像使用了 __cdecl 调用约定。

(仅仅因为我们到了这里,您能否澄清调用对流是否重要?)


好的,感谢 David Heffernan 的评论,我发现导致问题的错误指令如下:

 movups result[ 0], xmm4;
movups result[16], xmm5;

movups 指令将 16 个字节移动到(未对齐的)内存中。

该函数由以下代码调用:

 unsafe {
float* prodFix = (float*)prod.MatrixBuffer.AlignedBuffer.ToPointer();
float* m1Fix = (float*)m2.MatrixBuffer.AlignedBuffer.ToPointer();
float* m2Fix = (float*)m1.MatrixBuffer.AlignedBuffer.ToPointer();

if (Simd.Simd.MatrixMultiply == null) {
// ... unsafe C# code
} else {
Simd.Simd.MatrixMultiply(prodFix, m1Fix, m2Fix);
}
}

其中 MatrixBuffer 是我的一个类;它的成员 AlignedBuffer 是按以下方式分配的:

// Allocate unmanaged buffer
mUnmanagedBuffer = Marshal.AllocHGlobal(new IntPtr((long)(size + alignment - 1)));

// Align buffer pointer
long misalignment = mUnmanagedBuffer.ToInt64() % alignment;
if (misalignment != 0)
mAlignedBuffer = new IntPtr(mUnmanagedBuffer.ToInt64() + misalignment);
else
mAlignedBuffer = mUnmanagedBuffer;

也许错误是由 Marshal.AllocHGlobalIntPtr 黑魔法引起的?


这是发现错误的最小来源:

void Matrix4x4_Multiply_SSE(float *result, float *left, float *right)
{
__asm {
movups xmm0, right[ 0];

movups result, xmm0;
}
}


int main(int argc, char *argv[])
{
float r0[16];
float m1[16], m2[16];

m1[ 0] = 1.0f; m1[ 4] = 0.0f; m1[ 8] = 0.0f; m1[12] = 0.0f;
m1[ 1] = 0.0f; m1[ 5] = 1.0f; m1[ 9] = 0.0f; m1[13] = 0.0f;
m1[ 2] = 0.0f; m1[ 6] = 0.0f; m1[10] = 1.0f; m1[14] = 0.0f;
m1[ 3] = 0.0f; m1[ 7] = 0.0f; m1[11] = 0.0f; m1[15] = 1.0f;

m2[ 0] = 1.0f; m2[ 4] = 0.0f; m2[ 8] = 0.0f; m2[12] = 0.0f;
m2[ 1] = 0.0f; m2[ 5] = 1.0f; m2[ 9] = 0.0f; m2[13] = 0.0f;
m2[ 2] = 0.0f; m2[ 6] = 0.0f; m2[10] = 1.0f; m2[14] = 0.0f;
m2[ 3] = 0.0f; m2[ 7] = 0.0f; m2[11] = 0.0f; m2[15] = 1.0f;

r0[ 0] = 0.0f; r0[ 4] = 0.0f; r0[ 8] = 0.0f; r0[12] = 0.0f;
r0[ 1] = 0.0f; r0[ 5] = 0.0f; r0[ 9] = 0.0f; r0[13] = 0.0f;
r0[ 2] = 0.0f; r0[ 6] = 0.0f; r0[10] = 0.0f; r0[14] = 0.0f;
r0[ 3] = 0.0f; r0[ 7] = 0.0f; r0[11] = 0.0f; r0[15] = 0.0f;

Matrix4x4_Multiply_SSE(r0, m1, m2);
Matrix4x4_Multiply_SSE(r0, m1, m2);

return (0);
}

实际上在第二个 movups 之后,堆栈更改了 result 值(存储在堆栈中),并将 xmm0 的值存储在存储在 result 中的修改后的(错误的)地址。

从*Matrix4x4_Multiply_SSE*中退出后,原始内存没有被修改。

我错过了什么?

最佳答案

对齐校正错误。您需要添加 alignment-misalignment 来纠正对齐。所以代码应该是:

mAlignedBuffer = 
new IntPtr(mUnmanagedBuffer.ToInt64() + alignment - misalignment);

但是,我建议您先在 native 设置中测试该功能。一旦您知道它在那里工作,您就可以转到托管设置,并知道任何问题都是由托管代码引起的。

关于assembly - 使用手写汇编调用 native 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7865201/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com