有办法做到这一点,而且并不难。
您需要了解的主要内容是函数调用约定、对象格式和函数名称修改。
函数调用约定。
在 32 位模式下,Windows 和 Unix(即 Linux、BSD、Mac OS X...)使用相同的函数调用约定。
在 64 位模式下,Windows 和 Unix 使用不同的函数调用约定。为了使您使用 GCC 编译的目标文件能够在 64 位模式下与 MSVC 一起使用,您必须使用 Windows 函数调用约定。要使用 gcc 执行此操作,您可以使用 mabi=ms 例如:
g++ -c -mabi=ms -mavx -fopenmp -O3 foo.cpp
对象文件格式
Linux 的目标文件格式是 ELF,Windows 的目标文件格式是 COFF/PE。为了在 MSVC 中使用 GCC 编译的对象,需要将其从 ELF 转换为 COFF。为此,您需要一个目标文件转换器。我使用 Agner Fog 的objconv。例如要将 ELF64 转换为 64 位 COFF64 (PE32+),请执行以下操作:
objconv -fcoff64 foo.o foo.obj
函数名称修改
由于函数重载,C++ 会破坏函数名称。 GCC 和 MSVC 以不同的方式执行此操作。要解决此问题,您可以使用 external "C" 继续函数名称。
有关调用约定、对象格式和函数名称修改的更多详细信息,请参阅 Agner Fog 的手册 calling conventions。
下面是我用 GCC 编译然后在 MSVC (because GCC optimized it better) 中使用的模块。我用-mabi=ms 编译它,用objconv 将它转换为COFF64,然后将它链接到完美运行的Visual Studio。
#include <immintrin.h>
extern "C" void inner(const int n, const float *a, const float *b, float *c, const int stridea, const int strideb, const int stridec) {
const int vec_size = 8;
__m256 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
tmp0 = _mm256_loadu_ps(&c[0*vec_size]);
tmp1 = _mm256_loadu_ps(&c[1*vec_size]);
tmp2 = _mm256_loadu_ps(&c[2*vec_size]);
tmp3 = _mm256_loadu_ps(&c[3*vec_size]);
tmp4 = _mm256_loadu_ps(&c[4*vec_size]);
tmp5 = _mm256_loadu_ps(&c[5*vec_size]);
tmp6 = _mm256_loadu_ps(&c[6*vec_size]);
tmp7 = _mm256_loadu_ps(&c[7*vec_size]);
for(int i=0; i<n; i++) {
__m256 areg0 = _mm256_set1_ps(a[i]);
__m256 breg0 = _mm256_loadu_ps(&b[vec_size*(8*i + 0)]);
tmp0 = _mm256_add_ps(_mm256_mul_ps(areg0,breg0), tmp0);
__m256 breg1 = _mm256_loadu_ps(&b[vec_size*(8*i + 1)]);
tmp1 = _mm256_add_ps(_mm256_mul_ps(areg0,breg1), tmp1);
__m256 breg2 = _mm256_loadu_ps(&b[vec_size*(8*i + 2)]);
tmp2 = _mm256_add_ps(_mm256_mul_ps(areg0,breg2), tmp2);
__m256 breg3 = _mm256_loadu_ps(&b[vec_size*(8*i + 3)]);
tmp3 = _mm256_add_ps(_mm256_mul_ps(areg0,breg3), tmp3);
__m256 breg4 = _mm256_loadu_ps(&b[vec_size*(8*i + 4)]);
tmp4 = _mm256_add_ps(_mm256_mul_ps(areg0,breg4), tmp4);
__m256 breg5 = _mm256_loadu_ps(&b[vec_size*(8*i + 5)]);
tmp5 = _mm256_add_ps(_mm256_mul_ps(areg0,breg5), tmp5);
__m256 breg6 = _mm256_loadu_ps(&b[vec_size*(8*i + 6)]);
tmp6 = _mm256_add_ps(_mm256_mul_ps(areg0,breg6), tmp6);
__m256 breg7 = _mm256_loadu_ps(&b[vec_size*(8*i + 7)]);
tmp7 = _mm256_add_ps(_mm256_mul_ps(areg0,breg7), tmp7);
}
_mm256_storeu_ps(&c[0*vec_size], tmp0);
_mm256_storeu_ps(&c[1*vec_size], tmp1);
_mm256_storeu_ps(&c[2*vec_size], tmp2);
_mm256_storeu_ps(&c[3*vec_size], tmp3);
_mm256_storeu_ps(&c[4*vec_size], tmp4);
_mm256_storeu_ps(&c[5*vec_size], tmp5);
_mm256_storeu_ps(&c[6*vec_size], tmp6);
_mm256_storeu_ps(&c[7*vec_size], tmp7);
}