diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 7ed1174433..38d77c9c5f 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1800,8 +1800,95 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { } fn airMin(self: *Self, inst: Air.Inst.Index) !void { + const zcu = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement min for {}", .{self.target.cpu.arch}); + + const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: { + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.typeOf(bin_op.lhs); + const rhs_ty = self.typeOf(bin_op.rhs); + + const int_info = lhs_ty.intInfo(zcu); + + if (int_info.bits > 64) return self.fail("TODO: > 64 bit @min", .{}); + + const lhs_reg, const lhs_lock = blk: { + if (lhs == .register) break :blk .{ lhs.register, null }; + + const lhs_reg, const lhs_lock = try self.allocReg(); + try self.genSetReg(lhs_ty, lhs_reg, lhs); + break :blk .{ lhs_reg, lhs_lock }; + }; + defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); + + const rhs_reg, const rhs_lock = blk: { + if (rhs == .register) break :blk .{ rhs.register, null }; + + const rhs_reg, const rhs_lock = try self.allocReg(); + try self.genSetReg(rhs_ty, rhs_reg, rhs); + break :blk .{ rhs_reg, rhs_lock }; + }; + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + + const mask_reg, const mask_lock = try self.allocReg(); + defer self.register_manager.unlockReg(mask_lock); + + const result_reg, const result_lock = try self.allocReg(); + defer self.register_manager.unlockReg(result_lock); + + _ = try self.addInst(.{ + .tag = if (int_info.signedness == .unsigned) .sltu else .slt, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = mask_reg, + .rs1 = lhs_reg, + .rs2 = rhs_reg, + } }, + }); + + _ = try self.addInst(.{ + .tag = .sub, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = mask_reg, + .rs1 = .zero, + .rs2 = mask_reg, + } }, + }); + + _ = try self.addInst(.{ + .tag = .xor, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = result_reg, + .rs1 = lhs_reg, + .rs2 = rhs_reg, + } }, + }); + + _ = try self.addInst(.{ + .tag = .@"and", + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = mask_reg, + .rs1 = result_reg, + .rs2 = mask_reg, + } }, + }); + + _ = try self.addInst(.{ + .tag = .xor, + .ops = .rrr, + .data = .{ .r_type = .{ + .rd = result_reg, + .rs1 = rhs_reg, + .rs2 = mask_reg, + } }, + }); + + break :result .{ .register = result_reg }; + }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -3513,17 +3600,9 @@ fn genCall( .imm12 = Immediate.s(0), } }, }); - } else if (self.bin_file.cast(link.File.Coff)) |_| { - return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch}); - } else if (self.bin_file.cast(link.File.MachO)) |_| { - unreachable; // unsupported architecture for MachO - } else if (self.bin_file.cast(link.File.Plan9)) |_| { - return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}); } else unreachable; }, - .extern_func => { - return self.fail("TODO: extern func calls", .{}); - }, + .extern_func => return self.fail("TODO: extern func calls", .{}), else => return self.fail("TODO implement calling bitcasted functions", .{}), } } else { diff --git a/src/arch/riscv64/Encoding.zig b/src/arch/riscv64/Encoding.zig index 7953bb0cca..91f100993b 100644 --- a/src/arch/riscv64/Encoding.zig +++ b/src/arch/riscv64/Encoding.zig @@ -11,7 +11,6 @@ pub const Mnemonic = enum { lb, lbu, sltiu, - sltu, xori, andi, slli, @@ -38,9 +37,11 @@ pub const Mnemonic = enum { // R Type add, + @"and", sub, slt, mul, + sltu, xor, // System @@ -52,6 +53,8 @@ pub const Mnemonic = enum { return switch (mnem) { // zig fmt: off .add => .{ .opcode = 0b0110011, .funct3 = 0b000, .funct7 = 0b0000000 }, + .sltu => .{ .opcode = 0b0110011, .funct3 = 0b011, .funct7 = 0b0000000 }, + .@"and" => .{ .opcode = 0b0110011, .funct3 = 0b111, .funct7 = 0b0000000 }, .sub => .{ .opcode = 0b0110011, .funct3 = 0b000, .funct7 = 0b0100000 }, .ld => .{ .opcode = 0b0000011, .funct3 = 0b011, .funct7 = null }, @@ -84,7 +87,6 @@ pub const Mnemonic = enum { .beq => .{ .opcode = 0b1100011, .funct3 = 0b000, .funct7 = null }, .slt => .{ .opcode = 0b0110011, .funct3 = 0b010, .funct7 = 0b0000000 }, - .sltu => .{ .opcode = 0b0110011, .funct3 = 0b011, .funct7 = 0b0000000 }, .xor => .{ .opcode = 0b0110011, .funct3 = 0b100, .funct7 = 0b0000000 }, @@ -149,6 +151,7 @@ pub const InstEnc = enum { .xor, .add, .sub, + .@"and", => .R, .ecall, diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 08bda25de6..0ce2185197 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -32,6 +32,9 @@ pub const Inst = struct { lui, mv, + @"and", + xor, + ebreak, ecall, unimp, @@ -49,6 +52,9 @@ pub const Inst = struct { /// Absolute Value, uses i_type payload. abs, + sltu, + slt, + /// Immediate Logical Right Shift, uses i_type payload srli, /// Immediate Logical Left Shift, uses i_type payload diff --git a/src/arch/riscv64/abi.zig b/src/arch/riscv64/abi.zig index 9fbb63638e..5c5b0b0acd 100644 --- a/src/arch/riscv64/abi.zig +++ b/src/arch/riscv64/abi.zig @@ -3,6 +3,7 @@ const bits = @import("bits.zig"); const Register = bits.Register; const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; const Type = @import("../../type.zig").Type; +const InternPool = @import("../../InternPool.zig"); const Module = @import("../../Module.zig"); const assert = std.debug.assert; @@ -97,7 +98,10 @@ pub fn classifyType(ty: Type, mod: *Module) Class { pub fn classifySystem(ty: Type, zcu: *Module) [8]Class { const ip = zcu.intern_pool; var result = [1]Class{.none} ** 8; - + const memory_class = [_]Class{ + .memory, .none, .none, .none, + .none, .none, .none, .none, + }; switch (ty.zigTypeTag(zcu)) { .Bool, .Void, .NoReturn => { result[0] = .integer; @@ -146,7 +150,12 @@ pub fn classifySystem(ty: Type, zcu: *Module) [8]Class { // anyerror!void can fit into one register if (payload_bits == 0) return result; - std.debug.panic("support ErrorUnion payload {}", .{payload_ty.fmt(zcu)}); + if (payload_bits <= 64) { + result[1] = .integer; + return result; + } + + std.debug.panic("TODO: classifySystem ErrorUnion > 64 bit payload", .{}); }, .Struct => { const loaded_struct = ip.loadStructType(ty.toIntern()); @@ -158,13 +167,75 @@ pub fn classifySystem(ty: Type, zcu: *Module) [8]Class { if (ty_size > 8) result[1] = .integer; return result; } + if (ty_size > 64) + return memory_class; - std.debug.panic("support Struct in classifySystem", .{}); + var byte_offset: u64 = 0; + classifyStruct(&result, &byte_offset, loaded_struct, zcu); + + return result; }, else => |bad_ty| std.debug.panic("classifySystem {s}", .{@tagName(bad_ty)}), } } +fn classifyStruct( + result: *[8]Class, + byte_offset: *u64, + loaded_struct: InternPool.LoadedStructType, + zcu: *Module, +) void { + const ip = &zcu.intern_pool; + var field_it = loaded_struct.iterateRuntimeOrder(ip); + + while (field_it.next()) |field_index| { + const field_ty = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]); + const field_align = loaded_struct.fieldAlign(ip, field_index); + byte_offset.* = std.mem.alignForward( + u64, + byte_offset.*, + field_align.toByteUnits() orelse field_ty.abiAlignment(zcu).toByteUnits().?, + ); + if (zcu.typeToStruct(field_ty)) |field_loaded_struct| { + if (field_loaded_struct.layout != .@"packed") { + classifyStruct(result, byte_offset, field_loaded_struct, zcu); + continue; + } + } + const field_class = std.mem.sliceTo(&classifySystem(field_ty, zcu), .none); + const field_size = field_ty.abiSize(zcu); + + combine: { + const result_class = &result[@intCast(byte_offset.* / 8)]; + if (result_class.* == field_class[0]) { + break :combine; + } + + if (result_class.* == .none) { + result_class.* = field_class[0]; + break :combine; + } + assert(field_class[0] != .none); + + // "If one of the classes is MEMORY, the result is the MEMORY class." + if (result_class.* == .memory or field_class[0] == .memory) { + result_class.* = .memory; + break :combine; + } + + // "If one of the classes is INTEGER, the result is the INTEGER." + if (result_class.* == .integer or field_class[0] == .integer) { + result_class.* = .integer; + break :combine; + } + + result_class.* = .integer; + } + @memcpy(result[@intCast(byte_offset.* / 8 + 1)..][0 .. field_class.len - 1], field_class[1..]); + byte_offset.* += field_size; + } +} + pub const callee_preserved_regs = [_]Register{ // .s0 is ommited to be used as a frame pointer .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 1759e5c696..d524023c9b 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -75,7 +75,6 @@ test "array concat with tuple" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const array: [2]u8 = .{ 1, 2 }; { diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index eabac35787..ad955a8648 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -593,7 +593,6 @@ test "equality compare fn ptrs" { test "self reference through fn ptr field" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const A = struct { diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 3ea0d800cc..1113fcfeaa 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -2073,7 +2073,6 @@ test "peer type resolution: empty tuple pointer and slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var a: [:0]const u8 = "Hello"; var b = &.{}; @@ -2095,7 +2094,6 @@ test "peer type resolution: tuple pointer and slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var a: [:0]const u8 = "Hello"; var b = &.{ @as(u8, 'x'), @as(u8, 'y'), @as(u8, 'z') }; diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 0e14af68fa..b242d29d83 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -191,7 +191,6 @@ test "function with complex callconv and return type expressions" { test "pass by non-copying value" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expect(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); } @@ -219,7 +218,6 @@ fn addPointCoordsVar(pt: anytype) !i32 { test "pass by non-copying value as method" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var pt = Point2{ .x = 1, .y = 2 }; try expect(pt.addPointCoords() == 3); @@ -236,7 +234,6 @@ const Point2 = struct { test "pass by non-copying value as method, which is generic" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var pt = Point3{ .x = 1, .y = 2 }; try expect(pt.addPointCoords(i32) == 3); diff --git a/test/behavior/fn_delegation.zig b/test/behavior/fn_delegation.zig index 6a3d46c15d..95dbfeb4b2 100644 --- a/test/behavior/fn_delegation.zig +++ b/test/behavior/fn_delegation.zig @@ -34,7 +34,6 @@ fn custom(comptime T: type, comptime num: u64) fn (T) u64 { test "fn delegation" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const foo = Foo{}; try expect(foo.one() == 11); diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 7ed75f0ead..2c3dfaba01 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -395,7 +395,6 @@ test "extern function used as generic parameter" { test "generic struct as parameter type" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest(comptime Int: type, thing: struct { int: Int }) !void { diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 331a5689c8..ffeeca3986 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -434,7 +434,6 @@ test "indexing array with sentinel returns correct type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var s: [:0]const u8 = "abc"; try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0]))); diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 506baa2666..1be9ab1c3a 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -412,7 +412,6 @@ test "Extern function calls, dereferences and field access in @TypeOf" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const Test = struct { fn test_fn_1(a: c_long) @TypeOf(c_fopen("test", "r").*) { diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 1bcfc42dd5..437d248127 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -939,7 +939,6 @@ test "modify slice length at comptime" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const arr: [2]u8 = .{ 10, 20 }; comptime var s: []const u8 = arr[0..0]; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index dceac36c97..d0509e308e 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -176,7 +176,6 @@ const MemberFnTestFoo = struct { test "call member function directly" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const instance = MemberFnTestFoo{ .x = 1234 }; const result = MemberFnTestFoo.member(instance); @@ -185,7 +184,6 @@ test "call member function directly" { test "store member function in variable" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const instance = MemberFnTestFoo{ .x = 1234 }; const memberFn = MemberFnTestFoo.member; @@ -1561,7 +1559,6 @@ test "discarded struct initialization works as expected" { test "function pointer in struct returns the struct" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const A = struct { const A = @This(); @@ -1784,8 +1781,6 @@ fn countFields(v: anytype) usize { } test "struct init with no result pointer sets field result types" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const S = struct { // A function parameter has a result type, but no result pointer. fn f(s: struct { x: u32 }) u32 { @@ -1933,8 +1928,6 @@ test "circular dependency through pointer field of a struct" { } test "field calls do not force struct field init resolution" { - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const S = struct { x: u32 = blk: { _ = @TypeOf(make().dummyFn()); // runtime field call - S not fully resolved - dummyFn call should not force field init resolution diff --git a/test/behavior/type.zig b/test/behavior/type.zig index bf1b8a76f4..d3b8beb1c0 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -203,7 +203,6 @@ test "Type.Opaque" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const Opaque = @Type(.{ .Opaque = .{