authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-07 18:37:10-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:15-04:00
log54ca62fef4a621e0d64b6461706c8b5fe5a80348
treec14a6170630346376a3b4bcc182e828c5ae0fda8
parent5c0d4cef1afda3e01bded01636ef71846522909b

dwarf: fixup regBytes for the case where there is no context support

expressions: add more tests, fix tests for mipsel debug: add lookupModuleName implementation for macos

5 files changed, 412 insertions(+), 69 deletions(-)

lib/std/debug.zig+40-2
...@@ -1518,10 +1518,10 @@ pub const DebugInfo = struct {...@@ -1518,10 +1518,10 @@ pub const DebugInfo = struct {
15181518
1519 // Returns the module name for a given address.1519 // Returns the module name for a given address.
1520 // This can be called when getModuleForAddress fails, so implementations should provide1520 // This can be called when getModuleForAddress fails, so implementations should provide
1521 // a path that doesn't rely on any side-effects of successful module lookup.1521 // a path that doesn't rely on any side-effects of a prior successful module lookup.
1522 pub fn getModuleNameForAddress(self: *DebugInfo, address: usize) ?[]const u8 {1522 pub fn getModuleNameForAddress(self: *DebugInfo, address: usize) ?[]const u8 {
1523 if (comptime builtin.target.isDarwin()) {1523 if (comptime builtin.target.isDarwin()) {
1524 return null;1524 return self.lookupModuleNameDyld(address);
1525 } else if (native_os == .windows) {1525 } else if (native_os == .windows) {
1526 return self.lookupModuleNameWin32(address);1526 return self.lookupModuleNameWin32(address);
1527 } else if (native_os == .haiku) {1527 } else if (native_os == .haiku) {
...@@ -1590,6 +1590,44 @@ pub const DebugInfo = struct {...@@ -1590,6 +1590,44 @@ pub const DebugInfo = struct {
1590 return error.MissingDebugInfo;1590 return error.MissingDebugInfo;
1591 }1591 }
15921592
1593 fn lookupModuleNameDyld(self: *DebugInfo, address: usize) ?[]const u8 {
1594 _ = self;
1595 const image_count = std.c._dyld_image_count();
1596
1597 var i: u32 = 0;
1598 while (i < image_count) : (i += 1) {
1599 const header = std.c._dyld_get_image_header(i) orelse continue;
1600 const base_address = @intFromPtr(header);
1601 if (address < base_address) continue;
1602 const vmaddr_slide = std.c._dyld_get_image_vmaddr_slide(i);
1603
1604 var it = macho.LoadCommandIterator{
1605 .ncmds = header.ncmds,
1606 .buffer = @alignCast(@as(
1607 [*]u8,
1608 @ptrFromInt(@intFromPtr(header) + @sizeOf(macho.mach_header_64)),
1609 )[0..header.sizeofcmds]),
1610 };
1611
1612 while (it.next()) |cmd| switch (cmd.cmd()) {
1613 .SEGMENT_64 => {
1614 const segment_cmd = cmd.cast(macho.segment_command_64).?;
1615 if (!mem.eql(u8, "__TEXT", segment_cmd.segName())) continue;
1616
1617 const original_address = address - vmaddr_slide;
1618 const seg_start = segment_cmd.vmaddr;
1619 const seg_end = seg_start + segment_cmd.vmsize;
1620 if (original_address >= seg_start and original_address < seg_end) {
1621 return mem.sliceTo(std.c._dyld_get_image_name(i), 0);
1622 }
1623 },
1624 else => {},
1625 };
1626 }
1627
1628 return null;
1629 }
1630
1593 fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo {1631 fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
1594 for (self.modules.items) |*module| {1632 for (self.modules.items) |*module| {
1595 if (address >= module.base_address and address < module.base_address + module.size) {1633 if (address >= module.base_address and address < module.base_address + module.size) {
lib/std/dwarf.zig+5-3
...@@ -1699,11 +1699,13 @@ pub const DwarfInfo = struct {...@@ -1699,11 +1699,13 @@ pub const DwarfInfo = struct {
1699 expression,1699 expression,
1700 context.allocator,1700 context.allocator,
1701 expression_context,1701 expression_context,
1702 context.cfa orelse 0,1702 context.cfa,
1703 );1703 );
17041704
1705 if (value != .generic) return error.InvalidExpressionValue;1705 if (value) |v| {
1706 break :blk value.generic;1706 if (v != .generic) return error.InvalidExpressionValue;
1707 break :blk v.generic;
1708 } else return error.NoExpressionValue;
1707 },1709 },
1708 else => return error.InvalidCFARule,1710 else => return error.InvalidCFARule,
1709 };1711 };
lib/std/dwarf/abi.zig+3
...@@ -110,11 +110,14 @@ pub fn regBytes(thread_context_ptr: anytype, reg_number: u8, reg_context: ?Regis...@@ -110,11 +110,14 @@ pub fn regBytes(thread_context_ptr: anytype, reg_number: u8, reg_context: ?Regis
110 0...30 => mem.asBytes(&thread_context_ptr.DUMMYUNIONNAME.X[reg_number]),110 0...30 => mem.asBytes(&thread_context_ptr.DUMMYUNIONNAME.X[reg_number]),
111 31 => mem.asBytes(&thread_context_ptr.Sp),111 31 => mem.asBytes(&thread_context_ptr.Sp),
112 32 => mem.asBytes(&thread_context_ptr.Pc),112 32 => mem.asBytes(&thread_context_ptr.Pc),
113 else => error.InvalidRegister,
113 },114 },
114 else => error.UnimplementedArch,115 else => error.UnimplementedArch,
115 };116 };
116 }117 }
117118
119 if (!std.debug.have_ucontext) return error.ThreadContextNotSupported;
120
118 const ucontext_ptr = thread_context_ptr;121 const ucontext_ptr = thread_context_ptr;
119 var m = &ucontext_ptr.mcontext;122 var m = &ucontext_ptr.mcontext;
120 return switch (builtin.cpu.arch) {123 return switch (builtin.cpu.arch) {
lib/std/dwarf/call_frame.zig+10-7
...@@ -322,19 +322,22 @@ pub const VirtualMachine = struct {...@@ -322,19 +322,22 @@ pub const VirtualMachine = struct {
322 .expression => |expression| {322 .expression => |expression| {
323 context.stack_machine.reset();323 context.stack_machine.reset();
324 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);324 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
325 const addr = if (value) |v| blk: {
326 if (v != .generic) return error.InvalidExpressionValue;
327 break :blk v.generic;
328 } else return error.NoExpressionValue;
325329
326 if (value != .generic) return error.InvalidExpressionValue;330 if (!context.isValidMemory(addr)) return error.InvalidExpressionAddress;
327 if (!context.isValidMemory(value.generic)) return error.InvalidExpressionAddress;331 const ptr: *usize = @ptrFromInt(addr);
328
329 const ptr: *usize = @ptrFromInt(value.generic);
330 mem.writeIntSliceNative(usize, out, ptr.*);332 mem.writeIntSliceNative(usize, out, ptr.*);
331 },333 },
332 .val_expression => |expression| {334 .val_expression => |expression| {
333 context.stack_machine.reset();335 context.stack_machine.reset();
334 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);336 const value = try context.stack_machine.run(expression, context.allocator, expression_context, context.cfa.?);
335337 if (value) |v| {
336 if (value != .generic) return error.InvalidExpressionValue;338 if (v != .generic) return error.InvalidExpressionValue;
337 mem.writeIntSliceNative(usize, out, value.generic);339 mem.writeIntSliceNative(usize, out, v.generic);
340 } else return error.NoExpressionValue;
338 },341 },
339 .architectural => return error.UnimplementedRegisterRule,342 .architectural => return error.UnimplementedRegisterRule,
340 }343 }
lib/std/dwarf/expressions.zig+354-57
...@@ -263,12 +263,12 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -263,12 +263,12 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
263 expression: []const u8,263 expression: []const u8,
264 allocator: std.mem.Allocator,264 allocator: std.mem.Allocator,
265 context: ExpressionContext,265 context: ExpressionContext,
266 initial_value: usize,266 initial_value: ?usize,
267 ) !Value {267 ) !?Value {
268 try self.stack.append(allocator, .{ .generic = initial_value });268 if (initial_value) |i| try self.stack.append(allocator, .{ .generic = i });
269 var stream = std.io.fixedBufferStream(expression);269 var stream = std.io.fixedBufferStream(expression);
270 while (try self.step(&stream, allocator, context)) {}270 while (try self.step(&stream, allocator, context)) {}
271 if (self.stack.items.len == 0) return error.InvalidExpression;271 if (self.stack.items.len == 0) return null;
272 return self.stack.items[self.stack.items.len - 1];272 return self.stack.items[self.stack.items.len - 1];
273 }273 }
274274
...@@ -412,7 +412,11 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -412,7 +412,11 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
412 OP.xderef,412 OP.xderef,
413 OP.xderef_size,413 OP.xderef_size,
414 OP.xderef_type,414 OP.xderef_type,
415 => try self.stack.pop().asIntegral(),415 => blk: {
416 _ = self.stack.pop();
417 if (self.stack.items.len == 0) return error.InvalidExpression;
418 break :blk try self.stack.items[self.stack.items.len - 1].asIntegral();
419 },
416 else => null,420 else => null,
417 };421 };
418422
...@@ -424,7 +428,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -424,7 +428,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
424428
425 const operand = try readOperand(stream, opcode);429 const operand = try readOperand(stream, opcode);
426 const size = switch (opcode) {430 const size = switch (opcode) {
427 OP.deref => @sizeOf(addr_type),431 OP.deref,
432 OP.xderef,
433 => @sizeOf(addr_type),
428 OP.deref_size,434 OP.deref_size,
429 OP.xderef_size,435 OP.xderef_size,
430 => operand.?.type_size,436 => operand.?.type_size,
...@@ -442,16 +448,21 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {...@@ -442,16 +448,21 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
442 else => return error.InvalidExpression,448 else => return error.InvalidExpression,
443 })) orelse return error.InvalidExpression;449 })) orelse return error.InvalidExpression;
444450
445 if (opcode == OP.deref_type) {451 switch (opcode) {
446 self.stack.items[self.stack.items.len - 1] = .{452 OP.deref_type,
447 .regval_type = .{453 OP.xderef_type,
448 .type_offset = operand.?.deref_type.type_offset,454 => {
449 .type_size = operand.?.deref_type.size,455 self.stack.items[self.stack.items.len - 1] = .{
450 .value = value,456 .regval_type = .{
451 },457 .type_offset = operand.?.deref_type.type_offset,
452 };458 .type_size = operand.?.deref_type.size,
453 } else {459 .value = value,
454 self.stack.items[self.stack.items.len - 1] = .{ .generic = value };460 },
461 };
462 },
463 else => {
464 self.stack.items[self.stack.items.len - 1] = .{ .generic = value };
465 },
455 }466 }
456 },467 },
457 OP.push_object_address,468 OP.push_object_address,
...@@ -759,7 +770,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {...@@ -759,7 +770,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {
759 OP.ne,770 OP.ne,
760 OP.nop,771 OP.nop,
761 => try writer.writeByte(opcode),772 => try writer.writeByte(opcode),
762 else => @compileError("This opcode requires operands, use write<Opcode>() instead"),773 else => @compileError("This opcode requires operands, use `write<Opcode>()` instead"),
763 }774 }
764 }775 }
765776
...@@ -836,7 +847,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {...@@ -836,7 +847,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {
836847
837 pub fn writeBreg(writer: anytype, register: u8, offset: anytype) !void {848 pub fn writeBreg(writer: anytype, register: u8, offset: anytype) !void {
838 if (register > 31) return error.InvalidRegister;849 if (register > 31) return error.InvalidRegister;
839 try writer.writeByte(OP.reg0 + register);850 try writer.writeByte(OP.breg0 + register);
840 try leb.writeILEB128(writer, offset);851 try leb.writeILEB128(writer, offset);
841 }852 }
842853
...@@ -848,7 +859,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {...@@ -848,7 +859,7 @@ pub fn Builder(comptime options: ExpressionOptions) type {
848859
849 pub fn writeRegvalType(writer: anytype, register: anytype, offset: anytype) !void {860 pub fn writeRegvalType(writer: anytype, register: anytype, offset: anytype) !void {
850 if (options.call_frame_context) return error.InvalidCFAOpcode;861 if (options.call_frame_context) return error.InvalidCFAOpcode;
851 try writer.writeByte(OP.bregx);862 try writer.writeByte(OP.regval_type);
852 try leb.writeULEB128(writer, register);863 try leb.writeULEB128(writer, register);
853 try leb.writeULEB128(writer, offset);864 try leb.writeULEB128(writer, offset);
854 }865 }
...@@ -996,35 +1007,36 @@ test "DWARF expressions" {...@@ -996,35 +1007,36 @@ test "DWARF expressions" {
9961007
997 // Constants1008 // Constants
998 {1009 {
1010 stack_machine.reset();
999 program.clearRetainingCapacity();1011 program.clearRetainingCapacity();
10001012
1001 const expected = [_]comptime_int{1013 const input = [_]comptime_int{
1002 1,1014 1,
1003 -1,1015 -1,
1004 0x0fff,1016 @as(usize, @truncate(0x0fff)),
1005 -0x0fff,1017 @as(isize, @truncate(-0x0fff)),
1006 0x0fffffff,1018 @as(usize, @truncate(0x0fffffff)),
1007 -0x0fffffff,1019 @as(isize, @truncate(-0x0fffffff)),
1008 0x0fffffffffffffff,1020 @as(usize, @truncate(0x0fffffffffffffff)),
1009 -0x0fffffffffffffff,1021 @as(isize, @truncate(-0x0fffffffffffffff)),
1010 0x8000000,1022 @as(usize, @truncate(0x8000000)),
1011 -0x8000000,1023 @as(isize, @truncate(-0x8000000)),
1012 @as(usize, @truncate(0x12345678_12345678)),1024 @as(usize, @truncate(0x12345678_12345678)),
1013 @as(usize, @truncate(0xffffffff_ffffffff)),1025 @as(usize, @truncate(0xffffffff_ffffffff)),
1014 @as(usize, @truncate(0xeeeeeeee_eeeeeeee)),1026 @as(usize, @truncate(0xeeeeeeee_eeeeeeee)),
1015 };1027 };
10161028
1017 try b.writeConst(writer, u8, expected[0]);1029 try b.writeConst(writer, u8, input[0]);
1018 try b.writeConst(writer, i8, expected[1]);1030 try b.writeConst(writer, i8, input[1]);
1019 try b.writeConst(writer, u16, expected[2]);1031 try b.writeConst(writer, u16, input[2]);
1020 try b.writeConst(writer, i16, expected[3]);1032 try b.writeConst(writer, i16, input[3]);
1021 try b.writeConst(writer, u32, expected[4]);1033 try b.writeConst(writer, u32, input[4]);
1022 try b.writeConst(writer, i32, expected[5]);1034 try b.writeConst(writer, i32, input[5]);
1023 try b.writeConst(writer, u64, expected[6]);1035 try b.writeConst(writer, u64, input[6]);
1024 try b.writeConst(writer, i64, expected[7]);1036 try b.writeConst(writer, i64, input[7]);
1025 try b.writeConst(writer, u28, expected[8]);1037 try b.writeConst(writer, u28, input[8]);
1026 try b.writeConst(writer, i28, expected[9]);1038 try b.writeConst(writer, i28, input[9]);
1027 try b.writeAddr(writer, expected[10]);1039 try b.writeAddr(writer, input[10]);
10281040
1029 var mock_compile_unit: dwarf.CompileUnit = undefined;1041 var mock_compile_unit: dwarf.CompileUnit = undefined;
1030 mock_compile_unit.addr_base = 1;1042 mock_compile_unit.addr_base = 1;
...@@ -1033,8 +1045,8 @@ test "DWARF expressions" {...@@ -1033,8 +1045,8 @@ test "DWARF expressions" {
1033 defer mock_debug_addr.deinit();1045 defer mock_debug_addr.deinit();
10341046
1035 try mock_debug_addr.writer().writeIntNative(u16, 0);1047 try mock_debug_addr.writer().writeIntNative(u16, 0);
1036 try mock_debug_addr.writer().writeIntNative(usize, expected[11]);1048 try mock_debug_addr.writer().writeIntNative(usize, input[11]);
1037 try mock_debug_addr.writer().writeIntNative(usize, expected[12]);1049 try mock_debug_addr.writer().writeIntNative(usize, input[12]);
10381050
1039 const context = ExpressionContext{1051 const context = ExpressionContext{
1040 .compile_unit = &mock_compile_unit,1052 .compile_unit = &mock_compile_unit,
...@@ -1054,26 +1066,311 @@ test "DWARF expressions" {...@@ -1054,26 +1066,311 @@ test "DWARF expressions" {
1054 try testing.expectEqual(die_offset, const_type.type_offset);1066 try testing.expectEqual(die_offset, const_type.type_offset);
1055 try testing.expectEqualSlices(u8, type_bytes, const_type.value_bytes);1067 try testing.expectEqualSlices(u8, type_bytes, const_type.value_bytes);
10561068
1057 try testing.expectEqual(@as(usize, expected[12]), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));1069 const expected = .{
1058 try testing.expectEqual(@as(usize, expected[11]), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));1070 .{ usize, input[12], usize },
1059 try testing.expectEqual(@as(usize, expected[10]), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));1071 .{ usize, input[11], usize },
1060 try testing.expectEqual(@as(isize, @truncate(expected[9])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));1072 .{ usize, input[10], usize },
1061 try testing.expectEqual(@as(usize, @truncate(expected[8])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));1073 .{ isize, input[9], isize },
1062 try testing.expectEqual(@as(isize, @truncate(expected[7])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));1074 .{ usize, input[8], usize },
1063 try testing.expectEqual(@as(usize, @truncate(expected[6])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));1075 .{ isize, input[7], isize },
1064 try testing.expectEqual(@as(isize, @truncate(expected[5])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));1076 .{ usize, input[6], usize },
1065 try testing.expectEqual(@as(usize, @truncate(expected[4])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));1077 .{ isize, input[5], isize },
1066 try testing.expectEqual(@as(isize, @truncate(expected[3])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));1078 .{ usize, input[4], usize },
1067 try testing.expectEqual(@as(usize, @truncate(expected[2])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));1079 .{ isize, input[3], isize },
1068 try testing.expectEqual(@as(isize, @truncate(expected[1])), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));1080 .{ usize, input[2], usize },
1069 try testing.expectEqual(@as(usize, @truncate(expected[0])), @as(usize, @bitCast(stack_machine.stack.popOrNull().?.generic)));1081 .{ isize, input[1], isize },
1082 .{ usize, input[0], usize },
1083 };
1084
1085 inline for (expected) |e| {
1086 try testing.expectEqual(@as(e[0], e[1]), @as(e[2], @bitCast(stack_machine.stack.popOrNull().?.generic)));
1087 }
1070 }1088 }
10711089
1072 // Register values1090 // Register values
1073 if (@TypeOf(std.debug.ThreadContext) != void) {1091 if (@sizeOf(std.debug.ThreadContext) != 0) {
1092 stack_machine.reset();
1093 program.clearRetainingCapacity();
1094
1095 const reg_context = abi.RegisterContext{
1096 .eh_frame = true,
1097 .is_macho = builtin.os.tag == .macos,
1098 };
1074 var thread_context: std.debug.ThreadContext = undefined;1099 var thread_context: std.debug.ThreadContext = undefined;
1075 _ = thread_context;1100 const context = ExpressionContext{
1101 .thread_context = &thread_context,
1102 .reg_context = reg_context,
1103 };
1104
1105 // TODO: Test fbreg (once implemented): mock a DIE and point compile_unit.frame_base at it
1106
1107 mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, 0, reg_context), 0xee);
1108 mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.fpRegNum(reg_context), reg_context), 1);
1109 mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.spRegNum(reg_context), reg_context), 2);
1110 mem.writeIntSliceNative(usize, try abi.regBytes(&thread_context, abi.ipRegNum(), reg_context), 3);
10761111
1077 // TODO: Test fbreg, breg0..31, bregx, regval_type1112 try b.writeBreg(writer, abi.fpRegNum(reg_context), @as(usize, 100));
1113 try b.writeBreg(writer, abi.spRegNum(reg_context), @as(usize, 200));
1114 try b.writeBregx(writer, abi.ipRegNum(), @as(usize, 300));
1115 try b.writeRegvalType(writer, @as(u8, 0), @as(usize, 400));
1116
1117 _ = try stack_machine.run(program.items, allocator, context, 0);
1118
1119 const regval_type = stack_machine.stack.popOrNull().?.regval_type;
1120 try testing.expectEqual(@as(usize, 400), regval_type.type_offset);
1121 try testing.expectEqual(@as(u8, @sizeOf(usize)), regval_type.type_size);
1122 try testing.expectEqual(@as(usize, 0xee), regval_type.value);
1123
1124 try testing.expectEqual(@as(usize, 303), stack_machine.stack.popOrNull().?.generic);
1125 try testing.expectEqual(@as(usize, 202), stack_machine.stack.popOrNull().?.generic);
1126 try testing.expectEqual(@as(usize, 101), stack_machine.stack.popOrNull().?.generic);
1127 }
1128
1129 // Stack operations
1130 {
1131 var context = ExpressionContext{};
1132
1133 stack_machine.reset();
1134 program.clearRetainingCapacity();
1135 try b.writeConst(writer, u8, 1);
1136 try b.writeOpcode(writer, OP.dup);
1137 _ = try stack_machine.run(program.items, allocator, context, null);
1138 try testing.expectEqual(@as(usize, 1), stack_machine.stack.popOrNull().?.generic);
1139 try testing.expectEqual(@as(usize, 1), stack_machine.stack.popOrNull().?.generic);
1140
1141 stack_machine.reset();
1142 program.clearRetainingCapacity();
1143 try b.writeConst(writer, u8, 1);
1144 try b.writeOpcode(writer, OP.drop);
1145 _ = try stack_machine.run(program.items, allocator, context, null);
1146 try testing.expect(stack_machine.stack.popOrNull() == null);
1147
1148 stack_machine.reset();
1149 program.clearRetainingCapacity();
1150 try b.writeConst(writer, u8, 4);
1151 try b.writeConst(writer, u8, 5);
1152 try b.writeConst(writer, u8, 6);
1153 try b.writePick(writer, 2);
1154 _ = try stack_machine.run(program.items, allocator, context, null);
1155 try testing.expectEqual(@as(usize, 4), stack_machine.stack.popOrNull().?.generic);
1156
1157 stack_machine.reset();
1158 program.clearRetainingCapacity();
1159 try b.writeConst(writer, u8, 4);
1160 try b.writeConst(writer, u8, 5);
1161 try b.writeConst(writer, u8, 6);
1162 try b.writeOpcode(writer, OP.over);
1163 _ = try stack_machine.run(program.items, allocator, context, null);
1164 try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
1165
1166 stack_machine.reset();
1167 program.clearRetainingCapacity();
1168 try b.writeConst(writer, u8, 5);
1169 try b.writeConst(writer, u8, 6);
1170 try b.writeOpcode(writer, OP.swap);
1171 _ = try stack_machine.run(program.items, allocator, context, null);
1172 try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
1173 try testing.expectEqual(@as(usize, 6), stack_machine.stack.popOrNull().?.generic);
1174
1175 stack_machine.reset();
1176 program.clearRetainingCapacity();
1177 try b.writeConst(writer, u8, 4);
1178 try b.writeConst(writer, u8, 5);
1179 try b.writeConst(writer, u8, 6);
1180 try b.writeOpcode(writer, OP.rot);
1181 _ = try stack_machine.run(program.items, allocator, context, null);
1182 try testing.expectEqual(@as(usize, 5), stack_machine.stack.popOrNull().?.generic);
1183 try testing.expectEqual(@as(usize, 4), stack_machine.stack.popOrNull().?.generic);
1184 try testing.expectEqual(@as(usize, 6), stack_machine.stack.popOrNull().?.generic);
1185
1186 const deref_target: usize = @truncate(0xffeeffee_ffeeffee);
1187
1188 stack_machine.reset();
1189 program.clearRetainingCapacity();
1190 try b.writeAddr(writer, @intFromPtr(&deref_target));
1191 try b.writeOpcode(writer, OP.deref);
1192 _ = try stack_machine.run(program.items, allocator, context, null);
1193 try testing.expectEqual(deref_target, stack_machine.stack.popOrNull().?.generic);
1194
1195 stack_machine.reset();
1196 program.clearRetainingCapacity();
1197 try b.writeLiteral(writer, 0);
1198 try b.writeAddr(writer, @intFromPtr(&deref_target));
1199 try b.writeOpcode(writer, OP.xderef);
1200 _ = try stack_machine.run(program.items, allocator, context, null);
1201 try testing.expectEqual(deref_target, stack_machine.stack.popOrNull().?.generic);
1202
1203 stack_machine.reset();
1204 program.clearRetainingCapacity();
1205 try b.writeAddr(writer, @intFromPtr(&deref_target));
1206 try b.writeDerefSize(writer, 1);
1207 _ = try stack_machine.run(program.items, allocator, context, null);
1208 try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), stack_machine.stack.popOrNull().?.generic);
1209
1210 stack_machine.reset();
1211 program.clearRetainingCapacity();
1212 try b.writeLiteral(writer, 0);
1213 try b.writeAddr(writer, @intFromPtr(&deref_target));
1214 try b.writeXDerefSize(writer, 1);
1215 _ = try stack_machine.run(program.items, allocator, context, null);
1216 try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), stack_machine.stack.popOrNull().?.generic);
1217
1218 const type_offset: usize = @truncate(0xaabbaabb_aabbaabb);
1219
1220 stack_machine.reset();
1221 program.clearRetainingCapacity();
1222 try b.writeAddr(writer, @intFromPtr(&deref_target));
1223 try b.writeDerefType(writer, 1, type_offset);
1224 _ = try stack_machine.run(program.items, allocator, context, null);
1225 const deref_type = stack_machine.stack.popOrNull().?.regval_type;
1226 try testing.expectEqual(type_offset, deref_type.type_offset);
1227 try testing.expectEqual(@as(u8, 1), deref_type.type_size);
1228 try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), deref_type.value);
1229
1230 stack_machine.reset();
1231 program.clearRetainingCapacity();
1232 try b.writeLiteral(writer, 0);
1233 try b.writeAddr(writer, @intFromPtr(&deref_target));
1234 try b.writeXDerefType(writer, 1, type_offset);
1235 _ = try stack_machine.run(program.items, allocator, context, null);
1236 const xderef_type = stack_machine.stack.popOrNull().?.regval_type;
1237 try testing.expectEqual(type_offset, xderef_type.type_offset);
1238 try testing.expectEqual(@as(u8, 1), xderef_type.type_size);
1239 try testing.expectEqual(@as(usize, @as(*const u8, @ptrCast(&deref_target)).*), xderef_type.value);
1240
1241 // TODO: Test OP.push_object_address
1242 // TODO: Test OP.form_tls_address
1243
1244 context.cfa = @truncate(0xccddccdd_ccddccdd);
1245
1246 stack_machine.reset();
1247 program.clearRetainingCapacity();
1248 try b.writeOpcode(writer, OP.call_frame_cfa);
1249 _ = try stack_machine.run(program.items, allocator, context, null);
1250 try testing.expectEqual(context.cfa.?, stack_machine.stack.popOrNull().?.generic);
1251 }
1252
1253 // Arithmetic and Logical Operations
1254 {
1255 var context = ExpressionContext{};
1256
1257 stack_machine.reset();
1258 program.clearRetainingCapacity();
1259 try b.writeConst(writer, i16, -4096);
1260 try b.writeOpcode(writer, OP.abs);
1261 _ = try stack_machine.run(program.items, allocator, context, null);
1262 try testing.expectEqual(@as(usize, 4096), stack_machine.stack.popOrNull().?.generic);
1263
1264 stack_machine.reset();
1265 program.clearRetainingCapacity();
1266 try b.writeConst(writer, u16, 0xff0f);
1267 try b.writeConst(writer, u16, 0xf0ff);
1268 try b.writeOpcode(writer, OP.@"and");
1269 _ = try stack_machine.run(program.items, allocator, context, null);
1270 try testing.expectEqual(@as(usize, 0xf00f), stack_machine.stack.popOrNull().?.generic);
1271
1272 stack_machine.reset();
1273 program.clearRetainingCapacity();
1274 try b.writeConst(writer, i16, -404);
1275 try b.writeConst(writer, i16, 100);
1276 try b.writeOpcode(writer, OP.div);
1277 _ = try stack_machine.run(program.items, allocator, context, null);
1278 try testing.expectEqual(@as(isize, -404 / 100), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
1279
1280 stack_machine.reset();
1281 program.clearRetainingCapacity();
1282 try b.writeConst(writer, u16, 200);
1283 try b.writeConst(writer, u16, 50);
1284 try b.writeOpcode(writer, OP.minus);
1285 _ = try stack_machine.run(program.items, allocator, context, null);
1286 try testing.expectEqual(@as(usize, 150), stack_machine.stack.popOrNull().?.generic);
1287
1288 stack_machine.reset();
1289 program.clearRetainingCapacity();
1290 try b.writeConst(writer, u16, 123);
1291 try b.writeConst(writer, u16, 100);
1292 try b.writeOpcode(writer, OP.mod);
1293 _ = try stack_machine.run(program.items, allocator, context, null);
1294 try testing.expectEqual(@as(usize, 23), stack_machine.stack.popOrNull().?.generic);
1295
1296 stack_machine.reset();
1297 program.clearRetainingCapacity();
1298 try b.writeConst(writer, u16, 0xff);
1299 try b.writeConst(writer, u16, 0xee);
1300 try b.writeOpcode(writer, OP.mul);
1301 _ = try stack_machine.run(program.items, allocator, context, null);
1302 try testing.expectEqual(@as(usize, 0xed12), stack_machine.stack.popOrNull().?.generic);
1303
1304 stack_machine.reset();
1305 program.clearRetainingCapacity();
1306 try b.writeConst(writer, u16, 5);
1307 try b.writeOpcode(writer, OP.neg);
1308 try b.writeConst(writer, i16, -6);
1309 try b.writeOpcode(writer, OP.neg);
1310 _ = try stack_machine.run(program.items, allocator, context, null);
1311 try testing.expectEqual(@as(usize, 6), stack_machine.stack.popOrNull().?.generic);
1312 try testing.expectEqual(@as(isize, -5), @as(isize, @bitCast(stack_machine.stack.popOrNull().?.generic)));
1313
1314 stack_machine.reset();
1315 program.clearRetainingCapacity();
1316 try b.writeConst(writer, u16, 0xff0f);
1317 try b.writeOpcode(writer, OP.not);
1318 _ = try stack_machine.run(program.items, allocator, context, null);
1319 try testing.expectEqual(~@as(usize, 0xff0f), stack_machine.stack.popOrNull().?.generic);
1320
1321 stack_machine.reset();
1322 program.clearRetainingCapacity();
1323 try b.writeConst(writer, u16, 0xff0f);
1324 try b.writeConst(writer, u16, 0xf0ff);
1325 try b.writeOpcode(writer, OP.@"or");
1326 _ = try stack_machine.run(program.items, allocator, context, null);
1327 try testing.expectEqual(@as(usize, 0xffff), stack_machine.stack.popOrNull().?.generic);
1328
1329 stack_machine.reset();
1330 program.clearRetainingCapacity();
1331 try b.writeConst(writer, i16, 402);
1332 try b.writeConst(writer, i16, 100);
1333 try b.writeOpcode(writer, OP.plus);
1334 _ = try stack_machine.run(program.items, allocator, context, null);
1335 try testing.expectEqual(@as(usize, 502), stack_machine.stack.popOrNull().?.generic);
1336
1337 stack_machine.reset();
1338 program.clearRetainingCapacity();
1339 try b.writeConst(writer, u16, 4096);
1340 try b.writePlusUconst(writer, @as(usize, 8192));
1341 _ = try stack_machine.run(program.items, allocator, context, null);
1342 try testing.expectEqual(@as(usize, 4096 + 8192), stack_machine.stack.popOrNull().?.generic);
1343
1344 stack_machine.reset();
1345 program.clearRetainingCapacity();
1346 try b.writeConst(writer, u16, 0xfff);
1347 try b.writeConst(writer, u16, 1);
1348 try b.writeOpcode(writer, OP.shl);
1349 _ = try stack_machine.run(program.items, allocator, context, null);
1350 try testing.expectEqual(@as(usize, 0xfff << 1), stack_machine.stack.popOrNull().?.generic);
1351
1352 stack_machine.reset();
1353 program.clearRetainingCapacity();
1354 try b.writeConst(writer, u16, 0xfff);
1355 try b.writeConst(writer, u16, 1);
1356 try b.writeOpcode(writer, OP.shr);
1357 _ = try stack_machine.run(program.items, allocator, context, null);
1358 try testing.expectEqual(@as(usize, 0xfff >> 1), stack_machine.stack.popOrNull().?.generic);
1359
1360 stack_machine.reset();
1361 program.clearRetainingCapacity();
1362 try b.writeConst(writer, u16, 0xfff);
1363 try b.writeConst(writer, u16, 1);
1364 try b.writeOpcode(writer, OP.shr);
1365 _ = try stack_machine.run(program.items, allocator, context, null);
1366 try testing.expectEqual(@as(usize, @bitCast(@as(isize, 0xfff) >> 1)), stack_machine.stack.popOrNull().?.generic);
1367
1368 stack_machine.reset();
1369 program.clearRetainingCapacity();
1370 try b.writeConst(writer, u16, 0xf0ff);
1371 try b.writeConst(writer, u16, 0xff0f);
1372 try b.writeOpcode(writer, OP.xor);
1373 _ = try stack_machine.run(program.items, allocator, context, null);
1374 try testing.expectEqual(@as(usize, 0x0ff0), stack_machine.stack.popOrNull().?.generic);
1078 }1375 }
1079}1376}