Commit 626c3f7959

antlilja <liljaanton2001@gmail.com>
2024-02-21 16:23:15
LLVM Builder: Emit debug info and metadata
1 parent d35080b
Changed files (1)
src
codegen
src/codegen/llvm/Builder.zig
@@ -14466,19 +14466,449 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
             try constants_block.end();
         }
 
+        const MetadataAdapter = struct {
+            builder: *const Builder,
+            constant_adapter: ConstantAdapter,
+
+            pub fn init(
+                builder: *const Builder,
+                const_adapter: ConstantAdapter,
+            ) @This() {
+                return .{
+                    .builder = builder,
+                    .constant_adapter = const_adapter,
+                };
+            }
+
+            pub fn get(adapter: @This(), value: anytype, comptime field_name: []const u8) @TypeOf(value) {
+                _ = field_name;
+                const Ty = @TypeOf(value);
+                return switch (Ty) {
+                    Metadata => @enumFromInt(adapter.getMetadataIndex(value)),
+                    MetadataString => @enumFromInt(adapter.getMetadataStringIndex(value)),
+                    Constant => @enumFromInt(adapter.constant_adapter.getConstantIndex(value)),
+                    else => value,
+                };
+            }
+
+            pub fn getMetadataIndex(adapter: @This(), metadata: Metadata) u32 {
+                if (metadata == .none) return 0;
+                return @intCast(adapter.builder.metadata_strings.count() +
+                    @intFromEnum(metadata.unwrap(adapter.builder)));
+            }
+
+            pub fn getMetadataStringIndex(_: @This(), metadata_string: MetadataString) u32 {
+                return @intFromEnum(metadata_string) + 1;
+            }
+        };
+
+        const metadata_adapter = MetadataAdapter.init(self, constant_adapter);
+
+        // METADATA_BLOCK
+        if (!self.strip) {
+            const MetadataBlock = IR.MetadataBlock;
+            var metadata_block = try module_block.enterSubBlock(MetadataBlock);
+
+            const MetadataBlockWriter = @TypeOf(metadata_block);
+
+            // Emit all MetadataStrings
+            {
+                const strings_offset, const strings_size = blk: {
+                    var strings_offset: u32 = 0;
+                    var strings_size: u32 = 0;
+                    for (self.metadata_strings.keys()) |metadata_string| {
+                        if (metadata_string.slice(self)) |slice| {
+                            strings_offset += bitcode.bitsVBR(@as(u32, @intCast(slice.len)), 6);
+                            strings_size += @intCast(slice.len * 8);
+                        }
+                    }
+                    break :blk .{
+                        std.mem.alignForward(u32, strings_offset, 32) / 8,
+                        std.mem.alignForward(u32, strings_size, 32) / 8,
+                    };
+                };
+
+                try bitcode.writeBits(
+                    comptime MetadataBlockWriter.abbrevId(MetadataBlock.Strings),
+                    MetadataBlockWriter.abbrev_len,
+                );
+
+                try bitcode.writeVBR(@as(u32, @intCast(self.metadata_strings.count())), 6);
+                try bitcode.writeVBR(strings_offset, 6);
+
+                try bitcode.writeVBR(strings_size + strings_offset, 6);
+
+                try bitcode.alignTo32();
+
+                for (self.metadata_strings.keys()) |metadata_string| {
+                    if (metadata_string.slice(self)) |slice|
+                        try bitcode.writeVBR(@as(u32, @intCast(slice.len)), 6);
+                }
+
+                try bitcode.alignTo32();
+
+                for (self.metadata_strings.keys()) |metadata_string| {
+                    if (metadata_string.slice(self)) |slice| {
+                        for (slice) |c| {
+                            try bitcode.writeBits(c, 8);
+                        }
+                    }
+                }
+
+                try bitcode.alignTo32();
+            }
+
+            for (1..self.metadata_items.len) |metadata_index| {
+                const tag = self.metadata_items.items(.tag)[metadata_index];
+                const data = self.metadata_items.items(.data)[metadata_index];
+                switch (tag) {
+                    .none => unreachable,
+                    .file => {
+                        const extra = self.metadataExtraData(Metadata.File, data);
+
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.File{
+                            .name = extra.name,
+                            .path = extra.path,
+                        }, metadata_adapter);
+                    },
+                    .compile_unit, .@"compile_unit optimized" => |kind| {
+                        const is_optimized = kind == .@"compile_unit optimized";
+                        const extra = self.metadataExtraData(Metadata.CompileUnit, data);
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.CompileUnit{
+                            .file = extra.file,
+                            .producer = extra.producer,
+                            .is_optimized = is_optimized,
+                            .enums = extra.enums,
+                            .globals = extra.globals,
+                        }, metadata_adapter);
+                    },
+                    .subprogram,
+                    .@"subprogram optimized",
+                    .@"subprogram local",
+                    .@"subprogram definition",
+                    .@"subprogram optimized local",
+                    .@"subprogram optimized definition",
+                    .@"subprogram optimized local definition",
+                    .@"subprogram local definition",
+                    => |kind| {
+                        const sp_flags: u32 = switch (kind) {
+                            .subprogram => 0,
+                            .@"subprogram optimized" => 1 << 4,
+                            .@"subprogram local" => 1 << 2,
+                            .@"subprogram definition" => 1 << 3,
+                            .@"subprogram optimized local" => (1 << 4) | (1 << 2),
+                            .@"subprogram optimized definition" => (1 << 4) | (1 << 3),
+                            .@"subprogram optimized local definition" => (1 << 4) | (1 << 2) | (1 << 3),
+                            .@"subprogram local definition" => (1 << 2) | (1 << 3),
+                            else => unreachable,
+                        };
+                        const extra = self.metadataExtraData(Metadata.Subprogram, data);
+
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.Subprogram{
+                            .scope = extra.file,
+                            .name = extra.name,
+                            .linkage_name = extra.linkage_name,
+                            .file = extra.file,
+                            .line = extra.line,
+                            .ty = Metadata.none, //extra.ty,
+                            .scope_line = extra.scope_line,
+                            .sp_flags = sp_flags,
+                            .flags = extra.debug_info_flags,
+                            .compile_unit = extra.compile_unit,
+                        }, metadata_adapter);
+                    },
+                    .lexical_block => {
+                        const extra = self.metadataExtraData(Metadata.LexicalBlock, data);
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.LexicalBlock{
+                            .scope = extra.scope,
+                            .file = extra.file,
+                            .line = extra.line,
+                            .column = extra.column,
+                        }, metadata_adapter);
+                    },
+                    .location => {
+                        const extra = self.metadataExtraData(Metadata.Location, data);
+                        std.debug.assert(extra.scope != Metadata.none);
+                        try metadata_block.writeAbbrev(MetadataBlock.Location{
+                            .line = extra.line,
+                            .column = extra.column,
+                            .scope = metadata_adapter.getMetadataIndex(extra.scope) - 1,
+                            .inlined_at = @enumFromInt(metadata_adapter.getMetadataIndex(extra.inlined_at)),
+                        });
+                    },
+                    .basic_bool_type,
+                    .basic_unsigned_type,
+                    .basic_signed_type,
+                    .basic_float_type,
+                    => |kind| {
+                        const extra = self.metadataExtraData(Metadata.BasicType, data);
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.BasicType{
+                            .name = extra.name,
+                            .size_in_bits = @as(u64, extra.size_in_bits_lo) | @as(u64, extra.size_in_bits_hi) << 32,
+                            .encoding = switch (kind) {
+                                .basic_bool_type => std.dwarf.ATE.boolean,
+                                .basic_unsigned_type => std.dwarf.ATE.unsigned,
+                                .basic_signed_type => std.dwarf.ATE.signed,
+                                .basic_float_type => std.dwarf.ATE.float,
+                                else => unreachable,
+                            },
+                        }, metadata_adapter);
+                    },
+                    .composite_struct_type,
+                    .composite_union_type,
+                    .composite_enumeration_type,
+                    .composite_array_type,
+                    => |kind| {
+                        const extra = self.metadataExtraData(Metadata.CompositeType, data);
+
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.CompositeType{
+                            .tag = switch (kind) {
+                                .composite_struct_type => std.dwarf.TAG.structure_type,
+                                .composite_union_type => std.dwarf.TAG.union_type,
+                                .composite_enumeration_type => std.dwarf.TAG.enumeration_type,
+                                .composite_array_type => std.dwarf.TAG.array_type,
+                                else => unreachable,
+                            },
+                            .name = extra.name,
+                            .file = extra.file,
+                            .line = extra.line,
+                            .scope = extra.scope,
+                            .underlying_type = extra.underlying_type,
+                            .size_in_bits = @as(u64, extra.size_in_bits_lo) | @as(u64, extra.size_in_bits_hi) << 32,
+                            .align_in_bits = @as(u64, extra.align_in_bits_lo) | @as(u64, extra.align_in_bits_hi) << 32,
+                            .elements = extra.fields_tuple,
+                        }, metadata_adapter);
+                    },
+                    .derived_pointer_type,
+                    .derived_member_type,
+                    => |kind| {
+                        const extra = self.metadataExtraData(Metadata.DerivedType, data);
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.DerivedType{
+                            .tag = switch (kind) {
+                                .derived_pointer_type => std.dwarf.TAG.pointer_type,
+                                .derived_member_type => std.dwarf.TAG.member,
+                                else => unreachable,
+                            },
+                            .name = extra.name,
+                            .file = extra.file,
+                            .line = extra.line,
+                            .scope = extra.scope,
+                            .underlying_type = extra.underlying_type,
+                            .size_in_bits = @as(u64, extra.size_in_bits_lo) | @as(u64, extra.size_in_bits_hi) << 32,
+                            .align_in_bits = @as(u64, extra.align_in_bits_lo) | @as(u64, extra.align_in_bits_hi) << 32,
+                            .offset_in_bits = @as(u64, extra.offset_in_bits_lo) | @as(u64, extra.offset_in_bits_hi) << 32,
+                        }, metadata_adapter);
+                    },
+                    .subroutine_type => {
+                        const extra = self.metadataExtraData(Metadata.SubroutineType, data);
+
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.SubroutineType{
+                            .types = extra.types_tuple,
+                        }, metadata_adapter);
+                    },
+                    .enumerator_unsigned,
+                    .enumerator_signed_positive,
+                    .enumerator_signed_negative,
+                    => |kind| {
+                        const positive = switch (kind) {
+                            .enumerator_unsigned,
+                            .enumerator_signed_positive,
+                            => true,
+                            .enumerator_signed_negative => false,
+                            else => unreachable,
+                        };
+
+                        const unsigned = switch (kind) {
+                            .enumerator_unsigned => true,
+                            .enumerator_signed_positive,
+                            .enumerator_signed_negative,
+                            => false,
+                            else => unreachable,
+                        };
+
+                        const extra = self.metadataExtraData(Metadata.Enumerator, data);
+
+                        const limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len];
+
+                        const bigint = std.math.big.int.Const{
+                            .limbs = limbs,
+                            .positive = positive,
+                        };
+
+                        if (extra.bit_width <= 64) {
+                            const val = bigint.to(i64) catch unreachable;
+                            const emit_val = if (positive)
+                                @shlWithOverflow(val, 1)[0]
+                            else
+                                (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);
+                            try metadata_block.writeAbbrevAdapted(MetadataBlock.Enumerator{
+                                .flags = .{
+                                    .unsigned = unsigned,
+                                    .bigint = false,
+                                },
+                                .bit_width = extra.bit_width,
+                                .name = extra.name,
+                                .value = @bitCast(emit_val),
+                            }, metadata_adapter);
+                        } else {
+                            const word_count = std.mem.alignForward(u32, extra.bit_width, 64) / 64;
+                            try record.ensureUnusedCapacity(self.gpa, 3 + word_count);
+
+                            const flags = MetadataBlock.Enumerator.Flags{
+                                .unsigned = unsigned,
+                                .bigint = true,
+                            };
+
+                            const FlagsInt = @typeInfo(MetadataBlock.Enumerator.Flags).Struct.backing_integer.?;
+
+                            const flags_int: FlagsInt = @bitCast(flags);
+
+                            record.appendAssumeCapacity(@intCast(flags_int));
+                            record.appendAssumeCapacity(@intCast(extra.bit_width));
+                            record.appendAssumeCapacity(metadata_adapter.getMetadataStringIndex(extra.name));
+
+                            const buffer: [*]u8 = @ptrCast(record.items.ptr);
+                            bigint.writeTwosComplement(buffer[0..(word_count * 8)], .little);
+
+                            const signed_buffer: [*]i64 = @ptrCast(record.items.ptr);
+                            for (signed_buffer[0..word_count], 0..) |val, i| {
+                                signed_buffer[i] = if (val >= 0)
+                                    @shlWithOverflow(val, 1)[0]
+                                else
+                                    (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1);
+                            }
+
+                            try metadata_block.writeUnabbrev(
+                                MetadataBlock.Enumerator.id,
+                                record.items.ptr[0..(3 + word_count)],
+                            );
+                        }
+                    },
+                    .subrange => {
+                        const extra = self.metadataExtraData(Metadata.Subrange, data);
+
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.Subrange{
+                            .count = extra.count,
+                            .lower_bound = extra.lower_bound,
+                        }, metadata_adapter);
+                    },
+                    .expression => {
+                        var extra = self.metadataExtraDataTrail(Metadata.Expression, data);
+
+                        const elements = extra.trail.next(extra.data.elements_len, u32, self);
+
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.Expression{
+                            .elements = elements,
+                        }, metadata_adapter);
+                    },
+                    .tuple => {
+                        var extra = self.metadataExtraDataTrail(Metadata.Tuple, data);
+
+                        const elements = extra.trail.next(extra.data.elements_len, Metadata, self);
+
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.Node{
+                            .elements = elements,
+                        }, metadata_adapter);
+                    },
+                    .module_flag => {
+                        const extra = self.metadataExtraData(Metadata.ModuleFlag, data);
+                        try metadata_block.writeAbbrev(MetadataBlock.Node{
+                            .elements = &.{
+                                @enumFromInt(metadata_adapter.getMetadataIndex(extra.behaviour)),
+                                @enumFromInt(metadata_adapter.getMetadataStringIndex(extra.name)),
+                                @enumFromInt(metadata_adapter.getMetadataIndex(extra.constant)),
+                            },
+                        });
+                    },
+                    .local_var => {
+                        const extra = self.metadataExtraData(Metadata.LocalVar, data);
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.LocalVar{
+                            .scope = extra.scope,
+                            .name = extra.name,
+                            .file = extra.file,
+                            .line = extra.line,
+                            .ty = extra.ty,
+                        }, metadata_adapter);
+                    },
+                    .parameter => {
+                        const extra = self.metadataExtraData(Metadata.Parameter, data);
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.Parameter{
+                            .scope = extra.scope,
+                            .name = extra.name,
+                            .file = extra.file,
+                            .line = extra.line,
+                            .ty = extra.ty,
+                            .arg = extra.arg_no,
+                        }, metadata_adapter);
+                    },
+                    .global_var,
+                    .@"global_var local",
+                    => |kind| {
+                        const extra = self.metadataExtraData(Metadata.GlobalVar, data);
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.GlobalVar{
+                            .scope = extra.scope,
+                            .name = extra.name,
+                            .linkage_name = extra.linkage_name,
+                            .file = extra.file,
+                            .line = extra.line,
+                            .ty = extra.ty,
+                            .local = kind == .@"global_var local",
+                        }, metadata_adapter);
+                    },
+                    .global_var_expression => {
+                        const extra = self.metadataExtraData(Metadata.GlobalVarExpression, data);
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.GlobalVarExpression{
+                            .variable = extra.variable,
+                            .expression = extra.expression,
+                        }, metadata_adapter);
+                    },
+                    .constant => {
+                        const constant: Constant = @enumFromInt(data);
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.Constant{
+                            .ty = constant.typeOf(self),
+                            .constant = constant,
+                        }, metadata_adapter);
+                    },
+                }
+                record.clearRetainingCapacity();
+            }
+
+            // Write named metadata
+            for (self.metadata_named.keys(), self.metadata_named.values()) |name, operands| {
+                const slice = name.slice(self).?;
+                try metadata_block.writeAbbrev(MetadataBlock.Name{
+                    .name = slice,
+                });
+
+                const elements = self.metadata_extra.items[operands.index..][0..operands.len];
+                for (elements) |*e| {
+                    e.* = metadata_adapter.getMetadataIndex(@enumFromInt(e.*)) - 1;
+                }
+
+                try metadata_block.writeAbbrev(MetadataBlock.NamedNode{
+                    .elements = @ptrCast(elements),
+                });
+            }
+
+            try metadata_block.end();
+        }
+
         // FUNCTION_BLOCKS
         {
             const FunctionAdapter = struct {
                 constant_adapter: ConstantAdapter,
+                metadata_adapter: MetadataAdapter,
                 func: *const Function,
                 instruction_index: u32 = 0,
 
                 pub fn init(
                     const_adapter: ConstantAdapter,
+                    meta_adapter: MetadataAdapter,
                     func: *const Function,
                 ) @This() {
                     return .{
                         .constant_adapter = const_adapter,
+                        .metadata_adapter = meta_adapter,
                         .func = func,
                         .instruction_index = 0,
                     };
@@ -14499,12 +14929,21 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
                     return @intCast(switch (value.unwrap()) {
                         .instruction => |instruction| instruction.valueIndex(adapter.func) + adapter.firstInstr(),
                         .constant => |constant| adapter.constant_adapter.getConstantIndex(constant),
-                        .metadata => unreachable,
+                        .metadata => |metadata| if (!adapter.metadata_adapter.builder.strip) blk: {
+                            const real_metadata = metadata.unwrap(adapter.metadata_adapter.builder);
+                            if (@intFromEnum(real_metadata) < Metadata.first_local_metadata)
+                                break :blk adapter.metadata_adapter.getMetadataIndex(real_metadata) - 1;
+
+                            return @intCast(@intFromEnum(metadata) -
+                                Metadata.first_local_metadata +
+                                adapter.metadata_adapter.builder.metadata_strings.count() +
+                                adapter.metadata_adapter.builder.metadata_map.count() - 1);
+                        } else unreachable,
                     });
                 }
 
                 pub fn getOffsetValueIndex(adapter: @This(), value: Value) u32 {
-                    return adapter.offset() - adapter.getValueIndex(value);
+                    return @subWithOverflow(adapter.offset(), adapter.getValueIndex(value))[0];
                 }
 
                 pub fn getOffsetValueSignedIndex(adapter: @This(), value: Value) i32 {
@@ -14543,11 +14982,28 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
 
                 try function_block.writeAbbrev(FunctionBlock.DeclareBlocks{ .num_blocks = func.blocks.len });
 
-                var adapter = FunctionAdapter.init(constant_adapter, &func);
+                var adapter = FunctionAdapter.init(constant_adapter, metadata_adapter, &func);
+
+                // Emit function level metadata block
+                if (!self.strip and func.debug_values.len != 0) {
+                    const MetadataBlock = IR.FunctionMetadataBlock;
+                    var metadata_block = try function_block.enterSubBlock(MetadataBlock);
+
+                    for (func.debug_values) |value| {
+                        try metadata_block.writeAbbrev(MetadataBlock.Value{
+                            .ty = value.typeOf(@enumFromInt(func_index), self),
+                            .value = @enumFromInt(adapter.getValueIndex(value.toValue())),
+                        });
+                    }
+
+                    try metadata_block.end();
+                }
 
                 const tags = func.instructions.items(.tag);
                 const datas = func.instructions.items(.data);
 
+                var has_location = false;
+
                 var block_incoming_len: u32 = undefined;
                 for (0..func.instructions.len) |instr_index| {
                     const tag = tags[instr_index];
@@ -14990,6 +15446,25 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
                         },
                     }
 
+                    if (!self.strip) {
+                        if (func.debug_locations.get(@enumFromInt(instr_index))) |maybe_location| {
+                            if (maybe_location) |location| {
+                                try function_block.writeAbbrev(FunctionBlock.DebugLoc{
+                                    .line = location.line,
+                                    .column = location.column,
+                                    .scope = @enumFromInt(metadata_adapter.getMetadataIndex(location.scope)),
+                                    .inlined_at = @enumFromInt(metadata_adapter.getMetadataIndex(location.inlined_at)),
+                                    .is_implicit = false,
+                                });
+                                has_location = true;
+                            } else {
+                                has_location = false;
+                            }
+                        } else if (has_location) {
+                            try function_block.writeAbbrev(FunctionBlock.DebugLocAgain{});
+                        }
+                    }
+
                     adapter.next();
                 }