authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-04-16 18:48:33-07:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-05-11 02:17:24-07:00
log2fd83d8c0a8dd28c2474b26ead8cb24d6bde0901
treec3293e36e9233a9c9e7ce4b4d56677173662ae9e
parenta30af172e8dc360cb0a71a5c4dfd904120555715

riscv: by-value structs + `@min`


15 files changed, 174 insertions(+), 35 deletions(-)

src/arch/riscv64/CodeGen.zig+89-10
......@@ -1800,8 +1800,95 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
18001800}
18011801
18021802fn airMin(self: *Self, inst: Air.Inst.Index) !void {
1803 const zcu = self.bin_file.comp.module.?;
18031804 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
1804 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement min for {}", .{self.target.cpu.arch});
1805
1806 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
1807 const lhs = try self.resolveInst(bin_op.lhs);
1808 const rhs = try self.resolveInst(bin_op.rhs);
1809 const lhs_ty = self.typeOf(bin_op.lhs);
1810 const rhs_ty = self.typeOf(bin_op.rhs);
1811
1812 const int_info = lhs_ty.intInfo(zcu);
1813
1814 if (int_info.bits > 64) return self.fail("TODO: > 64 bit @min", .{});
1815
1816 const lhs_reg, const lhs_lock = blk: {
1817 if (lhs == .register) break :blk .{ lhs.register, null };
1818
1819 const lhs_reg, const lhs_lock = try self.allocReg();
1820 try self.genSetReg(lhs_ty, lhs_reg, lhs);
1821 break :blk .{ lhs_reg, lhs_lock };
1822 };
1823 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
1824
1825 const rhs_reg, const rhs_lock = blk: {
1826 if (rhs == .register) break :blk .{ rhs.register, null };
1827
1828 const rhs_reg, const rhs_lock = try self.allocReg();
1829 try self.genSetReg(rhs_ty, rhs_reg, rhs);
1830 break :blk .{ rhs_reg, rhs_lock };
1831 };
1832 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
1833
1834 const mask_reg, const mask_lock = try self.allocReg();
1835 defer self.register_manager.unlockReg(mask_lock);
1836
1837 const result_reg, const result_lock = try self.allocReg();
1838 defer self.register_manager.unlockReg(result_lock);
1839
1840 _ = try self.addInst(.{
1841 .tag = if (int_info.signedness == .unsigned) .sltu else .slt,
1842 .ops = .rrr,
1843 .data = .{ .r_type = .{
1844 .rd = mask_reg,
1845 .rs1 = lhs_reg,
1846 .rs2 = rhs_reg,
1847 } },
1848 });
1849
1850 _ = try self.addInst(.{
1851 .tag = .sub,
1852 .ops = .rrr,
1853 .data = .{ .r_type = .{
1854 .rd = mask_reg,
1855 .rs1 = .zero,
1856 .rs2 = mask_reg,
1857 } },
1858 });
1859
1860 _ = try self.addInst(.{
1861 .tag = .xor,
1862 .ops = .rrr,
1863 .data = .{ .r_type = .{
1864 .rd = result_reg,
1865 .rs1 = lhs_reg,
1866 .rs2 = rhs_reg,
1867 } },
1868 });
1869
1870 _ = try self.addInst(.{
1871 .tag = .@"and",
1872 .ops = .rrr,
1873 .data = .{ .r_type = .{
1874 .rd = mask_reg,
1875 .rs1 = result_reg,
1876 .rs2 = mask_reg,
1877 } },
1878 });
1879
1880 _ = try self.addInst(.{
1881 .tag = .xor,
1882 .ops = .rrr,
1883 .data = .{ .r_type = .{
1884 .rd = result_reg,
1885 .rs1 = rhs_reg,
1886 .rs2 = mask_reg,
1887 } },
1888 });
1889
1890 break :result .{ .register = result_reg };
1891 };
18051892 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
18061893}
18071894
......@@ -3513,17 +3600,9 @@ fn genCall(
35133600 .imm12 = Immediate.s(0),
35143601 } },
35153602 });
3516 } else if (self.bin_file.cast(link.File.Coff)) |_| {
3517 return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch});
3518 } else if (self.bin_file.cast(link.File.MachO)) |_| {
3519 unreachable; // unsupported architecture for MachO
3520 } else if (self.bin_file.cast(link.File.Plan9)) |_| {
3521 return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch});
35223603 } else unreachable;
35233604 },
3524 .extern_func => {
3525 return self.fail("TODO: extern func calls", .{});
3526 },
3605 .extern_func => return self.fail("TODO: extern func calls", .{}),
35273606 else => return self.fail("TODO implement calling bitcasted functions", .{}),
35283607 }
35293608 } else {
src/arch/riscv64/Encoding.zig+5-2
......@@ -11,7 +11,6 @@ pub const Mnemonic = enum {
1111 lb,
1212 lbu,
1313 sltiu,
14 sltu,
1514 xori,
1615 andi,
1716 slli,
......@@ -38,9 +37,11 @@ pub const Mnemonic = enum {
3837
3938 // R Type
4039 add,
40 @"and",
4141 sub,
4242 slt,
4343 mul,
44 sltu,
4445 xor,
4546
4647 // System
......@@ -52,6 +53,8 @@ pub const Mnemonic = enum {
5253 return switch (mnem) {
5354 // zig fmt: off
5455 .add => .{ .opcode = 0b0110011, .funct3 = 0b000, .funct7 = 0b0000000 },
56 .sltu => .{ .opcode = 0b0110011, .funct3 = 0b011, .funct7 = 0b0000000 },
57 .@"and" => .{ .opcode = 0b0110011, .funct3 = 0b111, .funct7 = 0b0000000 },
5558 .sub => .{ .opcode = 0b0110011, .funct3 = 0b000, .funct7 = 0b0100000 },
5659
5760 .ld => .{ .opcode = 0b0000011, .funct3 = 0b011, .funct7 = null },
......@@ -84,7 +87,6 @@ pub const Mnemonic = enum {
8487 .beq => .{ .opcode = 0b1100011, .funct3 = 0b000, .funct7 = null },
8588
8689 .slt => .{ .opcode = 0b0110011, .funct3 = 0b010, .funct7 = 0b0000000 },
87 .sltu => .{ .opcode = 0b0110011, .funct3 = 0b011, .funct7 = 0b0000000 },
8890
8991 .xor => .{ .opcode = 0b0110011, .funct3 = 0b100, .funct7 = 0b0000000 },
9092
......@@ -149,6 +151,7 @@ pub const InstEnc = enum {
149151 .xor,
150152 .add,
151153 .sub,
154 .@"and",
152155 => .R,
153156
154157 .ecall,
src/arch/riscv64/Mir.zig+6
......@@ -32,6 +32,9 @@ pub const Inst = struct {
3232 lui,
3333 mv,
3434
35 @"and",
36 xor,
37
3538 ebreak,
3639 ecall,
3740 unimp,
......@@ -49,6 +52,9 @@ pub const Inst = struct {
4952 /// Absolute Value, uses i_type payload.
5053 abs,
5154
55 sltu,
56 slt,
57
5258 /// Immediate Logical Right Shift, uses i_type payload
5359 srli,
5460 /// Immediate Logical Left Shift, uses i_type payload
src/arch/riscv64/abi.zig+74-3
......@@ -3,6 +3,7 @@ const bits = @import("bits.zig");
33const Register = bits.Register;
44const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
55const Type = @import("../../type.zig").Type;
6const InternPool = @import("../../InternPool.zig");
67const Module = @import("../../Module.zig");
78const assert = std.debug.assert;
89
......@@ -97,7 +98,10 @@ pub fn classifyType(ty: Type, mod: *Module) Class {
9798pub fn classifySystem(ty: Type, zcu: *Module) [8]Class {
9899 const ip = zcu.intern_pool;
99100 var result = [1]Class{.none} ** 8;
100
101 const memory_class = [_]Class{
102 .memory, .none, .none, .none,
103 .none, .none, .none, .none,
104 };
101105 switch (ty.zigTypeTag(zcu)) {
102106 .Bool, .Void, .NoReturn => {
103107 result[0] = .integer;
......@@ -146,7 +150,12 @@ pub fn classifySystem(ty: Type, zcu: *Module) [8]Class {
146150 // anyerror!void can fit into one register
147151 if (payload_bits == 0) return result;
148152
149 std.debug.panic("support ErrorUnion payload {}", .{payload_ty.fmt(zcu)});
153 if (payload_bits <= 64) {
154 result[1] = .integer;
155 return result;
156 }
157
158 std.debug.panic("TODO: classifySystem ErrorUnion > 64 bit payload", .{});
150159 },
151160 .Struct => {
152161 const loaded_struct = ip.loadStructType(ty.toIntern());
......@@ -158,13 +167,75 @@ pub fn classifySystem(ty: Type, zcu: *Module) [8]Class {
158167 if (ty_size > 8) result[1] = .integer;
159168 return result;
160169 }
170 if (ty_size > 64)
171 return memory_class;
161172
162 std.debug.panic("support Struct in classifySystem", .{});
173 var byte_offset: u64 = 0;
174 classifyStruct(&result, &byte_offset, loaded_struct, zcu);
175
176 return result;
163177 },
164178 else => |bad_ty| std.debug.panic("classifySystem {s}", .{@tagName(bad_ty)}),
165179 }
166180}
167181
182fn classifyStruct(
183 result: *[8]Class,
184 byte_offset: *u64,
185 loaded_struct: InternPool.LoadedStructType,
186 zcu: *Module,
187) void {
188 const ip = &zcu.intern_pool;
189 var field_it = loaded_struct.iterateRuntimeOrder(ip);
190
191 while (field_it.next()) |field_index| {
192 const field_ty = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]);
193 const field_align = loaded_struct.fieldAlign(ip, field_index);
194 byte_offset.* = std.mem.alignForward(
195 u64,
196 byte_offset.*,
197 field_align.toByteUnits() orelse field_ty.abiAlignment(zcu).toByteUnits().?,
198 );
199 if (zcu.typeToStruct(field_ty)) |field_loaded_struct| {
200 if (field_loaded_struct.layout != .@"packed") {
201 classifyStruct(result, byte_offset, field_loaded_struct, zcu);
202 continue;
203 }
204 }
205 const field_class = std.mem.sliceTo(&classifySystem(field_ty, zcu), .none);
206 const field_size = field_ty.abiSize(zcu);
207
208 combine: {
209 const result_class = &result[@intCast(byte_offset.* / 8)];
210 if (result_class.* == field_class[0]) {
211 break :combine;
212 }
213
214 if (result_class.* == .none) {
215 result_class.* = field_class[0];
216 break :combine;
217 }
218 assert(field_class[0] != .none);
219
220 // "If one of the classes is MEMORY, the result is the MEMORY class."
221 if (result_class.* == .memory or field_class[0] == .memory) {
222 result_class.* = .memory;
223 break :combine;
224 }
225
226 // "If one of the classes is INTEGER, the result is the INTEGER."
227 if (result_class.* == .integer or field_class[0] == .integer) {
228 result_class.* = .integer;
229 break :combine;
230 }
231
232 result_class.* = .integer;
233 }
234 @memcpy(result[@intCast(byte_offset.* / 8 + 1)..][0 .. field_class.len - 1], field_class[1..]);
235 byte_offset.* += field_size;
236 }
237}
238
168239pub const callee_preserved_regs = [_]Register{
169240 // .s0 is ommited to be used as a frame pointer
170241 .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
test/behavior/array.zig-1
......@@ -75,7 +75,6 @@ test "array concat with tuple" {
7575 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
7676 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7777 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
78 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
7978
8079 const array: [2]u8 = .{ 1, 2 };
8180 {
test/behavior/basic.zig-1
......@@ -593,7 +593,6 @@ test "equality compare fn ptrs" {
593593
594594test "self reference through fn ptr field" {
595595 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
596 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
597596
598597 const S = struct {
599598 const A = struct {
test/behavior/cast.zig-2
......@@ -2073,7 +2073,6 @@ test "peer type resolution: empty tuple pointer and slice" {
20732073 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
20742074 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
20752075 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2076 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
20772076
20782077 var a: [:0]const u8 = "Hello";
20792078 var b = &.{};
......@@ -2095,7 +2094,6 @@ test "peer type resolution: tuple pointer and slice" {
20952094 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
20962095 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
20972096 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2098 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
20992097
21002098 var a: [:0]const u8 = "Hello";
21012099 var b = &.{ @as(u8, 'x'), @as(u8, 'y'), @as(u8, 'z') };
test/behavior/fn.zig-3
......@@ -191,7 +191,6 @@ test "function with complex callconv and return type expressions" {
191191
192192test "pass by non-copying value" {
193193 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
194 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
195194
196195 try expect(addPointCoords(Point{ .x = 1, .y = 2 }) == 3);
197196}
......@@ -219,7 +218,6 @@ fn addPointCoordsVar(pt: anytype) !i32 {
219218
220219test "pass by non-copying value as method" {
221220 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
222 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
223221
224222 var pt = Point2{ .x = 1, .y = 2 };
225223 try expect(pt.addPointCoords() == 3);
......@@ -236,7 +234,6 @@ const Point2 = struct {
236234
237235test "pass by non-copying value as method, which is generic" {
238236 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
239 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
240237
241238 var pt = Point3{ .x = 1, .y = 2 };
242239 try expect(pt.addPointCoords(i32) == 3);
test/behavior/fn_delegation.zig-1
......@@ -34,7 +34,6 @@ fn custom(comptime T: type, comptime num: u64) fn (T) u64 {
3434test "fn delegation" {
3535 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
3636 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
37 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
3837
3938 const foo = Foo{};
4039 try expect(foo.one() == 11);
test/behavior/generics.zig-1
......@@ -395,7 +395,6 @@ test "extern function used as generic parameter" {
395395
396396test "generic struct as parameter type" {
397397 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
398 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
399398
400399 const S = struct {
401400 fn doTheTest(comptime Int: type, thing: struct { int: Int }) !void {
test/behavior/pointers.zig-1
......@@ -434,7 +434,6 @@ test "indexing array with sentinel returns correct type" {
434434 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
435435 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
436436 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
437 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
438437
439438 var s: [:0]const u8 = "abc";
440439 try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0])));
test/behavior/sizeof_and_typeof.zig-1
......@@ -412,7 +412,6 @@ test "Extern function calls, dereferences and field access in @TypeOf" {
412412 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
413413 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
414414 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
415 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
416415
417416 const Test = struct {
418417 fn test_fn_1(a: c_long) @TypeOf(c_fopen("test", "r").*) {
test/behavior/slice.zig-1
......@@ -939,7 +939,6 @@ test "modify slice length at comptime" {
939939 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
940940 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
941941 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
942 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
943942
944943 const arr: [2]u8 = .{ 10, 20 };
945944 comptime var s: []const u8 = arr[0..0];
test/behavior/struct.zig-7
......@@ -176,7 +176,6 @@ const MemberFnTestFoo = struct {
176176
177177test "call member function directly" {
178178 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
179 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
180179
181180 const instance = MemberFnTestFoo{ .x = 1234 };
182181 const result = MemberFnTestFoo.member(instance);
......@@ -185,7 +184,6 @@ test "call member function directly" {
185184
186185test "store member function in variable" {
187186 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
188 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
189187
190188 const instance = MemberFnTestFoo{ .x = 1234 };
191189 const memberFn = MemberFnTestFoo.member;
......@@ -1561,7 +1559,6 @@ test "discarded struct initialization works as expected" {
15611559test "function pointer in struct returns the struct" {
15621560 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
15631561 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1564 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
15651562
15661563 const A = struct {
15671564 const A = @This();
......@@ -1784,8 +1781,6 @@ fn countFields(v: anytype) usize {
17841781}
17851782
17861783test "struct init with no result pointer sets field result types" {
1787 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1788
17891784 const S = struct {
17901785 // A function parameter has a result type, but no result pointer.
17911786 fn f(s: struct { x: u32 }) u32 {
......@@ -1933,8 +1928,6 @@ test "circular dependency through pointer field of a struct" {
19331928}
19341929
19351930test "field calls do not force struct field init resolution" {
1936 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1937
19381931 const S = struct {
19391932 x: u32 = blk: {
19401933 _ = @TypeOf(make().dummyFn()); // runtime field call - S not fully resolved - dummyFn call should not force field init resolution
test/behavior/type.zig-1
......@@ -203,7 +203,6 @@ test "Type.Opaque" {
203203 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
204204 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
205205 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
206 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
207206
208207 const Opaque = @Type(.{
209208 .Opaque = .{