diff --git a/doc/langref.html.in b/doc/langref.html.in
index f496019838..3ab279ff12 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -1317,7 +1317,7 @@ const @"identifier with spaces in it" = 0xff;
const @"1SmallStep4Man" = 112358;
const c = @import("std").c;
-pub extern "c" fn @"error"() anyopaque;
+pub extern "c" fn @"error"() void;
pub extern "c" fn @"fstat$INODE64"(fd: c.fd_t, buf: *c.Stat) c_int;
const Color = enum {
diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig
index eb204d15ee..12ec357849 100644
--- a/lib/std/dwarf.zig
+++ b/lib/std/dwarf.zig
@@ -226,6 +226,21 @@ pub const LNCT = struct {
pub const hi_user = 0x3fff;
};
+pub const CC = enum(u8) {
+ normal = 0x1,
+ program = 0x2,
+ nocall = 0x3,
+
+ pass_by_reference = 0x4,
+ pass_by_value = 0x5,
+
+ lo_user = 0x40,
+ hi_user = 0xff,
+
+ GNU_renesas_sh = 0x40,
+ GNU_borland_fastcall_i386 = 0x41,
+};
+
const PcRange = struct {
start: u64,
end: u64,
diff --git a/src/Compilation.zig b/src/Compilation.zig
index b2ac9b249c..fa2e4ca68b 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -898,7 +898,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
// We put the `Compilation` itself in the arena. Freeing the arena will free the module.
// It's initialized later after we prepare the initialization options.
const comp = try arena.create(Compilation);
- const root_name = try arena.dupe(u8, options.root_name);
+ const root_name = try arena.dupeZ(u8, options.root_name);
const ofmt = options.object_format orelse options.target.getObjectFormat();
diff --git a/src/Sema.zig b/src/Sema.zig
index 85c2922d74..5bddead0ba 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -12399,7 +12399,7 @@ fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded);
defer anon_decl.deinit();
- const bytes = try ty.nameAlloc(anon_decl.arena());
+ const bytes = try ty.nameAllocArena(anon_decl.arena());
const new_decl = try anon_decl.finish(
try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index c837d9a00d..fb7daa80ec 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -5,12 +5,14 @@ const Allocator = std.mem.Allocator;
const log = std.log.scoped(.codegen);
const math = std.math;
const native_endian = builtin.cpu.arch.endian();
+const DW = std.dwarf;
const llvm = @import("llvm/bindings.zig");
const link = @import("../link.zig");
const Compilation = @import("../Compilation.zig");
const build_options = @import("build_options");
const Module = @import("../Module.zig");
+const Package = @import("../Package.zig");
const TypedValue = @import("../TypedValue.zig");
const Air = @import("../Air.zig");
const Liveness = @import("../Liveness.zig");
@@ -159,6 +161,12 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 {
pub const Object = struct {
llvm_module: *const llvm.Module,
+ di_builder: ?*llvm.DIBuilder,
+ /// One of these mappings:
+ /// - *Module.File => *DIFile
+ /// - *Module.Decl => *DISubprogram
+ di_map: std.AutoHashMapUnmanaged(*const anyopaque, *llvm.DIScope),
+ di_compile_unit: ?*llvm.DICompileUnit,
context: *const llvm.Context,
target_machine: *const llvm.TargetMachine,
target_data: *const llvm.TargetData,
@@ -180,8 +188,10 @@ pub const Object = struct {
/// The backing memory for `type_map`. Periodically garbage collected after flush().
/// The code for doing the periodical GC is not yet implemented.
type_map_arena: std.heap.ArenaAllocator,
- /// The LLVM global table which holds the names corresponding to Zig errors. Note that the values
- /// are not added until flushModule, when all errors in the compilation are known.
+ di_type_map: DITypeMap,
+ /// The LLVM global table which holds the names corresponding to Zig errors.
+ /// Note that the values are not added until flushModule, when all errors in
+ /// the compilation are known.
error_name_table: ?*const llvm.Value,
pub const TypeMap = std.HashMapUnmanaged(
@@ -191,6 +201,13 @@ pub const Object = struct {
std.hash_map.default_max_load_percentage,
);
+ pub const DITypeMap = std.HashMapUnmanaged(
+ Type,
+ *llvm.DIType,
+ Type.HashContext64,
+ std.hash_map.default_max_load_percentage,
+ );
+
pub fn create(gpa: Allocator, options: link.Options) !*Object {
const obj = try gpa.create(Object);
errdefer gpa.destroy(obj);
@@ -204,9 +221,7 @@ pub const Object = struct {
initializeLLVMTarget(options.target.cpu.arch);
- const root_nameZ = try gpa.dupeZ(u8, options.root_name);
- defer gpa.free(root_nameZ);
- const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context);
+ const llvm_module = llvm.Module.createWithName(options.root_name.ptr, context);
errdefer llvm_module.dispose();
const llvm_target_triple = try targetTriple(gpa, options.target);
@@ -221,6 +236,60 @@ pub const Object = struct {
return error.InvalidLlvmTriple;
}
+ llvm_module.setTarget(llvm_target_triple.ptr);
+ var opt_di_builder: ?*llvm.DIBuilder = null;
+ errdefer if (opt_di_builder) |di_builder| di_builder.dispose();
+
+ var di_compile_unit: ?*llvm.DICompileUnit = null;
+
+ if (!options.strip) {
+ switch (options.object_format) {
+ .coff => llvm_module.addModuleCodeViewFlag(),
+ else => llvm_module.addModuleDebugInfoFlag(),
+ }
+ const di_builder = llvm_module.createDIBuilder(true);
+ opt_di_builder = di_builder;
+
+ // Don't use the version string here; LLVM misparses it when it
+ // includes the git revision.
+ const producer = try std.fmt.allocPrintZ(gpa, "zig {d}.{d}.{d}", .{
+ build_options.semver.major,
+ build_options.semver.minor,
+ build_options.semver.patch,
+ });
+ defer gpa.free(producer);
+
+ // For macOS stack traces, we want to avoid having to parse the compilation unit debug
+ // info. As long as each debug info file has a path independent of the compilation unit
+ // directory (DW_AT_comp_dir), then we never have to look at the compilation unit debug
+ // info. If we provide an absolute path to LLVM here for the compilation unit debug
+ // info, LLVM will emit DWARF info that depends on DW_AT_comp_dir. To avoid this, we
+ // pass "." for the compilation unit directory. This forces each debug file to have a
+ // directory rather than be relative to DW_AT_comp_dir. According to DWARF 5, debug
+ // files will no longer reference DW_AT_comp_dir, for the purpose of being able to
+ // support the common practice of stripping all but the line number sections from an
+ // executable.
+ const compile_unit_dir = d: {
+ if (options.target.isDarwin()) break :d ".";
+ const mod = options.module orelse break :d ".";
+ break :d mod.root_pkg.root_src_directory.path orelse ".";
+ };
+ const compile_unit_dir_z = try gpa.dupeZ(u8, compile_unit_dir);
+ defer gpa.free(compile_unit_dir_z);
+
+ di_compile_unit = di_builder.createCompileUnit(
+ DW.LANG.C99,
+ di_builder.createFile(options.root_name, compile_unit_dir_z),
+ producer,
+ options.optimize_mode != .Debug,
+ "", // flags
+ 0, // runtime version
+ "", // split name
+ 0, // dwo id
+ true, // emit debug info
+ );
+ }
+
const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug)
.None
else
@@ -266,17 +335,26 @@ pub const Object = struct {
return Object{
.llvm_module = llvm_module,
+ .di_map = .{},
+ .di_builder = opt_di_builder,
+ .di_compile_unit = di_compile_unit,
.context = context,
.target_machine = target_machine,
.target_data = target_data,
.decl_map = .{},
.type_map = .{},
.type_map_arena = std.heap.ArenaAllocator.init(gpa),
+ .di_type_map = .{},
.error_name_table = null,
};
}
pub fn deinit(self: *Object, gpa: Allocator) void {
+ if (self.di_builder) |dib| {
+ dib.dispose();
+ self.di_map.deinit(gpa);
+ self.di_type_map.deinit(gpa);
+ }
self.target_data.dispose();
self.target_machine.dispose();
self.llvm_module.dispose();
@@ -357,6 +435,9 @@ pub const Object = struct {
pub fn flushModule(self: *Object, comp: *Compilation) !void {
try self.genErrorNameTable(comp);
+
+ if (self.di_builder) |dib| dib.finalize();
+
if (comp.verbose_llvm_ir) {
self.llvm_module.dump();
}
@@ -470,8 +551,9 @@ pub const Object = struct {
const target = dg.module.getTarget();
const sret = firstParamSRet(fn_info, target);
const ret_ptr = if (sret) llvm_func.getParam(0) else null;
+ const gpa = dg.gpa;
- var args = std.ArrayList(*const llvm.Value).init(dg.gpa);
+ var args = std.ArrayList(*const llvm.Value).init(gpa);
defer args.deinit();
const param_offset: c_uint = @boolToInt(ret_ptr != null);
@@ -482,8 +564,41 @@ pub const Object = struct {
try args.append(llvm_func.getParam(llvm_arg_i));
}
+ var di_file: ?*llvm.DIFile = null;
+ var di_scope: ?*llvm.DIScope = null;
+
+ if (dg.object.di_builder) |dib| {
+ di_file = try dg.object.getDIFile(gpa, decl.src_namespace.file_scope);
+
+ const line_number = decl.src_line + 1;
+ const is_internal_linkage = decl.val.tag() != .extern_fn and
+ !dg.module.decl_exports.contains(decl);
+ const noret_bit: c_uint = if (fn_info.return_type.isNoReturn())
+ llvm.DIFlags.NoReturn
+ else
+ 0;
+ const subprogram = dib.createFunction(
+ di_file.?.toScope(),
+ decl.name,
+ llvm_func.getValueName(),
+ di_file.?,
+ line_number,
+ try dg.lowerDebugType(decl.ty),
+ is_internal_linkage,
+ true, // is definition
+ line_number + func.lbrace_line, // scope line
+ llvm.DIFlags.StaticMember | noret_bit,
+ dg.module.comp.bin_file.options.optimize_mode != .Debug,
+ null, // decl_subprogram
+ );
+
+ llvm_func.fnSetSubprogram(subprogram);
+
+ di_scope = subprogram.toScope();
+ }
+
var fg: FuncGen = .{
- .gpa = dg.gpa,
+ .gpa = gpa,
.air = air,
.liveness = liveness,
.context = dg.context,
@@ -496,6 +611,8 @@ pub const Object = struct {
.llvm_func = llvm_func,
.blocks = .{},
.single_threaded = module.comp.bin_file.options.single_threaded,
+ .di_scope = di_scope,
+ .di_file = di_file,
};
defer fg.deinit();
@@ -599,6 +716,22 @@ pub const Object = struct {
const llvm_value = self.decl_map.get(decl) orelse return;
llvm_value.deleteGlobal();
}
+
+ fn getDIFile(o: *Object, gpa: Allocator, file: *const Module.File) !*llvm.DIFile {
+ const gop = try o.di_map.getOrPut(gpa, file);
+ errdefer assert(o.di_map.remove(file));
+ if (gop.found_existing) {
+ return @ptrCast(*llvm.DIFile, gop.value_ptr.*);
+ }
+ const dir_path = file.pkg.root_src_directory.path orelse ".";
+ const sub_file_path_z = try gpa.dupeZ(u8, file.sub_file_path);
+ defer gpa.free(sub_file_path_z);
+ const dir_path_z = try gpa.dupeZ(u8, dir_path);
+ defer gpa.free(dir_path_z);
+ const di_file = o.di_builder.?.createFile(sub_file_path_z, dir_path_z);
+ gop.value_ptr.* = di_file.toScope();
+ return di_file;
+ }
};
pub const DeclGen = struct {
@@ -942,15 +1075,13 @@ pub const DeclGen = struct {
},
.Optional => {
var buf: Type.Payload.ElemType = undefined;
- const child_type = t.optionalChild(&buf);
- if (!child_type.hasRuntimeBits()) {
+ const child_ty = t.optionalChild(&buf);
+ if (!child_ty.hasRuntimeBits()) {
return dg.context.intType(1);
}
- const payload_llvm_ty = try dg.llvmType(child_type);
+ const payload_llvm_ty = try dg.llvmType(child_ty);
if (t.isPtrLikeOptional()) {
return payload_llvm_ty;
- } else if (!child_type.hasRuntimeBits()) {
- return dg.context.intType(1);
}
const fields: [2]*const llvm.Type = .{
@@ -1771,6 +1902,768 @@ pub const DeclGen = struct {
}
}
+ fn lowerDebugType(dg: *DeclGen, ty: Type) Allocator.Error!*llvm.DIType {
+ const gpa = dg.gpa;
+ // Be careful not to reference this `gop` variable after any recursive calls
+ // to `lowerDebugType`.
+ const gop = try dg.object.di_type_map.getOrPut(gpa, ty);
+ if (gop.found_existing) return gop.value_ptr.*;
+ errdefer assert(dg.object.di_type_map.remove(ty));
+ // The Type memory is ephemeral; since we want to store a longer-lived
+ // reference, we need to copy it here.
+ gop.key_ptr.* = try ty.copy(dg.object.type_map_arena.allocator());
+ const target = dg.module.getTarget();
+ const dib = dg.object.di_builder.?;
+ switch (ty.zigTypeTag()) {
+ .Void, .NoReturn => {
+ gop.value_ptr.* = dib.createBasicType("void", 0, DW.ATE.signed);
+ return gop.value_ptr.*;
+ },
+ .Int => {
+ const info = ty.intInfo(target);
+ assert(info.bits != 0);
+ const name = try ty.nameAlloc(gpa);
+ defer gpa.free(name);
+ const dwarf_encoding: c_uint = switch (info.signedness) {
+ .signed => DW.ATE.signed,
+ .unsigned => DW.ATE.unsigned,
+ };
+ gop.value_ptr.* = dib.createBasicType(name, info.bits, dwarf_encoding);
+ return gop.value_ptr.*;
+ },
+ .Enum => {
+ const owner_decl = ty.getOwnerDecl();
+
+ if (!ty.hasRuntimeBits()) {
+ const enum_di_ty = try dg.makeEmptyNamespaceDIType(owner_decl);
+ // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
+ // means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, enum_di_ty);
+ return enum_di_ty;
+ }
+
+ const field_names = ty.enumFields().keys();
+
+ const enumerators = try gpa.alloc(*llvm.DIEnumerator, field_names.len);
+ defer gpa.free(enumerators);
+
+ var buf_field_index: Value.Payload.U32 = .{
+ .base = .{ .tag = .enum_field_index },
+ .data = undefined,
+ };
+ const field_index_val = Value.initPayload(&buf_field_index.base);
+
+ for (field_names) |field_name, i| {
+ const field_name_z = try gpa.dupeZ(u8, field_name);
+ defer gpa.free(field_name_z);
+
+ buf_field_index.data = @intCast(u32, i);
+ var buf_u64: Value.Payload.U64 = undefined;
+ const field_int_val = field_index_val.enumToInt(ty, &buf_u64);
+ // See https://github.com/ziglang/zig/issues/645
+ const field_int = field_int_val.toSignedInt();
+ enumerators[i] = dib.createEnumerator(field_name_z, field_int);
+ }
+
+ const di_file = try dg.object.getDIFile(gpa, owner_decl.src_namespace.file_scope);
+ const di_scope = try dg.namespaceToDebugScope(owner_decl.src_namespace);
+
+ const name = try ty.nameAlloc(gpa);
+ defer gpa.free(name);
+ var buffer: Type.Payload.Bits = undefined;
+ const int_ty = ty.intTagType(&buffer);
+
+ const enum_di_ty = dib.createEnumerationType(
+ di_scope,
+ name,
+ di_file,
+ owner_decl.src_node + 1,
+ ty.abiSize(target) * 8,
+ ty.abiAlignment(target) * 8,
+ enumerators.ptr,
+ @intCast(c_int, enumerators.len),
+ try lowerDebugType(dg, int_ty),
+ "",
+ );
+ // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, enum_di_ty);
+ return enum_di_ty;
+ },
+ .Float => {
+ const bits = ty.floatBits(target);
+ const name = try ty.nameAlloc(gpa);
+ defer gpa.free(name);
+ gop.value_ptr.* = dib.createBasicType(name, bits, DW.ATE.float);
+ return gop.value_ptr.*;
+ },
+ .Bool => {
+ gop.value_ptr.* = dib.createBasicType("bool", 1, DW.ATE.boolean);
+ return gop.value_ptr.*;
+ },
+ .Pointer => {
+ // Normalize everything that the debug info does not represent.
+ const ptr_info = ty.ptrInfo().data;
+
+ if (ptr_info.sentinel != null or
+ ptr_info.@"addrspace" != .generic or
+ ptr_info.bit_offset != 0 or
+ ptr_info.host_size != 0 or
+ ptr_info.@"allowzero" or
+ !ptr_info.mutable or
+ ptr_info.@"volatile" or
+ ptr_info.size == .Many or ptr_info.size == .C)
+ {
+ var payload: Type.Payload.Pointer = .{
+ .data = .{
+ .pointee_type = ptr_info.pointee_type,
+ .sentinel = null,
+ .@"align" = ptr_info.@"align",
+ .@"addrspace" = .generic,
+ .bit_offset = 0,
+ .host_size = 0,
+ .@"allowzero" = false,
+ .mutable = true,
+ .@"volatile" = false,
+ .size = switch (ptr_info.size) {
+ .Many, .C, .One => .One,
+ .Slice => .Slice,
+ },
+ },
+ };
+ const bland_ptr_ty = Type.initPayload(&payload.base);
+ const ptr_di_ty = try dg.lowerDebugType(bland_ptr_ty);
+ // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, ptr_di_ty);
+ return ptr_di_ty;
+ }
+
+ if (ty.isSlice()) {
+ var buf: Type.SlicePtrFieldTypeBuffer = undefined;
+ const ptr_ty = ty.slicePtrFieldType(&buf);
+ const len_ty = Type.usize;
+
+ const name = try ty.nameAlloc(gpa);
+ defer gpa.free(name);
+ const di_file: ?*llvm.DIFile = null;
+ const line = 0;
+ const compile_unit_scope = dg.object.di_compile_unit.?.toScope();
+ const fwd_decl = dib.createReplaceableCompositeType(
+ DW.TAG.structure_type,
+ name.ptr,
+ compile_unit_scope,
+ di_file,
+ line,
+ );
+ gop.value_ptr.* = fwd_decl;
+
+ const ptr_size = ptr_ty.abiSize(target);
+ const ptr_align = ptr_ty.abiAlignment(target);
+ const len_size = len_ty.abiSize(target);
+ const len_align = len_ty.abiAlignment(target);
+
+ var offset: u64 = 0;
+ offset += ptr_size;
+ offset = std.mem.alignForwardGeneric(u64, offset, len_align);
+ const len_offset = offset;
+
+ const fields: [2]*llvm.DIType = .{
+ dib.createMemberType(
+ fwd_decl.toScope(),
+ "ptr",
+ di_file,
+ line,
+ ptr_size * 8, // size in bits
+ ptr_align * 8, // align in bits
+ 0, // offset in bits
+ 0, // flags
+ try dg.lowerDebugType(ptr_ty),
+ ),
+ dib.createMemberType(
+ fwd_decl.toScope(),
+ "len",
+ di_file,
+ line,
+ len_size * 8, // size in bits
+ len_align * 8, // align in bits
+ len_offset * 8, // offset in bits
+ 0, // flags
+ try dg.lowerDebugType(len_ty),
+ ),
+ };
+
+ const replacement_di_ty = dib.createStructType(
+ compile_unit_scope,
+ name.ptr,
+ di_file,
+ line,
+ ty.abiSize(target) * 8, // size in bits
+ ty.abiAlignment(target) * 8, // align in bits
+ 0, // flags
+ null, // derived from
+ &fields,
+ fields.len,
+ 0, // run time lang
+ null, // vtable holder
+ "", // unique id
+ );
+ dib.replaceTemporary(fwd_decl, replacement_di_ty);
+ // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, replacement_di_ty);
+ return replacement_di_ty;
+ }
+
+ const elem_di_ty = try lowerDebugType(dg, ptr_info.pointee_type);
+ const name = try ty.nameAlloc(gpa);
+ defer gpa.free(name);
+ const ptr_di_ty = dib.createPointerType(
+ elem_di_ty,
+ target.cpu.arch.ptrBitWidth(),
+ ty.ptrAlignment(target) * 8,
+ name,
+ );
+ // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, ptr_di_ty);
+ return ptr_di_ty;
+ },
+ .Opaque => {
+ if (ty.tag() == .anyopaque) {
+ gop.value_ptr.* = dib.createBasicType("anyopaque", 0, DW.ATE.signed);
+ return gop.value_ptr.*;
+ }
+ const name = try ty.nameAlloc(gpa);
+ defer gpa.free(name);
+ const owner_decl = ty.getOwnerDecl();
+ const opaque_di_ty = dib.createForwardDeclType(
+ DW.TAG.structure_type,
+ name,
+ try dg.namespaceToDebugScope(owner_decl.src_namespace),
+ try dg.object.getDIFile(gpa, owner_decl.src_namespace.file_scope),
+ owner_decl.src_node + 1,
+ );
+ // The recursive call to `lowerDebugType` va `namespaceToDebugScope`
+ // means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, opaque_di_ty);
+ return opaque_di_ty;
+ },
+ .Array => {
+ const array_di_ty = dib.createArrayType(
+ ty.abiSize(target) * 8,
+ ty.abiAlignment(target) * 8,
+ try lowerDebugType(dg, ty.childType()),
+ @intCast(c_int, ty.arrayLen()),
+ );
+ // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, array_di_ty);
+ return array_di_ty;
+ },
+ .Vector => {
+ const vector_di_ty = dib.createVectorType(
+ ty.abiSize(target) * 8,
+ ty.abiAlignment(target) * 8,
+ try lowerDebugType(dg, ty.childType()),
+ ty.vectorLen(),
+ );
+ // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, vector_di_ty);
+ return vector_di_ty;
+ },
+ .Optional => {
+ const name = try ty.nameAlloc(gpa);
+ defer gpa.free(name);
+ var buf: Type.Payload.ElemType = undefined;
+ const child_ty = ty.optionalChild(&buf);
+ if (!child_ty.hasRuntimeBits()) {
+ gop.value_ptr.* = dib.createBasicType(name, 1, DW.ATE.boolean);
+ return gop.value_ptr.*;
+ }
+ if (ty.isPtrLikeOptional()) {
+ const ptr_di_ty = try dg.lowerDebugType(child_ty);
+ // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, ptr_di_ty);
+ return ptr_di_ty;
+ }
+
+ const di_file: ?*llvm.DIFile = null;
+ const line = 0;
+ const compile_unit_scope = dg.object.di_compile_unit.?.toScope();
+ const fwd_decl = dib.createReplaceableCompositeType(
+ DW.TAG.structure_type,
+ name.ptr,
+ compile_unit_scope,
+ di_file,
+ line,
+ );
+ gop.value_ptr.* = fwd_decl;
+
+ const non_null_ty = Type.bool;
+ const payload_size = child_ty.abiSize(target);
+ const payload_align = child_ty.abiAlignment(target);
+ const non_null_size = non_null_ty.abiSize(target);
+ const non_null_align = non_null_ty.abiAlignment(target);
+
+ var offset: u64 = 0;
+ offset += payload_size;
+ offset = std.mem.alignForwardGeneric(u64, offset, non_null_align);
+ const non_null_offset = offset;
+
+ const fields: [2]*llvm.DIType = .{
+ dib.createMemberType(
+ fwd_decl.toScope(),
+ "data",
+ di_file,
+ line,
+ payload_size * 8, // size in bits
+ payload_align * 8, // align in bits
+ 0, // offset in bits
+ 0, // flags
+ try dg.lowerDebugType(child_ty),
+ ),
+ dib.createMemberType(
+ fwd_decl.toScope(),
+ "some",
+ di_file,
+ line,
+ non_null_size * 8, // size in bits
+ non_null_align * 8, // align in bits
+ non_null_offset * 8, // offset in bits
+ 0, // flags
+ try dg.lowerDebugType(non_null_ty),
+ ),
+ };
+
+ const replacement_di_ty = dib.createStructType(
+ compile_unit_scope,
+ name.ptr,
+ di_file,
+ line,
+ ty.abiSize(target) * 8, // size in bits
+ ty.abiAlignment(target) * 8, // align in bits
+ 0, // flags
+ null, // derived from
+ &fields,
+ fields.len,
+ 0, // run time lang
+ null, // vtable holder
+ "", // unique id
+ );
+ dib.replaceTemporary(fwd_decl, replacement_di_ty);
+ // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, replacement_di_ty);
+ return replacement_di_ty;
+ },
+ .ErrorUnion => {
+ const err_set_ty = ty.errorUnionSet();
+ const payload_ty = ty.errorUnionPayload();
+ if (!payload_ty.hasRuntimeBits()) {
+ const err_set_di_ty = try dg.lowerDebugType(err_set_ty);
+ // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, err_set_di_ty);
+ return err_set_di_ty;
+ }
+ const name = try ty.nameAlloc(gpa);
+ defer gpa.free(name);
+ const di_file: ?*llvm.DIFile = null;
+ const line = 0;
+ const compile_unit_scope = dg.object.di_compile_unit.?.toScope();
+ const fwd_decl = dib.createReplaceableCompositeType(
+ DW.TAG.structure_type,
+ name.ptr,
+ compile_unit_scope,
+ di_file,
+ line,
+ );
+ gop.value_ptr.* = fwd_decl;
+
+ const err_set_size = err_set_ty.abiSize(target);
+ const err_set_align = err_set_ty.abiAlignment(target);
+ const payload_size = payload_ty.abiSize(target);
+ const payload_align = payload_ty.abiAlignment(target);
+
+ var offset: u64 = 0;
+ offset += err_set_size;
+ offset = std.mem.alignForwardGeneric(u64, offset, payload_align);
+ const payload_offset = offset;
+
+ const fields: [2]*llvm.DIType = .{
+ dib.createMemberType(
+ fwd_decl.toScope(),
+ "tag",
+ di_file,
+ line,
+ err_set_size * 8, // size in bits
+ err_set_align * 8, // align in bits
+ 0, // offset in bits
+ 0, // flags
+ try dg.lowerDebugType(err_set_ty),
+ ),
+ dib.createMemberType(
+ fwd_decl.toScope(),
+ "value",
+ di_file,
+ line,
+ payload_size * 8, // size in bits
+ payload_align * 8, // align in bits
+ payload_offset * 8, // offset in bits
+ 0, // flags
+ try dg.lowerDebugType(payload_ty),
+ ),
+ };
+
+ const replacement_di_ty = dib.createStructType(
+ compile_unit_scope,
+ name.ptr,
+ di_file,
+ line,
+ ty.abiSize(target) * 8, // size in bits
+ ty.abiAlignment(target) * 8, // align in bits
+ 0, // flags
+ null, // derived from
+ &fields,
+ fields.len,
+ 0, // run time lang
+ null, // vtable holder
+ "", // unique id
+ );
+ dib.replaceTemporary(fwd_decl, replacement_di_ty);
+ // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, replacement_di_ty);
+ return replacement_di_ty;
+ },
+ .ErrorSet => {
+ // TODO make this a proper enum with all the error codes in it.
+ // will need to consider how to take incremental compilation into account.
+ gop.value_ptr.* = dib.createBasicType("anyerror", 16, DW.ATE.unsigned);
+ return gop.value_ptr.*;
+ },
+ .Struct => {
+ const compile_unit_scope = dg.object.di_compile_unit.?.toScope();
+ const name = try ty.nameAlloc(gpa);
+ defer gpa.free(name);
+ const fwd_decl = dib.createReplaceableCompositeType(
+ DW.TAG.structure_type,
+ name.ptr,
+ compile_unit_scope,
+ null, // file
+ 0, // line
+ );
+ gop.value_ptr.* = fwd_decl;
+
+ if (ty.isTupleOrAnonStruct()) {
+ const tuple = ty.tupleFields();
+
+ var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{};
+ defer di_fields.deinit(gpa);
+
+ try di_fields.ensureUnusedCapacity(gpa, tuple.types.len);
+
+ comptime assert(struct_layout_version == 2);
+ var offset: u64 = 0;
+
+ for (tuple.types) |field_ty, i| {
+ const field_val = tuple.values[i];
+ if (field_val.tag() != .unreachable_value) continue;
+
+ const field_size = field_ty.abiSize(target);
+ const field_align = field_ty.abiAlignment(target);
+ const field_offset = std.mem.alignForwardGeneric(u64, offset, field_align);
+ offset = field_offset + field_size;
+
+ const field_name = if (ty.castTag(.anon_struct)) |payload|
+ try gpa.dupeZ(u8, payload.data.names[i])
+ else
+ try std.fmt.allocPrintZ(gpa, "{d}", .{i});
+ defer gpa.free(field_name);
+
+ try di_fields.append(gpa, dib.createMemberType(
+ fwd_decl.toScope(),
+ field_name,
+ null, // file
+ 0, // line
+ field_size * 8, // size in bits
+ field_align * 8, // align in bits
+ field_offset * 8, // offset in bits
+ 0, // flags
+ try dg.lowerDebugType(field_ty),
+ ));
+ }
+
+ const replacement_di_ty = dib.createStructType(
+ compile_unit_scope,
+ name.ptr,
+ null, // file
+ 0, // line
+ ty.abiSize(target) * 8, // size in bits
+ ty.abiAlignment(target) * 8, // align in bits
+ 0, // flags
+ null, // derived from
+ di_fields.items.ptr,
+ @intCast(c_int, di_fields.items.len),
+ 0, // run time lang
+ null, // vtable holder
+ "", // unique id
+ );
+ dib.replaceTemporary(fwd_decl, replacement_di_ty);
+ // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, replacement_di_ty);
+ return replacement_di_ty;
+ }
+
+ const TODO_implement_this = true; // TODO
+ if (TODO_implement_this or !ty.hasRuntimeBits()) {
+ const owner_decl = ty.getOwnerDecl();
+ const struct_di_ty = try dg.makeEmptyNamespaceDIType(owner_decl);
+ dib.replaceTemporary(fwd_decl, struct_di_ty);
+ // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
+ // means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, struct_di_ty);
+ return struct_di_ty;
+ }
+ @panic("TODO debug info type for struct");
+
+ //const struct_obj = ty.castTag(.@"struct").?.data;
+
+ //if (struct_obj.layout == .Packed) {
+ // var buf: Type.Payload.Bits = undefined;
+ // const int_ty = struct_obj.packedIntegerType(target, &buf);
+ // const int_llvm_ty = try dg.llvmType(int_ty);
+ // gop.value_ptr.* = int_llvm_ty;
+ // return int_llvm_ty;
+ //}
+
+ //const name = try struct_obj.getFullyQualifiedName(gpa);
+ //defer gpa.free(name);
+
+ //const llvm_struct_ty = dg.context.structCreateNamed(name);
+ //gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls
+
+ //assert(struct_obj.haveFieldTypes());
+
+ //var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{};
+ //defer llvm_field_types.deinit(gpa);
+
+ //try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count());
+
+ //comptime assert(struct_layout_version == 2);
+ //var offset: u64 = 0;
+ //var big_align: u32 = 0;
+
+ //for (struct_obj.fields.values()) |field| {
+ // if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
+
+ // const field_align = field.normalAlignment(target);
+ // big_align = @maximum(big_align, field_align);
+ // const prev_offset = offset;
+ // offset = std.mem.alignForwardGeneric(u64, offset, field_align);
+
+ // const padding_len = offset - prev_offset;
+ // if (padding_len > 0) {
+ // const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len));
+ // try llvm_field_types.append(gpa, llvm_array_ty);
+ // }
+ // const field_llvm_ty = try dg.llvmType(field.ty);
+ // try llvm_field_types.append(gpa, field_llvm_ty);
+
+ // offset += field.ty.abiSize(target);
+ //}
+ //{
+ // const prev_offset = offset;
+ // offset = std.mem.alignForwardGeneric(u64, offset, big_align);
+ // const padding_len = offset - prev_offset;
+ // if (padding_len > 0) {
+ // const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len));
+ // try llvm_field_types.append(gpa, llvm_array_ty);
+ // }
+ //}
+
+ //llvm_struct_ty.structSetBody(
+ // llvm_field_types.items.ptr,
+ // @intCast(c_uint, llvm_field_types.items.len),
+ // .False,
+ //);
+
+ //return llvm_struct_ty;
+ },
+ .Union => {
+ const owner_decl = ty.getOwnerDecl();
+
+ const name = try ty.nameAlloc(gpa);
+ defer gpa.free(name);
+ const fwd_decl = dib.createReplaceableCompositeType(
+ DW.TAG.structure_type,
+ name.ptr,
+ dg.object.di_compile_unit.?.toScope(),
+ null, // file
+ 0, // line
+ );
+ gop.value_ptr.* = fwd_decl;
+
+ const TODO_implement_this = true; // TODO
+ if (TODO_implement_this or !ty.hasRuntimeBits()) {
+ const union_di_ty = try dg.makeEmptyNamespaceDIType(owner_decl);
+ dib.replaceTemporary(fwd_decl, union_di_ty);
+ // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
+ // means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, union_di_ty);
+ return union_di_ty;
+ }
+
+ @panic("TODO debug info type for union");
+ //const gop = try dg.object.type_map.getOrPut(gpa, ty);
+ //if (gop.found_existing) return gop.value_ptr.*;
+
+ //// The Type memory is ephemeral; since we want to store a longer-lived
+ //// reference, we need to copy it here.
+ //gop.key_ptr.* = try ty.copy(dg.object.type_map_arena.allocator());
+
+ //const layout = ty.unionGetLayout(target);
+ //const union_obj = ty.cast(Type.Payload.Union).?.data;
+
+ //if (layout.payload_size == 0) {
+ // const enum_tag_llvm_ty = try dg.llvmType(union_obj.tag_ty);
+ // gop.value_ptr.* = enum_tag_llvm_ty;
+ // return enum_tag_llvm_ty;
+ //}
+
+ //const name = try union_obj.getFullyQualifiedName(gpa);
+ //defer gpa.free(name);
+
+ //const llvm_union_ty = dg.context.structCreateNamed(name);
+ //gop.value_ptr.* = llvm_union_ty; // must be done before any recursive calls
+
+ //const aligned_field = union_obj.fields.values()[layout.most_aligned_field];
+ //const llvm_aligned_field_ty = try dg.llvmType(aligned_field.ty);
+
+ //const llvm_payload_ty = ty: {
+ // if (layout.most_aligned_field_size == layout.payload_size) {
+ // break :ty llvm_aligned_field_ty;
+ // }
+ // const padding_len = @intCast(c_uint, layout.payload_size - layout.most_aligned_field_size);
+ // const fields: [2]*const llvm.Type = .{
+ // llvm_aligned_field_ty,
+ // dg.context.intType(8).arrayType(padding_len),
+ // };
+ // break :ty dg.context.structType(&fields, fields.len, .True);
+ //};
+
+ //if (layout.tag_size == 0) {
+ // var llvm_fields: [1]*const llvm.Type = .{llvm_payload_ty};
+ // llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False);
+ // return llvm_union_ty;
+ //}
+ //const enum_tag_llvm_ty = try dg.llvmType(union_obj.tag_ty);
+
+ //// Put the tag before or after the payload depending on which one's
+ //// alignment is greater.
+ //var llvm_fields: [3]*const llvm.Type = undefined;
+ //var llvm_fields_len: c_uint = 2;
+
+ //if (layout.tag_align >= layout.payload_align) {
+ // llvm_fields = .{ enum_tag_llvm_ty, llvm_payload_ty, undefined };
+ //} else {
+ // llvm_fields = .{ llvm_payload_ty, enum_tag_llvm_ty, undefined };
+ //}
+
+ //// Insert padding to make the LLVM struct ABI size match the Zig union ABI size.
+ //if (layout.padding != 0) {
+ // llvm_fields[2] = dg.context.intType(8).arrayType(layout.padding);
+ // llvm_fields_len = 3;
+ //}
+
+ //llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False);
+ //return llvm_union_ty;
+ },
+ .Fn => {
+ const fn_info = ty.fnInfo();
+ const sret = firstParamSRet(fn_info, target);
+
+ var param_di_types = std.ArrayList(*llvm.DIType).init(dg.gpa);
+ defer param_di_types.deinit();
+
+ // Return type goes first.
+ const di_ret_ty = if (sret or !fn_info.return_type.hasRuntimeBits())
+ Type.void
+ else
+ fn_info.return_type;
+ try param_di_types.append(try dg.lowerDebugType(di_ret_ty));
+
+ if (sret) {
+ var ptr_ty_payload: Type.Payload.ElemType = .{
+ .base = .{ .tag = .single_mut_pointer },
+ .data = fn_info.return_type,
+ };
+ const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
+ try param_di_types.append(try dg.lowerDebugType(ptr_ty));
+ }
+
+ for (fn_info.param_types) |param_ty| {
+ if (!param_ty.hasRuntimeBits()) continue;
+
+ if (isByRef(param_ty)) {
+ var ptr_ty_payload: Type.Payload.ElemType = .{
+ .base = .{ .tag = .single_mut_pointer },
+ .data = param_ty,
+ };
+ const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
+ try param_di_types.append(try dg.lowerDebugType(ptr_ty));
+ } else {
+ try param_di_types.append(try dg.lowerDebugType(param_ty));
+ }
+ }
+
+ const fn_di_ty = dib.createSubroutineType(
+ param_di_types.items.ptr,
+ @intCast(c_int, param_di_types.items.len),
+ 0,
+ );
+ // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
+ try dg.object.di_type_map.put(gpa, ty, fn_di_ty);
+ return fn_di_ty;
+ },
+ .ComptimeInt => unreachable,
+ .ComptimeFloat => unreachable,
+ .Type => unreachable,
+ .Undefined => unreachable,
+ .Null => unreachable,
+ .EnumLiteral => unreachable,
+
+ .BoundFn => @panic("TODO remove BoundFn from the language"),
+
+ .Frame => @panic("TODO implement lowerDebugType for Frame types"),
+ .AnyFrame => @panic("TODO implement lowerDebugType for AnyFrame types"),
+ }
+ }
+
+ fn namespaceToDebugScope(dg: *DeclGen, namespace: *const Module.Namespace) !*llvm.DIScope {
+ if (namespace.parent == null) {
+ const di_file = try dg.object.getDIFile(dg.gpa, namespace.file_scope);
+ return di_file.toScope();
+ }
+ const di_type = try dg.lowerDebugType(namespace.ty);
+ return di_type.toScope();
+ }
+
+ /// This is to be used instead of void for debug info types, to avoid tripping
+ /// Assertion `!isa(Scope) && "shouldn't make a namespace scope for a type"'
+ /// when targeting CodeView (Windows).
+ fn makeEmptyNamespaceDIType(dg: *DeclGen, decl: *const Module.Decl) !*llvm.DIType {
+ const fields: [0]*llvm.DIType = .{};
+ return dg.object.di_builder.?.createStructType(
+ try dg.namespaceToDebugScope(decl.src_namespace),
+ decl.name, // TODO use fully qualified name
+ try dg.object.getDIFile(dg.gpa, decl.src_namespace.file_scope),
+ decl.src_line + 1,
+ 0, // size in bits
+ 0, // align in bits
+ 0, // flags
+ null, // derived from
+ undefined, // TODO should be able to pass &fields,
+ fields.len,
+ 0, // run time lang
+ null, // vtable holder
+ "", // unique id
+ );
+ }
+
const ParentPtr = struct {
ty: Type,
llvm_ptr: *const llvm.Value,
@@ -2085,6 +2978,8 @@ pub const FuncGen = struct {
liveness: Liveness,
context: *const llvm.Context,
builder: *const llvm.Builder,
+ di_scope: ?*llvm.DIScope,
+ di_file: ?*llvm.DIFile,
/// This stores the LLVM values used in a function, such that they can be referred to
/// in other instructions. This table is cleared before every function is generated.
@@ -2100,7 +2995,7 @@ pub const FuncGen = struct {
/// it omits 0-bit types. If the function uses sret as the first parameter,
/// this slice does not include it.
args: []const *const llvm.Value,
- arg_index: usize,
+ arg_index: c_uint,
llvm_func: *const llvm.Value,
@@ -2330,10 +3225,7 @@ pub const FuncGen = struct {
.constant => unreachable,
.const_ty => unreachable,
.unreach => self.airUnreach(inst),
- .dbg_stmt => blk: {
- // TODO: implement debug info
- break :blk null;
- },
+ .dbg_stmt => self.airDbgStmt(inst),
// zig fmt: on
};
if (opt_value) |val| {
@@ -3043,6 +3935,17 @@ pub const FuncGen = struct {
return null;
}
+ fn airDbgStmt(self: *FuncGen, inst: Air.Inst.Index) ?*const llvm.Value {
+ const di_scope = self.di_scope orelse return null;
+ const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
+ self.builder.setCurrentDebugLocation(
+ @intCast(c_int, self.dg.decl.src_line + dbg_stmt.line + 1),
+ @intCast(c_int, dbg_stmt.column + 1),
+ di_scope,
+ );
+ return null;
+ }
+
fn airAssembly(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
// Eventually, the Zig compiler needs to be reworked to have inline assembly go
// through the same parsing code regardless of backend, and have LLVM-flavored
@@ -4243,15 +5146,42 @@ pub const FuncGen = struct {
self.arg_index += 1;
const inst_ty = self.air.typeOfIndex(inst);
- if (isByRef(inst_ty)) {
- // TODO declare debug variable
- return arg_val;
- } else {
- const ptr_val = self.buildAlloca(try self.dg.llvmType(inst_ty));
- _ = self.builder.buildStore(arg_val, ptr_val);
- // TODO declare debug variable
- return arg_val;
+ if (self.dg.object.di_builder) |dib| {
+ const src_index = self.getSrcArgIndex(self.arg_index - 1);
+ const func = self.dg.decl.getFunction().?;
+ const lbrace_line = func.owner_decl.src_line + func.lbrace_line + 1;
+ const lbrace_col = func.lbrace_column + 1;
+ const di_local_var = dib.createParameterVariable(
+ self.di_scope.?,
+ func.getParamName(src_index).ptr, // TODO test 0 bit args
+ self.di_file.?,
+ lbrace_line,
+ try self.dg.lowerDebugType(inst_ty),
+ true, // always preserve
+ 0, // flags
+ self.arg_index, // includes +1 because 0 is return type
+ );
+
+ const debug_loc = llvm.getDebugLoc(lbrace_line, lbrace_col, self.di_scope.?);
+ const insert_block = self.builder.getInsertBlock();
+ if (isByRef(inst_ty)) {
+ _ = dib.insertDeclareAtEnd(arg_val, di_local_var, debug_loc, insert_block);
+ } else {
+ _ = dib.insertDbgValueIntrinsicAtEnd(arg_val, di_local_var, debug_loc, insert_block);
+ }
}
+
+ return arg_val;
+ }
+
+ fn getSrcArgIndex(self: *FuncGen, runtime_index: u32) u32 {
+ const fn_info = self.dg.decl.ty.fnInfo();
+ var i: u32 = 0;
+ for (fn_info.param_types) |param_ty, src_index| {
+ if (!param_ty.hasRuntimeBits()) continue;
+ if (i == runtime_index) return @intCast(u32, src_index);
+ i += 1;
+ } else unreachable;
}
fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
@@ -4774,7 +5704,7 @@ pub const FuncGen = struct {
const prev_debug_location = self.builder.getCurrentDebugLocation2();
defer {
self.builder.positionBuilderAtEnd(prev_block);
- if (!self.dg.module.comp.bin_file.options.strip) {
+ if (self.di_scope != null) {
self.builder.setCurrentDebugLocation2(prev_debug_location);
}
}
diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig
index fd095ae511..f590e46c23 100644
--- a/src/codegen/llvm/bindings.zig
+++ b/src/codegen/llvm/bindings.zig
@@ -184,12 +184,18 @@ pub const Value = opaque {
pub const setFunctionCallConv = LLVMSetFunctionCallConv;
extern fn LLVMSetFunctionCallConv(Fn: *const Value, CC: CallConv) void;
+ pub const fnSetSubprogram = ZigLLVMFnSetSubprogram;
+ extern fn ZigLLVMFnSetSubprogram(f: *const Value, subprogram: *DISubprogram) void;
+
pub const setValueName = LLVMSetValueName;
extern fn LLVMSetValueName(Val: *const Value, Name: [*:0]const u8) void;
pub const setValueName2 = LLVMSetValueName2;
extern fn LLVMSetValueName2(Val: *const Value, Name: [*]const u8, NameLen: usize) void;
+ pub const getValueName = LLVMGetValueName;
+ extern fn LLVMGetValueName(Val: *const Value) [*:0]const u8;
+
pub const takeName = ZigLLVMTakeName;
extern fn ZigLLVMTakeName(new_owner: *const Value, victim: *const Value) void;
@@ -354,6 +360,18 @@ pub const Module = opaque {
Name: [*:0]const u8,
NameLen: usize,
) ?*const Value;
+
+ pub const setTarget = LLVMSetTarget;
+ extern fn LLVMSetTarget(M: *const Module, Triple: [*:0]const u8) void;
+
+ pub const addModuleDebugInfoFlag = ZigLLVMAddModuleDebugInfoFlag;
+ extern fn ZigLLVMAddModuleDebugInfoFlag(module: *const Module) void;
+
+ pub const addModuleCodeViewFlag = ZigLLVMAddModuleCodeViewFlag;
+ extern fn ZigLLVMAddModuleCodeViewFlag(module: *const Module) void;
+
+ pub const createDIBuilder = ZigLLVMCreateDIBuilder;
+ extern fn ZigLLVMCreateDIBuilder(module: *const Module, allow_unresolved: bool) *DIBuilder;
};
pub const lookupIntrinsicID = LLVMLookupIntrinsicID;
@@ -821,7 +839,7 @@ pub const Builder = opaque {
pub const buildExactSDiv = LLVMBuildExactSDiv;
extern fn LLVMBuildExactSDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
- pub const zigSetCurrentDebugLocation = ZigLLVMSetCurrentDebugLocation;
+ pub const setCurrentDebugLocation = ZigLLVMSetCurrentDebugLocation;
extern fn ZigLLVMSetCurrentDebugLocation(builder: *const Builder, line: c_int, column: c_int, scope: *DIScope) void;
pub const clearCurrentDebugLocation = ZigLLVMClearCurrentDebugLocation;
@@ -1203,7 +1221,7 @@ pub const WriteImportLibrary = ZigLLVMWriteImportLibrary;
extern fn ZigLLVMWriteImportLibrary(
def_path: [*:0]const u8,
arch: ArchType,
- output_lib_path: [*c]const u8,
+ output_lib_path: [*:0]const u8,
kill_at: bool,
) bool;
@@ -1400,3 +1418,333 @@ pub const address_space = struct {
pub const constant_buffer_15: c_uint = 23;
};
};
+
+pub const DIEnumerator = opaque {};
+pub const DILocalVariable = opaque {};
+pub const DIGlobalVariable = opaque {};
+pub const DILocation = opaque {};
+
+pub const DIType = opaque {
+ pub const toScope = ZigLLVMTypeToScope;
+ extern fn ZigLLVMTypeToScope(ty: *DIType) *DIScope;
+};
+pub const DIFile = opaque {
+ pub const toScope = ZigLLVMFileToScope;
+ extern fn ZigLLVMFileToScope(difile: *DIFile) *DIScope;
+};
+pub const DILexicalBlock = opaque {
+ pub const toScope = ZigLLVMLexicalBlockToScope;
+ extern fn ZigLLVMLexicalBlockToScope(lexical_block: *DILexicalBlock) *DIScope;
+};
+pub const DICompileUnit = opaque {
+ pub const toScope = ZigLLVMCompileUnitToScope;
+ extern fn ZigLLVMCompileUnitToScope(compile_unit: *DICompileUnit) *DIScope;
+};
+pub const DISubprogram = opaque {
+ pub const toScope = ZigLLVMSubprogramToScope;
+ extern fn ZigLLVMSubprogramToScope(subprogram: *DISubprogram) *DIScope;
+};
+
+pub const getDebugLoc = ZigLLVMGetDebugLoc;
+extern fn ZigLLVMGetDebugLoc(line: c_uint, col: c_uint, scope: *DIScope) *DILocation;
+
+pub const DIBuilder = opaque {
+ pub const dispose = ZigLLVMDisposeDIBuilder;
+ extern fn ZigLLVMDisposeDIBuilder(dib: *DIBuilder) void;
+
+ pub const finalize = ZigLLVMDIBuilderFinalize;
+ extern fn ZigLLVMDIBuilderFinalize(dib: *DIBuilder) void;
+
+ pub const createPointerType = ZigLLVMCreateDebugPointerType;
+ extern fn ZigLLVMCreateDebugPointerType(
+ dib: *DIBuilder,
+ pointee_type: *DIType,
+ size_in_bits: u64,
+ align_in_bits: u64,
+ name: [*:0]const u8,
+ ) *DIType;
+
+ pub const createBasicType = ZigLLVMCreateDebugBasicType;
+ extern fn ZigLLVMCreateDebugBasicType(
+ dib: *DIBuilder,
+ name: [*:0]const u8,
+ size_in_bits: u64,
+ encoding: c_uint,
+ ) *DIType;
+
+ pub const createArrayType = ZigLLVMCreateDebugArrayType;
+ extern fn ZigLLVMCreateDebugArrayType(
+ dib: *DIBuilder,
+ size_in_bits: u64,
+ align_in_bits: u64,
+ elem_type: *DIType,
+ elem_count: c_int,
+ ) *DIType;
+
+ pub const createEnumerator = ZigLLVMCreateDebugEnumerator;
+ extern fn ZigLLVMCreateDebugEnumerator(
+ dib: *DIBuilder,
+ name: [*:0]const u8,
+ val: i64,
+ ) *DIEnumerator;
+
+ pub const createEnumerationType = ZigLLVMCreateDebugEnumerationType;
+ extern fn ZigLLVMCreateDebugEnumerationType(
+ dib: *DIBuilder,
+ scope: *DIScope,
+ name: [*:0]const u8,
+ file: *DIFile,
+ line_number: c_uint,
+ size_in_bits: u64,
+ align_in_bits: u64,
+ enumerator_array: [*]const *DIEnumerator,
+ enumerator_array_len: c_int,
+ underlying_type: *DIType,
+ unique_id: [*:0]const u8,
+ ) *DIType;
+
+ pub const createStructType = ZigLLVMCreateDebugStructType;
+ extern fn ZigLLVMCreateDebugStructType(
+ dib: *DIBuilder,
+ scope: *DIScope,
+ name: [*:0]const u8,
+ file: ?*DIFile,
+ line_number: c_uint,
+ size_in_bits: u64,
+ align_in_bits: u64,
+ flags: c_uint,
+ derived_from: ?*DIType,
+ types_array: [*]const *DIType,
+ types_array_len: c_int,
+ run_time_lang: c_uint,
+ vtable_holder: ?*DIType,
+ unique_id: [*:0]const u8,
+ ) *DIType;
+
+ pub const createUnionType = ZigLLVMCreateDebugUnionType;
+ extern fn ZigLLVMCreateDebugUnionType(
+ dib: *DIBuilder,
+ scope: *DIScope,
+ name: [*:0]const u8,
+ file: *DIFile,
+ line_number: c_uint,
+ size_in_bits: u64,
+ align_in_bits: u64,
+ flags: c_uint,
+ types_array: [*]const *DIType,
+ types_array_len: c_int,
+ run_time_lang: c_uint,
+ unique_id: [*:0]const u8,
+ ) *DIType;
+
+ pub const createMemberType = ZigLLVMCreateDebugMemberType;
+ extern fn ZigLLVMCreateDebugMemberType(
+ dib: *DIBuilder,
+ scope: *DIScope,
+ name: [*:0]const u8,
+ file: ?*DIFile,
+ line: c_uint,
+ size_in_bits: u64,
+ align_in_bits: u64,
+ offset_in_bits: u64,
+ flags: c_uint,
+ ty: *DIType,
+ ) *DIType;
+
+ pub const createReplaceableCompositeType = ZigLLVMCreateReplaceableCompositeType;
+ extern fn ZigLLVMCreateReplaceableCompositeType(
+ dib: *DIBuilder,
+ tag: c_uint,
+ name: [*:0]const u8,
+ scope: *DIScope,
+ file: ?*DIFile,
+ line: c_uint,
+ ) *DIType;
+
+ pub const createForwardDeclType = ZigLLVMCreateDebugForwardDeclType;
+ extern fn ZigLLVMCreateDebugForwardDeclType(
+ dib: *DIBuilder,
+ tag: c_uint,
+ name: [*:0]const u8,
+ scope: *DIScope,
+ file: *DIFile,
+ line: c_uint,
+ ) *DIType;
+
+ pub const replaceTemporary = ZigLLVMReplaceTemporary;
+ extern fn ZigLLVMReplaceTemporary(dib: *DIBuilder, ty: *DIType, replacement: *DIType) void;
+
+ pub const replaceDebugArrays = ZigLLVMReplaceDebugArrays;
+ extern fn ZigLLVMReplaceDebugArrays(
+ dib: *DIBuilder,
+ ty: *DIType,
+ types_array: [*]const *DIType,
+ types_array_len: c_int,
+ ) void;
+
+ pub const createSubroutineType = ZigLLVMCreateSubroutineType;
+ extern fn ZigLLVMCreateSubroutineType(
+ dib: *DIBuilder,
+ types_array: [*]const *DIType,
+ types_array_len: c_int,
+ flags: c_uint,
+ ) *DIType;
+
+ pub const createAutoVariable = ZigLLVMCreateAutoVariable;
+ extern fn ZigLLVMCreateAutoVariable(
+ dib: *DIBuilder,
+ scope: *DIScope,
+ name: [*:0]const u8,
+ file: *DIFile,
+ line_no: c_uint,
+ ty: *DIType,
+ always_preserve: bool,
+ flags: c_uint,
+ ) *DILocalVariable;
+
+ pub const createGlobalVariable = ZigLLVMCreateGlobalVariable;
+ extern fn ZigLLVMCreateGlobalVariable(
+ dib: *DIBuilder,
+ scope: *DIScope,
+ name: [*:0]const u8,
+ linkage_name: [*:0]const u8,
+ file: *DIFile,
+ line_no: c_uint,
+ di_type: *DIType,
+ is_local_to_unit: bool,
+ ) *DIGlobalVariable;
+
+ pub const createParameterVariable = ZigLLVMCreateParameterVariable;
+ extern fn ZigLLVMCreateParameterVariable(
+ dib: *DIBuilder,
+ scope: *DIScope,
+ name: [*:0]const u8,
+ file: *DIFile,
+ line_no: c_uint,
+ ty: *DIType,
+ always_preserve: bool,
+ flags: c_uint,
+ arg_no: c_uint,
+ ) *DILocalVariable;
+
+ pub const createLexicalBlock = ZigLLVMCreateLexicalBlock;
+ extern fn ZigLLVMCreateLexicalBlock(
+ dib: *DIBuilder,
+ scope: *DIScope,
+ file: *DIFile,
+ line: c_uint,
+ col: c_uint,
+ ) *DILexicalBlock;
+
+ pub const createCompileUnit = ZigLLVMCreateCompileUnit;
+ extern fn ZigLLVMCreateCompileUnit(
+ dib: *DIBuilder,
+ lang: c_uint,
+ difile: *DIFile,
+ producer: [*:0]const u8,
+ is_optimized: bool,
+ flags: [*:0]const u8,
+ runtime_version: c_uint,
+ split_name: [*:0]const u8,
+ dwo_id: u64,
+ emit_debug_info: bool,
+ ) *DICompileUnit;
+
+ pub const createFile = ZigLLVMCreateFile;
+ extern fn ZigLLVMCreateFile(
+ dib: *DIBuilder,
+ filename: [*:0]const u8,
+ directory: [*:0]const u8,
+ ) *DIFile;
+
+ pub const createFunction = ZigLLVMCreateFunction;
+ extern fn ZigLLVMCreateFunction(
+ dib: *DIBuilder,
+ scope: *DIScope,
+ name: [*:0]const u8,
+ linkage_name: [*:0]const u8,
+ file: *DIFile,
+ lineno: c_uint,
+ fn_di_type: *DIType,
+ is_local_to_unit: bool,
+ is_definition: bool,
+ scope_line: c_uint,
+ flags: c_uint,
+ is_optimized: bool,
+ decl_subprogram: ?*DISubprogram,
+ ) *DISubprogram;
+
+ pub const createVectorType = ZigLLVMDIBuilderCreateVectorType;
+ extern fn ZigLLVMDIBuilderCreateVectorType(
+ dib: *DIBuilder,
+ SizeInBits: u64,
+ AlignInBits: u32,
+ Ty: *DIType,
+ elem_count: u32,
+ ) *DIType;
+
+ pub const insertDeclareAtEnd = ZigLLVMInsertDeclareAtEnd;
+ extern fn ZigLLVMInsertDeclareAtEnd(
+ dib: *DIBuilder,
+ storage: *const Value,
+ var_info: *DILocalVariable,
+ debug_loc: *DILocation,
+ basic_block_ref: *const BasicBlock,
+ ) *const Value;
+
+ pub const insertDeclare = ZigLLVMInsertDeclare;
+ extern fn ZigLLVMInsertDeclare(
+ dib: *DIBuilder,
+ storage: *const Value,
+ var_info: *DILocalVariable,
+ debug_loc: *DILocation,
+ insert_before_instr: *const Value,
+ ) *const Value;
+
+ pub const insertDbgValueIntrinsicAtEnd = ZigLLVMInsertDbgValueIntrinsicAtEnd;
+ extern fn ZigLLVMInsertDbgValueIntrinsicAtEnd(
+ dib: *DIBuilder,
+ val: *const Value,
+ var_info: *DILocalVariable,
+ debug_loc: *DILocation,
+ basic_block_ref: *const BasicBlock,
+ ) *const Value;
+};
+
+pub const DIFlags = opaque {
+ pub const Zero = 0;
+ pub const Private = 1;
+ pub const Protected = 2;
+ pub const Public = 3;
+
+ pub const FwdDecl = 1 << 2;
+ pub const AppleBlock = 1 << 3;
+ pub const BlockByrefStruct = 1 << 4;
+ pub const Virtual = 1 << 5;
+ pub const Artificial = 1 << 6;
+ pub const Explicit = 1 << 7;
+ pub const Prototyped = 1 << 8;
+ pub const ObjcClassComplete = 1 << 9;
+ pub const ObjectPointer = 1 << 10;
+ pub const Vector = 1 << 11;
+ pub const StaticMember = 1 << 12;
+ pub const LValueReference = 1 << 13;
+ pub const RValueReference = 1 << 14;
+ pub const Reserved = 1 << 15;
+
+ pub const SingleInheritance = 1 << 16;
+ pub const MultipleInheritance = 2 << 16;
+ pub const VirtualInheritance = 3 << 16;
+
+ pub const IntroducedVirtual = 1 << 18;
+ pub const BitField = 1 << 19;
+ pub const NoReturn = 1 << 20;
+ pub const TypePassByValue = 1 << 22;
+ pub const TypePassByReference = 1 << 23;
+ pub const EnumClass = 1 << 24;
+ pub const Thunk = 1 << 25;
+ pub const NonTrivial = 1 << 26;
+ pub const BigEndian = 1 << 27;
+ pub const LittleEndian = 1 << 28;
+ pub const AllCallsDescribed = 1 << 29;
+};
diff --git a/src/link.zig b/src/link.zig
index 577a78d893..34e2bb8ed8 100644
--- a/src/link.zig
+++ b/src/link.zig
@@ -72,7 +72,7 @@ pub const Options = struct {
object_format: std.Target.ObjectFormat,
optimize_mode: std.builtin.Mode,
machine_code_model: std.builtin.CodeModel,
- root_name: []const u8,
+ root_name: [:0]const u8,
/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
module: ?*Module,
dynamic_linker: ?[]const u8,
diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig
index c4e31eed6d..fc8f1fab55 100644
--- a/src/link/Dwarf.zig
+++ b/src/link/Dwarf.zig
@@ -882,7 +882,7 @@ fn addDbgInfoType(
const abi_size = ty.abiSize(target);
try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size);
// DW.AT.name, DW.FORM.string
- const struct_name = try ty.nameAlloc(arena);
+ const struct_name = try ty.nameAllocArena(arena);
try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1);
dbg_info_buffer.appendSliceAssumeCapacity(struct_name);
dbg_info_buffer.appendAssumeCapacity(0);
diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp
index ff925f265f..a2db15c622 100644
--- a/src/stage1/analyze.cpp
+++ b/src/stage1/analyze.cpp
@@ -9073,8 +9073,7 @@ static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type, ResolveStatu
for (uint32_t i = 0; i < field_count; i += 1) {
TypeEnumField *enum_field = &enum_type->data.enumeration.fields[i];
- // TODO send patch to LLVM to support APInt in createEnumerator instead of int64_t
- // http://lists.llvm.org/pipermail/llvm-dev/2017-December/119456.html
+ // https://github.com/ziglang/zig/issues/645
di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(enum_field->name),
bigint_as_signed(&enum_field->value));
}
diff --git a/src/type.zig b/src/type.zig
index 6c284f58d4..b6728b4be0 100644
--- a/src/type.zig
+++ b/src/type.zig
@@ -1766,8 +1766,20 @@ pub const Type = extern union {
}
}
+ pub fn nameAllocArena(ty: Type, arena: Allocator) Allocator.Error![:0]const u8 {
+ return nameAllocAdvanced(ty, arena, true);
+ }
+
+ pub fn nameAlloc(ty: Type, gpa: Allocator) Allocator.Error![:0]const u8 {
+ return nameAllocAdvanced(ty, gpa, false);
+ }
+
/// Returns a name suitable for `@typeName`.
- pub fn nameAlloc(ty: Type, arena: Allocator) Allocator.Error![:0]const u8 {
+ pub fn nameAllocAdvanced(
+ ty: Type,
+ ally: Allocator,
+ is_arena: bool,
+ ) Allocator.Error![:0]const u8 {
const t = ty.tag();
switch (t) {
.inferred_alloc_const => unreachable,
@@ -1812,71 +1824,79 @@ pub const Type = extern union {
.noreturn,
.var_args_param,
.bound_fn,
- => return @tagName(t),
+ => return maybeDupe(@tagName(t), ally, is_arena),
- .enum_literal => return "@Type(.EnumLiteral)",
- .@"null" => return "@Type(.Null)",
- .@"undefined" => return "@Type(.Undefined)",
+ .enum_literal => return maybeDupe("@Type(.EnumLiteral)", ally, is_arena),
+ .@"null" => return maybeDupe("@Type(.Null)", ally, is_arena),
+ .@"undefined" => return maybeDupe("@Type(.Undefined)", ally, is_arena),
- .empty_struct, .empty_struct_literal => return "struct {}",
+ .empty_struct, .empty_struct_literal => return maybeDupe("struct {}", ally, is_arena),
.@"struct" => {
const struct_obj = ty.castTag(.@"struct").?.data;
- return try arena.dupeZ(u8, std.mem.sliceTo(struct_obj.owner_decl.name, 0));
+ return try ally.dupeZ(u8, std.mem.sliceTo(struct_obj.owner_decl.name, 0));
},
.@"union", .union_tagged => {
const union_obj = ty.cast(Payload.Union).?.data;
- return try arena.dupeZ(u8, std.mem.sliceTo(union_obj.owner_decl.name, 0));
+ return try ally.dupeZ(u8, std.mem.sliceTo(union_obj.owner_decl.name, 0));
},
.enum_full, .enum_nonexhaustive => {
const enum_full = ty.cast(Payload.EnumFull).?.data;
- return try arena.dupeZ(u8, std.mem.sliceTo(enum_full.owner_decl.name, 0));
+ return try ally.dupeZ(u8, std.mem.sliceTo(enum_full.owner_decl.name, 0));
},
.enum_simple => {
const enum_simple = ty.castTag(.enum_simple).?.data;
- return try arena.dupeZ(u8, std.mem.sliceTo(enum_simple.owner_decl.name, 0));
+ return try ally.dupeZ(u8, std.mem.sliceTo(enum_simple.owner_decl.name, 0));
},
.enum_numbered => {
const enum_numbered = ty.castTag(.enum_numbered).?.data;
- return try arena.dupeZ(u8, std.mem.sliceTo(enum_numbered.owner_decl.name, 0));
+ return try ally.dupeZ(u8, std.mem.sliceTo(enum_numbered.owner_decl.name, 0));
},
.@"opaque" => {
- // TODO use declaration name
- return "opaque {}";
+ const opaque_obj = ty.cast(Payload.Opaque).?.data;
+ return try ally.dupeZ(u8, std.mem.sliceTo(opaque_obj.owner_decl.name, 0));
},
- .anyerror_void_error_union => return "anyerror!void",
- .const_slice_u8 => return "[]const u8",
- .const_slice_u8_sentinel_0 => return "[:0]const u8",
- .fn_noreturn_no_args => return "fn() noreturn",
- .fn_void_no_args => return "fn() void",
- .fn_naked_noreturn_no_args => return "fn() callconv(.Naked) noreturn",
- .fn_ccc_void_no_args => return "fn() callconv(.C) void",
- .single_const_pointer_to_comptime_int => return "*const comptime_int",
- .manyptr_u8 => return "[*]u8",
- .manyptr_const_u8 => return "[*]const u8",
- .manyptr_const_u8_sentinel_0 => return "[*:0]const u8",
- .atomic_order => return "AtomicOrder",
- .atomic_rmw_op => return "AtomicRmwOp",
- .calling_convention => return "CallingConvention",
- .address_space => return "AddressSpace",
- .float_mode => return "FloatMode",
- .reduce_op => return "ReduceOp",
- .call_options => return "CallOptions",
- .prefetch_options => return "PrefetchOptions",
- .export_options => return "ExportOptions",
- .extern_options => return "ExternOptions",
- .type_info => return "Type",
+ .anyerror_void_error_union => return maybeDupe("anyerror!void", ally, is_arena),
+ .const_slice_u8 => return maybeDupe("[]const u8", ally, is_arena),
+ .const_slice_u8_sentinel_0 => return maybeDupe("[:0]const u8", ally, is_arena),
+ .fn_noreturn_no_args => return maybeDupe("fn() noreturn", ally, is_arena),
+ .fn_void_no_args => return maybeDupe("fn() void", ally, is_arena),
+ .fn_naked_noreturn_no_args => return maybeDupe("fn() callconv(.Naked) noreturn", ally, is_arena),
+ .fn_ccc_void_no_args => return maybeDupe("fn() callconv(.C) void", ally, is_arena),
+ .single_const_pointer_to_comptime_int => return maybeDupe("*const comptime_int", ally, is_arena),
+ .manyptr_u8 => return maybeDupe("[*]u8", ally, is_arena),
+ .manyptr_const_u8 => return maybeDupe("[*]const u8", ally, is_arena),
+ .manyptr_const_u8_sentinel_0 => return maybeDupe("[*:0]const u8", ally, is_arena),
+ .atomic_order => return maybeDupe("AtomicOrder", ally, is_arena),
+ .atomic_rmw_op => return maybeDupe("AtomicRmwOp", ally, is_arena),
+ .calling_convention => return maybeDupe("CallingConvention", ally, is_arena),
+ .address_space => return maybeDupe("AddressSpace", ally, is_arena),
+ .float_mode => return maybeDupe("FloatMode", ally, is_arena),
+ .reduce_op => return maybeDupe("ReduceOp", ally, is_arena),
+ .call_options => return maybeDupe("CallOptions", ally, is_arena),
+ .prefetch_options => return maybeDupe("PrefetchOptions", ally, is_arena),
+ .export_options => return maybeDupe("ExportOptions", ally, is_arena),
+ .extern_options => return maybeDupe("ExternOptions", ally, is_arena),
+ .type_info => return maybeDupe("Type", ally, is_arena),
else => {
// TODO this is wasteful and also an incorrect implementation of `@typeName`
- var buf = std.ArrayList(u8).init(arena);
+ var buf = std.ArrayList(u8).init(ally);
try buf.writer().print("{}", .{ty});
return try buf.toOwnedSliceSentinel(0);
},
}
}
+ fn maybeDupe(s: [:0]const u8, ally: Allocator, is_arena: bool) Allocator.Error![:0]const u8 {
+ if (is_arena) {
+ return s;
+ } else {
+ return try ally.dupeZ(u8, s);
+ }
+ }
+
pub fn toValue(self: Type, allocator: Allocator) Allocator.Error!Value {
switch (self.tag()) {
.u1 => return Value.initTag(.u1_type),
@@ -4683,7 +4703,10 @@ pub const Type = extern union {
const union_obj = ty.cast(Payload.Union).?.data;
return union_obj.owner_decl;
},
- .@"opaque" => @panic("TODO"),
+ .@"opaque" => {
+ const opaque_obj = ty.cast(Payload.Opaque).?.data;
+ return opaque_obj.owner_decl;
+ },
.atomic_order,
.atomic_rmw_op,
.calling_convention,
@@ -4695,7 +4718,8 @@ pub const Type = extern union {
.export_options,
.extern_options,
.type_info,
- => @panic("TODO resolve std.builtin types"),
+ => unreachable, // These need to be resolved earlier.
+
else => unreachable,
}
}
diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
index aa07a7fed1..21e83319d9 100644
--- a/src/zig_llvm.cpp
+++ b/src/zig_llvm.cpp
@@ -942,6 +942,19 @@ LLVMValueRef ZigLLVMInsertDeclareAtEnd(ZigLLVMDIBuilder *dibuilder, LLVMValueRef
return wrap(result);
}
+LLVMValueRef ZigLLVMInsertDbgValueIntrinsicAtEnd(ZigLLVMDIBuilder *dib, LLVMValueRef val,
+ ZigLLVMDILocalVariable *var_info, ZigLLVMDILocation *debug_loc,
+ LLVMBasicBlockRef basic_block_ref)
+{
+ Instruction *result = reinterpret_cast(dib)->insertDbgValueIntrinsic(
+ unwrap(val),
+ reinterpret_cast(var_info),
+ reinterpret_cast(dib)->createExpression(),
+ reinterpret_cast(debug_loc),
+ static_cast(unwrap(basic_block_ref)));
+ return wrap(result);
+}
+
LLVMValueRef ZigLLVMInsertDeclare(ZigLLVMDIBuilder *dibuilder, LLVMValueRef storage,
ZigLLVMDILocalVariable *var_info, ZigLLVMDILocation *debug_loc, LLVMValueRef insert_before_instr)
{
diff --git a/src/zig_llvm.h b/src/zig_llvm.h
index 2b8156d51d..b19ff1f947 100644
--- a/src/zig_llvm.h
+++ b/src/zig_llvm.h
@@ -273,13 +273,20 @@ ZIG_EXTERN_C void ZigLLVMFnSetSubprogram(LLVMValueRef fn, struct ZigLLVMDISubpro
ZIG_EXTERN_C void ZigLLVMDIBuilderFinalize(struct ZigLLVMDIBuilder *dibuilder);
-ZIG_EXTERN_C LLVMValueRef ZigLLVMInsertDeclareAtEnd(struct ZigLLVMDIBuilder *dibuilder, LLVMValueRef storage,
- struct ZigLLVMDILocalVariable *var_info, struct ZigLLVMDILocation *debug_loc,
- LLVMBasicBlockRef basic_block_ref);
+ZIG_EXTERN_C struct ZigLLVMDILocation *ZigLLVMGetDebugLoc(unsigned line, unsigned col,
+ struct ZigLLVMDIScope *scope);
-ZIG_EXTERN_C LLVMValueRef ZigLLVMInsertDeclare(struct ZigLLVMDIBuilder *dibuilder, LLVMValueRef storage,
- struct ZigLLVMDILocalVariable *var_info, struct ZigLLVMDILocation *debug_loc, LLVMValueRef insert_before_instr);
-ZIG_EXTERN_C struct ZigLLVMDILocation *ZigLLVMGetDebugLoc(unsigned line, unsigned col, struct ZigLLVMDIScope *scope);
+ZIG_EXTERN_C LLVMValueRef ZigLLVMInsertDeclareAtEnd(struct ZigLLVMDIBuilder *dib,
+ LLVMValueRef storage, struct ZigLLVMDILocalVariable *var_info,
+ struct ZigLLVMDILocation *debug_loc, LLVMBasicBlockRef basic_block_ref);
+
+ZIG_EXTERN_C LLVMValueRef ZigLLVMInsertDeclare(struct ZigLLVMDIBuilder *dib,
+ LLVMValueRef storage, struct ZigLLVMDILocalVariable *var_info,
+ struct ZigLLVMDILocation *debug_loc, LLVMValueRef insert_before_instr);
+
+ZIG_EXTERN_C LLVMValueRef ZigLLVMInsertDbgValueIntrinsicAtEnd(struct ZigLLVMDIBuilder *dib,
+ LLVMValueRef val, struct ZigLLVMDILocalVariable *var_info,
+ struct ZigLLVMDILocation *debug_loc, LLVMBasicBlockRef basic_block_ref);
ZIG_EXTERN_C void ZigLLVMSetFastMath(LLVMBuilderRef builder_wrapped, bool on_state);
ZIG_EXTERN_C void ZigLLVMSetTailCall(LLVMValueRef Call);