Commit 69a6f31596

Jacob Young <jacobly0@users.noreply.github.com>
2024-02-22 18:33:36
Builder: fix llvm ir value names
Hello world now verifies when not stripped.
1 parent 6cc51d3
Changed files (3)
lib
src
codegen
lib/std/meta.zig
@@ -464,7 +464,8 @@ pub fn fieldNames(comptime T: type) *const [fields(T).len][:0]const u8 {
     return comptime blk: {
         const fieldInfos = fields(T);
         var names: [fieldInfos.len][:0]const u8 = undefined;
-        for (&names, fieldInfos) |*name, field| name.* = field.name;
+        // This concat can be removed with the next zig1 update.
+        for (&names, fieldInfos) |*name, field| name.* = field.name ++ "";
         break :blk &names;
     };
 }
src/codegen/llvm/Builder.zig
@@ -77,11 +77,11 @@ pub const String = enum(u32) {
         return self.toIndex() == null;
     }
 
-    pub fn slice(self: String, builder: *const Builder) ?[:0]const u8 {
+    pub fn slice(self: String, builder: *const Builder) ?[]const u8 {
         const index = self.toIndex() orelse return null;
         const start = builder.string_indices.items[index];
         const end = builder.string_indices.items[index + 1];
-        return builder.string_bytes.items[start .. end - 1 :0];
+        return builder.string_bytes.items[start..end];
     }
 
     const FormatData = struct {
@@ -94,17 +94,21 @@ pub const String = enum(u32) {
         _: std.fmt.FormatOptions,
         writer: anytype,
     ) @TypeOf(writer).Error!void {
-        if (comptime std.mem.indexOfNone(u8, fmt_str, "@\"")) |_|
+        if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_|
             @compileError("invalid format string: '" ++ fmt_str ++ "'");
         assert(data.string != .none);
-        const sentinel_slice = data.string.slice(data.builder) orelse
+        const string_slice = data.string.slice(data.builder) orelse
             return writer.print("{d}", .{@intFromEnum(data.string)});
-        try printEscapedString(sentinel_slice[0 .. sentinel_slice.len + comptime @intFromBool(
-            std.mem.indexOfScalar(u8, fmt_str, '@') != null,
-        )], if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_|
-            .always_quote
-        else
-            .quote_unless_valid_identifier, writer);
+        if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_|
+            return writer.writeAll(string_slice);
+        try printEscapedString(
+            string_slice,
+            if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_|
+                .always_quote
+            else
+                .quote_unless_valid_identifier,
+            writer,
+        );
     }
     pub fn fmt(self: String, builder: *const Builder) std.fmt.Formatter(format) {
         return .{ .data = .{ .string = self, .builder = builder } };
@@ -4597,13 +4601,6 @@ pub const Function = struct {
             ) std.fmt.Formatter(format) {
                 return .{ .data = .{ .instruction = self, .function = function, .builder = builder } };
             }
-
-            fn llvmName(self: Instruction.Index, wip: *const WipFunction) [:0]const u8 {
-                return if (wip.builder.strip)
-                    ""
-                else
-                    wip.names.items[@intFromEnum(self)].slice(wip.builder).?;
-            }
         };
 
         pub const ExtraIndex = u32;
@@ -5931,15 +5928,47 @@ pub const WipFunction = struct {
 
         var wip_name: struct {
             next_name: String = @enumFromInt(0),
+            next_unique_name: std.AutoHashMap(String, String),
+            builder: *Builder,
 
-            fn map(wip_name: *@This(), old_name: String) String {
-                if (old_name != .empty) return old_name;
+            fn map(wip_name: *@This(), name: String, sep: []const u8) Allocator.Error!String {
+                switch (name) {
+                    .none => return .none,
+                    .empty => {
+                        assert(wip_name.next_name != .none);
+                        defer wip_name.next_name = @enumFromInt(@intFromEnum(wip_name.next_name) + 1);
+                        return wip_name.next_name;
+                    },
+                    _ => {
+                        assert(!name.isAnon());
+                        const gop = try wip_name.next_unique_name.getOrPut(name);
+                        if (!gop.found_existing) {
+                            gop.value_ptr.* = @enumFromInt(0);
+                            return name;
+                        }
 
-                const new_name = wip_name.next_name;
-                wip_name.next_name = @enumFromInt(@intFromEnum(new_name) + 1);
-                return new_name;
+                        while (true) {
+                            gop.value_ptr.* = @enumFromInt(@intFromEnum(gop.value_ptr.*) + 1);
+                            const unique_name = try wip_name.builder.fmt("{r}{s}{r}", .{
+                                name.fmt(wip_name.builder),
+                                sep,
+                                gop.value_ptr.fmt(wip_name.builder),
+                            });
+                            const unique_gop = try wip_name.next_unique_name.getOrPut(unique_name);
+                            if (!unique_gop.found_existing) {
+                                unique_gop.value_ptr.* = @enumFromInt(0);
+                                return unique_name;
+                            }
+                        }
+                    },
+                }
             }
-        } = .{};
+        } = .{
+            .next_unique_name = std.AutoHashMap(String, String).init(gpa),
+            .builder = self.builder,
+        };
+        defer wip_name.next_unique_name.deinit();
+
         var value_index: u32 = 0;
         for (0..params_len) |param_index| {
             const old_argument_index: Instruction.Index = @enumFromInt(param_index);
@@ -5950,8 +5979,9 @@ pub const WipFunction = struct {
             value_indices[function.instructions.len] = value_index;
             value_index += 1;
             function.instructions.appendAssumeCapacity(argument);
-            names[@intFromEnum(new_argument_index)] = wip_name.map(
+            names[@intFromEnum(new_argument_index)] = try wip_name.map(
                 if (self.builder.strip) .empty else self.names.items[@intFromEnum(old_argument_index)],
+                ".",
             );
             if (self.debug_locations.get(old_argument_index)) |location| {
                 debug_locations.putAssumeCapacity(new_argument_index, location);
@@ -5967,7 +5997,7 @@ pub const WipFunction = struct {
                 .tag = .block,
                 .data = current_block.incoming,
             });
-            names[@intFromEnum(new_block_index)] = wip_name.map(current_block.name);
+            names[@intFromEnum(new_block_index)] = try wip_name.map(current_block.name, "");
             for (current_block.instructions.items) |old_instruction_index| {
                 const new_instruction_index: Instruction.Index =
                     @enumFromInt(function.instructions.len);
@@ -6268,10 +6298,10 @@ pub const WipFunction = struct {
                     },
                 }
                 function.instructions.appendAssumeCapacity(instruction);
-                names[@intFromEnum(new_instruction_index)] = wip_name.map(if (self.builder.strip)
+                names[@intFromEnum(new_instruction_index)] = try wip_name.map(if (self.builder.strip)
                     if (old_instruction_index.hasResultWip(self)) .empty else .none
                 else
-                    self.names.items[@intFromEnum(old_instruction_index)]);
+                    self.names.items[@intFromEnum(old_instruction_index)], ".");
 
                 if (self.debug_locations.get(old_instruction_index)) |location| {
                     debug_locations.putAssumeCapacity(new_instruction_index, location);
@@ -6687,7 +6717,6 @@ pub const Constant = enum(u32) {
         packed_structure,
         array,
         string,
-        string_null,
         vector,
         splat,
         zeroinitializer,
@@ -6943,10 +6972,8 @@ pub const Constant = enum(u32) {
                     => builder.constantExtraData(Aggregate, item.data).type,
                     .splat => builder.constantExtraData(Splat, item.data).type,
                     .string,
-                    .string_null,
                     => builder.arrayTypeAssumeCapacity(
-                        @as(String, @enumFromInt(item.data)).slice(builder).?.len +
-                            @intFromBool(item.tag == .string_null),
+                        @as(String, @enumFromInt(item.data)).slice(builder).?.len,
                         .i8,
                     ),
                     .blockaddress => builder.ptrTypeAssumeCapacity(
@@ -7281,13 +7308,9 @@ pub const Constant = enum(u32) {
                         }
                         try writer.writeByte('>');
                     },
-                    inline .string,
-                    .string_null,
-                    => |tag| try writer.print("c{\"" ++ switch (tag) {
-                        .string => "",
-                        .string_null => "@",
-                        else => unreachable,
-                    } ++ "}", .{@as(String, @enumFromInt(item.data)).fmt(data.builder)}),
+                    .string => try writer.print("c{\"}", .{
+                        @as(String, @enumFromInt(item.data)).fmt(data.builder),
+                    }),
                     .blockaddress => |tag| {
                         const extra = data.builder.constantExtraData(BlockAddress, item.data);
                         const function = extra.function.ptrConst(data.builder);
@@ -7658,7 +7681,7 @@ pub const Metadata = enum(u32) {
         {
             const index = @intFromEnum(metadata) - Metadata.first_forward_reference;
             metadata = builder.metadata_forward_references.items[index];
-            std.debug.assert(metadata != .none);
+            assert(metadata != .none);
         }
         return metadata;
     }
@@ -8058,30 +8081,33 @@ pub const Metadata = enum(u32) {
                     }
                 },
                 .index => |item| try writer.print("!{d}", .{item}),
-                .value => |item| try Value.format(.{
-                    .value = switch (item.value.unwrap()) {
-                        .instruction, .constant => item.value,
-                        .metadata => |metadata| if (@intFromEnum(metadata) >=
-                            Metadata.first_local_metadata)
-                            item.function.ptrConst(builder).debug_values[
+                .value => |item| switch (item.value.unwrap()) {
+                    .instruction, .constant => try Value.format(.{
+                        .value = item.value,
+                        .function = item.function,
+                        .builder = builder,
+                    }, fmt_str, fmt_opts, writer),
+                    .metadata => |metadata| if (@intFromEnum(metadata) >=
+                        Metadata.first_local_metadata)
+                        try Value.format(.{
+                            .value = item.function.ptrConst(builder).debug_values[
                                 @intFromEnum(metadata) - Metadata.first_local_metadata
-                            ].toValue()
-                        else if (metadata != .none) {
-                            if (comptime std.mem.eql(u8, fmt_str, "%"))
-                                try writer.print("{%} ", .{Type.metadata.fmt(builder)});
-                            try Metadata.Formatter.format(.{
-                                .formatter = data.formatter,
-                                .item = data.formatter.unwrapAssumeExists(metadata),
-                            }, "", fmt_opts, writer);
-                            return;
-                        } else return,
+                            ].toValue(),
+                            .function = item.function,
+                            .builder = builder,
+                        }, "%", fmt_opts, writer)
+                    else if (metadata != .none) {
+                        if (comptime std.mem.eql(u8, fmt_str, "%"))
+                            try writer.print("{%} ", .{Type.metadata.fmt(builder)});
+                        try Metadata.Formatter.format(.{
+                            .formatter = data.formatter,
+                            .item = data.formatter.unwrapAssumeExists(metadata),
+                        }, "", fmt_opts, writer);
                     },
-                    .function = item.function,
-                    .builder = builder,
-                }, "%", fmt_opts, writer),
+                },
                 .string => |item| try writer.print("{}", .{item.fmt(data.formatter.builder)}),
                 inline .bool, .u32, .u64 => |item| try writer.print("{}", .{item}),
-                .raw => |item| try writer.print("{s}", .{item}),
+                .raw => |item| try writer.writeAll(item),
             }
         }
         inline fn fmt(formatter: *Formatter, prefix: []const u8, item: anytype) switch (@TypeOf(item)) {
@@ -8188,7 +8214,7 @@ pub const Metadata = enum(u32) {
             inline for (fields[2..], names) |*field, name| {
                 fmt_str = fmt_str ++ "{[" ++ name ++ "]}";
                 field.* = .{
-                    .name = name ++ "",
+                    .name = name,
                     .type = std.fmt.Formatter(format),
                     .default_value = null,
                     .is_comptime = false,
@@ -8305,7 +8331,7 @@ pub fn init(options: Options) Allocator.Error!Builder {
     assert(try self.intConst(.i1, 0) == .false);
     assert(try self.intConst(.i1, 1) == .true);
     assert(try self.noneConst(.token) == .none);
-    assert(try self.debugNone() == .none);
+    if (!self.strip) assert(try self.debugNone() == .none);
 
     try self.metadata_string_indices.append(self.gpa, 0);
     assert(try self.metadataString("") == .none);
@@ -8374,19 +8400,22 @@ pub fn finishModuleAsm(self: *Builder) Allocator.Error!void {
 }
 
 pub fn string(self: *Builder, bytes: []const u8) Allocator.Error!String {
-    try self.string_bytes.ensureUnusedCapacity(self.gpa, bytes.len + 1);
+    try self.string_bytes.ensureUnusedCapacity(self.gpa, bytes.len);
     try self.string_indices.ensureUnusedCapacity(self.gpa, 1);
     try self.string_map.ensureUnusedCapacity(self.gpa, 1);
 
     const gop = self.string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self });
     if (!gop.found_existing) {
         self.string_bytes.appendSliceAssumeCapacity(bytes);
-        self.string_bytes.appendAssumeCapacity(0);
         self.string_indices.appendAssumeCapacity(@intCast(self.string_bytes.items.len));
     }
     return String.fromIndex(gop.index);
 }
 
+pub fn stringNull(self: *Builder, bytes: [:0]const u8) Allocator.Error!String {
+    return self.string(bytes[0 .. bytes.len + 1]);
+}
+
 pub fn stringIfExists(self: *const Builder, bytes: []const u8) ?String {
     return String.fromIndex(
         self.string_map.getIndexAdapted(bytes, String.Adapter{ .builder = self }) orelse return null,
@@ -8395,15 +8424,15 @@ pub fn stringIfExists(self: *const Builder, bytes: []const u8) ?String {
 
 pub fn fmt(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) Allocator.Error!String {
     try self.string_map.ensureUnusedCapacity(self.gpa, 1);
-    try self.string_bytes.ensureUnusedCapacity(self.gpa, @intCast(std.fmt.count(fmt_str ++ .{0}, fmt_args)));
+    try self.string_bytes.ensureUnusedCapacity(self.gpa, @intCast(std.fmt.count(fmt_str, fmt_args)));
     try self.string_indices.ensureUnusedCapacity(self.gpa, 1);
     return self.fmtAssumeCapacity(fmt_str, fmt_args);
 }
 
 pub fn fmtAssumeCapacity(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) String {
     const start = self.string_bytes.items.len;
-    self.string_bytes.writer(self.gpa).print(fmt_str ++ .{0}, fmt_args) catch unreachable;
-    const bytes: []const u8 = self.string_bytes.items[start .. self.string_bytes.items.len - 1];
+    self.string_bytes.writer(undefined).print(fmt_str, fmt_args) catch unreachable;
+    const bytes: []const u8 = self.string_bytes.items[start..];
 
     const gop = self.string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self });
     if (gop.found_existing) {
@@ -8468,7 +8497,7 @@ pub fn structType(
 pub fn opaqueType(self: *Builder, name: String) Allocator.Error!Type {
     try self.string_map.ensureUnusedCapacity(self.gpa, 1);
     if (name.slice(self)) |id| {
-        const count: usize = comptime std.fmt.count("{d}" ++ .{0}, .{std.math.maxInt(u32)});
+        const count: usize = comptime std.fmt.count("{d}", .{std.math.maxInt(u32)});
         try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len + count);
     }
     try self.string_indices.ensureUnusedCapacity(self.gpa, 1);
@@ -8911,16 +8940,6 @@ pub fn stringValue(self: *Builder, val: String) Allocator.Error!Value {
     return (try self.stringConst(val)).toValue();
 }
 
-pub fn stringNullConst(self: *Builder, val: String) Allocator.Error!Constant {
-    try self.ensureUnusedTypeCapacity(1, Type.Array, 0);
-    try self.ensureUnusedConstantCapacity(1, NoExtra, 0);
-    return self.stringNullConstAssumeCapacity(val);
-}
-
-pub fn stringNullValue(self: *Builder, val: String) Allocator.Error!Value {
-    return (try self.stringNullConst(val)).toValue();
-}
-
 pub fn vectorConst(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Constant {
     try self.ensureUnusedConstantCapacity(1, Constant.Aggregate, vals.len);
     return self.vectorConstAssumeCapacity(ty, vals);
@@ -9354,7 +9373,7 @@ pub fn printUnbuffered(
             defer metadata_formatter.need_comma = undefined;
             try writer.print("{ }{}", .{
                 function.alignment,
-                try metadata_formatter.fmt("!dbg ", global.dbg),
+                try metadata_formatter.fmt(" !dbg ", global.dbg),
             });
         }
         if (function.instructions.len > 0) {
@@ -9513,7 +9532,8 @@ pub fn printUnbuffered(
                         const name = instruction_index.name(&function);
                         if (@intFromEnum(instruction_index) > params_len)
                             try writer.writeByte('\n');
-                        try writer.print("{}:", .{name.fmt(self)});
+                        try writer.print("{}:\n", .{name.fmt(self)});
+                        continue;
                     },
                     .br => |tag| {
                         const target: Function.Block.Index = @enumFromInt(instruction.data);
@@ -9965,6 +9985,7 @@ pub fn printUnbuffered(
                 .composite_union_type,
                 .composite_enumeration_type,
                 .composite_array_type,
+                .composite_vector_type,
                 => |kind| {
                     const extra = self.metadataExtraData(Metadata.CompositeType, metadata_item.data);
                     try metadata_formatter.specialized(.@"distinct !", .DICompositeType, .{
@@ -10165,9 +10186,6 @@ pub fn printUnbuffered(
                         .expr = extra.expression,
                     }, writer);
                 },
-                else => {
-                    try writer.writeByte('\n');
-                },
             }
         }
     }
@@ -10206,7 +10224,7 @@ fn printEscapedString(
 fn ensureUnusedGlobalCapacity(self: *Builder, name: String) Allocator.Error!void {
     try self.string_map.ensureUnusedCapacity(self.gpa, 1);
     if (name.slice(self)) |id| {
-        const count: usize = comptime std.fmt.count("{d}" ++ .{0}, .{std.math.maxInt(u32)});
+        const count: usize = comptime std.fmt.count("{d}", .{std.math.maxInt(u32)});
         try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len + count);
     }
     try self.string_indices.ensureUnusedCapacity(self.gpa, 1);
@@ -10881,16 +10899,6 @@ fn stringConstAssumeCapacity(self: *Builder, val: String) Constant {
     return result.constant;
 }
 
-fn stringNullConstAssumeCapacity(self: *Builder, val: String) Constant {
-    const slice = val.slice(self).?;
-    const ty = self.arrayTypeAssumeCapacity(slice.len + 1, .i8);
-    if (std.mem.allEqual(u8, slice, 0)) return self.zeroInitConstAssumeCapacity(ty);
-    const result = self.getOrPutConstantNoExtraAssumeCapacity(
-        .{ .tag = .string_null, .data = @intFromEnum(val) },
-    );
-    return result.constant;
-}
-
 fn vectorConstAssumeCapacity(self: *Builder, ty: Type, vals: []const Constant) Constant {
     assert(ty.isVector(self));
     assert(ty.vectorLen(self) == vals.len);
@@ -11756,8 +11764,8 @@ pub fn metadataStringFmt(self: *Builder, comptime fmt_str: []const u8, fmt_args:
 
 pub fn metadataStringFmtAssumeCapacity(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) MetadataString {
     const start = self.metadata_string_bytes.items.len;
-    self.metadata_string_bytes.writer(self.gpa).print(fmt_str, fmt_args) catch unreachable;
-    const bytes: []const u8 = self.metadata_string_bytes.items[start..self.metadata_string_bytes.items.len];
+    self.metadata_string_bytes.writer(undefined).print(fmt_str, fmt_args) catch unreachable;
+    const bytes: []const u8 = self.metadata_string_bytes.items[start..];
 
     const gop = self.metadata_string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self });
     if (gop.found_existing) {
@@ -12042,7 +12050,7 @@ pub fn debugEnumerator(
     bit_width: u32,
     value: std.math.big.int.Const,
 ) Allocator.Error!Metadata {
-    std.debug.assert(!(unsigned and !value.positive));
+    assert(!(unsigned and !value.positive));
     try self.ensureUnusedMetadataCapacity(1, Metadata.Enumerator, 0);
     try self.metadata_limbs.ensureUnusedCapacity(self.gpa, value.limbs.len);
     return self.debugEnumeratorAssumeCapacity(name, unsigned, bit_width, value);
@@ -12147,7 +12155,7 @@ pub fn debugConstant(self: *Builder, value: Constant) Allocator.Error!Metadata {
 }
 
 pub fn debugForwardReferenceSetType(self: *Builder, fwd_ref: Metadata, ty: Metadata) void {
-    std.debug.assert(
+    assert(
         @intFromEnum(fwd_ref) >= Metadata.first_forward_reference and
             @intFromEnum(fwd_ref) <= Metadata.first_local_metadata,
     );
@@ -12226,7 +12234,8 @@ fn metadataDistinctAssumeCapacity(self: *Builder, tag: Metadata.Tag, value: anyt
 }
 
 fn debugNamedAssumeCapacity(self: *Builder, name: MetadataString, operands: []const Metadata) void {
-    std.debug.assert(name != .none);
+    assert(!self.strip);
+    assert(name != .none);
     const extra_index: u32 = @intCast(self.metadata_extra.items.len);
     self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(operands));
 
@@ -12238,6 +12247,7 @@ fn debugNamedAssumeCapacity(self: *Builder, name: MetadataString, operands: []co
 }
 
 pub fn debugNoneAssumeCapacity(self: *Builder) Metadata {
+    assert(!self.strip);
     return self.metadataSimpleAssumeCapacity(.none, .{});
 }
 
@@ -12246,6 +12256,7 @@ fn debugFileAssumeCapacity(
     filename: MetadataString,
     directory: MetadataString,
 ) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(.file, Metadata.File{
         .filename = filename,
         .directory = directory,
@@ -12260,6 +12271,7 @@ pub fn debugCompileUnitAssumeCapacity(
     globals: Metadata,
     options: Metadata.CompileUnit.Options,
 ) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(
         if (options.optimized) .@"compile_unit optimized" else .compile_unit,
         Metadata.CompileUnit{
@@ -12282,6 +12294,7 @@ fn debugSubprogramAssumeCapacity(
     options: Metadata.Subprogram.Options,
     compile_unit: Metadata,
 ) Metadata {
+    assert(!self.strip);
     const tag: Metadata.Tag = @enumFromInt(@intFromEnum(Metadata.Tag.subprogram) +
         @as(u3, @truncate(@as(u32, @bitCast(options.sp_flags)) >> 2)));
     return self.metadataDistinctAssumeCapacity(tag, Metadata.Subprogram{
@@ -12297,6 +12310,7 @@ fn debugSubprogramAssumeCapacity(
 }
 
 fn debugLexicalBlockAssumeCapacity(self: *Builder, scope: Metadata, file: Metadata, line: u32, column: u32) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(.lexical_block, Metadata.LexicalBlock{
         .scope = scope,
         .file = file,
@@ -12306,6 +12320,7 @@ fn debugLexicalBlockAssumeCapacity(self: *Builder, scope: Metadata, file: Metada
 }
 
 fn debugLocationAssumeCapacity(self: *Builder, line: u32, column: u32, scope: Metadata, inlined_at: Metadata) Metadata {
+    assert(!self.strip);
     return self.metadataSimpleAssumeCapacity(.location, Metadata.Location{
         .line = line,
         .column = column,
@@ -12315,6 +12330,7 @@ fn debugLocationAssumeCapacity(self: *Builder, line: u32, column: u32, scope: Me
 }
 
 fn debugBoolTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(.basic_bool_type, Metadata.BasicType{
         .name = name,
         .size_in_bits_lo = @truncate(size_in_bits),
@@ -12323,6 +12339,7 @@ fn debugBoolTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bit
 }
 
 fn debugUnsignedTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(.basic_unsigned_type, Metadata.BasicType{
         .name = name,
         .size_in_bits_lo = @truncate(size_in_bits),
@@ -12331,6 +12348,7 @@ fn debugUnsignedTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in
 }
 
 fn debugSignedTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(.basic_signed_type, Metadata.BasicType{
         .name = name,
         .size_in_bits_lo = @truncate(size_in_bits),
@@ -12339,6 +12357,7 @@ fn debugSignedTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_b
 }
 
 fn debugFloatTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(.basic_float_type, Metadata.BasicType{
         .name = name,
         .size_in_bits_lo = @truncate(size_in_bits),
@@ -12347,6 +12366,7 @@ fn debugFloatTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bi
 }
 
 fn debugForwardReferenceAssumeCapacity(self: *Builder) Metadata {
+    assert(!self.strip);
     const index = Metadata.first_forward_reference + self.metadata_forward_references.items.len;
     self.metadata_forward_references.appendAssumeCapacity(.none);
     return @enumFromInt(index);
@@ -12363,6 +12383,7 @@ fn debugStructTypeAssumeCapacity(
     align_in_bits: u64,
     fields_tuple: Metadata,
 ) Metadata {
+    assert(!self.strip);
     return self.debugCompositeTypeAssumeCapacity(
         .composite_struct_type,
         name,
@@ -12387,6 +12408,7 @@ fn debugUnionTypeAssumeCapacity(
     align_in_bits: u64,
     fields_tuple: Metadata,
 ) Metadata {
+    assert(!self.strip);
     return self.debugCompositeTypeAssumeCapacity(
         .composite_union_type,
         name,
@@ -12411,6 +12433,7 @@ fn debugEnumerationTypeAssumeCapacity(
     align_in_bits: u64,
     fields_tuple: Metadata,
 ) Metadata {
+    assert(!self.strip);
     return self.debugCompositeTypeAssumeCapacity(
         .composite_enumeration_type,
         name,
@@ -12435,6 +12458,7 @@ fn debugArrayTypeAssumeCapacity(
     align_in_bits: u64,
     fields_tuple: Metadata,
 ) Metadata {
+    assert(!self.strip);
     return self.debugCompositeTypeAssumeCapacity(
         .composite_array_type,
         name,
@@ -12459,6 +12483,7 @@ fn debugVectorTypeAssumeCapacity(
     align_in_bits: u64,
     fields_tuple: Metadata,
 ) Metadata {
+    assert(!self.strip);
     return self.debugCompositeTypeAssumeCapacity(
         .composite_vector_type,
         name,
@@ -12484,6 +12509,7 @@ fn debugCompositeTypeAssumeCapacity(
     align_in_bits: u64,
     fields_tuple: Metadata,
 ) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(tag, Metadata.CompositeType{
         .name = name,
         .file = file,
@@ -12509,6 +12535,7 @@ fn debugPointerTypeAssumeCapacity(
     align_in_bits: u64,
     offset_in_bits: u64,
 ) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(.derived_pointer_type, Metadata.DerivedType{
         .name = name,
         .file = file,
@@ -12535,6 +12562,7 @@ fn debugMemberTypeAssumeCapacity(
     align_in_bits: u64,
     offset_in_bits: u64,
 ) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(.derived_member_type, Metadata.DerivedType{
         .name = name,
         .file = file,
@@ -12554,6 +12582,7 @@ fn debugSubroutineTypeAssumeCapacity(
     self: *Builder,
     types_tuple: Metadata,
 ) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(.subroutine_type, Metadata.SubroutineType{
         .types_tuple = types_tuple,
     });
@@ -12566,6 +12595,7 @@ fn debugEnumeratorAssumeCapacity(
     bit_width: u32,
     value: std.math.big.int.Const,
 ) Metadata {
+    assert(!self.strip);
     const Key = struct { tag: Metadata.Tag, index: Metadata };
     const Adapter = struct {
         pub fn hash(_: @This(), key: Key) u32 {
@@ -12587,7 +12617,7 @@ fn debugEnumeratorAssumeCapacity(
     else
         .enumerator_signed_negative;
 
-    std.debug.assert(!(tag == .enumerator_unsigned and !value.positive));
+    assert(!(tag == .enumerator_unsigned and !value.positive));
 
     const gop = self.metadata_map.getOrPutAssumeCapacityAdapted(
         Key{ .tag = tag, .index = @enumFromInt(self.metadata_map.count()) },
@@ -12616,6 +12646,7 @@ fn debugSubrangeAssumeCapacity(
     lower_bound: Metadata,
     count: Metadata,
 ) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(.subrange, Metadata.Subrange{
         .lower_bound = lower_bound,
         .count = count,
@@ -12626,6 +12657,7 @@ fn debugExpressionAssumeCapacity(
     self: *Builder,
     elements: []const u32,
 ) Metadata {
+    assert(!self.strip);
     const Key = struct {
         elements: []const u32,
     };
@@ -12672,6 +12704,7 @@ fn debugTupleAssumeCapacity(
     self: *Builder,
     elements: []const Metadata,
 ) Metadata {
+    assert(!self.strip);
     const Key = struct {
         elements: []const Metadata,
     };
@@ -12720,6 +12753,7 @@ fn debugModuleFlagAssumeCapacity(
     name: MetadataString,
     constant: Metadata,
 ) Metadata {
+    assert(!self.strip);
     return self.metadataSimpleAssumeCapacity(.module_flag, Metadata.ModuleFlag{
         .behavior = behavior,
         .name = name,
@@ -12734,7 +12768,8 @@ fn debugLocalVarAssumeCapacity(
     scope: Metadata,
     line: u32,
     ty: Metadata,
-) Allocator.Error!Metadata {
+) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(.local_var, Metadata.LocalVar{
         .name = name,
         .file = file,
@@ -12752,7 +12787,8 @@ fn debugParameterAssumeCapacity(
     line: u32,
     ty: Metadata,
     arg_no: u32,
-) Allocator.Error!Metadata {
+) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(.parameter, Metadata.Parameter{
         .name = name,
         .file = file,
@@ -12773,7 +12809,8 @@ fn debugGlobalVarAssumeCapacity(
     ty: Metadata,
     variable: Variable.Index,
     options: Metadata.GlobalVar.Options,
-) Allocator.Error!Metadata {
+) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(
         if (options.local) .@"global_var local" else .global_var,
         Metadata.GlobalVar{
@@ -12793,6 +12830,7 @@ fn debugGlobalVarExpressionAssumeCapacity(
     variable: Metadata,
     expression: Metadata,
 ) Metadata {
+    assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(.global_var_expression, Metadata.GlobalVarExpression{
         .variable = variable,
         .expression = expression,
@@ -12800,6 +12838,7 @@ fn debugGlobalVarExpressionAssumeCapacity(
 }
 
 fn debugConstantAssumeCapacity(self: *Builder, constant: Constant) Metadata {
+    assert(!self.strip);
     const Adapter = struct {
         builder: *const Builder,
         pub fn hash(_: @This(), key: Constant) u32 {
@@ -13528,18 +13567,16 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
                         }
                     },
                     .string,
-                    .string_null,
                     => {
                         const str: String = @enumFromInt(data);
                         if (str == .none) {
                             try constants_block.writeAbbrev(Constants.Null{});
                         } else {
                             const slice = str.slice(self) orelse unreachable;
-                            switch (tags[index]) {
-                                .string => try constants_block.writeAbbrev(Constants.String{ .string = slice }),
-                                .string_null => try constants_block.writeAbbrev(Constants.CString{ .string = slice }),
-                                else => unreachable,
-                            }
+                            if (slice.len > 0 and slice[slice.len - 1] == 0)
+                                try constants_block.writeAbbrev(Constants.CString{ .string = slice[0 .. slice.len - 1] })
+                            else
+                                try constants_block.writeAbbrev(Constants.String{ .string = slice });
                         }
                     },
                     .bitcast,
@@ -13918,7 +13955,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
                     },
                     .location => {
                         const extra = self.metadataExtraData(Metadata.Location, data);
-                        std.debug.assert(extra.scope != .none);
+                        assert(extra.scope != .none);
                         try metadata_block.writeAbbrev(MetadataBlock.Location{
                             .line = extra.line,
                             .column = extra.column,
src/codegen/llvm.zig
@@ -849,51 +849,53 @@ pub const Object = struct {
 
         builder.data_layout = try builder.fmt("{}", .{DataLayoutBuilder{ .target = target }});
 
-        // We fully resolve all paths at this point to avoid lack of
-        // source line info in stack traces or lack of debugging
-        // information which, if relative paths were used, would be
-        // very location dependent.
-        // TODO: the only concern I have with this is WASI as either host or target, should
-        // we leave the paths as relative then?
-        // TODO: This is totally wrong. In dwarf, paths are encoded as relative to
-        // a particular directory, and then the directory path is specified elsewhere.
-        // In the compiler frontend we have it stored correctly in this
-        // way already, but here we throw all that sweet information
-        // into the garbage can by converting into absolute paths. What
-        // a terrible tragedy.
-        const compile_unit_dir = blk: {
-            if (comp.module) |zcu| m: {
-                const d = try zcu.root_mod.root.joinString(arena, "");
-                if (d.len == 0) break :m;
-                if (std.fs.path.isAbsolute(d)) break :blk d;
-                break :blk std.fs.realpathAlloc(arena, d) catch break :blk d;
-            }
-            break :blk try std.process.getCwdAlloc(arena);
-        };
+        const debug_compile_unit, const debug_enums_fwd_ref, const debug_globals_fwd_ref =
+            if (!builder.strip)
+        debug_info: {
+            // We fully resolve all paths at this point to avoid lack of
+            // source line info in stack traces or lack of debugging
+            // information which, if relative paths were used, would be
+            // very location dependent.
+            // TODO: the only concern I have with this is WASI as either host or target, should
+            // we leave the paths as relative then?
+            // TODO: This is totally wrong. In dwarf, paths are encoded as relative to
+            // a particular directory, and then the directory path is specified elsewhere.
+            // In the compiler frontend we have it stored correctly in this
+            // way already, but here we throw all that sweet information
+            // into the garbage can by converting into absolute paths. What
+            // a terrible tragedy.
+            const compile_unit_dir = blk: {
+                if (comp.module) |zcu| m: {
+                    const d = try zcu.root_mod.root.joinString(arena, "");
+                    if (d.len == 0) break :m;
+                    if (std.fs.path.isAbsolute(d)) break :blk d;
+                    break :blk std.fs.realpathAlloc(arena, d) catch break :blk d;
+                }
+                break :blk try std.process.getCwdAlloc(arena);
+            };
 
-        const debug_file = try builder.debugFile(
-            try builder.metadataString(comp.root_name),
-            try builder.metadataString(compile_unit_dir),
-        );
+            const debug_file = try builder.debugFile(
+                try builder.metadataString(comp.root_name),
+                try builder.metadataString(compile_unit_dir),
+            );
 
-        const debug_enums_fwd_ref = try builder.debugForwardReference();
-        const debug_globals_fwd_ref = try builder.debugForwardReference();
-
-        const debug_compile_unit = try builder.debugCompileUnit(
-            debug_file,
-            // Don't use the version string here; LLVM misparses it when it
-            // includes the git revision.
-            try builder.metadataStringFmt("zig {d}.{d}.{d}", .{
-                build_options.semver.major,
-                build_options.semver.minor,
-                build_options.semver.patch,
-            }),
-            debug_enums_fwd_ref,
-            debug_globals_fwd_ref,
-            .{ .optimized = comp.root_mod.optimize_mode != .Debug },
-        );
+            const debug_enums_fwd_ref = try builder.debugForwardReference();
+            const debug_globals_fwd_ref = try builder.debugForwardReference();
+
+            const debug_compile_unit = try builder.debugCompileUnit(
+                debug_file,
+                // Don't use the version string here; LLVM misparses it when it
+                // includes the git revision.
+                try builder.metadataStringFmt("zig {d}.{d}.{d}", .{
+                    build_options.semver.major,
+                    build_options.semver.minor,
+                    build_options.semver.patch,
+                }),
+                debug_enums_fwd_ref,
+                debug_globals_fwd_ref,
+                .{ .optimized = comp.root_mod.optimize_mode != .Debug },
+            );
 
-        if (!builder.strip) {
             const debug_info_version = try builder.debugModuleFlag(
                 try builder.debugConstant(try builder.intConst(.i32, 2)),
                 try builder.metadataString("Debug Info Version"),
@@ -901,6 +903,7 @@ pub const Object = struct {
             );
 
             switch (comp.config.debug_format) {
+                .strip => unreachable,
                 .dwarf => |f| {
                     const dwarf_version = try builder.debugModuleFlag(
                         try builder.debugConstant(try builder.intConst(.i32, 2)),
@@ -939,11 +942,11 @@ pub const Object = struct {
                         code_view,
                     });
                 },
-                .strip => unreachable,
             }
 
             try builder.debugNamed(try builder.metadataString("llvm.dbg.cu"), &.{debug_compile_unit});
-        }
+            break :debug_info .{ debug_compile_unit, debug_enums_fwd_ref, debug_globals_fwd_ref };
+        } else .{.none} ** 3;
 
         const obj = try arena.create(Object);
         obj.* = .{
@@ -1004,8 +1007,8 @@ pub const Object = struct {
 
         llvm_errors[0] = try o.builder.undefConst(llvm_slice_ty);
         for (llvm_errors[1..], error_name_list[1..]) |*llvm_error, name| {
-            const name_string = try o.builder.string(mod.intern_pool.stringToSlice(name));
-            const name_init = try o.builder.stringNullConst(name_string);
+            const name_string = try o.builder.stringNull(mod.intern_pool.stringToSlice(name));
+            const name_init = try o.builder.stringConst(name_string);
             const name_variable_index =
                 try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default);
             try name_variable_index.setInitializer(name_init, &o.builder);
@@ -1016,7 +1019,7 @@ pub const Object = struct {
 
             llvm_error.* = try o.builder.structConst(llvm_slice_ty, &.{
                 name_variable_index.toConst(&o.builder),
-                try o.builder.intConst(llvm_usize_ty, name_string.slice(&o.builder).?.len),
+                try o.builder.intConst(llvm_usize_ty, name_string.slice(&o.builder).?.len - 1),
             });
         }
 
@@ -1145,28 +1148,27 @@ pub const Object = struct {
         try self.genCmpLtErrorsLenFunction();
         try self.genModuleLevelAssembly();
 
-        {
-            var i: usize = 0;
-            while (i < self.debug_unresolved_namespace_scopes.count()) : (i += 1) {
-                const namespace_index = self.debug_unresolved_namespace_scopes.keys()[i];
-                const fwd_ref = self.debug_unresolved_namespace_scopes.values()[i];
+        if (!self.builder.strip) {
+            for (
+                self.debug_unresolved_namespace_scopes.keys(),
+                self.debug_unresolved_namespace_scopes.values(),
+            ) |namespace_index, fwd_ref| {
                 const namespace = self.module.namespacePtr(namespace_index);
-
                 const debug_type = try self.lowerDebugType(namespace.ty);
 
                 self.builder.debugForwardReferenceSetType(fwd_ref, debug_type);
             }
-        }
 
-        self.builder.debugForwardReferenceSetType(
-            self.debug_enums_fwd_ref,
-            try self.builder.debugTuple(self.debug_enums.items),
-        );
+            self.builder.debugForwardReferenceSetType(
+                self.debug_enums_fwd_ref,
+                try self.builder.debugTuple(self.debug_enums.items),
+            );
 
-        self.builder.debugForwardReferenceSetType(
-            self.debug_globals_fwd_ref,
-            try self.builder.debugTuple(self.debug_globals.items),
-        );
+            self.builder.debugForwardReferenceSetType(
+                self.debug_globals_fwd_ref,
+                try self.builder.debugTuple(self.debug_globals.items),
+            );
+        }
 
         if (options.pre_ir_path) |path| {
             if (std.mem.eql(u8, path, "-")) {
@@ -1241,13 +1243,12 @@ pub const Object = struct {
         };
         bitcode_arena_allocator.deinit();
 
-        var error_message: [*:0]const u8 = undefined;
+        const target_triple_sentinel =
+            try self.gpa.dupeZ(u8, self.builder.target_triple.slice(&self.builder).?);
+        defer self.gpa.free(target_triple_sentinel);
         var target: *llvm.Target = undefined;
-        if (llvm.Target.getFromTriple(
-            self.builder.target_triple.slice(&self.builder).?,
-            &target,
-            &error_message,
-        ).toBool()) {
+        var error_message: [*:0]const u8 = undefined;
+        if (llvm.Target.getFromTriple(target_triple_sentinel, &target, &error_message).toBool()) {
             defer llvm.disposeMessage(error_message);
 
             log.err("LLVM failed to parse '{s}': {s}", .{
@@ -1286,7 +1287,7 @@ pub const Object = struct {
 
         var target_machine = llvm.TargetMachine.create(
             target,
-            self.builder.target_triple.slice(&self.builder).?,
+            target_triple_sentinel,
             if (self.module.comp.root_mod.resolved_target.result.cpu.model.llvm_name) |s| s.ptr else null,
             self.module.comp.root_mod.resolved_target.llvm_cpu_features.?,
             opt_level,
@@ -1640,15 +1641,15 @@ pub const Object = struct {
 
         function_index.setAttributes(try attributes.finish(&o.builder), &o.builder);
 
-        const file = try o.getDebugFile(namespace.file_scope);
+        const file, const subprogram = if (!o.builder.strip) debug_info: {
+            const file = try o.getDebugFile(namespace.file_scope);
 
-        const subprogram = blk: {
             const line_number = decl.src_line + 1;
             const is_internal_linkage = decl.val.getExternFunc(zcu) == null and
                 !zcu.decl_exports.contains(decl_index);
             const debug_decl_type = try o.lowerDebugType(decl.ty);
 
-            break :blk try o.builder.debugSubprogram(
+            const subprogram = try o.builder.debugSubprogram(
                 file,
                 try o.builder.metadataString(ip.stringToSlice(decl.name)),
                 try o.builder.metadataStringFromString(function_index.name(&o.builder)),
@@ -1668,8 +1669,9 @@ pub const Object = struct {
                 },
                 o.debug_compile_unit,
             );
-        };
-        function_index.setSubprogram(subprogram, &o.builder);
+            function_index.setSubprogram(subprogram, &o.builder);
+            break :debug_info .{ file, subprogram };
+        } else .{.none} ** 2;
 
         var fg: FuncGen = .{
             .gpa = gpa,
@@ -1924,7 +1926,8 @@ pub const Object = struct {
         o: *Object,
         ty: Type,
     ) Allocator.Error!Builder.Metadata {
-        if (o.builder.strip) return .none;
+        assert(!o.builder.strip);
+
         const gpa = o.gpa;
         const target = o.target;
         const mod = o.module;
@@ -4608,8 +4611,8 @@ pub const Object = struct {
         defer wip_switch.finish(&wip);
 
         for (0..enum_type.names.len) |field_index| {
-            const name = try o.builder.string(ip.stringToSlice(enum_type.names.get(ip)[field_index]));
-            const name_init = try o.builder.stringNullConst(name);
+            const name = try o.builder.stringNull(ip.stringToSlice(enum_type.names.get(ip)[field_index]));
+            const name_init = try o.builder.stringConst(name);
             const name_variable_index =
                 try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default);
             try name_variable_index.setInitializer(name_init, &o.builder);
@@ -4620,7 +4623,7 @@ pub const Object = struct {
 
             const name_val = try o.builder.structValue(ret_ty, &.{
                 name_variable_index.toConst(&o.builder),
-                try o.builder.intConst(usize_ty, name.slice(&o.builder).?.len),
+                try o.builder.intConst(usize_ty, name.slice(&o.builder).?.len - 1),
             });
 
             const return_block = try wip.block(1, "Name");