What are the differences bwteen clang/clang++ and "zig cc/c++"?

I have built the skia lib successfully with clang with the following config (although there are some AVX warnings).

bin/gn gen out/Static-m138 --args='
    is_official_build=true
    skia_enable_gpu=true
    skia_use_system_expat=false
    skia_use_system_freetype2=false
    skia_use_system_harfbuzz=false
    skia_use_system_icu=false
    skia_use_system_libjpeg_turbo=false
    skia_use_system_libpng=false
    skia_use_system_libwebp=false
    skia_use_system_zlib=false
    cc="clang"
    cxx="clang++"
    '

However, when I replace the compilers with

    cc="zig cc"
    cxx="zig c++"

The build fails with many AVX errors:

...
../../modules/skcms/src/Transform_inl.h:884:9: error: AVX vector return of type 'float __attribute__((ext_vector_type(16)))' (vector of 16 'float' values) without 'evex512' enabled changes the ABI
  884 |     b = cast<F>(load_3<U32>(rgb+2) ) * (1/255.0f);
      |         ^
../../modules/skcms/src/Transform_inl.h:889:16: error: AVX vector return of type 'unsigned int __attribute__((ext_vector_type(16)))' (vector of 16 'unsigned int' values) without 'evex512' enabled changes the ABI
  889 |     U32 rgba = load<U32>(src + 4*i);
      |                ^
fatal error: too many errors emitted, stopping now [-ferror-limit=]
20 errors generated.

What extra options do I need to make the build with zig compiler succeed?

zig cc/c++ will add ‘opinionated’ command line options to the clang frontend invocation which clang/clang++ doesn’t enable by default, e.g. one notable difference is that zig cc runs the clang frontend with -march=native the other being that various clang sanitizers are enabled by default in non-optimized modes.

E.g. you could try if your clang/clang++ build also fails the same way when you explicitly add the -march=native command line option.

A solution might be to provide a generic target triple so that the zig cc build will not attempt to use AVX instruction.

I’m not sure what the best solution is if you actually want to use AVX (but the error message seems to provide a hint that evex512 is needed (whatever that is).

2 Likes

I only had a quick glance at the Skia source code so consider this guesswork, I could be completely wrong. Assuming you’re compiling for your native target, I suspect that the problem is that Skia detects that your CPU supports AVX512 but then goes on to use features that technically also require evex512, which the Zig compiler has not detected/enabled for your CPU model.

I haven’t tried this, but I would first start by trying to explicitly enabling it with

cc="zig cc -target-feature +evex512"
cxx="zig c++ -target-feature +evex512"

but be aware that this could cause issues if your CPU truly doesn’t support this feature. Another potential recourse seeing if there’s some option you can pass to Skia to tell it to not enable AVX512.

2 Likes

With

cc="clang -march=native"
cxx="clang++ -march=native"

the build also succeeded.

My notebook is old. It supports avx2 but not avx512.
The current BUILD.gn in the skia project doesn’t provide an options to disable avx512. I commented out the following lines in it but the zig compiler build still failed with the same errors.

# defines += [ "SK_ENABLE_AVX512_OPTS" ]

  deps = [
...
    #":skx",
...
  ]

After adding extra_cflags=["-DSKCMS_PORTABLE"] as gn argument, the avx errors go, but some other new errors come. The new errors look related to some definitions. Strangely, clang build doesn’t need the definitions. I stopped here.

I tried this, but it looks the options are invalid.

I’m a newbie in c/c++ compiler options. I tried the -mno-avx512f compiler option, but it doesn’t help too.

That should be -Xclang -target-feature -Xclang +evex512.

3 Likes

Yes, these are valid. Without extra_cflags=["-DSKCMS_PORTABLE"], they indeed remove all the avx errors. Now the same situation as where I stopped here: some new errors related to missing some definitions come. But the clang build doesn’t need these definitions.

Could you post the output of zig build-exe --show-builtin on your machine?

Also, what definitions?

const std = @import("std");
/// Zig version. When writing code that supports multiple versions of Zig, prefer
/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
pub const zig_version = std.SemanticVersion.parse(zig_version_string) catch unreachable;
pub const zig_version_string = "0.14.1";
pub const zig_backend = std.builtin.CompilerBackend.stage2_llvm;

pub const output_mode: std.builtin.OutputMode = .Exe;
pub const link_mode: std.builtin.LinkMode = .static;
pub const unwind_tables: std.builtin.UnwindTables = .@"async";
pub const is_test = false;
pub const single_threaded = false;
pub const abi: std.Target.Abi = .gnu;
pub const cpu: std.Target.Cpu = .{
    .arch = .x86_64,
    .model = &std.Target.x86.cpu.haswell,
    .features = std.Target.x86.featureSet(&.{
        .@"64bit",
        .aes,
        .allow_light_256_bit,
        .avx,
        .avx2,
        .bmi,
        .bmi2,
        .cmov,
        .crc32,
        .cx16,
        .cx8,
        .ermsb,
        .f16c,
        .false_deps_lzcnt_tzcnt,
        .false_deps_popcnt,
        .fast_15bytenop,
        .fast_scalar_fsqrt,
        .fast_shld_rotate,
        .fast_variable_crosslane_shuffle,
        .fast_variable_perlane_shuffle,
        .fma,
        .fsgsbase,
        .fxsr,
        .idivq_to_divl,
        .invpcid,
        .lzcnt,
        .macrofusion,
        .mmx,
        .movbe,
        .no_bypass_delay_mov,
        .no_bypass_delay_shuffle,
        .nopl,
        .pclmul,
        .popcnt,
        .rdrnd,
        .sahf,
        .slow_3ops_lea,
        .smep,
        .sse,
        .sse2,
        .sse3,
        .sse4_1,
        .sse4_2,
        .ssse3,
        .vzeroupper,
        .x87,
        .xsave,
        .xsaveopt,
    }),
};
pub const os: std.Target.Os = .{
    .tag = .linux,
    .version_range = .{ .linux = .{
        .range = .{
            .min = .{
                .major = 6,
                .minor = 1,
                .patch = 0,
            },
            .max = .{
                .major = 6,
                .minor = 1,
                .patch = 0,
            },
        },
        .glibc = .{
            .major = 2,
            .minor = 36,
            .patch = 0,
        },
        .android = 14,
    }},
};
pub const target: std.Target = .{
    .cpu = cpu,
    .os = os,
    .abi = abi,
    .ofmt = object_format,
    .dynamic_linker = .init("/lib64/ld-linux-x86-64.so.2"),
};
pub const object_format: std.Target.ObjectFormat = .elf;
pub const mode: std.builtin.OptimizeMode = .Debug;
pub const link_libc = false;
pub const link_libcpp = false;
pub const have_error_return_tracing = true;
pub const valgrind_support = true;
pub const sanitize_thread = false;
pub const fuzz = false;
pub const position_independent_code = false;
pub const position_independent_executable = false;
pub const strip_debug_info = false;
pub const code_model: std.builtin.CodeModel = .default;
pub const omit_frame_pointer = false;

Such as "-DXML_GE=1",

I’ve managed to solve the problem with the next gn flags

extra_cflags+=["-stdlib=libc++", "-DSKCMS_PORTABLE", "-march=x86_64"]

After this, no problems with AVX512, but eventually I faced

  = note: ld.lld: error: undefined symbol: SkDebugf(char const*, ...)
          >>> referenced by SkContainers.cpp:30 (../../../../../../../../.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/skia-bindings-0.86.1/skia/src/base/SkContainers.cpp:30)
          >>>               libskia.SkContainers.o:(SkContainerAllocator::allocate(int, double)::$_0::operator()() const) in archive /home/lisovskiy/test-slint/target/x86_64-unknown-linux-gnu/debug/deps/libskia_bindings-ef2d2a51dac56a90.rlib
          >>> referenced by SkTDArray.cpp:216 (../../../../../../../../.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/skia-bindings-0.86.1/skia/src/base/SkTDArray.cpp:216)
          >>>               libskia.SkTDArray.o:(SkTDStorage::calculateSizeOrDie(int)::$_0::operator()() const) in archive /home/lisovskiy/test-slint/target/x86_64-unknown-linux-gnu/debug/deps/libskia_bindings-ef2d2a51dac56a90.rlib
          >>> referenced by SkTDArray.cpp:223 (../../../../../../../../.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/skia-bindings-0.86.1/skia/src/base/SkTDArray.cpp:223)
          >>>               libskia.SkTDArray.o:(SkTDStorage::calculateSizeOrDie(int)::$_1::operator()() const) in archive /home/lisovskiy/test-slint/target/x86_64-unknown-linux-gnu/debug/deps/libskia_bindings-ef2d2a51dac56a90.rlib
          >>> referenced 215 more times

Looks like that’s the last compile problem, but I’m struggling to find out how to fix it

Going the most stupid way, I commented out all SkDebugf from source code and after this it successfully compiled.

My initial intention was to build it with target x86_64-linux-gnu.2.17 to greatly lower glibc required version. Below the proof that binary doesn’t require glibc higher than 2.17

ldd --verbose ./target/x86_64-unknown-linux-gnu/debug/test-slint
...
        ./target/x86_64-unknown-linux-gnu/debug/test-slint:
                libm.so.6 (GLIBC_2.2.5) => /usr/lib/libm.so.6
                libpthread.so.0 (GLIBC_2.2.5) => /usr/lib/libpthread.so.0
                libpthread.so.0 (GLIBC_2.3.2) => /usr/lib/libpthread.so.0
                libpthread.so.0 (GLIBC_2.12) => /usr/lib/libpthread.so.0
                libc.so.6 (GLIBC_2.2.5) => /usr/lib/libc.so.6
                libc.so.6 (GLIBC_2.3) => /usr/lib/libc.so.6
                libc.so.6 (GLIBC_2.3.2) => /usr/lib/libc.so.6
                libc.so.6 (GLIBC_2.3.3) => /usr/lib/libc.so.6
                libc.so.6 (GLIBC_2.3.4) => /usr/lib/libc.so.6
                libc.so.6 (GLIBC_2.4) => /usr/lib/libc.so.6
                libc.so.6 (GLIBC_2.7) => /usr/lib/libc.so.6
                libc.so.6 (GLIBC_2.9) => /usr/lib/libc.so.6
                libc.so.6 (GLIBC_2.14) => /usr/lib/libc.so.6
                libc.so.6 (GLIBC_2.15) => /usr/lib/libc.so.6
                libc.so.6 (GLIBC_2.16) => /usr/lib/libc.so.6
                libc.so.6 (GLIBC_2.17) => /usr/lib/libc.so.6
                libdl.so.2 (GLIBC_2.2.5) => /usr/lib/libdl.so.2
                ld-linux-x86-64.so.2 (GLIBC_2.3) => /usr/lib64/ld-linux-x86-64.so.2
...

I think there should be a way to “repair” SkDebugf without commenting lines out, which allow to build Skia with Zig smoothly

Great work!

Could you show the final complete gn args?

Sure!

is_official_build=true
is_debug=false
skia_enable_svg=false
skia_enable_gpu=true
skia_enable_skottie=false
skia_enable_pdf=true
skia_use_gl=true
skia_use_egl=false
skia_use_x11=false
skia_use_system_libpng=false
skia_use_libwebp_encode=false
skia_use_libwebp_decode=false
skia_use_system_zlib=false
skia_use_xps=false
skia_use_dng_sdk=false
cc="zig cc --target=x86_64-linux-gnu"
cxx="zig c++ --target=x86_64-linux-gnu"
skia_enable_skshaper=true
skia_use_icu=true
skia_use_system_icu=false
skia_use_harfbuzz=true
skia_pdf_subset_harfbuzz=true
skia_use_system_harfbuzz=false
skia_enable_skparagraph=true
skia_use_freetype=true
skia_use_freetype_woff2=false
skia_use_system_freetype2=false
skia_use_system_libjpeg_turbo=false
skia_use_expat=true
skia_use_system_expat=false
target_os="gnu"
target_cpu="x86_64"
extra_cflags=["-Os","--target=x86_64-linux-gnu"]
extra_asmflags=["--target=x86_64-linux-gnu"]
extra_cflags+=["-stdlib=libc++", "-DSKCMS_PORTABLE", "-march=x86_64"]

Also if you face ld.lld: error: undefined symbol: SkDebugf(char const*, ...), please let me know how you will solve it

I think the root of the problem in Skia BUILD.gn option:

opts("skx") {
  enabled = is_x86
  sources = skia_opts.skx_sources
  if (is_win) {
    cflags = [ "/arch:AVX512" ]
  } else {
    cflags = [ "-march=skylake-avx512" ]
  }
}

By default it tries to compile with -march=skylake-avx512 and it makes Zig compiler unhappy, because host CPU doesn’t support AVX512 instructions. So the above solution is to overwrite -march with regular x86_64

With this gn args setup, it builds successfully for me. Except the avx warnings, it also mentions that “-stdlib=libc++” is unused during compilation.

No SkDebugf undefined problem (with the chrome/m138 branch). I also saw this problem on an ever tip (before m138).

About the “skx” optimizations, I ever disabled it, which does removes avx warnings, but brings some new problems.

Thanks for the info!

After trying many different gn args setups, I find the build also succeeds with the following one:

bin/gn gen out/Static-m138-zig --args='
    is_official_build=true
    is_debug=false
    
    skia_enable_gpu=true
    skia_enable_skshaper=true
    skia_enable_skparagraph=true
    
    skia_use_expat=true
    skia_use_system_expat=false
    
    skia_use_freetype=true
    skia_use_system_freetype2=false
    skia_use_freetype_woff2=false
    
    skia_use_harfbuzz=true
    skia_use_system_harfbuzz=false
    skia_pdf_subset_harfbuzz=true
    
    skia_use_icu=true
    skia_use_system_icu=false
    
    skia_use_system_libjpeg_turbo=false
    skia_use_system_libpng=false
    skia_use_system_zlib=false
    skia_use_libwebp_encode=false
    skia_use_libwebp_decode=false
    
    skia_enable_svg=true
    skia_enable_pdf=true
    skia_use_xps=false
    skia_use_dng_sdk=false
    skia_enable_skottie=false

    cc="zig cc"
    cxx="zig c++"
    extra_cflags=["-DSKCMS_PORTABLE", "-DXML_GE=1", "-march=x86_64", "--target=x86_64-linux-gnu"]
    target_cpu="x86_64"
    target_os="gnu"
    '

It is strange that, without the "--target=x86_64-linux-gnu"] c flag or target_os="gnu" arg, the build will fail with errors like

error: no member named 'requestLocale' in 'icu::BreakIterator'

However, the icu::BreakIterator class indeed declares a requestLocale field which is not guarded in any define vars.