diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6648e0a8e407..b559cfdae0ff 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -6780,7 +6780,7 @@ pub const FuncGen = struct { const max_param_count = inputs.len + outputs.len; const llvm_param_types = try arena.alloc(Builder.Type, max_param_count); - const llvm_param_values = try arena.alloc(*llvm.Value, max_param_count); + const llvm_param_values = try arena.alloc(Builder.Value, max_param_count); // This stores whether we need to add an elementtype attribute and // if so, the element type itself. const llvm_param_attrs = try arena.alloc(Builder.Type, max_param_count); @@ -6820,7 +6820,7 @@ pub const FuncGen = struct { // Pass the result by reference as an indirect output (e.g. "=*m") llvm_constraints.appendAssumeCapacity('*'); - llvm_param_values[llvm_param_i] = output_inst.toLlvm(&self.wip); + llvm_param_values[llvm_param_i] = output_inst; llvm_param_types[llvm_param_i] = output_inst.typeOfWip(&self.wip); llvm_param_attrs[llvm_param_i] = elem_llvm_ty; llvm_param_i += 1; @@ -6870,25 +6870,25 @@ pub const FuncGen = struct { if (isByRef(arg_ty, mod)) { llvm_elem_ty = try o.lowerPtrElemTy(arg_ty); if (constraintAllowsMemory(constraint)) { - llvm_param_values[llvm_param_i] = arg_llvm_value.toLlvm(&self.wip); + llvm_param_values[llvm_param_i] = arg_llvm_value; llvm_param_types[llvm_param_i] = arg_llvm_value.typeOfWip(&self.wip); } else { const alignment = Builder.Alignment.fromByteUnits(arg_ty.abiAlignment(mod)); const arg_llvm_ty = try o.lowerType(arg_ty); const load_inst = try self.wip.load(.normal, arg_llvm_ty, arg_llvm_value, alignment, ""); - llvm_param_values[llvm_param_i] = load_inst.toLlvm(&self.wip); + llvm_param_values[llvm_param_i] = load_inst; llvm_param_types[llvm_param_i] = arg_llvm_ty; } } else { if (constraintAllowsRegister(constraint)) { - llvm_param_values[llvm_param_i] = arg_llvm_value.toLlvm(&self.wip); + llvm_param_values[llvm_param_i] = arg_llvm_value; llvm_param_types[llvm_param_i] = arg_llvm_value.typeOfWip(&self.wip); } else { const alignment = Builder.Alignment.fromByteUnits(arg_ty.abiAlignment(mod)); const arg_ptr = try self.buildAlloca(arg_llvm_value.typeOfWip(&self.wip), alignment); _ = try self.wip.store(.normal, arg_llvm_value, arg_ptr, alignment); - llvm_param_values[llvm_param_i] = arg_ptr.toLlvm(&self.wip); + llvm_param_values[llvm_param_i] = arg_ptr; llvm_param_types[llvm_param_i] = arg_ptr.typeOfWip(&self.wip); } } @@ -7037,50 +7037,33 @@ pub const FuncGen = struct { var attributes: Builder.FunctionAttributes.Wip = .{}; defer attributes.deinit(&o.builder); + for (llvm_param_attrs[0..param_count], 0..) |llvm_elem_ty, i| if (llvm_elem_ty != .none) + try attributes.addParamAttr(i, .{ .elementtype = llvm_elem_ty }, &o.builder); const ret_llvm_ty = switch (return_count) { 0 => .void, 1 => llvm_ret_types[0], else => try o.builder.structType(.normal, llvm_ret_types), }; - const llvm_fn_ty = try o.builder.fnType(ret_llvm_ty, llvm_param_types[0..param_count], .normal); - const asm_fn = llvm.getInlineAsm( - llvm_fn_ty.toLlvm(&o.builder), - rendered_template.items.ptr, - rendered_template.items.len, - llvm_constraints.items.ptr, - llvm_constraints.items.len, - llvm.Bool.fromBool(is_volatile), - .False, - .ATT, - .False, - ); - const call = (try self.wip.unimplemented(ret_llvm_ty, "")).finish(self.builder.buildCallOld( - llvm_fn_ty.toLlvm(&o.builder), - asm_fn, - llvm_param_values.ptr, - @intCast(param_count), - .C, - .Auto, + var ret_val = try self.wip.callAsm( + try attributes.finish(&o.builder), + llvm_fn_ty, + .{ .sideeffect = is_volatile }, + try o.builder.string(rendered_template.items), + try o.builder.string(llvm_constraints.items), + llvm_param_values[0..param_count], "", - ), &self.wip); - for (llvm_param_attrs[0..param_count], 0..) |llvm_elem_ty, i| { - if (llvm_elem_ty != .none) { - try attributes.addParamAttr(i, .{ .elementtype = llvm_elem_ty }, &o.builder); - llvm.setCallElemTypeAttr(call.toLlvm(&self.wip), i, llvm_elem_ty.toLlvm(&o.builder)); - } - } + ); - var ret_val = call; llvm_ret_i = 0; for (outputs, 0..) |output, i| { if (llvm_ret_indirect[i]) continue; const output_value = if (return_count > 1) - try self.wip.extractValue(call, &[_]u32{@intCast(llvm_ret_i)}, "") + try self.wip.extractValue(ret_val, &[_]u32{@intCast(llvm_ret_i)}, "") else - call; + ret_val; if (output != .none) { const output_ptr = try self.resolveInst(output); diff --git a/src/codegen/llvm/Builder.zig b/src/codegen/llvm/Builder.zig index 1026b9c88ee1..08ada8a81bb1 100644 --- a/src/codegen/llvm/Builder.zig +++ b/src/codegen/llvm/Builder.zig @@ -1072,7 +1072,7 @@ pub const Attribute = union(Kind) { _: 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, "\"#")) |_| @compileError("invalid format string: '" ++ fmt_str ++ "'"); const attribute = data.attribute_index.toAttribute(data.builder); switch (attribute) { @@ -1154,17 +1154,72 @@ pub const Attribute = union(Kind) { .sret, .elementtype, => |ty| try writer.print(" {s}({%})", .{ @tagName(attribute), ty.fmt(data.builder) }), - .@"align" => @panic("todo"), + .@"align" => |alignment| try writer.print("{}", .{alignment}), .dereferenceable, .dereferenceable_or_null, - => @panic("todo"), - .nofpclass => @panic("todo"), - .alignstack => @panic("todo"), - .allockind => @panic("todo"), - .allocsize => @panic("todo"), - .memory => @panic("todo"), - .uwtable => @panic("todo"), - .vscale_range => @panic("todo"), + => |size| try writer.print(" {s}({d})", .{ @tagName(attribute), size }), + .nofpclass => |fpclass| { + const Int = @typeInfo(FpClass).Struct.backing_integer.?; + try writer.print("{s}(", .{@tagName(attribute)}); + var any = false; + var remaining: Int = @bitCast(fpclass); + inline for (@typeInfo(FpClass).Struct.decls) |decl| { + if (!decl.is_pub) continue; + const pattern: Int = @bitCast(@field(FpClass, decl.name)); + if (remaining & pattern == pattern) { + if (!any) { + try writer.writeByte(' '); + any = true; + } + try writer.writeAll(decl.name); + remaining &= ~pattern; + } + } + try writer.writeByte(')'); + }, + .alignstack => |alignment| try writer.print( + if (comptime std.mem.indexOfScalar(u8, fmt_str, '#') != null) + "{s}={d}" + else + "{s}({d})", + .{ @tagName(attribute), alignment.toByteUnits() orelse return }, + ), + .allockind => |allockind| { + try writer.print("{s}(\"", .{@tagName(attribute)}); + var any = false; + inline for (@typeInfo(AllocKind).Struct.fields) |field| { + if (comptime std.mem.eql(u8, field.name, "_")) continue; + if (@field(allockind, field.name)) { + if (!any) { + try writer.writeByte(','); + any = true; + } + try writer.writeAll(field.name); + } + } + try writer.writeAll("\")"); + }, + .allocsize => |allocsize| { + try writer.print("{s}({d}", .{ @tagName(attribute), allocsize.elem_size }); + if (allocsize.num_elems != AllocSize.none) + try writer.print(",{d}", .{allocsize.num_elems}); + try writer.writeByte(')'); + }, + .memory => |memory| try writer.print("{s}({s}, argmem: {s}, inaccessiblemem: {s})", .{ + @tagName(attribute), + @tagName(memory.other), + @tagName(memory.argmem), + @tagName(memory.inaccessiblemem), + }), + .uwtable => |uwtable| if (uwtable != .none) { + try writer.writeAll(@tagName(attribute)); + if (uwtable != UwTable.default) try writer.print("({s})", .{@tagName(uwtable)}); + }, + .vscale_range => |vscale_range| try writer.print("{s}({d},{d})", .{ + @tagName(attribute), + vscale_range.min.toByteUnits().?, + vscale_range.max.toByteUnits() orelse 0, + }), .string => |string_attr| if (comptime std.mem.indexOfScalar(u8, fmt_str, '"') != null) { try writer.print(" {\"}", .{string_attr.kind.fmt(data.builder)}); if (string_attr.value != .empty) @@ -1314,11 +1369,6 @@ pub const Attribute = union(Kind) { positive_infinity: bool = false, _: u22 = 0, - pub const nan = FpClass{ .signaling_nan = true, .quiet_nan = true }; - pub const inf = FpClass{ .negative_infinity = true, .positive_infinity = true }; - pub const norm = FpClass{ .positive_normal = true, .negative_normal = true }; - pub const sub = FpClass{ .positive_subnormal = true, .negative_subnormal = true }; - pub const zero = FpClass{ .positive_zero = true, .negative_zero = true }; pub const all = FpClass{ .signaling_nan = true, .quiet_nan = true, @@ -1331,16 +1381,26 @@ pub const Attribute = union(Kind) { .positive_normal = true, .positive_infinity = true, }; + + pub const nan = FpClass{ .signaling_nan = true, .quiet_nan = true }; pub const snan = FpClass{ .signaling_nan = true }; pub const qnan = FpClass{ .quiet_nan = true }; + + pub const inf = FpClass{ .negative_infinity = true, .positive_infinity = true }; pub const ninf = FpClass{ .negative_infinity = true }; - pub const nnorm = FpClass{ .negative_normal = true }; - pub const nsub = FpClass{ .negative_subnormal = true }; + pub const pinf = FpClass{ .positive_infinity = true }; + + pub const zero = FpClass{ .positive_zero = true, .negative_zero = true }; pub const nzero = FpClass{ .negative_zero = true }; pub const pzero = FpClass{ .positive_zero = true }; + + pub const sub = FpClass{ .positive_subnormal = true, .negative_subnormal = true }; + pub const nsub = FpClass{ .negative_subnormal = true }; pub const psub = FpClass{ .positive_subnormal = true }; + + pub const norm = FpClass{ .positive_normal = true, .negative_normal = true }; + pub const nnorm = FpClass{ .negative_normal = true }; pub const pnorm = FpClass{ .positive_normal = true }; - pub const pinf = FpClass{ .positive_infinity = true }; }; pub const AllocKind = packed struct(u32) { @@ -1924,7 +1984,7 @@ pub const CallConv = enum(u10) { writer: anytype, ) @TypeOf(writer).Error!void { switch (self) { - .ccc => {}, + default => {}, .fastcc, .coldcc, .ghccc, @@ -2445,6 +2505,7 @@ pub const Function = struct { .br_cond, .ret, .@"ret void", + .@"switch", .@"unreachable", => true, else => false, @@ -2462,6 +2523,7 @@ pub const Function = struct { .@"store atomic", .@"store atomic volatile", .@"store volatile", + .@"switch", .@"unreachable", => false, .call, @@ -2472,6 +2534,7 @@ pub const Function = struct { .@"notail call fast", .@"tail call", .@"tail call fast", + .unimplemented, => self.typeOfWip(wip) != .void, else => true, }; @@ -4165,7 +4228,7 @@ pub const WipFunction = struct { callee: Value, args: []const Value, name: []const u8, - ) if (build_options.have_llvm) Allocator.Error!Value else Value { + ) Allocator.Error!Value { const ret_ty = ty.functionReturn(self.builder); assert(ty.isFunction(self.builder)); assert(callee.typeOfWip(self).isPointer(self.builder)); @@ -4238,6 +4301,20 @@ pub const WipFunction = struct { return instruction.toValue(); } + pub fn callAsm( + self: *WipFunction, + function_attributes: FunctionAttributes, + ty: Type, + kind: Constant.Asm.Info, + assembly: String, + constraints: String, + args: []const Value, + name: []const u8, + ) Allocator.Error!Value { + const callee = try self.builder.asmValue(ty, kind, assembly, constraints); + return self.call(.normal, CallConv.default, function_attributes, ty, callee, args, name); + } + pub fn vaArg(self: *WipFunction, list: Value, ty: Type, name: []const u8) Allocator.Error!Value { try self.ensureUnusedExtraCapacity(1, Instruction.VaArg, 0); const instruction = try self.addInst(name, .{ @@ -5216,7 +5293,7 @@ pub const Constant = enum(u32) { const first_global: Constant = @enumFromInt(1 << 30); - pub const Tag = enum(u6) { + pub const Tag = enum(u7) { positive_integer, negative_integer, half, @@ -5276,6 +5353,22 @@ pub const Constant = enum(u32) { @"and", @"or", xor, + @"asm", + @"asm sideeffect", + @"asm alignstack", + @"asm sideeffect alignstack", + @"asm inteldialect", + @"asm sideeffect inteldialect", + @"asm alignstack inteldialect", + @"asm sideeffect alignstack inteldialect", + @"asm unwind", + @"asm sideeffect unwind", + @"asm alignstack unwind", + @"asm sideeffect alignstack unwind", + @"asm inteldialect unwind", + @"asm sideeffect inteldialect unwind", + @"asm alignstack inteldialect unwind", + @"asm sideeffect alignstack inteldialect unwind", }; pub const Item = struct { @@ -5371,6 +5464,19 @@ pub const Constant = enum(u32) { rhs: Constant, }; + pub const Asm = extern struct { + type: Type, + assembly: String, + constraints: String, + + pub const Info = packed struct { + sideeffect: bool = false, + alignstack: bool = false, + inteldialect: bool = false, + unwind: bool = false, + }; + }; + pub fn unwrap(self: Constant) union(enum) { constant: u30, global: Global.Index, @@ -5489,6 +5595,23 @@ pub const Constant = enum(u32) { .@"or", .xor, => builder.constantExtraData(Binary, item.data).lhs.typeOf(builder), + .@"asm", + .@"asm sideeffect", + .@"asm alignstack", + .@"asm sideeffect alignstack", + .@"asm inteldialect", + .@"asm sideeffect inteldialect", + .@"asm alignstack inteldialect", + .@"asm sideeffect alignstack inteldialect", + .@"asm unwind", + .@"asm sideeffect unwind", + .@"asm alignstack unwind", + .@"asm sideeffect alignstack unwind", + .@"asm inteldialect unwind", + .@"asm sideeffect inteldialect unwind", + .@"asm alignstack inteldialect unwind", + .@"asm sideeffect alignstack inteldialect unwind", + => .ptr, }; }, .global => |global| return builder.ptrTypeAssumeCapacity( @@ -5836,6 +5959,30 @@ pub const Constant = enum(u32) { extra.rhs.fmt(data.builder), }); }, + .@"asm", + .@"asm sideeffect", + .@"asm alignstack", + .@"asm sideeffect alignstack", + .@"asm inteldialect", + .@"asm sideeffect inteldialect", + .@"asm alignstack inteldialect", + .@"asm sideeffect alignstack inteldialect", + .@"asm unwind", + .@"asm sideeffect unwind", + .@"asm alignstack unwind", + .@"asm sideeffect alignstack unwind", + .@"asm inteldialect unwind", + .@"asm sideeffect inteldialect unwind", + .@"asm alignstack inteldialect unwind", + .@"asm sideeffect alignstack inteldialect unwind", + => |tag| { + const extra = data.builder.constantExtraData(Asm, item.data); + try writer.print("{s} {\"}, {\"}", .{ + @tagName(tag), + extra.assembly.fmt(data.builder), + extra.constraints.fmt(data.builder), + }); + }, } }, .global => |global| try writer.print("{}", .{global.fmt(data.builder)}), @@ -6972,6 +7119,27 @@ pub fn binValue(self: *Builder, tag: Constant.Tag, lhs: Constant, rhs: Constant) return (try self.binConst(tag, lhs, rhs)).toValue(); } +pub fn asmConst( + self: *Builder, + ty: Type, + info: Constant.Asm.Info, + assembly: String, + constraints: String, +) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Asm, 0); + return self.asmConstAssumeCapacity(ty, info, assembly, constraints); +} + +pub fn asmValue( + self: *Builder, + ty: Type, + info: Constant.Asm.Info, + assembly: String, + constraints: String, +) Allocator.Error!Value { + return (try self.asmConst(ty, info, assembly, constraints)).toValue(); +} + pub fn dump(self: *Builder) void { if (self.useLibLlvm()) self.llvm.module.?.dump() @@ -7303,7 +7471,15 @@ pub fn printUnbuffered( arg.fmt(function_index, self), }); } - try writer.print("){}\n", .{extra.data.attributes.func(self).fmt(self)}); + try writer.writeByte(')'); + const call_function_attributes = extra.data.attributes.func(self); + if (call_function_attributes != .none) try writer.print(" #{d}", .{ + (try attribute_groups.getOrPutValue( + self.gpa, + call_function_attributes, + {}, + )).index, + }); }, .extractelement => |tag| { const extra = @@ -7401,13 +7577,19 @@ pub fn printUnbuffered( => |tag| { const extra = function.extraData(Function.Instruction.Binary, instruction.data); const ty = instruction_index.typeOf(function_index, self); - try writer.print(" %{} = call {%} @{s}{m}({%}, {%})\n", .{ + try writer.print(" %{} = call {%} @{s}{m}({%}, {%}{s})\n", .{ instruction_index.name(&function).fmt(self), ty.fmt(self), @tagName(tag), ty.fmt(self), extra.lhs.fmt(function_index, self), extra.rhs.fmt(function_index, self), + switch (tag) { + .@"llvm.smul.fix.sat.", + .@"llvm.umul.fix.sat.", + => ", i32 0", + else => "", + }, }); }, .load, @@ -7494,7 +7676,7 @@ pub fn printUnbuffered( const vals = extra.trail.next(extra.data.cases_len, Constant, &function); const blocks = extra.trail.next(extra.data.cases_len, Function.Block.Index, &function); - try writer.print(" {s} {%}, {%} [", .{ + try writer.print(" {s} {%}, {%} [\n", .{ @tagName(tag), extra.data.val.fmt(function_index, self), extra.data.default.toInst(&function).fmt(function_index, self), @@ -7507,14 +7689,22 @@ pub fn printUnbuffered( }, .unimplemented => |tag| { const ty: Type = @enumFromInt(instruction.data); - try writer.writeAll(" "); - switch (ty) { + if (true) { + try writer.writeAll(" "); + switch (ty) { + .none, .void => {}, + else => try writer.print("%{} = ", .{ + instruction_index.name(&function).fmt(self), + }), + } + try writer.print("{s} {%}\n", .{ @tagName(tag), ty.fmt(self) }); + } else switch (ty) { .none, .void => {}, - else => try writer.print("%{} = ", .{ + else => try writer.print(" %{} = load {%}, ptr undef\n", .{ instruction_index.name(&function).fmt(self), + ty.fmt(self), }), } - try writer.print("{s} {%}\n", .{ @tagName(tag), ty.fmt(self) }); }, .va_arg => |tag| { const extra = function.extraData(Function.Instruction.VaArg, instruction.data); @@ -7534,7 +7724,7 @@ pub fn printUnbuffered( for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group| try writer.print( - \\attribute #{d} = {{{"} }} + \\attributes #{d} = {{{#"} }} \\ , .{ attribute_group_index, attribute_group.fmt(self) }); } @@ -9080,30 +9270,30 @@ fn binConstAssumeCapacity( => {}, else => unreachable, } - const Key = struct { tag: Constant.Tag, bin: Constant.Binary }; + const Key = struct { tag: Constant.Tag, extra: Constant.Binary }; const Adapter = struct { builder: *const Builder, pub fn hash(_: @This(), key: Key) u32 { return @truncate(std.hash.Wyhash.hash( std.hash.uint32(@intFromEnum(key.tag)), - std.mem.asBytes(&key.bin), + std.mem.asBytes(&key.extra), )); } pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; const rhs_extra = ctx.builder.constantExtraData(Constant.Binary, rhs_data); - return std.meta.eql(lhs_key.bin, rhs_extra); + return std.meta.eql(lhs_key.extra, rhs_extra); } }; - const data = Key{ .tag = tag, .bin = .{ .lhs = lhs, .rhs = rhs } }; + const data = Key{ .tag = tag, .extra = .{ .lhs = lhs, .rhs = rhs } }; const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); if (!gop.found_existing) { gop.key_ptr.* = {}; gop.value_ptr.* = {}; self.constant_items.appendAssumeCapacity(.{ .tag = tag, - .data = self.addConstantExtraAssumeCapacity(data.bin), + .data = self.addConstantExtraAssumeCapacity(data.extra), }); if (self.useLibLlvm()) self.llvm.constants.appendAssumeCapacity(switch (tag) { .add => &llvm.Value.constAdd, @@ -9121,6 +9311,61 @@ fn binConstAssumeCapacity( return @enumFromInt(gop.index); } +fn asmConstAssumeCapacity( + self: *Builder, + ty: Type, + info: Constant.Asm.Info, + assembly: String, + constraints: String, +) Constant { + const Key = struct { tag: Constant.Tag, extra: Constant.Asm }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + return @truncate(std.hash.Wyhash.hash( + std.hash.uint32(@intFromEnum(key.tag)), + std.mem.asBytes(&key.extra), + )); + } + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.constantExtraData(Constant.Asm, rhs_data); + return std.meta.eql(lhs_key.extra, rhs_extra); + } + }; + + const data = Key{ + .tag = @enumFromInt(@intFromEnum(Constant.Tag.@"asm") + @as(u4, @bitCast(info))), + .extra = .{ .type = ty, .assembly = assembly, .constraints = constraints }, + }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .tag = data.tag, + .data = self.addConstantExtraAssumeCapacity(data.extra), + }); + if (self.useLibLlvm()) { + const assembly_slice = assembly.slice(self).?; + const constraints_slice = constraints.slice(self).?; + self.llvm.constants.appendAssumeCapacity(llvm.getInlineAsm( + ty.toLlvm(self), + assembly_slice.ptr, + assembly_slice.len, + constraints_slice.ptr, + constraints_slice.len, + llvm.Bool.fromBool(info.sideeffect), + llvm.Bool.fromBool(info.alignstack), + if (info.inteldialect) .Intel else .ATT, + llvm.Bool.fromBool(info.unwind), + )); + } + } + return @enumFromInt(gop.index); +} + fn ensureUnusedConstantCapacity( self: *Builder, count: usize, @@ -9211,7 +9456,7 @@ fn addConstantExtraAssumeCapacity(self: *Builder, extra: anytype) Constant.Item. const value = @field(extra, field.name); self.constant_extra.appendAssumeCapacity(switch (field.type) { u32 => value, - Type, Constant, Function.Index, Function.Block.Index => @intFromEnum(value), + String, Type, Constant, Function.Index, Function.Block.Index => @intFromEnum(value), Constant.GetElementPtr.Info => @bitCast(value), else => @compileError("bad field type: " ++ @typeName(field.type)), }); @@ -9250,7 +9495,7 @@ fn constantExtraDataTrail( inline for (fields, self.constant_extra.items[index..][0..fields.len]) |field, value| @field(result, field.name) = switch (field.type) { u32 => value, - Type, Constant, Function.Index, Function.Block.Index => @enumFromInt(value), + String, Type, Constant, Function.Index, Function.Block.Index => @enumFromInt(value), Constant.GetElementPtr.Info => @bitCast(value), else => @compileError("bad field type: " ++ @typeName(field.type)), };