Commit 2fd83d8c0a
Changed files (15)
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,
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 {
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,
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
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 };
{
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 {
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') };
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);
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);
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 {
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])));
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").*) {
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];
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
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 = .{