如何使用或不使用 SIMD 内在函数从 Zig 构建和链接到 CGLM

问题描述

我想链接和使用 cglm C 库。我正在使用 Zig 0.7.1 和 Zig 0.8.0(主)在没有 msvc(所以针对 gnu C ABI)的 Windows 上工作,但没有任何运气。

我已经能够从 Zig build.zig 构建 CGLM 静态库,但是由于报告了 CGLM SIMD 内在函数优化错误,所以没有运气从 Zig 程序链接到它。

const Builder = @import("std").build.Builder;

pub fn build(b: *Builder) void {
    // Standard target options allows the person running `zig build` to choose
    // what target to build for. Here we do not override the defaults,which
    // means any target is allowed,and the default is native. Other options
    // for restricting supported target set are available.
    const target = b.standardTargetOptions(.{});

    // Standard release options allow the person running `zig build` to select
    // between Debug,ReleaseSafe,ReleaseFast,and ReleaseSmall.
    const mode = b.standardReleaseOptions();

    // cglm
    const cglmLibBasePath = "vendor/cglm/";
    const cglmFiles = &[_][]const u8{ 
        cglmLibBasePath ++ "src/euler.c",cglmLibBasePath ++ "src/affine.c",cglmLibBasePath ++ "src/io.c",cglmLibBasePath ++ "src/quat.c",cglmLibBasePath ++ "src/cam.c",cglmLibBasePath ++ "src/vec2.c",cglmLibBasePath ++ "src/vec3.c",cglmLibBasePath ++ "src/vec4.c",cglmLibBasePath ++ "src/mat2.c",cglmLibBasePath ++ "src/mat3.c",cglmLibBasePath ++ "src/mat4.c",cglmLibBasePath ++ "src/plane.c",cglmLibBasePath ++ "src/frustum.c",cglmLibBasePath ++ "src/box.c",cglmLibBasePath ++ "src/project.c",cglmLibBasePath ++ "src/sphere.c",cglmLibBasePath ++ "src/ease.c",cglmLibBasePath ++ "src/curve.c",cglmLibBasePath ++ "src/bezier.c",cglmLibBasePath ++ "src/ray.c",cglmLibBasePath ++ "src/affine2d.c",};
    const cglmLib = b.addStaticLibrary("cglm",null);
    cglmLib.setBuildMode(mode);
    cglmLib.setTarget(target);
    cglmLib.defineCMacro("CGLM_STATIC");
    cglmLib.defineCMacro("WIN32");
    cglmLib.addIncludeDir(cglmLibBasePath ++ "src/");
    for (cglmFiles) |cglmFile| {
        cglmLib.addCSourceFile(cglmFile,&[_][]const u8 { 
            "-std=c11","-Wall","-Werror","-O3",});
    }    
    cglmLib.linkLibC();
    cglmLib.install();

    const exe = b.addExecutable("app-zig-cglm","src/main.zig");
    exe.setBuildMode(mode);
    exe.setTarget(target);  
    // cglm
    exe.linkLibrary(cglmLib);
    exe.addSystemIncludeDir(cglmLibBasePath ++ "include/");
    // C and win32
    exe.linkLibC();
    exe.install();
    

    const run_cmd = exe.run();
    run_cmd.step.dependOn(b.getInstallStep());
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    const run_step = b.step("run","Run the app");
    run_step.dependOn(&run_cmd.step);
}

当我尝试使用链接的生成库构建/运行 exe 时,会报告以下错误(在 0.7.1 和 0.8.0 主版 Zig 中类似)来自 0.7.1 Zig 版本的 Ex。

alagt@LAPTOP-HS5L5VEH MINGW64 /c/Dev/Projects/test-bed/app-zig-cglm (master)
$ zig build -Dtarget=x86_64-windows-gnu
.\zig-cache\o\1bc9e6dc93c2ab6590b8006381a9000e\cimport.zig:4255:26: error: unable to translate function
pub const glm_mul_sse2 = @compileError("unable to translate function"); // C:\Dev\Projects\test-bed\app-zig-cglm\vendor\cglm\include\cglm/simd/sse2/affine.h:17:1
                         ^
.\zig-cache\o\1bc9e6dc93c2ab6590b8006381a9000e\cimport.zig:4264:5: note: referenced here
    glm_mul_sse2(m1,m2,dest);
    ^
.\zig-cache\o\1bc9e6dc93c2ab6590b8006381a9000e\cimport.zig:657:20: error: unable to resolve typedef child type
pub const __m128 = @compileError("unable to resolve typedef child type"); // C:\Dev\Tools\zig-0.7.1\lib\include\xmmintrin.h:17:15
                   ^
.\zig-cache\o\1bc9e6dc93c2ab6590b8006381a9000e\cimport.zig:1255:48: note: referenced here
pub fn _mm_store_ps(arg___p: [*c]f32,arg___a: __m128) callconv(.C) void {
                                               ^
.\zig-cache\o\1bc9e6dc93c2ab6590b8006381a9000e\cimport.zig:3557:5: note: referenced here
    _mm_store_ps(&dest[@intCast(c_uint,@as(c_int,0))],_mm_load_ps(&mat[@intCast(c_uint,0))]));
    ^
app-zig-cglm...The following command exited with error code 1:
C:\Dev\Tools\zig-0.7.1\zig.exe build-exe C:\Dev\Projects\test-bed\app-zig-cglm\src\main.zig C:\Dev\Projects\test-bed\app-zig-cglm\zig-cache\o\dfacf78e7514990119de31b967d43202\cglm.lib --library c --cache-dir C:\Dev\Projects\test-bed\app-zig-cglm\zig-cache --global-cache-dir C:\Users\alagt\AppData\Local\zig --name app-zig-cglm -target x86_64-windows-gnu -isystem C:\Dev\Projects\test-bed\app-zig-cglm\vendor\cglm\include --enable-cache
error: the following build command failed with exit code 1:
C:\Dev\Projects\test-bed\app-zig-cglm\zig-cache\o\64a99c599e98cac00a19cffa57c71a68\build.exe C:\Dev\Tools\zig-0.7.1\zig.exe C:\Dev\Projects\test-bed\app-zig-cglm C:\Dev\Projects\test-bed\app-zig-cglm\zig-cache C:\Users\alagt\AppData\Local\zig -Dtarget=x86_64-windows-gnu

avxintrin.h 无法解析的行如下

typedef float __m128 __attribute__((__vector_size__(16),__aligned__(16)));

我想知道使用内在函数的 C 库现在是否可以工作,如果不能,我想要一种方法来禁用 build.zig 中的内在函数功能 sse/avx/... 以便可以构建 CGLM 和未进行 SIMD 优化的链接。

编辑(从 build.zig 禁用 SIMD 功能)

我对 lib/zig/std/build.zig 进行了一些研究以了解如何禁用 SIMD 功能,最后我能够在我的 build.zig 上使用以下代码禁用

var target = b.standardTargetOptions(.{});
    target.cpu_features_sub = x86.featureSet(&[_]x86.Feature{
        x86.Feature.avx,x86.Feature.avx2,x86.Feature.avx512bf16,x86.Feature.avx512bitalg,x86.Feature.avx512bw,x86.Feature.avx512cd,x86.Feature.avx512dq,x86.Feature.avx512er,x86.Feature.avx512f,x86.Feature.avx512ifma,x86.Feature.avx512pf,x86.Feature.avx512vbmi,x86.Feature.avx512vbmi2,x86.Feature.avx512vl,x86.Feature.avx512vnni,x86.Feature.avx512vp2intersect,x86.Feature.avx512vpopcntdq,x86.Feature.sse,x86.Feature.sse_unaligned_mem,x86.Feature.sse2,x86.Feature.sse3,x86.Feature.sse4_1,x86.Feature.sse4_2,x86.Feature.sse4a,x86.Feature.ssse3,});    

状态:

现在我能够构建(没有 SIMD 优化,什么是不理想的)并从 zig 代码链接 CGLM 库,但真正令人担忧的问题出现了,因为调用标准 C tanf 函数的代码正在计算非常错误的结果。

遵循为 glm_perspective 函数生成的 zig 代码。

pub fn glm_perspective(arg_fovy: f32,arg_aspect: f32,arg_nearVal: f32,arg_farVal: f32,arg_dest: [*c]vec4) callconv(.C) void {
    var fovy = arg_fovy;
    var aspect = arg_aspect;
    var nearVal = arg_nearVal;
    var farVal = arg_farVal;
    var dest = arg_dest;
    var f: f32 = undefined;
    var @"fn": f32 = undefined;
    glm_mat4_zero(dest);
    f = (1 / tanf((fovy * 0.5)));
    @"fn" = (1 / (nearVal - farVal));
    dest[@intCast(c_uint,0))][@intCast(c_uint,0))] = (f / aspect);
    dest[@intCast(c_uint,1))][@intCast(c_uint,1))] = f;
    dest[@intCast(c_uint,2))][@intCast(c_uint,2))] = ((nearVal + farVal) * @"fn");
    dest[@intCast(c_uint,3))] = -1;
    dest[@intCast(c_uint,3))][@intCast(c_uint,2))] = (((2 * nearVal) * farVal) * @"fn");
}

执行f = (1 / tanf((fovy * 0.5)))的结果; arg_fovy = 0.785398185(弧度为 45 度)的线 返回 134217728。 这是完全错误的。正确的结果应该是大约。 2.4142134

VSCode cppvsdbg 不允许我进入 tanf 以了解 tanf 实现可能有什么问题。

编辑(使用 Zig 编译器构建和编译最小的 C 代码):

我使用 math tanf 编译了一个 min C case,并使用 Zig builder 机制构建。

build.zig

const Builder = @import("std").build.Builder;
const x86 = @import("std").Target.x86;

pub fn build(b: *Builder) void {
    // Standard target options allows the person running `zig build` to choose
    // what target to build for. Here we do not override the defaults,and the default is native. Other options
    // for restricting supported target set are available.
    var target = b.standardTargetOptions(.{});    
    target.cpu_features_sub = x86.featureSet(&[_]x86.Feature{
        x86.Feature.avx,});
    

    // Standard release options allow the person running `zig build` to select
    // between Debug,and ReleaseSmall.
    const mode = b.standardReleaseOptions();

    const exeC = b.addExecutable("app-c","src/main.c");
    exeC.setBuildMode(mode);
    exeC.setTarget(target);    
    // C and win32
    exeC.linkLibC();
    exeC.install();
    

    const run_cmd = exeC.run();
    run_cmd.step.dependOn(b.getInstallStep());
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    const run_step = b.step("run","Run the app");
    run_step.dependOn(&run_cmd.step);
}

src/main.c

#include <stdio.h>
#include <math.h>

int main(int argc,char **argv) {
    float fovDeg = (argc == 1 ? 45.0f : 50.0f);
    while (1) {
        float fovRad = fovDeg * (M_PI / 180.0f);
        float tanFov = tanf(fovRad);
        fprintf(stdout,"fovDeg=%f,fovRad=%f,tanFov=%f\n",fovDeg,fovRad,tanFov);
    }
    return 0;
}

如果我在没有 SIMD 排除的情况下构建或只是将目标设置为本机,则代码将正确执行并获得预期结果。

$ zig build -Dtarget=native-windows-gnu run -- 1

fovDeg=50.000000,fovRad=0.872665,tanFov=1.191754

如果我使用 SIMD 排除项构建并执行它,则 tanf 返回 0

$ zig build -Dtarget=x86_64-windows-gnu 运行 -- 1

fovDeg=50.000000,tanFov=0.000000

当前状态

在我看来

  • zig mingw64 或 msvcrt 不支持基本的数学函数,并且在禁用 x86_64 arch SIMD 功能时存在浮点错误。
  • Zig mingw64 不支持 SIMD C 代码。
  • Zig 无法以可行的方式编译 CGLM。

如果有人可以提供一些线索,以防万一我遗漏了什么,我们将不胜感激。

提前致谢。

解决方法

也许,我误解了你的问题,但这对我有用,没有错误。添加到您的 build.zig:

exe.addIncludeDir("path_to_your_cglm/cglm/include");
exe.addLibPath("path_to_your_cglm/cglm/win/Release");

exe.linkSystemLibrary("cglm");

此外,Discord 和 reddit Zig 社区非常活跃,因此请考虑在那里发帖。看看这个页面:https://github.com/ziglang/zig/wiki/Community。 还有 Zig Forum

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...