Commit d1ed1c167b

kcbanner <kcbanner@gmail.com>
2023-07-24 16:43:14
simplify call frame instruction parser
1 parent 20ce745
Changed files (1)
lib
std
lib/std/dwarf/call_frame.zig
@@ -50,189 +50,246 @@ const Opcode = enum(u8) {
     pub const hi_user = 0x3f;
 };
 
-const Operand = enum {
-    opcode_delta,
-    opcode_register,
-    uleb128_register,
-    uleb128_offset,
-    sleb128_offset,
-    address,
-    u8_delta,
-    u16_delta,
-    u32_delta,
-    block,
-
-    fn Storage(comptime self: Operand) type {
-        return switch (self) {
-            .opcode_delta, .opcode_register => u8,
-            .uleb128_register => u8,
-            .uleb128_offset => u64,
-            .sleb128_offset => i64,
-            .address => u64,
-            .u8_delta => u8,
-            .u16_delta => u16,
-            .u32_delta => u32,
-            .block => []const u8,
-        };
-    }
-
-    fn read(
-        comptime self: Operand,
-        stream: *std.io.FixedBufferStream([]const u8),
-        opcode_value: ?u6,
-        addr_size_bytes: u8,
-        endian: std.builtin.Endian,
-    ) !Storage(self) {
-        const reader = stream.reader();
-        return switch (self) {
-            .opcode_delta, .opcode_register => opcode_value orelse return error.InvalidOperand,
-            .uleb128_register => try leb.readULEB128(u8, reader),
-            .uleb128_offset => try leb.readULEB128(u64, reader),
-            .sleb128_offset => try leb.readILEB128(i64, reader),
-            .address => switch (addr_size_bytes) {
-                2 => try reader.readInt(u16, endian),
-                4 => try reader.readInt(u32, endian),
-                8 => try reader.readInt(u64, endian),
-                else => return error.InvalidAddrSize,
-            },
-            .u8_delta => try reader.readByte(),
-            .u16_delta => try reader.readInt(u16, endian),
-            .u32_delta => try reader.readInt(u32, endian),
-            .block => {
-                const block_len = try leb.readULEB128(usize, reader);
-                if (stream.pos + block_len > stream.buffer.len) return error.InvalidOperand;
-
-                const block = stream.buffer[stream.pos..][0..block_len];
-                reader.context.pos += block_len;
+fn readBlock(stream: *std.io.FixedBufferStream([]const u8)) ![]const u8 {
+    const reader = stream.reader();
+    const block_len = try leb.readULEB128(usize, reader);
+    if (stream.pos + block_len > stream.buffer.len) return error.InvalidOperand;
 
-                return block;
-            },
-        };
-    }
-};
-
-fn InstructionType(comptime definition: anytype) type {
-    const definition_type = @typeInfo(@TypeOf(definition));
-    assert(definition_type == .Struct);
-
-    const definition_len = definition_type.Struct.fields.len;
-    comptime var fields: [definition_len]std.builtin.Type.StructField = undefined;
-    inline for (definition_type.Struct.fields, &fields) |definition_field, *operands_field| {
-        const opcode = std.enums.nameCast(Operand, @field(definition, definition_field.name));
-        const storage_type = opcode.Storage();
-        operands_field.* = .{
-            .name = definition_field.name,
-            .type = storage_type,
-            .default_value = null,
-            .is_comptime = false,
-            .alignment = @alignOf(storage_type),
-        };
-    }
-
-    const InstructionOperands = @Type(.{
-        .Struct = .{
-            .layout = .Auto,
-            .fields = &fields,
-            .decls = &.{},
-            .is_tuple = false,
-        },
-    });
-
-    return struct {
-        const Self = @This();
-        operands: InstructionOperands,
-
-        pub fn read(
-            stream: *std.io.FixedBufferStream([]const u8),
-            opcode_value: ?u6,
-            addr_size_bytes: u8,
-            endian: std.builtin.Endian,
-        ) !Self {
-            var operands: InstructionOperands = undefined;
-            inline for (definition_type.Struct.fields) |definition_field| {
-                const operand = comptime std.enums.nameCast(Operand, @field(definition, definition_field.name));
-                @field(operands, definition_field.name) = try operand.read(stream, opcode_value, addr_size_bytes, endian);
-            }
+    const block = stream.buffer[stream.pos..][0..block_len];
+    reader.context.pos += block_len;
 
-            return .{ .operands = operands };
-        }
-    };
+    return block;
 }
 
 pub const Instruction = union(Opcode) {
-    advance_loc: InstructionType(.{ .delta = .opcode_delta }),
-    offset: InstructionType(.{ .register = .opcode_register, .offset = .uleb128_offset }),
-    offset_extended: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }),
-    restore: InstructionType(.{ .register = .opcode_register }),
-    restore_extended: InstructionType(.{ .register = .uleb128_register }),
-    nop: InstructionType(.{}),
-    set_loc: InstructionType(.{ .address = .address }),
-    advance_loc1: InstructionType(.{ .delta = .u8_delta }),
-    advance_loc2: InstructionType(.{ .delta = .u16_delta }),
-    advance_loc4: InstructionType(.{ .delta = .u32_delta }),
-    undefined: InstructionType(.{ .register = .uleb128_register }),
-    same_value: InstructionType(.{ .register = .uleb128_register }),
-    register: InstructionType(.{ .register = .uleb128_register, .target_register = .uleb128_register }),
-    remember_state: InstructionType(.{}),
-    restore_state: InstructionType(.{}),
-    def_cfa: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }),
-    def_cfa_register: InstructionType(.{ .register = .uleb128_register }),
-    def_cfa_offset: InstructionType(.{ .offset = .uleb128_offset }),
-    def_cfa_expression: InstructionType(.{ .block = .block }),
-    expression: InstructionType(.{ .register = .uleb128_register, .block = .block }),
-    offset_extended_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }),
-    def_cfa_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }),
-    def_cfa_offset_sf: InstructionType(.{ .offset = .sleb128_offset }),
-    val_offset: InstructionType(.{ .register = .uleb128_register, .offset = .uleb128_offset }),
-    val_offset_sf: InstructionType(.{ .register = .uleb128_register, .offset = .sleb128_offset }),
-    val_expression: InstructionType(.{ .register = .uleb128_register, .block = .block }),
-
-    fn readOperands(
-        self: *Instruction,
-        stream: *std.io.FixedBufferStream([]const u8),
-        opcode_value: ?u6,
-        addr_size_bytes: u8,
-        endian: std.builtin.Endian,
-    ) !void {
-        switch (self.*) {
-            inline else => |*inst| inst.* = try @TypeOf(inst.*).read(stream, opcode_value, addr_size_bytes, endian),
-        }
-    }
+    advance_loc: struct {
+        delta: u8,
+    },
+    offset: struct {
+        register: u8,
+        offset: u64,
+    },
+    offset_extended: struct {
+        register: u8,
+        offset: u64,
+    },
+    restore: struct {
+        register: u8,
+    },
+    restore_extended: struct {
+        register: u8,
+    },
+    nop: void,
+    set_loc: struct {
+        address: u64,
+    },
+    advance_loc1: struct {
+        delta: u8,
+    },
+    advance_loc2: struct {
+        delta: u16,
+    },
+    advance_loc4: struct {
+        delta: u32,
+    },
+    undefined: struct {
+        register: u8,
+    },
+    same_value: struct {
+        register: u8,
+    },
+    register: struct {
+        register: u8,
+        target_register: u8,
+    },
+    remember_state: void,
+    restore_state: void,
+    def_cfa: struct {
+        register: u8,
+        offset: u64,
+    },
+    def_cfa_register: struct {
+        register: u8,
+    },
+    def_cfa_offset: struct {
+        offset: u64,
+    },
+    def_cfa_expression: struct {
+        block: []const u8,
+    },
+    expression: struct {
+        register: u8,
+        block: []const u8,
+    },
+    offset_extended_sf: struct {
+        register: u8,
+        offset: i64,
+    },
+    def_cfa_sf: struct {
+        register: u8,
+        offset: i64,
+    },
+    def_cfa_offset_sf: struct {
+        offset: i64,
+    },
+    val_offset: struct {
+        register: u8,
+        offset: u64,
+    },
+    val_offset_sf: struct {
+        register: u8,
+        offset: i64,
+    },
+    val_expression: struct {
+        register: u8,
+        block: []const u8,
+    },
 
     pub fn read(
         stream: *std.io.FixedBufferStream([]const u8),
         addr_size_bytes: u8,
         endian: std.builtin.Endian,
-
     ) !Instruction {
-        switch (try stream.reader().readByte()) {
+        const reader = stream.reader();
+        switch (try reader.readByte()) {
             Opcode.lo_inline...Opcode.hi_inline => |opcode| {
                 const e: Opcode = @enumFromInt(opcode & 0b11000000);
-                switch (e) {
-                    inline .advance_loc,
-                    .offset,
-                    .restore,
-                    => |tag| {
-                        var result = @unionInit(Instruction, @tagName(tag), undefined);
-                        try result.readOperands(stream, @as(u6, @intCast(opcode & 0b111111)), addr_size_bytes, endian);
-                        return result;
+                const value: u6 = @intCast(opcode & 0b111111);
+                return switch (e) {
+                    .advance_loc => .{
+                        .advance_loc = .{ .delta = value },
+                    },
+                    .offset => .{
+                        .offset = .{
+                            .register = value,
+                            .offset = try leb.readULEB128(u64, reader),
+                        },
+                    },
+                    .restore => .{
+                        .restore = .{ .register = value },
                     },
                     else => unreachable,
-                }
+                };
             },
             Opcode.lo_reserved...Opcode.hi_reserved => |opcode| {
                 const e: Opcode = @enumFromInt(opcode);
-                switch (e) {
+                return switch (e) {
                     .advance_loc,
                     .offset,
                     .restore,
                     => unreachable,
-                    inline else => |tag| {
-                        var result = @unionInit(Instruction, @tagName(tag), undefined);
-                        try result.readOperands(stream, null, addr_size_bytes, endian);
-                        return result;
+                    .nop => .{ .nop = {} },
+                    .set_loc => .{
+                        .set_loc = .{
+                            .address = switch (addr_size_bytes) {
+                                2 => try reader.readInt(u16, endian),
+                                4 => try reader.readInt(u32, endian),
+                                8 => try reader.readInt(u64, endian),
+                                else => return error.InvalidAddrSize,
+                            },
+                        },
+                    },
+                    .advance_loc1 => .{
+                        .advance_loc1 = .{ .delta = try reader.readByte() },
+                    },
+                    .advance_loc2 => .{
+                        .advance_loc2 = .{ .delta = try reader.readInt(u16, endian) },
+                    },
+                    .advance_loc4 => .{
+                        .advance_loc4 = .{ .delta = try reader.readInt(u32, endian) },
                     },
-                }
+                    .offset_extended => .{
+                        .offset_extended = .{
+                            .register = try leb.readULEB128(u8, reader),
+                            .offset = try leb.readULEB128(u64, reader),
+                        },
+                    },
+                    .restore_extended => .{
+                        .restore_extended = .{
+                            .register = try leb.readULEB128(u8, reader),
+                        },
+                    },
+                    .undefined => .{
+                        .undefined = .{
+                            .register = try leb.readULEB128(u8, reader),
+                        },
+                    },
+                    .same_value => .{
+                        .same_value = .{
+                            .register = try leb.readULEB128(u8, reader),
+                        },
+                    },
+                    .register => .{
+                        .register = .{
+                            .register = try leb.readULEB128(u8, reader),
+                            .target_register = try leb.readULEB128(u8, reader),
+                        },
+                    },
+                    .remember_state => .{ .remember_state = {} },
+                    .restore_state => .{ .restore_state = {} },
+                    .def_cfa => .{
+                        .def_cfa = .{
+                            .register = try leb.readULEB128(u8, reader),
+                            .offset = try leb.readULEB128(u64, reader),
+                        },
+                    },
+                    .def_cfa_register => .{
+                        .def_cfa_register = .{
+                            .register = try leb.readULEB128(u8, reader),
+                        },
+                    },
+                    .def_cfa_offset => .{
+                        .def_cfa_offset = .{
+                            .offset = try leb.readULEB128(u64, reader),
+                        },
+                    },
+                    .def_cfa_expression => .{
+                        .def_cfa_expression = .{
+                            .block = try readBlock(stream),
+                        },
+                    },
+                    .expression => .{
+                        .expression = .{
+                            .register = try leb.readULEB128(u8, reader),
+                            .block = try readBlock(stream),
+                        },
+                    },
+                    .offset_extended_sf => .{
+                        .offset_extended_sf = .{
+                            .register = try leb.readULEB128(u8, reader),
+                            .offset = try leb.readILEB128(i64, reader),
+                        },
+                    },
+                    .def_cfa_sf => .{
+                        .def_cfa_sf = .{
+                            .register = try leb.readULEB128(u8, reader),
+                            .offset = try leb.readILEB128(i64, reader),
+                        },
+                    },
+                    .def_cfa_offset_sf => .{
+                        .def_cfa_offset_sf = .{
+                            .offset = try leb.readILEB128(i64, reader),
+                        },
+                    },
+                    .val_offset => .{
+                        .val_offset = .{
+                            .register = try leb.readULEB128(u8, reader),
+                            .offset = try leb.readULEB128(u64, reader),
+                        },
+                    },
+                    .val_offset_sf => .{
+                        .val_offset_sf = .{
+                            .register = try leb.readULEB128(u8, reader),
+                            .offset = try leb.readILEB128(i64, reader),
+                        },
+                    },
+                    .val_expression => .{
+                        .val_expression = .{
+                            .register = try leb.readULEB128(u8, reader),
+                            .block = try readBlock(stream),
+                        },
+                    },
+                };
             },
             Opcode.lo_user...Opcode.hi_user => return error.UnimplementedUserOpcode,
             else => return error.InvalidOpcode,
@@ -492,16 +549,16 @@ pub const VirtualMachine = struct {
         const prev_row = self.current_row;
         switch (instruction) {
             .set_loc => |i| {
-                if (i.operands.address <= self.current_row.offset) return error.InvalidOperation;
+                if (i.address <= self.current_row.offset) return error.InvalidOperation;
                 // TODO: Check cie.segment_selector_size != 0 for DWARFV4
-                self.current_row.offset = i.operands.address;
+                self.current_row.offset = i.address;
             },
             inline .advance_loc,
             .advance_loc1,
             .advance_loc2,
             .advance_loc4,
             => |i| {
-                self.current_row.offset += i.operands.delta * cie.code_alignment_factor;
+                self.current_row.offset += i.delta * cie.code_alignment_factor;
                 self.current_row.copy_on_write = true;
             },
             inline .offset,
@@ -509,35 +566,35 @@ pub const VirtualMachine = struct {
             .offset_extended_sf,
             => |i| {
                 try self.resolveCopyOnWrite(allocator);
-                const column = try self.getOrAddColumn(allocator, i.operands.register);
-                column.rule = .{ .offset = @as(i64, @intCast(i.operands.offset)) * cie.data_alignment_factor };
+                const column = try self.getOrAddColumn(allocator, i.register);
+                column.rule = .{ .offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor };
             },
             inline .restore,
             .restore_extended,
             => |i| {
                 try self.resolveCopyOnWrite(allocator);
                 if (self.cie_row) |cie_row| {
-                    const column = try self.getOrAddColumn(allocator, i.operands.register);
+                    const column = try self.getOrAddColumn(allocator, i.register);
                     column.rule = for (self.rowColumns(cie_row)) |cie_column| {
-                        if (cie_column.register == i.operands.register) break cie_column.rule;
+                        if (cie_column.register == i.register) break cie_column.rule;
                     } else .{ .default = {} };
                 } else return error.InvalidOperation;
             },
             .nop => {},
             .undefined => |i| {
                 try self.resolveCopyOnWrite(allocator);
-                const column = try self.getOrAddColumn(allocator, i.operands.register);
+                const column = try self.getOrAddColumn(allocator, i.register);
                 column.rule = .{ .undefined = {} };
             },
             .same_value => |i| {
                 try self.resolveCopyOnWrite(allocator);
-                const column = try self.getOrAddColumn(allocator, i.operands.register);
+                const column = try self.getOrAddColumn(allocator, i.register);
                 column.rule = .{ .same_value = {} };
             },
             .register => |i| {
                 try self.resolveCopyOnWrite(allocator);
-                const column = try self.getOrAddColumn(allocator, i.operands.register);
-                column.rule = .{ .register = i.operands.target_register };
+                const column = try self.getOrAddColumn(allocator, i.register);
+                column.rule = .{ .register = i.target_register };
             },
             .remember_state => {
                 try self.stack.append(allocator, self.current_row.columns);
@@ -555,69 +612,69 @@ pub const VirtualMachine = struct {
             .def_cfa => |i| {
                 try self.resolveCopyOnWrite(allocator);
                 self.current_row.cfa = .{
-                    .register = i.operands.register,
-                    .rule = .{ .val_offset = @intCast(i.operands.offset) },
+                    .register = i.register,
+                    .rule = .{ .val_offset = @intCast(i.offset) },
                 };
             },
             .def_cfa_sf => |i| {
                 try self.resolveCopyOnWrite(allocator);
                 self.current_row.cfa = .{
-                    .register = i.operands.register,
-                    .rule = .{ .val_offset = i.operands.offset * cie.data_alignment_factor },
+                    .register = i.register,
+                    .rule = .{ .val_offset = i.offset * cie.data_alignment_factor },
                 };
             },
             .def_cfa_register => |i| {
                 try self.resolveCopyOnWrite(allocator);
                 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
-                self.current_row.cfa.register = i.operands.register;
+                self.current_row.cfa.register = i.register;
             },
             .def_cfa_offset => |i| {
                 try self.resolveCopyOnWrite(allocator);
                 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
                 self.current_row.cfa.rule = .{
-                    .val_offset = @intCast(i.operands.offset),
+                    .val_offset = @intCast(i.offset),
                 };
             },
             .def_cfa_offset_sf => |i| {
                 try self.resolveCopyOnWrite(allocator);
                 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
                 self.current_row.cfa.rule = .{
-                    .val_offset = i.operands.offset * cie.data_alignment_factor,
+                    .val_offset = i.offset * cie.data_alignment_factor,
                 };
             },
             .def_cfa_expression => |i| {
                 try self.resolveCopyOnWrite(allocator);
                 self.current_row.cfa.register = undefined;
                 self.current_row.cfa.rule = .{
-                    .expression = i.operands.block,
+                    .expression = i.block,
                 };
             },
             .expression => |i| {
                 try self.resolveCopyOnWrite(allocator);
-                const column = try self.getOrAddColumn(allocator, i.operands.register);
+                const column = try self.getOrAddColumn(allocator, i.register);
                 column.rule = .{
-                    .expression = i.operands.block,
+                    .expression = i.block,
                 };
             },
             .val_offset => |i| {
                 try self.resolveCopyOnWrite(allocator);
-                const column = try self.getOrAddColumn(allocator, i.operands.register);
+                const column = try self.getOrAddColumn(allocator, i.register);
                 column.rule = .{
-                    .val_offset = @as(i64, @intCast(i.operands.offset)) * cie.data_alignment_factor,
+                    .val_offset = @as(i64, @intCast(i.offset)) * cie.data_alignment_factor,
                 };
             },
             .val_offset_sf => |i| {
                 try self.resolveCopyOnWrite(allocator);
-                const column = try self.getOrAddColumn(allocator, i.operands.register);
+                const column = try self.getOrAddColumn(allocator, i.register);
                 column.rule = .{
-                    .val_offset = i.operands.offset * cie.data_alignment_factor,
+                    .val_offset = i.offset * cie.data_alignment_factor,
                 };
             },
             .val_expression => |i| {
                 try self.resolveCopyOnWrite(allocator);
-                const column = try self.getOrAddColumn(allocator, i.operands.register);
+                const column = try self.getOrAddColumn(allocator, i.register);
                 column.rule = .{
-                    .val_expression = i.operands.block,
+                    .val_expression = i.block,
                 };
             },
         }