mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
riscv: by-value structs + @min
This commit is contained in:
parent
a30af172e8
commit
2fd83d8c0a
@ -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 {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 };
|
||||
{
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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') };
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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])));
|
||||
|
||||
@ -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").*) {
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 = .{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user