Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

evalInstructions

Evaluates instructions from instruction_bytes until target_addr is reached or all instructions have been evaluated.

VirtualMachine.evalInstructions
fn evalInstructions(
    vm: *VirtualMachine,
    gpa: Allocator,
    cie: *const Unwind.CommonInformationEntry,
    target_addr: u64,
    instruction_bytes: []const u8,
    addr_size_bytes: u8,
    endian: std.builtin.Endian,
) !void

File

lib/std/debug/Dwarf/Unwind/VirtualMachine.zig:177

Code

fn evalInstructions(
    vm: *VirtualMachine,
    gpa: Allocator,
    cie: *const Unwind.CommonInformationEntry,
    target_addr: u64,
    instruction_bytes: []const u8,
    addr_size_bytes: u8,
    endian: std.builtin.Endian,
) !void {
    var fr: std.Io.Reader = .fixed(instruction_bytes);
    while (fr.seek < fr.buffer.len) {
        switch (try Instruction.read(&fr, addr_size_bytes, endian)) {
            .nop => {
                // If there was one nop, there's a good chance we've reached the padding and so
                // everything left is a nop, which is represented by a 0 byte.
                if (std.mem.allEqual(u8, fr.buffered(), 0)) return;
            },

            .remember_state => {
                try vm.stack.append(gpa, .{
                    .cfa = vm.current_row.cfa,
                    .columns = vm.current_row.columns,
                });
                const cols_len = vm.current_row.columns.len;
                const copy_start = vm.columns.items.len;
                assert(vm.current_row.columns.start == copy_start - cols_len);
                try vm.columns.ensureUnusedCapacity(gpa, cols_len); // to prevent aliasing issues
                vm.columns.appendSliceAssumeCapacity(vm.columns.items[copy_start - cols_len ..]);
                vm.current_row.columns.start = copy_start;
            },
            .restore_state => {
                const restored = vm.stack.pop() orelse return error.InvalidOperation;
                vm.columns.shrinkRetainingCapacity(restored.columns.start + restored.columns.len);

                vm.current_row.cfa = restored.cfa;
                vm.current_row.columns = restored.columns;
            },

            .advance_loc => |delta| {
                const new_addr = vm.current_row.offset + delta * cie.code_alignment_factor;
                if (new_addr > target_addr) return;
                vm.current_row.offset = new_addr;
            },
            .set_loc => |new_addr| {
                if (new_addr <= vm.current_row.offset) return error.InvalidOperation;
                if (cie.segment_selector_size != 0) return error.InvalidOperation; // unsupported
                // TODO: Check cie.segment_selector_size != 0 for DWARFV4

                if (new_addr > target_addr) return;
                vm.current_row.offset = new_addr;
            },

            .register => |reg| {
                const column = try vm.getOrAddColumn(gpa, reg.index);
                column.rule = switch (reg.rule) {
                    .restore => rule: {
                        const cie_row = &(vm.cie_row orelse return error.InvalidOperation);
                        for (vm.rowColumns(cie_row)) |cie_col| {
                            if (cie_col.register == reg.index) break :rule cie_col.rule;
                        }
                        break :rule .default;
                    },
                    .undefined => .undefined,
                    .same_value => .same_value,
                    .offset_uf => |off| .{ .offset = @as(i64, @intCast(off)) * cie.data_alignment_factor },
                    .offset_sf => |off| .{ .offset = off * cie.data_alignment_factor },
                    .val_offset_uf => |off| .{ .val_offset = @as(i64, @intCast(off)) * cie.data_alignment_factor },
                    .val_offset_sf => |off| .{ .val_offset = off * cie.data_alignment_factor },
                    .register => |callee_reg| .{ .register = callee_reg },
                    .expr => |len| .{ .expression = try takeExprBlock(&fr, len) },
                    .val_expr => |len| .{ .val_expression = try takeExprBlock(&fr, len) },
                };
            },
            .def_cfa => |cfa| vm.current_row.cfa = .{
                .reg_off = .{
                    .register = cfa.register,
                    // Unfortunately, LLVM emits negative CFI directives as their unsigned variants
                    // rather than the signed variants that DWARF has for exactly that purpose, hence
                    // `@bitCast` instead of `@intCast`.
                    .offset = @bitCast(cfa.offset),
                },
            },
            .def_cfa_sf => |cfa| vm.current_row.cfa = .{ .reg_off = .{
                .register = cfa.register,
                .offset = cfa.offset_sf * cie.data_alignment_factor,
            } },
            .def_cfa_reg => |register| switch (vm.current_row.cfa) {
                .none => {
                    // According to the DWARF specification, this is not valid, because this
                    // instruction can only be used to replace the register if the rule is already a
                    // `.reg_off`. However, this is emitted in practice by GNU toolchains for some
                    // targets, and so by convention is interpreted as equivalent to `.def_cfa` with
                    // an offset of 0.
                    vm.current_row.cfa = .{ .reg_off = .{
                        .register = register,
                        .offset = 0,
                    } };
                },
                .expression => return error.InvalidOperation,
                .reg_off => |*ro| ro.register = register,
            },
            .def_cfa_offset => |offset| switch (vm.current_row.cfa) {
                .none, .expression => return error.InvalidOperation,
                // See the comment for `def_cfa` above.
                .reg_off => |*ro| ro.offset = @bitCast(offset),
            },
            .def_cfa_offset_sf => |offset_sf| switch (vm.current_row.cfa) {
                .none, .expression => return error.InvalidOperation,
                .reg_off => |*ro| ro.offset = offset_sf * cie.data_alignment_factor,
            },
            .def_cfa_expr => |len| {
                vm.current_row.cfa = .{ .expression = try takeExprBlock(&fr, len) };
            },
        }
    }
}