Error when compiling assembly with zig build

I’m getting these errors when I try to compile a riscv assembly file

path/main.s:1:1: error: clang exited with code 1
error: error(compilation): clang failed with stderr:
    path/main.s:32:2: error: instruction requires the following: 'Zbb' (Basic Bit-Manipulation) or 'Zbkb' (Bitmanip instructions for Cryptography)
        roriw a0, x0, 6
        ^
    path/main.s:84:2: error: instruction requires the following: 'Zcb' (Compressed basic bit manipulation instructions)
        c.lbu a5, (a4)
        ^
    path/main.s:85:2: error: instruction requires the following: 'Zcb' (Compressed basic bit manipulation instructions)
        c.sb a5, (a3)
        ^
    path/main.s:136:2: error: instruction requires the following: 'Zcb' (Compressed basic bit manipulation instructions)
        c.sb a0, (a3)
        ^

This is the build script I’m using

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.resolveTargetQuery(.{
        .cpu_arch = .riscv64,
        .os_tag = .linux,
        .cpu_features_add = std.Target.riscv.featureSet(&.{
            .c,
            .zcb,
            .zbb,
        }),
    });

    var mod = b.createModule(.{
        .target = target,
        .optimize = .ReleaseFast,
        .strip = true,
    });

    mod.addAssemblyFile(b.path("main.s"));

    const exe = b.addExecutable(.{ .name = "main", .root_module = mod, });

    b.installArtifact(exe);
}

Is adding cpu features in the target not enough? or is this supposed to work?

Have a look at src/Compilation.zig lines 6677-6726:

            // The Clang assembler does not accept the list of CPU features like the
            // compiler frontend does. Therefore we must hard-code the -m flags for
            // all CPU features here.
            switch (target.cpu.arch) {
                .riscv32, .riscv32be, .riscv64, .riscv64be => {
                    const RvArchFeat = struct { char: u8, feat: std.Target.riscv.Feature };
                    const letters = [_]RvArchFeat{
                        .{ .char = 'm', .feat = .m },
                        .{ .char = 'a', .feat = .a },
                        .{ .char = 'f', .feat = .f },
                        .{ .char = 'd', .feat = .d },
                        .{ .char = 'c', .feat = .c },
                    };
                    const prefix: []const u8 = if (target.cpu.arch == .riscv64) "rv64" else "rv32";
                    const prefix_len = 4;
                    assert(prefix.len == prefix_len);
                    var march_buf: [prefix_len + letters.len + 1]u8 = undefined;
                    var march_index: usize = prefix_len;
                    @memcpy(march_buf[0..prefix.len], prefix);

                    if (target.cpu.has(.riscv, .e)) {
                        march_buf[march_index] = 'e';
                    } else {
                        march_buf[march_index] = 'i';
                    }
                    march_index += 1;

                    for (letters) |letter| {
                        if (target.cpu.has(.riscv, letter.feat)) {
                            march_buf[march_index] = letter.char;
                            march_index += 1;
                        }
                    }

                    const march_arg = try std.fmt.allocPrint(arena, "-march={s}", .{
                        march_buf[0..march_index],
                    });
                    try argv.append(march_arg);

                    if (target.cpu.has(.riscv, .relax)) {
                        try argv.append("-mrelax");
                    } else {
                        try argv.append("-mno-relax");
                    }
                    if (target.cpu.has(.riscv, .save_restore)) {
                        try argv.append("-msave-restore");
                    } else {
                        try argv.append("-mno-save-restore");
                    }
                },

You can set ZIG_VERBOSE_CC=1 environment variable to obtain the zig clang command line being used to attempt to build the assembly.

If you can figure out what flags are needed to be passed to correctly communicate the target, you’re 95% of the way to fixing this problem in the compiler.

I was compiling with llvm-mc before which just takes the list of extentions as comma seperated values :sweat_smile:. I assumed that’s what zig used under the hood

So the issue was it completely ignores those feature sets and we have to include them by just concatnating the feature set enum tagnames with _ in between

This bug is fixed in the upcoming 0.17.0: https://codeberg.org/ziglang/zig/commit/bddd915dd92abbf106a9dc5600782c3fd61b0a37

It should have been fixed in 0.16.0 as part of https://codeberg.org/ziglang/zig/commit/8c47dda7df2ff123699856eea513a266952b91bf but it looks like some refactoring to that function broke the fix between that commit and the release.

The code block Andrew pointed to should probably be deleted after verifying that it no longer does anything useful.

https://codeberg.org/ziglang/zig/pulls/36953