master
   1const builtin = @import("builtin");
   2const native_arch = builtin.cpu.arch;
   3const native_endian = native_arch.endian();
   4
   5const std = @import("std");
   6const leb = std.leb;
   7const OP = std.dwarf.OP;
   8const mem = std.mem;
   9const assert = std.debug.assert;
  10const testing = std.testing;
  11const Writer = std.Io.Writer;
  12
  13const regNative = std.debug.Dwarf.SelfUnwinder.regNative;
  14
  15const ip_reg_num = std.debug.Dwarf.ipRegNum(native_arch).?;
  16const fp_reg_num = std.debug.Dwarf.fpRegNum(native_arch);
  17const sp_reg_num = std.debug.Dwarf.spRegNum(native_arch);
  18
  19/// Expressions can be evaluated in different contexts, each requiring its own set of inputs.
  20/// Callers should specify all the fields relevant to their context. If a field is required
  21/// by the expression and it isn't in the context, error.IncompleteExpressionContext is returned.
  22pub const Context = struct {
  23    /// The dwarf format of the section this expression is in
  24    format: std.dwarf.Format = .@"32",
  25    /// The compilation unit this expression relates to, if any
  26    compile_unit: ?*const std.debug.Dwarf.CompileUnit = null,
  27    /// When evaluating a user-presented expression, this is the address of the object being evaluated
  28    object_address: ?*const anyopaque = null,
  29    /// .debug_addr section
  30    debug_addr: ?[]const u8 = null,
  31    cpu_context: ?*std.debug.cpu_context.Native = null,
  32    /// Call frame address, if in a CFI context
  33    cfa: ?usize = null,
  34    /// This expression is a sub-expression from an OP.entry_value instruction
  35    entry_value_context: bool = false,
  36};
  37
  38pub const Options = struct {
  39    /// The address size of the target architecture
  40    addr_size: u8 = @sizeOf(usize),
  41    /// Endianness of the target architecture
  42    endian: std.builtin.Endian = native_endian,
  43    /// Restrict the stack machine to a subset of opcodes used in call frame instructions
  44    call_frame_context: bool = false,
  45};
  46
  47// Explicitly defined to support executing sub-expressions
  48pub const Error = error{
  49    UnimplementedExpressionCall,
  50    UnimplementedOpcode,
  51    UnimplementedUserOpcode,
  52    UnimplementedTypedComparison,
  53    UnimplementedTypeConversion,
  54
  55    UnknownExpressionOpcode,
  56
  57    IncompleteExpressionContext,
  58
  59    InvalidCFAOpcode,
  60    InvalidExpression,
  61    InvalidFrameBase,
  62    InvalidIntegralTypeSize,
  63    InvalidRegister,
  64    InvalidSubExpression,
  65    InvalidTypeLength,
  66
  67    TruncatedIntegralType,
  68
  69    IncompatibleRegisterSize,
  70} || std.debug.cpu_context.DwarfRegisterError || error{ EndOfStream, Overflow, OutOfMemory, DivisionByZero, ReadFailed };
  71
  72/// A stack machine that can decode and run DWARF expressions.
  73/// Expressions can be decoded for non-native address size and endianness,
  74/// but can only be executed if the current target matches the configuration.
  75pub fn StackMachine(comptime options: Options) type {
  76    const addr_type = switch (options.addr_size) {
  77        2 => u16,
  78        4 => u32,
  79        8 => u64,
  80        else => @compileError("Unsupported address size of " ++ options.addr_size),
  81    };
  82
  83    const addr_type_signed = switch (options.addr_size) {
  84        2 => i16,
  85        4 => i32,
  86        8 => i64,
  87        else => @compileError("Unsupported address size of " ++ options.addr_size),
  88    };
  89
  90    return struct {
  91        const Self = @This();
  92
  93        const Operand = union(enum) {
  94            generic: addr_type,
  95            register: u8,
  96            type_size: u8,
  97            branch_offset: i16,
  98            base_register: struct {
  99                base_register: u8,
 100                offset: i64,
 101            },
 102            composite_location: struct {
 103                size: u64,
 104                offset: i64,
 105            },
 106            block: []const u8,
 107            register_type: struct {
 108                register: u8,
 109                type_offset: addr_type,
 110            },
 111            const_type: struct {
 112                type_offset: addr_type,
 113                value_bytes: []const u8,
 114            },
 115            deref_type: struct {
 116                size: u8,
 117                type_offset: addr_type,
 118            },
 119        };
 120
 121        const Value = union(enum) {
 122            generic: addr_type,
 123
 124            // Typed value with a maximum size of a register
 125            regval_type: struct {
 126                // Offset of DW_TAG_base_type DIE
 127                type_offset: addr_type,
 128                type_size: u8,
 129                value: addr_type,
 130            },
 131
 132            // Typed value specified directly in the instruction stream
 133            const_type: struct {
 134                // Offset of DW_TAG_base_type DIE
 135                type_offset: addr_type,
 136                // Backed by the instruction stream
 137                value_bytes: []const u8,
 138            },
 139
 140            pub fn asIntegral(self: Value) !addr_type {
 141                return switch (self) {
 142                    .generic => |v| v,
 143
 144                    // TODO: For these two prongs, look up the type and assert it's integral?
 145                    .regval_type => |regval_type| regval_type.value,
 146                    .const_type => |const_type| {
 147                        const value: u64 = switch (const_type.value_bytes.len) {
 148                            1 => mem.readInt(u8, const_type.value_bytes[0..1], native_endian),
 149                            2 => mem.readInt(u16, const_type.value_bytes[0..2], native_endian),
 150                            4 => mem.readInt(u32, const_type.value_bytes[0..4], native_endian),
 151                            8 => mem.readInt(u64, const_type.value_bytes[0..8], native_endian),
 152                            else => return error.InvalidIntegralTypeSize,
 153                        };
 154
 155                        return std.math.cast(addr_type, value) orelse error.TruncatedIntegralType;
 156                    },
 157                };
 158            }
 159        };
 160
 161        stack: std.ArrayList(Value) = .empty,
 162
 163        pub fn reset(self: *Self) void {
 164            self.stack.clearRetainingCapacity();
 165        }
 166
 167        pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
 168            self.stack.deinit(allocator);
 169        }
 170
 171        fn generic(value: anytype) Operand {
 172            const int_info = @typeInfo(@TypeOf(value)).int;
 173            if (@sizeOf(@TypeOf(value)) > options.addr_size) {
 174                return .{ .generic = switch (int_info.signedness) {
 175                    .signed => @bitCast(@as(addr_type_signed, @truncate(value))),
 176                    .unsigned => @truncate(value),
 177                } };
 178            } else {
 179                return .{ .generic = switch (int_info.signedness) {
 180                    .signed => @bitCast(@as(addr_type_signed, @intCast(value))),
 181                    .unsigned => @intCast(value),
 182                } };
 183            }
 184        }
 185
 186        pub fn readOperand(reader: *std.Io.Reader, opcode: u8, context: Context) !?Operand {
 187            return switch (opcode) {
 188                OP.addr => generic(try reader.takeInt(addr_type, options.endian)),
 189                OP.call_ref => switch (context.format) {
 190                    .@"32" => generic(try reader.takeInt(u32, options.endian)),
 191                    .@"64" => generic(try reader.takeInt(u64, options.endian)),
 192                },
 193                OP.const1u,
 194                OP.pick,
 195                => generic(try reader.takeByte()),
 196                OP.deref_size,
 197                OP.xderef_size,
 198                => .{ .type_size = try reader.takeByte() },
 199                OP.const1s => generic(try reader.takeByteSigned()),
 200                OP.const2u,
 201                OP.call2,
 202                => generic(try reader.takeInt(u16, options.endian)),
 203                OP.call4 => generic(try reader.takeInt(u32, options.endian)),
 204                OP.const2s => generic(try reader.takeInt(i16, options.endian)),
 205                OP.bra,
 206                OP.skip,
 207                => .{ .branch_offset = try reader.takeInt(i16, options.endian) },
 208                OP.const4u => generic(try reader.takeInt(u32, options.endian)),
 209                OP.const4s => generic(try reader.takeInt(i32, options.endian)),
 210                OP.const8u => generic(try reader.takeInt(u64, options.endian)),
 211                OP.const8s => generic(try reader.takeInt(i64, options.endian)),
 212                OP.constu,
 213                OP.plus_uconst,
 214                OP.addrx,
 215                OP.constx,
 216                OP.convert,
 217                OP.reinterpret,
 218                => generic(try reader.takeLeb128(u64)),
 219                OP.consts,
 220                OP.fbreg,
 221                => generic(try reader.takeLeb128(i64)),
 222                OP.lit0...OP.lit31 => |n| generic(n - OP.lit0),
 223                OP.reg0...OP.reg31 => |n| .{ .register = n - OP.reg0 },
 224                OP.breg0...OP.breg31 => |n| .{ .base_register = .{
 225                    .base_register = n - OP.breg0,
 226                    .offset = try reader.takeLeb128(i64),
 227                } },
 228                OP.regx => .{ .register = try reader.takeLeb128(u8) },
 229                OP.bregx => blk: {
 230                    const base_register = try reader.takeLeb128(u8);
 231                    const offset = try reader.takeLeb128(i64);
 232                    break :blk .{ .base_register = .{
 233                        .base_register = base_register,
 234                        .offset = offset,
 235                    } };
 236                },
 237                OP.regval_type => blk: {
 238                    const register = try reader.takeLeb128(u8);
 239                    const type_offset = try reader.takeLeb128(addr_type);
 240                    break :blk .{ .register_type = .{
 241                        .register = register,
 242                        .type_offset = type_offset,
 243                    } };
 244                },
 245                OP.piece => .{
 246                    .composite_location = .{
 247                        .size = try reader.takeLeb128(u8),
 248                        .offset = 0,
 249                    },
 250                },
 251                OP.bit_piece => blk: {
 252                    const size = try reader.takeLeb128(u8);
 253                    const offset = try reader.takeLeb128(i64);
 254                    break :blk .{ .composite_location = .{
 255                        .size = size,
 256                        .offset = offset,
 257                    } };
 258                },
 259                OP.implicit_value, OP.entry_value => blk: {
 260                    const size = try reader.takeLeb128(u8);
 261                    const block = try reader.take(size);
 262                    break :blk .{ .block = block };
 263                },
 264                OP.const_type => blk: {
 265                    const type_offset = try reader.takeLeb128(addr_type);
 266                    const size = try reader.takeByte();
 267                    const value_bytes = try reader.take(size);
 268                    break :blk .{ .const_type = .{
 269                        .type_offset = type_offset,
 270                        .value_bytes = value_bytes,
 271                    } };
 272                },
 273                OP.deref_type,
 274                OP.xderef_type,
 275                => .{
 276                    .deref_type = .{
 277                        .size = try reader.takeByte(),
 278                        .type_offset = try reader.takeLeb128(addr_type),
 279                    },
 280                },
 281                OP.lo_user...OP.hi_user => return error.UnimplementedUserOpcode,
 282                else => null,
 283            };
 284        }
 285
 286        pub fn run(
 287            self: *Self,
 288            expression: []const u8,
 289            allocator: std.mem.Allocator,
 290            context: Context,
 291            initial_value: ?usize,
 292        ) Error!?Value {
 293            if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i });
 294            var stream: std.Io.Reader = .fixed(expression);
 295            while (try self.step(&stream, allocator, context)) {}
 296            if (self.stack.items.len == 0) return null;
 297            return self.stack.items[self.stack.items.len - 1];
 298        }
 299
 300        /// Reads an opcode and its operands from `stream`, then executes it
 301        pub fn step(
 302            self: *Self,
 303            stream: *std.Io.Reader,
 304            allocator: std.mem.Allocator,
 305            context: Context,
 306        ) Error!bool {
 307            if (@sizeOf(usize) != @sizeOf(addr_type) or options.endian != native_endian)
 308                @compileError("Execution of non-native address sizes / endianness is not supported");
 309
 310            const opcode = try stream.takeByte();
 311            if (options.call_frame_context and !isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
 312            const operand = try readOperand(stream, opcode, context);
 313            switch (opcode) {
 314
 315                // 2.5.1.1: Literal Encodings
 316                OP.lit0...OP.lit31,
 317                OP.addr,
 318                OP.const1u,
 319                OP.const2u,
 320                OP.const4u,
 321                OP.const8u,
 322                OP.const1s,
 323                OP.const2s,
 324                OP.const4s,
 325                OP.const8s,
 326                OP.constu,
 327                OP.consts,
 328                => try self.stack.append(allocator, .{ .generic = operand.?.generic }),
 329
 330                OP.const_type => {
 331                    const const_type = operand.?.const_type;
 332                    try self.stack.append(allocator, .{ .const_type = .{
 333                        .type_offset = const_type.type_offset,
 334                        .value_bytes = const_type.value_bytes,
 335                    } });
 336                },
 337
 338                OP.addrx,
 339                OP.constx,
 340                => {
 341                    if (context.compile_unit == null) return error.IncompleteExpressionContext;
 342                    if (context.debug_addr == null) return error.IncompleteExpressionContext;
 343                    const debug_addr_index = operand.?.generic;
 344                    const offset = context.compile_unit.?.addr_base + debug_addr_index;
 345                    if (offset >= context.debug_addr.?.len) return error.InvalidExpression;
 346                    const value = mem.readInt(usize, context.debug_addr.?[offset..][0..@sizeOf(usize)], native_endian);
 347                    try self.stack.append(allocator, .{ .generic = value });
 348                },
 349
 350                // 2.5.1.2: Register Values
 351                OP.fbreg => {
 352                    if (context.compile_unit == null) return error.IncompleteExpressionContext;
 353                    if (context.compile_unit.?.frame_base == null) return error.IncompleteExpressionContext;
 354
 355                    const offset: i64 = @intCast(operand.?.generic);
 356                    _ = offset;
 357
 358                    switch (context.compile_unit.?.frame_base.?.*) {
 359                        .exprloc => {
 360                            // TODO: Run this expression in a nested stack machine
 361                            return error.UnimplementedOpcode;
 362                        },
 363                        .loclistx => {
 364                            // TODO: Read value from .debug_loclists
 365                            return error.UnimplementedOpcode;
 366                        },
 367                        .sec_offset => {
 368                            // TODO: Read value from .debug_loclists
 369                            return error.UnimplementedOpcode;
 370                        },
 371                        else => return error.InvalidFrameBase,
 372                    }
 373                },
 374                OP.breg0...OP.breg31,
 375                OP.bregx,
 376                => {
 377                    const cpu_context = context.cpu_context orelse return error.IncompleteExpressionContext;
 378
 379                    const br = operand.?.base_register;
 380                    const value: i64 = @intCast((try regNative(cpu_context, br.base_register)).*);
 381                    try self.stack.append(allocator, .{ .generic = @intCast(value + br.offset) });
 382                },
 383                OP.regval_type => {
 384                    const cpu_context = context.cpu_context orelse return error.IncompleteExpressionContext;
 385                    const rt = operand.?.register_type;
 386                    try self.stack.append(allocator, .{
 387                        .regval_type = .{
 388                            .type_offset = rt.type_offset,
 389                            .type_size = @sizeOf(addr_type),
 390                            .value = (try regNative(cpu_context, rt.register)).*,
 391                        },
 392                    });
 393                },
 394
 395                // 2.5.1.3: Stack Operations
 396                OP.dup => {
 397                    if (self.stack.items.len == 0) return error.InvalidExpression;
 398                    try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1]);
 399                },
 400                OP.drop => {
 401                    _ = self.stack.pop();
 402                },
 403                OP.pick, OP.over => {
 404                    const stack_index = if (opcode == OP.over) 1 else operand.?.generic;
 405                    if (stack_index >= self.stack.items.len) return error.InvalidExpression;
 406                    try self.stack.append(allocator, self.stack.items[self.stack.items.len - 1 - stack_index]);
 407                },
 408                OP.swap => {
 409                    if (self.stack.items.len < 2) return error.InvalidExpression;
 410                    mem.swap(Value, &self.stack.items[self.stack.items.len - 1], &self.stack.items[self.stack.items.len - 2]);
 411                },
 412                OP.rot => {
 413                    if (self.stack.items.len < 3) return error.InvalidExpression;
 414                    const first = self.stack.items[self.stack.items.len - 1];
 415                    self.stack.items[self.stack.items.len - 1] = self.stack.items[self.stack.items.len - 2];
 416                    self.stack.items[self.stack.items.len - 2] = self.stack.items[self.stack.items.len - 3];
 417                    self.stack.items[self.stack.items.len - 3] = first;
 418                },
 419                OP.deref,
 420                OP.xderef,
 421                OP.deref_size,
 422                OP.xderef_size,
 423                OP.deref_type,
 424                OP.xderef_type,
 425                => {
 426                    if (self.stack.items.len == 0) return error.InvalidExpression;
 427                    const addr = try self.stack.items[self.stack.items.len - 1].asIntegral();
 428                    const addr_space_identifier: ?usize = switch (opcode) {
 429                        OP.xderef,
 430                        OP.xderef_size,
 431                        OP.xderef_type,
 432                        => blk: {
 433                            _ = self.stack.pop();
 434                            if (self.stack.items.len == 0) return error.InvalidExpression;
 435                            break :blk try self.stack.items[self.stack.items.len - 1].asIntegral();
 436                        },
 437                        else => null,
 438                    };
 439
 440                    // Usage of addr_space_identifier in the address calculation is implementation defined.
 441                    // This code will need to be updated to handle any architectures that utilize this.
 442                    _ = addr_space_identifier;
 443
 444                    const size = switch (opcode) {
 445                        OP.deref,
 446                        OP.xderef,
 447                        => @sizeOf(addr_type),
 448                        OP.deref_size,
 449                        OP.xderef_size,
 450                        => operand.?.type_size,
 451                        OP.deref_type,
 452                        OP.xderef_type,
 453                        => operand.?.deref_type.size,
 454                        else => unreachable,
 455                    };
 456
 457                    const value: addr_type = std.math.cast(addr_type, @as(u64, switch (size) {
 458                        1 => @as(*const u8, @ptrFromInt(addr)).*,
 459                        2 => @as(*const u16, @ptrFromInt(addr)).*,
 460                        4 => @as(*const u32, @ptrFromInt(addr)).*,
 461                        8 => @as(*const u64, @ptrFromInt(addr)).*,
 462                        else => return error.InvalidExpression,
 463                    })) orelse return error.InvalidExpression;
 464
 465                    switch (opcode) {
 466                        OP.deref_type,
 467                        OP.xderef_type,
 468                        => {
 469                            self.stack.items[self.stack.items.len - 1] = .{
 470                                .regval_type = .{
 471                                    .type_offset = operand.?.deref_type.type_offset,
 472                                    .type_size = operand.?.deref_type.size,
 473                                    .value = value,
 474                                },
 475                            };
 476                        },
 477                        else => {
 478                            self.stack.items[self.stack.items.len - 1] = .{ .generic = value };
 479                        },
 480                    }
 481                },
 482                OP.push_object_address => {
 483                    // In sub-expressions, `push_object_address` is not meaningful (as per the
 484                    // spec), so treat it like a nop
 485                    if (!context.entry_value_context) {
 486                        if (context.object_address == null) return error.IncompleteExpressionContext;
 487                        try self.stack.append(allocator, .{ .generic = @intFromPtr(context.object_address.?) });
 488                    }
 489                },
 490                OP.form_tls_address => {
 491                    return error.UnimplementedOpcode;
 492                },
 493                OP.call_frame_cfa => {
 494                    if (context.cfa) |cfa| {
 495                        try self.stack.append(allocator, .{ .generic = cfa });
 496                    } else return error.IncompleteExpressionContext;
 497                },
 498
 499                // 2.5.1.4: Arithmetic and Logical Operations
 500                OP.abs => {
 501                    if (self.stack.items.len == 0) return error.InvalidExpression;
 502                    const value: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
 503                    self.stack.items[self.stack.items.len - 1] = .{
 504                        .generic = @abs(value),
 505                    };
 506                },
 507                OP.@"and" => {
 508                    if (self.stack.items.len < 2) return error.InvalidExpression;
 509                    const a = try self.stack.pop().?.asIntegral();
 510                    self.stack.items[self.stack.items.len - 1] = .{
 511                        .generic = a & try self.stack.items[self.stack.items.len - 1].asIntegral(),
 512                    };
 513                },
 514                OP.div => {
 515                    if (self.stack.items.len < 2) return error.InvalidExpression;
 516                    const a: isize = @bitCast(try self.stack.pop().?.asIntegral());
 517                    const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
 518                    self.stack.items[self.stack.items.len - 1] = .{
 519                        .generic = @bitCast(try std.math.divTrunc(isize, b, a)),
 520                    };
 521                },
 522                OP.minus => {
 523                    if (self.stack.items.len < 2) return error.InvalidExpression;
 524                    const b = try self.stack.pop().?.asIntegral();
 525                    self.stack.items[self.stack.items.len - 1] = .{
 526                        .generic = try std.math.sub(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), b),
 527                    };
 528                },
 529                OP.mod => {
 530                    if (self.stack.items.len < 2) return error.InvalidExpression;
 531                    const a: isize = @bitCast(try self.stack.pop().?.asIntegral());
 532                    const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
 533                    self.stack.items[self.stack.items.len - 1] = .{
 534                        .generic = @bitCast(@mod(b, a)),
 535                    };
 536                },
 537                OP.mul => {
 538                    if (self.stack.items.len < 2) return error.InvalidExpression;
 539                    const a: isize = @bitCast(try self.stack.pop().?.asIntegral());
 540                    const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
 541                    self.stack.items[self.stack.items.len - 1] = .{
 542                        .generic = @bitCast(@mulWithOverflow(a, b)[0]),
 543                    };
 544                },
 545                OP.neg => {
 546                    if (self.stack.items.len == 0) return error.InvalidExpression;
 547                    self.stack.items[self.stack.items.len - 1] = .{
 548                        .generic = @bitCast(
 549                            try std.math.negate(
 550                                @as(isize, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())),
 551                            ),
 552                        ),
 553                    };
 554                },
 555                OP.not => {
 556                    if (self.stack.items.len == 0) return error.InvalidExpression;
 557                    self.stack.items[self.stack.items.len - 1] = .{
 558                        .generic = ~try self.stack.items[self.stack.items.len - 1].asIntegral(),
 559                    };
 560                },
 561                OP.@"or" => {
 562                    if (self.stack.items.len < 2) return error.InvalidExpression;
 563                    const a = try self.stack.pop().?.asIntegral();
 564                    self.stack.items[self.stack.items.len - 1] = .{
 565                        .generic = a | try self.stack.items[self.stack.items.len - 1].asIntegral(),
 566                    };
 567                },
 568                OP.plus => {
 569                    if (self.stack.items.len < 2) return error.InvalidExpression;
 570                    const b = try self.stack.pop().?.asIntegral();
 571                    self.stack.items[self.stack.items.len - 1] = .{
 572                        .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), b),
 573                    };
 574                },
 575                OP.plus_uconst => {
 576                    if (self.stack.items.len == 0) return error.InvalidExpression;
 577                    const constant = operand.?.generic;
 578                    self.stack.items[self.stack.items.len - 1] = .{
 579                        .generic = try std.math.add(addr_type, try self.stack.items[self.stack.items.len - 1].asIntegral(), constant),
 580                    };
 581                },
 582                OP.shl => {
 583                    if (self.stack.items.len < 2) return error.InvalidExpression;
 584                    const a = try self.stack.pop().?.asIntegral();
 585                    const b = try self.stack.items[self.stack.items.len - 1].asIntegral();
 586                    self.stack.items[self.stack.items.len - 1] = .{
 587                        .generic = std.math.shl(usize, b, a),
 588                    };
 589                },
 590                OP.shr => {
 591                    if (self.stack.items.len < 2) return error.InvalidExpression;
 592                    const a = try self.stack.pop().?.asIntegral();
 593                    const b = try self.stack.items[self.stack.items.len - 1].asIntegral();
 594                    self.stack.items[self.stack.items.len - 1] = .{
 595                        .generic = std.math.shr(usize, b, a),
 596                    };
 597                },
 598                OP.shra => {
 599                    if (self.stack.items.len < 2) return error.InvalidExpression;
 600                    const a = try self.stack.pop().?.asIntegral();
 601                    const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
 602                    self.stack.items[self.stack.items.len - 1] = .{
 603                        .generic = @bitCast(std.math.shr(isize, b, a)),
 604                    };
 605                },
 606                OP.xor => {
 607                    if (self.stack.items.len < 2) return error.InvalidExpression;
 608                    const a = try self.stack.pop().?.asIntegral();
 609                    self.stack.items[self.stack.items.len - 1] = .{
 610                        .generic = a ^ try self.stack.items[self.stack.items.len - 1].asIntegral(),
 611                    };
 612                },
 613
 614                // 2.5.1.5: Control Flow Operations
 615                OP.le,
 616                OP.ge,
 617                OP.eq,
 618                OP.lt,
 619                OP.gt,
 620                OP.ne,
 621                => {
 622                    if (self.stack.items.len < 2) return error.InvalidExpression;
 623                    const a = self.stack.pop().?;
 624                    const b = self.stack.items[self.stack.items.len - 1];
 625
 626                    if (a == .generic and b == .generic) {
 627                        const a_int: isize = @bitCast(a.asIntegral() catch unreachable);
 628                        const b_int: isize = @bitCast(b.asIntegral() catch unreachable);
 629                        const result = @intFromBool(switch (opcode) {
 630                            OP.le => b_int <= a_int,
 631                            OP.ge => b_int >= a_int,
 632                            OP.eq => b_int == a_int,
 633                            OP.lt => b_int < a_int,
 634                            OP.gt => b_int > a_int,
 635                            OP.ne => b_int != a_int,
 636                            else => unreachable,
 637                        });
 638
 639                        self.stack.items[self.stack.items.len - 1] = .{ .generic = result };
 640                    } else {
 641                        // TODO: Load the types referenced by these values, find their comparison operator, and run it
 642                        return error.UnimplementedTypedComparison;
 643                    }
 644                },
 645                OP.skip, OP.bra => {
 646                    const branch_offset = operand.?.branch_offset;
 647                    const condition = if (opcode == OP.bra) blk: {
 648                        if (self.stack.items.len == 0) return error.InvalidExpression;
 649                        break :blk try self.stack.pop().?.asIntegral() != 0;
 650                    } else true;
 651
 652                    if (condition) {
 653                        const new_pos = std.math.cast(
 654                            usize,
 655                            try std.math.add(isize, @as(isize, @intCast(stream.seek)), branch_offset),
 656                        ) orelse return error.InvalidExpression;
 657
 658                        if (new_pos < 0 or new_pos > stream.buffer.len) return error.InvalidExpression;
 659                        stream.seek = new_pos;
 660                    }
 661                },
 662                OP.call2,
 663                OP.call4,
 664                OP.call_ref,
 665                => {
 666                    const debug_info_offset = operand.?.generic;
 667                    _ = debug_info_offset;
 668
 669                    // TODO: Load a DIE entry at debug_info_offset in a .debug_info section (the spec says that it
 670                    //       can be in a separate exe / shared object from the one containing this expression).
 671                    //       Transfer control to the DW_AT_location attribute, with the current stack as input.
 672
 673                    return error.UnimplementedExpressionCall;
 674                },
 675
 676                // 2.5.1.6: Type Conversions
 677                OP.convert => {
 678                    if (self.stack.items.len == 0) return error.InvalidExpression;
 679                    const type_offset = operand.?.generic;
 680
 681                    // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size
 682                    const value = self.stack.items[self.stack.items.len - 1];
 683                    if (type_offset == 0) {
 684                        self.stack.items[self.stack.items.len - 1] = .{ .generic = try value.asIntegral() };
 685                    } else {
 686                        // TODO: Load the DW_TAG_base_type entry in context.compile_unit, find a conversion operator
 687                        //       from the old type to the new type, run it.
 688                        return error.UnimplementedTypeConversion;
 689                    }
 690                },
 691                OP.reinterpret => {
 692                    if (self.stack.items.len == 0) return error.InvalidExpression;
 693                    const type_offset = operand.?.generic;
 694
 695                    // TODO: Load the DW_TAG_base_type entries in context.compile_unit and verify both types are the same size
 696                    const value = self.stack.items[self.stack.items.len - 1];
 697                    if (type_offset == 0) {
 698                        self.stack.items[self.stack.items.len - 1] = .{ .generic = try value.asIntegral() };
 699                    } else {
 700                        self.stack.items[self.stack.items.len - 1] = switch (value) {
 701                            .generic => |v| .{
 702                                .regval_type = .{
 703                                    .type_offset = type_offset,
 704                                    .type_size = @sizeOf(addr_type),
 705                                    .value = v,
 706                                },
 707                            },
 708                            .regval_type => |r| .{
 709                                .regval_type = .{
 710                                    .type_offset = type_offset,
 711                                    .type_size = r.type_size,
 712                                    .value = r.value,
 713                                },
 714                            },
 715                            .const_type => |c| .{
 716                                .const_type = .{
 717                                    .type_offset = type_offset,
 718                                    .value_bytes = c.value_bytes,
 719                                },
 720                            },
 721                        };
 722                    }
 723                },
 724
 725                // 2.5.1.7: Special Operations
 726                OP.nop => {},
 727                OP.entry_value => {
 728                    const block = operand.?.block;
 729                    if (block.len == 0) return error.InvalidSubExpression;
 730
 731                    // TODO: The spec states that this sub-expression needs to observe the state (ie. registers)
 732                    //       as it was upon entering the current subprogram. If this isn't being called at the
 733                    //       end of a frame unwind operation, an additional cpu_context.Native with this state will be needed.
 734
 735                    if (isOpcodeRegisterLocation(block[0])) {
 736                        const cpu_context = context.cpu_context orelse return error.IncompleteExpressionContext;
 737
 738                        var block_stream: std.Io.Reader = .fixed(block);
 739                        const register = (try readOperand(&block_stream, block[0], context)).?.register;
 740                        const value = (try regNative(cpu_context, register)).*;
 741                        try self.stack.append(allocator, .{ .generic = value });
 742                    } else {
 743                        var stack_machine: Self = .{};
 744                        defer stack_machine.deinit(allocator);
 745
 746                        var sub_context = context;
 747                        sub_context.entry_value_context = true;
 748                        const result = try stack_machine.run(block, allocator, sub_context, null);
 749                        try self.stack.append(allocator, result orelse return error.InvalidSubExpression);
 750                    }
 751                },
 752
 753                // These have already been handled by readOperand
 754                OP.lo_user...OP.hi_user => unreachable,
 755                else => {
 756                    //std.debug.print("Unknown DWARF expression opcode: {x}\n", .{opcode});
 757                    return error.UnknownExpressionOpcode;
 758                },
 759            }
 760
 761            return stream.seek < stream.buffer.len;
 762        }
 763    };
 764}
 765
 766pub fn Builder(comptime options: Options) type {
 767    const addr_type = switch (options.addr_size) {
 768        2 => u16,
 769        4 => u32,
 770        8 => u64,
 771        else => @compileError("Unsupported address size of " ++ options.addr_size),
 772    };
 773
 774    return struct {
 775        /// Zero-operand instructions
 776        pub fn writeOpcode(writer: *Writer, comptime opcode: u8) !void {
 777            if (options.call_frame_context and !comptime isOpcodeValidInCFA(opcode)) return error.InvalidCFAOpcode;
 778            switch (opcode) {
 779                OP.dup,
 780                OP.drop,
 781                OP.over,
 782                OP.swap,
 783                OP.rot,
 784                OP.deref,
 785                OP.xderef,
 786                OP.push_object_address,
 787                OP.form_tls_address,
 788                OP.call_frame_cfa,
 789                OP.abs,
 790                OP.@"and",
 791                OP.div,
 792                OP.minus,
 793                OP.mod,
 794                OP.mul,
 795                OP.neg,
 796                OP.not,
 797                OP.@"or",
 798                OP.plus,
 799                OP.shl,
 800                OP.shr,
 801                OP.shra,
 802                OP.xor,
 803                OP.le,
 804                OP.ge,
 805                OP.eq,
 806                OP.lt,
 807                OP.gt,
 808                OP.ne,
 809                OP.nop,
 810                OP.stack_value,
 811                => try writer.writeByte(opcode),
 812                else => @compileError("This opcode requires operands, use `write<Opcode>()` instead"),
 813            }
 814        }
 815
 816        // 2.5.1.1: Literal Encodings
 817        pub fn writeLiteral(writer: *Writer, literal: u8) !void {
 818            switch (literal) {
 819                0...31 => |n| try writer.writeByte(n + OP.lit0),
 820                else => return error.InvalidLiteral,
 821            }
 822        }
 823
 824        pub fn writeConst(writer: *Writer, comptime T: type, value: T) !void {
 825            if (@typeInfo(T) != .int) @compileError("Constants must be integers");
 826
 827            switch (T) {
 828                u8, i8, u16, i16, u32, i32, u64, i64 => {
 829                    try writer.writeByte(switch (T) {
 830                        u8 => OP.const1u,
 831                        i8 => OP.const1s,
 832                        u16 => OP.const2u,
 833                        i16 => OP.const2s,
 834                        u32 => OP.const4u,
 835                        i32 => OP.const4s,
 836                        u64 => OP.const8u,
 837                        i64 => OP.const8s,
 838                        else => unreachable,
 839                    });
 840
 841                    try writer.writeInt(T, value, options.endian);
 842                },
 843                else => switch (@typeInfo(T).int.signedness) {
 844                    .unsigned => {
 845                        try writer.writeByte(OP.constu);
 846                        try writer.writeUleb128(value);
 847                    },
 848                    .signed => {
 849                        try writer.writeByte(OP.consts);
 850                        try writer.writeLeb128(value);
 851                    },
 852                },
 853            }
 854        }
 855
 856        pub fn writeConstx(writer: *Writer, debug_addr_offset: anytype) !void {
 857            try writer.writeByte(OP.constx);
 858            try writer.writeUleb128(debug_addr_offset);
 859        }
 860
 861        pub fn writeConstType(writer: *Writer, die_offset: anytype, value_bytes: []const u8) !void {
 862            if (options.call_frame_context) return error.InvalidCFAOpcode;
 863            if (value_bytes.len > 0xff) return error.InvalidTypeLength;
 864            try writer.writeByte(OP.const_type);
 865            try writer.writeUleb128(die_offset);
 866            try writer.writeByte(@intCast(value_bytes.len));
 867            try writer.writeAll(value_bytes);
 868        }
 869
 870        pub fn writeAddr(writer: *Writer, value: addr_type) !void {
 871            try writer.writeByte(OP.addr);
 872            try writer.writeInt(addr_type, value, options.endian);
 873        }
 874
 875        pub fn writeAddrx(writer: *Writer, debug_addr_offset: anytype) !void {
 876            if (options.call_frame_context) return error.InvalidCFAOpcode;
 877            try writer.writeByte(OP.addrx);
 878            try writer.writeUleb128(debug_addr_offset);
 879        }
 880
 881        // 2.5.1.2: Register Values
 882        pub fn writeFbreg(writer: *Writer, offset: anytype) !void {
 883            try writer.writeByte(OP.fbreg);
 884            try writer.writeSleb128(offset);
 885        }
 886
 887        pub fn writeBreg(writer: *Writer, register: u8, offset: anytype) !void {
 888            if (register > 31) return error.InvalidRegister;
 889            try writer.writeByte(OP.breg0 + register);
 890            try writer.writeSleb128(offset);
 891        }
 892
 893        pub fn writeBregx(writer: *Writer, register: anytype, offset: anytype) !void {
 894            try writer.writeByte(OP.bregx);
 895            try writer.writeUleb128(register);
 896            try writer.writeSleb128(offset);
 897        }
 898
 899        pub fn writeRegvalType(writer: *Writer, register: anytype, offset: anytype) !void {
 900            if (options.call_frame_context) return error.InvalidCFAOpcode;
 901            try writer.writeByte(OP.regval_type);
 902            try writer.writeUleb128(register);
 903            try writer.writeUleb128(offset);
 904        }
 905
 906        // 2.5.1.3: Stack Operations
 907        pub fn writePick(writer: *Writer, index: u8) !void {
 908            try writer.writeByte(OP.pick);
 909            try writer.writeByte(index);
 910        }
 911
 912        pub fn writeDerefSize(writer: *Writer, size: u8) !void {
 913            try writer.writeByte(OP.deref_size);
 914            try writer.writeByte(size);
 915        }
 916
 917        pub fn writeXDerefSize(writer: *Writer, size: u8) !void {
 918            try writer.writeByte(OP.xderef_size);
 919            try writer.writeByte(size);
 920        }
 921
 922        pub fn writeDerefType(writer: *Writer, size: u8, die_offset: anytype) !void {
 923            if (options.call_frame_context) return error.InvalidCFAOpcode;
 924            try writer.writeByte(OP.deref_type);
 925            try writer.writeByte(size);
 926            try writer.writeUleb128(die_offset);
 927        }
 928
 929        pub fn writeXDerefType(writer: *Writer, size: u8, die_offset: anytype) !void {
 930            try writer.writeByte(OP.xderef_type);
 931            try writer.writeByte(size);
 932            try writer.writeUleb128(die_offset);
 933        }
 934
 935        // 2.5.1.4: Arithmetic and Logical Operations
 936
 937        pub fn writePlusUconst(writer: *Writer, uint_value: anytype) !void {
 938            try writer.writeByte(OP.plus_uconst);
 939            try writer.writeUleb128(uint_value);
 940        }
 941
 942        // 2.5.1.5: Control Flow Operations
 943
 944        pub fn writeSkip(writer: *Writer, offset: i16) !void {
 945            try writer.writeByte(OP.skip);
 946            try writer.writeInt(i16, offset, options.endian);
 947        }
 948
 949        pub fn writeBra(writer: *Writer, offset: i16) !void {
 950            try writer.writeByte(OP.bra);
 951            try writer.writeInt(i16, offset, options.endian);
 952        }
 953
 954        pub fn writeCall(writer: *Writer, comptime T: type, offset: T) !void {
 955            if (options.call_frame_context) return error.InvalidCFAOpcode;
 956            switch (T) {
 957                u16 => try writer.writeByte(OP.call2),
 958                u32 => try writer.writeByte(OP.call4),
 959                else => @compileError("Call operand must be a 2 or 4 byte offset"),
 960            }
 961
 962            try writer.writeInt(T, offset, options.endian);
 963        }
 964
 965        pub fn writeCallRef(writer: *Writer, comptime is_64: bool, value: if (is_64) u64 else u32) !void {
 966            if (options.call_frame_context) return error.InvalidCFAOpcode;
 967            try writer.writeByte(OP.call_ref);
 968            try writer.writeInt(if (is_64) u64 else u32, value, options.endian);
 969        }
 970
 971        pub fn writeConvert(writer: *Writer, die_offset: anytype) !void {
 972            if (options.call_frame_context) return error.InvalidCFAOpcode;
 973            try writer.writeByte(OP.convert);
 974            try writer.writeUleb128(die_offset);
 975        }
 976
 977        pub fn writeReinterpret(writer: *Writer, die_offset: anytype) !void {
 978            if (options.call_frame_context) return error.InvalidCFAOpcode;
 979            try writer.writeByte(OP.reinterpret);
 980            try writer.writeUleb128(die_offset);
 981        }
 982
 983        // 2.5.1.7: Special Operations
 984
 985        pub fn writeEntryValue(writer: *Writer, expression: []const u8) !void {
 986            try writer.writeByte(OP.entry_value);
 987            try writer.writeUleb128(expression.len);
 988            try writer.writeAll(expression);
 989        }
 990
 991        // 2.6: Location Descriptions
 992        pub fn writeReg(writer: *Writer, register: u8) !void {
 993            try writer.writeByte(OP.reg0 + register);
 994        }
 995
 996        pub fn writeRegx(writer: *Writer, register: anytype) !void {
 997            try writer.writeByte(OP.regx);
 998            try writer.writeUleb128(register);
 999        }
1000
1001        pub fn writeImplicitValue(writer: *Writer, value_bytes: []const u8) !void {
1002            try writer.writeByte(OP.implicit_value);
1003            try writer.writeUleb128(value_bytes.len);
1004            try writer.writeAll(value_bytes);
1005        }
1006    };
1007}
1008
1009// Certain opcodes are not allowed in a CFA context, see 6.4.2
1010fn isOpcodeValidInCFA(opcode: u8) bool {
1011    return switch (opcode) {
1012        OP.addrx,
1013        OP.call2,
1014        OP.call4,
1015        OP.call_ref,
1016        OP.const_type,
1017        OP.constx,
1018        OP.convert,
1019        OP.deref_type,
1020        OP.regval_type,
1021        OP.reinterpret,
1022        OP.push_object_address,
1023        OP.call_frame_cfa,
1024        => false,
1025        else => true,
1026    };
1027}
1028
1029fn isOpcodeRegisterLocation(opcode: u8) bool {
1030    return switch (opcode) {
1031        OP.reg0...OP.reg31, OP.regx => true,
1032        else => false,
1033    };
1034}
1035
1036test "basics" {
1037    const allocator = std.testing.allocator;
1038
1039    const options = Options{};
1040    var stack_machine = StackMachine(options){};
1041    defer stack_machine.deinit(allocator);
1042
1043    const b = Builder(options);
1044
1045    var program: std.Io.Writer.Allocating = .init(allocator);
1046    defer program.deinit();
1047
1048    const writer = &program.writer;
1049
1050    // Literals
1051    {
1052        const context = Context{};
1053        for (0..32) |i| {
1054            try b.writeLiteral(writer, @intCast(i));
1055        }
1056
1057        _ = try stack_machine.run(program.written(), allocator, context, 0);
1058
1059        for (0..32) |i| {
1060            const expected = 31 - i;
1061            try testing.expectEqual(expected, stack_machine.stack.pop().?.generic);
1062        }
1063    }
1064
1065    // Constants
1066    {
1067        stack_machine.reset();
1068        program.clearRetainingCapacity();
1069
1070        const input = [_]comptime_int{
1071            1,
1072            -1,
1073            @as(usize, @truncate(0x0fff)),
1074            @as(isize, @truncate(-0x0fff)),
1075            @as(usize, @truncate(0x0fffffff)),
1076            @as(isize, @truncate(-0x0fffffff)),
1077            @as(usize, @truncate(0x0fffffffffffffff)),
1078            @as(isize, @truncate(-0x0fffffffffffffff)),
1079            @as(usize, @truncate(0x8000000)),
1080            @as(isize, @truncate(-0x8000000)),
1081            @as(usize, @truncate(0x12345678_12345678)),
1082            @as(usize, @truncate(0xffffffff_ffffffff)),
1083            @as(usize, @truncate(0xeeeeeeee_eeeeeeee)),
1084        };
1085
1086        try b.writeConst(writer, u8, input[0]);
1087        try b.writeConst(writer, i8, input[1]);
1088        try b.writeConst(writer, u16, input[2]);
1089        try b.writeConst(writer, i16, input[3]);
1090        try b.writeConst(writer, u32, input[4]);
1091        try b.writeConst(writer, i32, input[5]);
1092        try b.writeConst(writer, u64, input[6]);
1093        try b.writeConst(writer, i64, input[7]);
1094        try b.writeConst(writer, u28, input[8]);
1095        try b.writeConst(writer, i28, input[9]);
1096        try b.writeAddr(writer, input[10]);
1097
1098        var mock_compile_unit: std.debug.Dwarf.CompileUnit = undefined;
1099        mock_compile_unit.addr_base = 1;
1100
1101        var mock_debug_addr: std.Io.Writer.Allocating = .init(allocator);
1102        defer mock_debug_addr.deinit();
1103
1104        try mock_debug_addr.writer.writeInt(u16, 0, native_endian);
1105        try mock_debug_addr.writer.writeInt(usize, input[11], native_endian);
1106        try mock_debug_addr.writer.writeInt(usize, input[12], native_endian);
1107
1108        const context: Context = .{
1109            .compile_unit = &mock_compile_unit,
1110            .debug_addr = mock_debug_addr.written(),
1111        };
1112
1113        try b.writeConstx(writer, @as(usize, 1));
1114        try b.writeAddrx(writer, @as(usize, 1 + @sizeOf(usize)));
1115
1116        const die_offset: usize = @truncate(0xaabbccdd);
1117        const type_bytes: []const u8 = &.{ 1, 2, 3, 4 };
1118        try b.writeConstType(writer, die_offset, type_bytes);
1119
1120        _ = try stack_machine.run(program.written(), allocator, context, 0);
1121
1122        const const_type = stack_machine.stack.pop().?.const_type;
1123        try testing.expectEqual(die_offset, const_type.type_offset);
1124        try testing.expectEqualSlices(u8, type_bytes, const_type.value_bytes);
1125
1126        const expected = .{
1127            .{ usize, input[12], usize },
1128            .{ usize, input[11], usize },
1129            .{ usize, input[10], usize },
1130            .{ isize, input[9], isize },
1131            .{ usize, input[8], usize },
1132            .{ isize, input[7], isize },
1133            .{ usize, input[6], usize },
1134            .{ isize, input[5], isize },
1135            .{ usize, input[4], usize },
1136            .{ isize, input[3], isize },
1137            .{ usize, input[2], usize },
1138            .{ isize, input[1], isize },
1139            .{ usize, input[0], usize },
1140        };
1141
1142        inline for (expected) |e| {
1143            try testing.expectEqual(@as(e[0], e[1]), @as(e[2], @bitCast(stack_machine.stack.pop().?.generic)));
1144        }
1145    }
1146
1147    // Register values
1148    if (std.debug.cpu_context.Native != noreturn) {
1149        stack_machine.reset();
1150        program.clearRetainingCapacity();
1151
1152        var cpu_context: std.debug.cpu_context.Native = undefined;
1153        const context = Context{
1154            .cpu_context = &cpu_context,
1155        };
1156
1157        const reg_bytes = try cpu_context.dwarfRegisterBytes(0);
1158
1159        // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it
1160
1161        mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian);
1162        (try regNative(&cpu_context, fp_reg_num)).* = 1;
1163        (try regNative(&cpu_context, ip_reg_num)).* = 2;
1164
1165        try b.writeBreg(writer, fp_reg_num, @as(usize, 100));
1166        try b.writeBregx(writer, ip_reg_num, @as(usize, 200));
1167        try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 300));
1168
1169        _ = try stack_machine.run(program.written(), allocator, context, 0);
1170
1171        const regval_type = stack_machine.stack.pop().?.regval_type;
1172        try testing.expectEqual(@as(usize, 300), regval_type.type_offset);
1173        try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size);
1174        try testing.expectEqual(@as(usize, 0xee), regval_type.value);
1175
1176        try testing.expectEqual(@as(usize, 202), stack_machine.stack.pop().?.generic);
1177        try testing.expectEqual(@as(usize, 101), stack_machine.stack.pop().?.generic);
1178    }
1179
1180    // Stack operations
1181    {
1182        var context = Context{};
1183
1184        stack_machine.reset();
1185        program.clearRetainingCapacity();
1186        try b.writeConst(writer, u8, 1);
1187        try b.writeOpcode(writer, OP.dup);
1188        _ = try stack_machine.run(program.written(), allocator, context, null);
1189        try testing.expectEqual(@as(usize, 1), stack_machine.stack.pop().?.generic);
1190        try testing.expectEqual(@as(usize, 1), stack_machine.stack.pop().?.generic);
1191
1192        stack_machine.reset();
1193        program.clearRetainingCapacity();
1194        try b.writeConst(writer, u8, 1);
1195        try b.writeOpcode(writer, OP.drop);
1196        _ = try stack_machine.run(program.written(), allocator, context, null);
1197        try testing.expect(stack_machine.stack.pop() == null);
1198
1199        stack_machine.reset();
1200        program.clearRetainingCapacity();
1201        try b.writeConst(writer, u8, 4);
1202        try b.writeConst(writer, u8, 5);
1203        try b.writeConst(writer, u8, 6);
1204        try b.writePick(writer, 2);
1205        _ = try stack_machine.run(program.written(), allocator, context, null);
1206        try testing.expectEqual(@as(usize, 4), stack_machine.stack.pop().?.generic);
1207
1208        stack_machine.reset();
1209        program.clearRetainingCapacity();
1210        try b.writeConst(writer, u8, 4);
1211        try b.writeConst(writer, u8, 5);
1212        try b.writeConst(writer, u8, 6);
1213        try b.writeOpcode(writer, OP.over);
1214        _ = try stack_machine.run(program.written(), allocator, context, null);
1215        try testing.expectEqual(@as(usize, 5), stack_machine.stack.pop().?.generic);
1216
1217        stack_machine.reset();
1218        program.clearRetainingCapacity();
1219        try b.writeConst(writer, u8, 5);
1220        try b.writeConst(writer, u8, 6);
1221        try b.writeOpcode(writer, OP.swap);
1222        _ = try stack_machine.run(program.written(), allocator, context, null);
1223        try testing.expectEqual(@as(usize, 5), stack_machine.stack.pop().?.generic);
1224        try testing.expectEqual(@as(usize, 6), stack_machine.stack.pop().?.generic);
1225
1226        stack_machine.reset();
1227        program.clearRetainingCapacity();
1228        try b.writeConst(writer, u8, 4);
1229        try b.writeConst(writer, u8, 5);
1230        try b.writeConst(writer, u8, 6);
1231        try b.writeOpcode(writer, OP.rot);
1232        _ = try stack_machine.run(program.written(), allocator, context, null);
1233        try testing.expectEqual(@as(usize, 5), stack_machine.stack.pop().?.generic);
1234        try testing.expectEqual(@as(usize, 4), stack_machine.stack.pop().?.generic);
1235        try testing.expectEqual(@as(usize, 6), stack_machine.stack.pop().?.generic);
1236
1237        const deref_target: usize = @truncate(0xffeeffee_ffeeffee);
1238
1239        stack_machine.reset();
1240        program.clearRetainingCapacity();
1241        try b.writeAddr(writer, @intFromPtr(&deref_target));
1242        try b.writeOpcode(writer, OP.deref);
1243        _ = try stack_machine.run(program.written(), allocator, context, null);
1244        try testing.expectEqual(deref_target, stack_machine.stack.pop().?.generic);
1245
1246        stack_machine.reset();
1247        program.clearRetainingCapacity();
1248        try b.writeLiteral(writer, 0);
1249        try b.writeAddr(writer, @intFromPtr(&deref_target));
1250        try b.writeOpcode(writer, OP.xderef);
1251        _ = try stack_machine.run(program.written(), allocator, context, null);
1252        try testing.expectEqual(deref_target, stack_machine.stack.pop().?.generic);
1253
1254        stack_machine.reset();
1255        program.clearRetainingCapacity();
1256        try b.writeAddr(writer, @intFromPtr(&deref_target));
1257        try b.writeDerefSize(writer, 1);
1258        _ = try stack_machine.run(program.written(), allocator, context, null);
1259        try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), stack_machine.stack.pop().?.generic);
1260
1261        stack_machine.reset();
1262        program.clearRetainingCapacity();
1263        try b.writeLiteral(writer, 0);
1264        try b.writeAddr(writer, @intFromPtr(&deref_target));
1265        try b.writeXDerefSize(writer, 1);
1266        _ = try stack_machine.run(program.written(), allocator, context, null);
1267        try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), stack_machine.stack.pop().?.generic);
1268
1269        const type_offset: usize = @truncate(0xaabbaabb_aabbaabb);
1270
1271        stack_machine.reset();
1272        program.clearRetainingCapacity();
1273        try b.writeAddr(writer, @intFromPtr(&deref_target));
1274        try b.writeDerefType(writer, 1, type_offset);
1275        _ = try stack_machine.run(program.written(), allocator, context, null);
1276        const deref_type = stack_machine.stack.pop().?.regval_type;
1277        try testing.expectEqual(type_offset, deref_type.type_offset);
1278        try testing.expectEqual(@as(u8, 1), deref_type.type_size);
1279        try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), deref_type.value);
1280
1281        stack_machine.reset();
1282        program.clearRetainingCapacity();
1283        try b.writeLiteral(writer, 0);
1284        try b.writeAddr(writer, @intFromPtr(&deref_target));
1285        try b.writeXDerefType(writer, 1, type_offset);
1286        _ = try stack_machine.run(program.written(), allocator, context, null);
1287        const xderef_type = stack_machine.stack.pop().?.regval_type;
1288        try testing.expectEqual(type_offset, xderef_type.type_offset);
1289        try testing.expectEqual(@as(u8, 1), xderef_type.type_size);
1290        try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), xderef_type.value);
1291
1292        context.object_address = &deref_target;
1293
1294        stack_machine.reset();
1295        program.clearRetainingCapacity();
1296        try b.writeOpcode(writer, OP.push_object_address);
1297        _ = try stack_machine.run(program.written(), allocator, context, null);
1298        try testing.expectEqual(@as(usize, @intFromPtr(context.object_address.?)), stack_machine.stack.pop().?.generic);
1299
1300        // TODO: Test OP.form_tls_address
1301
1302        context.cfa = @truncate(0xccddccdd_ccddccdd);
1303
1304        stack_machine.reset();
1305        program.clearRetainingCapacity();
1306        try b.writeOpcode(writer, OP.call_frame_cfa);
1307        _ = try stack_machine.run(program.written(), allocator, context, null);
1308        try testing.expectEqual(context.cfa.?, stack_machine.stack.pop().?.generic);
1309    }
1310
1311    // Arithmetic and Logical Operations
1312    {
1313        const context = Context{};
1314
1315        stack_machine.reset();
1316        program.clearRetainingCapacity();
1317        try b.writeConst(writer, i16, -4096);
1318        try b.writeOpcode(writer, OP.abs);
1319        _ = try stack_machine.run(program.written(), allocator, context, null);
1320        try testing.expectEqual(@as(usize, 4096), stack_machine.stack.pop().?.generic);
1321
1322        stack_machine.reset();
1323        program.clearRetainingCapacity();
1324        try b.writeConst(writer, u16, 0xff0f);
1325        try b.writeConst(writer, u16, 0xf0ff);
1326        try b.writeOpcode(writer, OP.@"and");
1327        _ = try stack_machine.run(program.written(), allocator, context, null);
1328        try testing.expectEqual(@as(usize, 0xf00f), stack_machine.stack.pop().?.generic);
1329
1330        stack_machine.reset();
1331        program.clearRetainingCapacity();
1332        try b.writeConst(writer, i16, -404);
1333        try b.writeConst(writer, i16, 100);
1334        try b.writeOpcode(writer, OP.div);
1335        _ = try stack_machine.run(program.written(), allocator, context, null);
1336        try testing.expectEqual(@as(isize, -404 / 100), @as(isize, @bitCast(stack_machine.stack.pop().?.generic)));
1337
1338        stack_machine.reset();
1339        program.clearRetainingCapacity();
1340        try b.writeConst(writer, u16, 200);
1341        try b.writeConst(writer, u16, 50);
1342        try b.writeOpcode(writer, OP.minus);
1343        _ = try stack_machine.run(program.written(), allocator, context, null);
1344        try testing.expectEqual(@as(usize, 150), stack_machine.stack.pop().?.generic);
1345
1346        stack_machine.reset();
1347        program.clearRetainingCapacity();
1348        try b.writeConst(writer, u16, 123);
1349        try b.writeConst(writer, u16, 100);
1350        try b.writeOpcode(writer, OP.mod);
1351        _ = try stack_machine.run(program.written(), allocator, context, null);
1352        try testing.expectEqual(@as(usize, 23), stack_machine.stack.pop().?.generic);
1353
1354        stack_machine.reset();
1355        program.clearRetainingCapacity();
1356        try b.writeConst(writer, u16, 0xff);
1357        try b.writeConst(writer, u16, 0xee);
1358        try b.writeOpcode(writer, OP.mul);
1359        _ = try stack_machine.run(program.written(), allocator, context, null);
1360        try testing.expectEqual(@as(usize, 0xed12), stack_machine.stack.pop().?.generic);
1361
1362        stack_machine.reset();
1363        program.clearRetainingCapacity();
1364        try b.writeConst(writer, u16, 5);
1365        try b.writeOpcode(writer, OP.neg);
1366        try b.writeConst(writer, i16, -6);
1367        try b.writeOpcode(writer, OP.neg);
1368        _ = try stack_machine.run(program.written(), allocator, context, null);
1369        try testing.expectEqual(@as(usize, 6), stack_machine.stack.pop().?.generic);
1370        try testing.expectEqual(@as(isize, -5), @as(isize, @bitCast(stack_machine.stack.pop().?.generic)));
1371
1372        stack_machine.reset();
1373        program.clearRetainingCapacity();
1374        try b.writeConst(writer, u16, 0xff0f);
1375        try b.writeOpcode(writer, OP.not);
1376        _ = try stack_machine.run(program.written(), allocator, context, null);
1377        try testing.expectEqual(~@as(usize, 0xff0f), stack_machine.stack.pop().?.generic);
1378
1379        stack_machine.reset();
1380        program.clearRetainingCapacity();
1381        try b.writeConst(writer, u16, 0xff0f);
1382        try b.writeConst(writer, u16, 0xf0ff);
1383        try b.writeOpcode(writer, OP.@"or");
1384        _ = try stack_machine.run(program.written(), allocator, context, null);
1385        try testing.expectEqual(@as(usize, 0xffff), stack_machine.stack.pop().?.generic);
1386
1387        stack_machine.reset();
1388        program.clearRetainingCapacity();
1389        try b.writeConst(writer, i16, 402);
1390        try b.writeConst(writer, i16, 100);
1391        try b.writeOpcode(writer, OP.plus);
1392        _ = try stack_machine.run(program.written(), allocator, context, null);
1393        try testing.expectEqual(@as(usize, 502), stack_machine.stack.pop().?.generic);
1394
1395        stack_machine.reset();
1396        program.clearRetainingCapacity();
1397        try b.writeConst(writer, u16, 4096);
1398        try b.writePlusUconst(writer, @as(usize, 8192));
1399        _ = try stack_machine.run(program.written(), allocator, context, null);
1400        try testing.expectEqual(@as(usize, 4096 + 8192), stack_machine.stack.pop().?.generic);
1401
1402        stack_machine.reset();
1403        program.clearRetainingCapacity();
1404        try b.writeConst(writer, u16, 0xfff);
1405        try b.writeConst(writer, u16, 1);
1406        try b.writeOpcode(writer, OP.shl);
1407        _ = try stack_machine.run(program.written(), allocator, context, null);
1408        try testing.expectEqual(@as(usize, 0xfff << 1), stack_machine.stack.pop().?.generic);
1409
1410        stack_machine.reset();
1411        program.clearRetainingCapacity();
1412        try b.writeConst(writer, u16, 0xfff);
1413        try b.writeConst(writer, u16, 1);
1414        try b.writeOpcode(writer, OP.shr);
1415        _ = try stack_machine.run(program.written(), allocator, context, null);
1416        try testing.expectEqual(@as(usize, 0xfff >> 1), stack_machine.stack.pop().?.generic);
1417
1418        stack_machine.reset();
1419        program.clearRetainingCapacity();
1420        try b.writeConst(writer, u16, 0xfff);
1421        try b.writeConst(writer, u16, 1);
1422        try b.writeOpcode(writer, OP.shr);
1423        _ = try stack_machine.run(program.written(), allocator, context, null);
1424        try testing.expectEqual(@as(usize, @bitCast(@as(isize, 0xfff) >> 1)), stack_machine.stack.pop().?.generic);
1425
1426        stack_machine.reset();
1427        program.clearRetainingCapacity();
1428        try b.writeConst(writer, u16, 0xf0ff);
1429        try b.writeConst(writer, u16, 0xff0f);
1430        try b.writeOpcode(writer, OP.xor);
1431        _ = try stack_machine.run(program.written(), allocator, context, null);
1432        try testing.expectEqual(@as(usize, 0x0ff0), stack_machine.stack.pop().?.generic);
1433    }
1434
1435    // Control Flow Operations
1436    {
1437        const context = Context{};
1438        const expected = .{
1439            .{ OP.le, 1, 1, 0 },
1440            .{ OP.ge, 1, 0, 1 },
1441            .{ OP.eq, 1, 0, 0 },
1442            .{ OP.lt, 0, 1, 0 },
1443            .{ OP.gt, 0, 0, 1 },
1444            .{ OP.ne, 0, 1, 1 },
1445        };
1446
1447        inline for (expected) |e| {
1448            stack_machine.reset();
1449            program.clearRetainingCapacity();
1450
1451            try b.writeConst(writer, u16, 0);
1452            try b.writeConst(writer, u16, 0);
1453            try b.writeOpcode(writer, e[0]);
1454            try b.writeConst(writer, u16, 0);
1455            try b.writeConst(writer, u16, 1);
1456            try b.writeOpcode(writer, e[0]);
1457            try b.writeConst(writer, u16, 1);
1458            try b.writeConst(writer, u16, 0);
1459            try b.writeOpcode(writer, e[0]);
1460            _ = try stack_machine.run(program.written(), allocator, context, null);
1461            try testing.expectEqual(@as(usize, e[3]), stack_machine.stack.pop().?.generic);
1462            try testing.expectEqual(@as(usize, e[2]), stack_machine.stack.pop().?.generic);
1463            try testing.expectEqual(@as(usize, e[1]), stack_machine.stack.pop().?.generic);
1464        }
1465
1466        stack_machine.reset();
1467        program.clearRetainingCapacity();
1468        try b.writeLiteral(writer, 2);
1469        try b.writeSkip(writer, 1);
1470        try b.writeLiteral(writer, 3);
1471        _ = try stack_machine.run(program.written(), allocator, context, null);
1472        try testing.expectEqual(@as(usize, 2), stack_machine.stack.pop().?.generic);
1473
1474        stack_machine.reset();
1475        program.clearRetainingCapacity();
1476        try b.writeLiteral(writer, 2);
1477        try b.writeBra(writer, 1);
1478        try b.writeLiteral(writer, 3);
1479        try b.writeLiteral(writer, 0);
1480        try b.writeBra(writer, 1);
1481        try b.writeLiteral(writer, 4);
1482        try b.writeLiteral(writer, 5);
1483        _ = try stack_machine.run(program.written(), allocator, context, null);
1484        try testing.expectEqual(@as(usize, 5), stack_machine.stack.pop().?.generic);
1485        try testing.expectEqual(@as(usize, 4), stack_machine.stack.pop().?.generic);
1486        try testing.expect(stack_machine.stack.pop() == null);
1487
1488        // TODO: Test call2, call4, call_ref once implemented
1489
1490    }
1491
1492    // Type conversions
1493    {
1494        const context = Context{};
1495        stack_machine.reset();
1496        program.clearRetainingCapacity();
1497
1498        // TODO: Test typed OP.convert once implemented
1499
1500        const value: usize = @truncate(0xffeeffee_ffeeffee);
1501        var value_bytes: [options.addr_size]u8 = undefined;
1502        mem.writeInt(usize, &value_bytes, value, native_endian);
1503
1504        // Convert to generic type
1505        stack_machine.reset();
1506        program.clearRetainingCapacity();
1507        try b.writeConstType(writer, @as(usize, 0), &value_bytes);
1508        try b.writeConvert(writer, @as(usize, 0));
1509        _ = try stack_machine.run(program.written(), allocator, context, null);
1510        try testing.expectEqual(value, stack_machine.stack.pop().?.generic);
1511
1512        // Reinterpret to generic type
1513        stack_machine.reset();
1514        program.clearRetainingCapacity();
1515        try b.writeConstType(writer, @as(usize, 0), &value_bytes);
1516        try b.writeReinterpret(writer, @as(usize, 0));
1517        _ = try stack_machine.run(program.written(), allocator, context, null);
1518        try testing.expectEqual(value, stack_machine.stack.pop().?.generic);
1519
1520        // Reinterpret to new type
1521        const die_offset: usize = 0xffee;
1522
1523        stack_machine.reset();
1524        program.clearRetainingCapacity();
1525        try b.writeConstType(writer, @as(usize, 0), &value_bytes);
1526        try b.writeReinterpret(writer, die_offset);
1527        _ = try stack_machine.run(program.written(), allocator, context, null);
1528        const const_type = stack_machine.stack.pop().?.const_type;
1529        try testing.expectEqual(die_offset, const_type.type_offset);
1530
1531        stack_machine.reset();
1532        program.clearRetainingCapacity();
1533        try b.writeLiteral(writer, 0);
1534        try b.writeReinterpret(writer, die_offset);
1535        _ = try stack_machine.run(program.written(), allocator, context, null);
1536        const regval_type = stack_machine.stack.pop().?.regval_type;
1537        try testing.expectEqual(die_offset, regval_type.type_offset);
1538    }
1539
1540    // Special operations
1541    {
1542        var context = Context{};
1543
1544        stack_machine.reset();
1545        program.clearRetainingCapacity();
1546        try b.writeOpcode(writer, OP.nop);
1547        _ = try stack_machine.run(program.written(), allocator, context, null);
1548        try testing.expect(stack_machine.stack.pop() == null);
1549
1550        // Sub-expression
1551        {
1552            var sub_program: std.Io.Writer.Allocating = .init(allocator);
1553            defer sub_program.deinit();
1554            const sub_writer = &sub_program.writer;
1555            try b.writeLiteral(sub_writer, 3);
1556
1557            stack_machine.reset();
1558            program.clearRetainingCapacity();
1559            try b.writeEntryValue(writer, sub_program.written());
1560            _ = try stack_machine.run(program.written(), allocator, context, null);
1561            try testing.expectEqual(@as(usize, 3), stack_machine.stack.pop().?.generic);
1562        }
1563
1564        // Register location description
1565        var cpu_context: std.debug.cpu_context.Native = undefined;
1566        context = .{ .cpu_context = &cpu_context };
1567
1568        const reg_bytes = try cpu_context.dwarfRegisterBytes(0);
1569        mem.writeInt(usize, reg_bytes[0..@sizeOf(usize)], 0xee, native_endian);
1570
1571        var sub_program: std.Io.Writer.Allocating = .init(allocator);
1572        defer sub_program.deinit();
1573        const sub_writer = &sub_program.writer;
1574        try b.writeReg(sub_writer, 0);
1575
1576        stack_machine.reset();
1577        program.clearRetainingCapacity();
1578        try b.writeEntryValue(writer, sub_program.written());
1579        _ = try stack_machine.run(program.written(), allocator, context, null);
1580        try testing.expectEqual(@as(usize, 0xee), stack_machine.stack.pop().?.generic);
1581    }
1582}