Skip to content

build.zig generates invalid LLVM IR #12588

@folkertdev

Description

@folkertdev

Zig Version

0.10.0-dev.3672+cd5a9ba1f

Steps to Reproduce

main.zig

const std = @import("std");

export fn strFromFloatHelp(float: f64) void {
    var buf: [400]u8 = undefined;
    _ = std.fmt.bufPrint(&buf, "{d}", .{float}) catch unreachable;
}

build.zig

const std = @import("std");
const mem = std.mem;
const Builder = std.build.Builder;
const CrossTarget = std.zig.CrossTarget;
const Arch = std.Target.Cpu.Arch;

pub fn build(b: *Builder) void {
    // b.setPreferredReleaseMode(.Debug);
    b.setPreferredReleaseMode(.ReleaseFast);
    const mode = b.standardReleaseOptions();

    // Targets
    const host_target = b.standardTargetOptions(.{
        .default_target = CrossTarget{
            .cpu_model = .baseline,
            // TODO allow for native target for maximum speed
        },
    });

    // LLVM IR
    const obj = b.addObject("builtins-host", "main.zig");
    obj.setBuildMode(mode);
    obj.strip = true;
    obj.emit_llvm_ir = .emit;
    obj.emit_llvm_bc = .emit;
    obj.emit_bin = .no_emit;
    obj.target = host_target;

    const ir = b.step("ir", "Build LLVM ir");
    ir.dependOn(&obj.step);

    removeInstallSteps(b);
}

fn removeInstallSteps(b: *Builder) void {
    for (b.top_level_steps.items) |top_level_step, i| {
        const name = top_level_step.step.name;
        if (mem.eql(u8, name, "install") or mem.eql(u8, name, "uninstall")) {
            _ = b.top_level_steps.swapRemove(i);
        }
    }
}

Now run

zig build ir

That produces builtins-host.ll. This file does not pass LLVM verification

> clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04/bin/opt -S -verify builtins-host.ll

builtins-host.ll:55:1: error: global variable reference must have pointer type
@"io.fixed_buffer_stream.FixedBufferStream([]u8).write__anon_886" = internal unnamed_addr global [16 x i8]
^

Expected Behavior

I expect the zig LLVM IR output to pass LLVM verification. The build.zig output does not. But, using the zig cli, we can do

> zig build-obj -O ReleaseFast main.zig -femit-llvm-ir

then

clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04/bin/opt -S -verify main.ll

works. the llvm IR code contains proper constants

@fmt.errol.enum3.enum3_data__anon_7191 = internal unnamed_addr constant [17 x i8] c"8054164326565191\00", align 1
@fmt.errol.enum3.enum3_data__anon_7193 = internal unnamed_addr constant [18 x i8] c"32216657306260762\00", align 1
@fmt.errol.enum3.enum3_data__anon_7195 = internal unnamed_addr constant [18 x i8] c"30423431424080128\00", align 1

Actual Behavior

The build.zig version does not pass the llvm verifier pass

indeed, some weird-looking globals are generated

@fmt.errol.enum3.enum3_data__anon_9449 = internal unnamed_addr global [18 x i8]
@fmt.errol.enum3.enum3_data__anon_9451 = internal unnamed_addr global [18 x i8]
@fmt.errol.enum3.enum3_data__anon_9453 = internal unnamed_addr global [17 x i8]
@fmt.errol.enum3.enum3_data__anon_9455 = internal unnamed_addr global [18 x i8]
@fmt.errol.enum3.enum3_data__anon_9457 = internal unnamed_addr global [18 x i8]

I don't know what is different exactly between a build.zig and a CLI invocation. Maybe I'm just missing some configuration? I know this works correctly in zig 0.9

Metadata

Metadata

Assignees

No one assigned

    Labels

    backend-llvmThe LLVM backend outputs an LLVM IR Module.bugObserved behavior contradicts documented or intended behaviorfrontendTokenization, parsing, AstGen, Sema, and Liveness.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions