我正在尝试使用32位操作系统上的XMM0 128位寄存器从/向char指针数组加载/存储内存。
我尝试的很简单:
int main() {
char *data = new char[33];
for (int i = 0; i < 32; i++)
data[i] = 'a';
data[32] = 0;
ASM
{
movdqu xmm0,[data]
}
delete[] data;
}
问题是这似乎不起作用。我第一次调试Win32应用程序时得到了:
xmm0 = 0024F8380000000000F818E30055F158
我第二次调试它时得到了:
xmm0 = 0043FD6800000000002C18E3008CF158
所以行必须有一些东西:
movdqu xmm0,[data]
我尝试使用此代码:
movdqu xmm0,data
但我得到了相同的结果。
我认为问题在于我复制地址而不是地址处的数据。但是,xmm0
寄存器中显示的值对于32位地址而言太大,因此必须从另一个地址复制内存。
我还尝试了一些我在互联网上找到的其他说明,但结果相同。
这是我传递指针的方式还是我误解了xmm基础知识?
我们将非常感谢有效的解决方案。
即使我找到了解决方案(最后三小时后),我还是想解释一下:
ASM
{
push eax
mov eax,data
movdqu xmm0,[eax]
pop eax
}
为什么要将指针传递给32位寄存器?
答案 0 :(得分:3)
代码的问题是function getFragIdFromDbId(viewer, dbid) {
var returnValue;
var it = viewer.model.getData().instanceTree;
it.enumNodeFragments(dbid, function (fragId) {
console.log("dbId: " + dbid + " FragId : " + fragId);
returnValue = fragId;
}, false);
return returnValue;
}
...
// only need the start vertex
var floatArray = [];
for (var i = 0; i < dbidArray.length; i++) {
var fragId = getFragIdFromDbId(viewer, dbidArray[i]);
var mesh = viewer.impl.getRenderProxy(viewer.model, fragId);
var matrixWorld = mesh.matrixWorld;
var lmvBufferGeometry = mesh.geometry;
var lmvFloatArray = lmvBufferGeometry.vb; //this will have an array of 6 values 0,1,2 are start vertext , 3,4,5 are end vertex
floatArray.push(lmvFloatArray[0]);
floatArray.push(lmvFloatArray[1]);
floatArray.push(lmvFloatArray[2]);
}
//use matrixWorld to convert array to worldSpace
是一个指针。汇编代码data
将movdqu xmm0,[data]
地址处的16个字节加载到寄存器data
中。这意味着4或8个字节包含指针的值和内存中的任何字节。幸运的是指针地址在内存中正确对齐,否则会出现分段错误。没有什么能保证这种一致性。
使用自动数组xmm0
的备选方案可以解决寻址问题(char data[33];
将从数组加载数据)但不解决对齐问题,您仍然可能会遇到违规,具体取决于编译器如何对齐具有自动存储的阵列。同样,无法保证正确对齐。
您找到的解决方案可能是一种很好的方法,但与movqdu
不同,我不确定malloc()
返回的指针是否对任何对齐都有效。
这应该适用于所有情况:
new
正如Peter Cordes所评论的那样,将内在函数用于此类事情要好得多,即mm_loadu_si128
。有两个主要原因:首先,64位版本不支持内联汇编,因此通过使用内在函数,您的代码变得更加轻松。其次,编译器在优化内联汇编方面做得相对较差,特别是,往往会做很多无意义的内存存储和加载。编译器在优化内在函数方面做得更好,这使得代码运行得更快(这是使用内联汇编的重点!)。
答案 1 :(得分:1)
#include <iostream>
int main()
{
char *dataptr = new char[33];
char datalocal[33];
dataptr[0] = 'a'; dataptr[1] = 0;
datalocal[0] = 'a'; datalocal[1] = 0;
printf("%p %p %c\n", dataptr, &dataptr, dataptr[0]);
printf("%p %p %c\n", datalocal, &datalocal, datalocal[0]);
delete[] dataptr;
}
输出:
0xd38050 0x7635bd709448 a
0x7635bd709450 0x7635bd709450 a
正如我们所看到的,动态指针data
实际上是指针变量(0x7635BD709448
处的32位或64位),包含指向堆的指针0xD38050
。
局部变量直接是一个33个字符长的缓冲区,在地址0x7635BD709450
分配。
但datalocal
也可以作为char *
值。
我对这个正式的C ++解释有点困惑。在编写C ++代码时,这感觉非常自然,dataptr [0]是堆内存中的第一个元素(即两次取消引用dataptr),但在汇编程序中,您可以看到dataptr
的真实性质,它是地址的指针变量。因此,您首先使用mov eax,[data]
加载eax
=加载0xD38050
的堆指针,然后使用{{1}将0xD38050
的内容加载到XMM0中}。
对于局部变量,没有变量的地址;符号[eax]
已经是第一个元素的地址,因此datalocal
将起作用。
在“错误”的情况下,你仍然可以movdqu xmm0,[data]
;从32位变量加载128位不是CPU的问题。它将继续读取超过32位并读取属于其他变量/代码的另外96位。如果你是一个内存边界,这是应用程序的最后一个内存页面,它将在无效访问时崩溃。
评论中几次提到了对齐。这是一个有效的观点;要通过movdqu xmm0,[data]
访问内存,它应该对齐。检查您的C ++编译器内在函数。对于Visual Studio,这应该有效:
movdqu
关于我的C ++解释:也许我从一开始就弄错了。
__declspec(align(16)) char datalocal[33];
char *dataptr = _aligned_malloc(33, 16);
_aligned_free(dataptr);
是dataptr符号的值,即堆地址。然后dataptr
取消引用堆地址,访问分配的内存的第一个元素。 dataptr[0]
是&dataptr
值的地址。这也适用于dataptr
这样的语法,您将nullptr值存储到dataptr变量中,而不是覆盖dataptr符号地址。
对于dataptr = nullptr;
,访问纯datalocal[]
基本上没有意义,就像datalocal
一样,因为它是一个数组变量,所以你应该总是提供datalocal = 'a';
索引。而[]
是这种数组的地址。然后,纯&datalocal
是一个别名快捷方式,可以更容易地使用数组等进行数学计算,同时还有datalocal
类型,但如果纯char *
会抛出语法错误,它仍然会可以编写C ++代码(使用datalocal
作为指针,&datalocal
作为元素),它完全符合datalocal[..]
逻辑。
结论:从一开始你的示例就出错了,因为汇编语言dataptr
正在加载[data]
的值,data
是指向new
返回的堆的指针。 / p>
这是我自己的解释,现在一些C ++专家会从正式的角度来解读它...... :))