如何在有或没有 SIMD 内在函数的情况下从 Zig 构建和链接到 CGLM
Posted
技术标签:
【中文标题】如何在有或没有 SIMD 内在函数的情况下从 Zig 构建和链接到 CGLM【英文标题】:How to build and link to CGLM from Zig with or without SIMD intrinsics 【发布时间】:2021-03-01 17:10:32 【问题描述】:我想链接和使用 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 时,会报告以下错误(在 Zig 版本 0.7.1 和 0.8.0 主版本中都类似)Ex from 0.7.1 Zig version。
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, @as(c_int, 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, @as(c_int, 0))][@intCast(c_uint, @as(c_int, 0))] = (f / aspect);
dest[@intCast(c_uint, @as(c_int, 1))][@intCast(c_uint, @as(c_int, 1))] = f;
dest[@intCast(c_uint, @as(c_int, 2))][@intCast(c_uint, @as(c_int, 2))] = ((nearVal + farVal) * @"fn");
dest[@intCast(c_uint, @as(c_int, 2))][@intCast(c_uint, @as(c_int, 3))] = -1;
dest[@intCast(c_uint, @as(c_int, 3))][@intCast(c_uint, @as(c_int, 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 代码):
我使用数学 tanf 编译了一个 min C 案例,并使用 Zig 构建器机制构建。
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, which
// means any target is allowed, 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,
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,
);
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, 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 run -- 1
fovDeg=50.000000, fovRad=0.872665, tanFov=0.000000
当前状态
在我看来
zig mingw64 或 msvcrt 不支持基本数学函数,并且在禁用 x86_64 架构 SIMD 功能时会出现浮点错误。 Zig mingw64 不支持 SIMD C 代码。 Zig 无法以可行的方式编译 CGLM。如果有人能带来一些启示,以防我遗漏了什么,将不胜感激。
提前致谢。
【问题讨论】:
【参考方案1】:也许,我误解了你的问题,但这对我有用,没有错误。添加到您的 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。
【讨论】:
我要做的是从 Zig 构建系统构建,而不仅仅是使用 Zig 交叉编译器并使用本机 Zig C 翻译直接在 Zig 中导入 C 头文件。所以我不需要依赖 cmake 构建系统。以上是关于如何在有或没有 SIMD 内在函数的情况下从 Zig 构建和链接到 CGLM的主要内容,如果未能解决你的问题,请参考以下文章
网站网址可以在有或没有 index.php 的情况下访问 [重复]