Commit b2cb090c37

David Rubin <daviru007@icloud.com>
2024-05-11 11:04:18
riscv: float args
1 parent 031d824
Changed files (6)
src/arch/riscv64/abi.zig
@@ -7,7 +7,7 @@ const InternPool = @import("../../InternPool.zig");
 const Module = @import("../../Module.zig");
 const assert = std.debug.assert;
 
-pub const Class = enum { memory, byval, integer, double_integer, fields, none };
+pub const Class = enum { memory, byval, integer, double_integer, fields };
 
 pub fn classifyType(ty: Type, mod: *Module) Class {
     const target = mod.getTarget();
@@ -93,11 +93,13 @@ pub fn classifyType(ty: Type, mod: *Module) Class {
     }
 }
 
+pub const SystemClass = enum { integer, float, memory, none };
+
 /// There are a maximum of 8 possible return slots. Returned values are in
 /// the beginning of the array; unused slots are filled with .none.
-pub fn classifySystem(ty: Type, zcu: *Module) [8]Class {
-    var result = [1]Class{.none} ** 8;
-    const memory_class = [_]Class{
+pub fn classifySystem(ty: Type, zcu: *Module) [8]SystemClass {
+    var result = [1]SystemClass{.none} ** 8;
+    const memory_class = [_]SystemClass{
         .memory, .none, .none, .none,
         .none,   .none, .none, .none,
     };
@@ -139,6 +141,18 @@ pub fn classifySystem(ty: Type, zcu: *Module) [8]Class {
             }
             unreachable; // support > 128 bit int arguments
         },
+        .Float => {
+            const target = zcu.getTarget();
+            const features = target.cpu.features;
+
+            const float_bits = ty.floatBits(zcu.getTarget());
+            const float_reg_size: u32 = if (std.Target.riscv.featureSetHas(features, .d)) 64 else 32;
+            if (float_bits <= float_reg_size) {
+                result[0] = .float;
+                return result;
+            }
+            unreachable; // support split float args
+        },
         .ErrorUnion => {
             const payload_ty = ty.errorUnionPayload(zcu);
             const payload_bits = payload_ty.bitSize(zcu);
src/arch/riscv64/bits.zig
@@ -2,6 +2,9 @@ const std = @import("std");
 const DW = std.dwarf;
 const assert = std.debug.assert;
 const testing = std.testing;
+const Target = std.Target;
+
+const Module = @import("../../Module.zig");
 const Encoding = @import("Encoding.zig");
 const Mir = @import("Mir.zig");
 const abi = @import("abi.zig");
@@ -227,11 +230,13 @@ pub const Register = enum(u8) {
         return @as(u8, reg.id());
     }
 
-    pub fn bitSize(reg: Register) u32 {
+    pub fn bitSize(reg: Register, zcu: Module) u32 {
+        const features = zcu.getTarget().cpu.features;
+
         return switch (@intFromEnum(reg)) {
             // zig fmt: off
             @intFromEnum(Register.zero) ... @intFromEnum(Register.x31) => 64,
-            @intFromEnum(Register.ft0)  ... @intFromEnum(Register.f31) => 32,
+            @intFromEnum(Register.ft0)  ... @intFromEnum(Register.f31) => if (Target.riscv.featureSetHas(features, .d)) 64 else 32,
             else => unreachable,
             // zig fmt: on
         };
src/arch/riscv64/CodeGen.zig
@@ -5226,13 +5226,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
 
             const src_reg_class = src_reg.class();
 
-            if (src_reg_class == .float) {
-                if (dst_reg_class == .float) {
-                    return self.fail("TODO: genSetReg float -> float", .{});
-                }
-
-                assert(dst_reg_class == .int); // a bit of future proofing
-
+            if (src_reg_class == .float and dst_reg_class == .int) {
                 // to move from float -> int, we use FMV.X.W
                 return self.fail("TODO: genSetReg float -> int", .{});
             }
@@ -6031,6 +6025,7 @@ fn resolveCallingConventionValues(
             } else {
                 var ret_tracking: [2]InstTracking = undefined;
                 var ret_tracking_i: usize = 0;
+                var ret_float_reg_i: usize = 0;
 
                 const classes = mem.sliceTo(&abi.classifySystem(ret_ty, zcu), .none);
 
@@ -6042,6 +6037,13 @@ fn resolveCallingConventionValues(
                         ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_int_reg });
                         ret_tracking_i += 1;
                     },
+                    .float => {
+                        const ret_float_reg = abi.Registers.Float.function_ret_regs[ret_float_reg_i];
+                        ret_float_reg_i += 1;
+
+                        ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_float_reg });
+                        ret_tracking_i += 1;
+                    },
                     .memory => {
                         const ret_int_reg = abi.Registers.Integer.function_ret_regs[ret_int_reg_i];
                         ret_int_reg_i += 1;
@@ -6076,6 +6078,8 @@ fn resolveCallingConventionValues(
                 var arg_mcv: [2]MCValue = undefined;
                 var arg_mcv_i: usize = 0;
 
+                var param_float_reg_i: usize = 0;
+
                 const classes = mem.sliceTo(&abi.classifySystem(ty, zcu), .none);
 
                 for (classes) |class| switch (class) {
@@ -6089,6 +6093,16 @@ fn resolveCallingConventionValues(
                         arg_mcv[arg_mcv_i] = .{ .register = param_int_reg };
                         arg_mcv_i += 1;
                     },
+                    .float => {
+                        const param_float_regs = abi.Registers.Float.function_arg_regs;
+                        if (param_float_reg_i >= param_float_regs.len) break;
+
+                        const param_float_reg = param_float_regs[param_float_reg_i];
+                        param_float_reg_i += 1;
+
+                        arg_mcv[arg_mcv_i] = .{ .register = param_float_reg };
+                        arg_mcv_i += 1;
+                    },
                     .memory => {
                         const param_int_regs = abi.Registers.Integer.function_arg_regs;
 
@@ -6118,9 +6132,8 @@ fn resolveCallingConventionValues(
     return result;
 }
 
-/// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
 fn wantSafety(self: *Self) bool {
-    return switch (self.bin_file.comp.root_mod.optimize_mode) {
+    return switch (self.mod.optimize_mode) {
         .Debug => true,
         .ReleaseSafe => true,
         .ReleaseFast => false,
src/arch/riscv64/Encoding.zig
@@ -124,115 +124,119 @@ pub const Mnemonic = enum {
     fsd,
     fsw,
 
+    fsgnjns,
+
     pub fn encoding(mnem: Mnemonic) Enc {
         return switch (mnem) {
             // zig fmt: off
 
             // OP
 
-            .add    => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000000 } } },
-            .sub    => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0100000 } } }, 
+            .add     => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000000 } } },
+            .sub     => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0100000 } } }, 
 
-            .@"and" => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000000 } } },
-            .@"or"  => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000000 } } },
-            .xor    => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000000 } } },
+            .@"and"  => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b111, .funct7 = 0b0000000 } } },
+            .@"or"   => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b110, .funct7 = 0b0000000 } } },
+            .xor     => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b100, .funct7 = 0b0000000 } } },
 
-            .sltu   => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b011, .funct7 = 0b0000000 } } },
-            .slt    => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b010, .funct7 = 0b0000000 } } },
+            .sltu    => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b011, .funct7 = 0b0000000 } } },
+            .slt     => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b010, .funct7 = 0b0000000 } } },
 
-            .mul    => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000001 } } },
+            .mul     => .{ .opcode = .OP, .data = .{ .ff = .{ .funct3 = 0b000, .funct7 = 0b0000001 } } },
 
 
             // OP_IMM
 
-            .addi   => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b000 } } },
-            .andi   => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b111 } } },
-            .xori   => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b100 } } },
+            .addi    => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b000 } } },
+            .andi    => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b111 } } },
+            .xori    => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b100 } } },
             
-            .sltiu  => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b011 } } },
+            .sltiu   => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b011 } } },
 
-            .slli   => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b001 } } },
-            .srli   => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b101 } } },
-            .srai   => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b101, .offset = 1 << 10 } } },
+            .slli    => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b001 } } },
+            .srli    => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b101 } } },
+            .srai    => .{ .opcode = .OP_IMM, .data = .{ .fo = .{ .funct3 = 0b101, .offset = 1 << 10 } } },
 
 
             // OP_FP
 
-            .fadds  => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .S, .rm = 0b111 } } },
-            .faddd  => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .D, .rm = 0b111 } } },
+            .fadds   => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .S, .rm = 0b111 } } },
+            .faddd   => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00000, .fmt = .D, .rm = 0b111 } } },
+
+            .feqs    => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b010 } } },
+            .feqd    => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b010 } } },
 
-            .feqs   => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .S, .rm = 0b010 } } },
-            .feqd   => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b10100, .fmt = .D, .rm = 0b010 } } },
+            .fsgnjns => .{ .opcode = .OP_FP, .data = .{ .fmt = .{ .funct5 = 0b00100, .fmt = .S, .rm = 0b000 } } },
 
             // LOAD
 
-            .ld     => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b011 } } },
-            .lw     => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b010 } } },
-            .lwu    => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b110 } } },
-            .lh     => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b001 } } },
-            .lhu    => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b101 } } },
-            .lb     => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b000 } } },
-            .lbu    => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b100 } } },
+            .ld      => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b011 } } },
+            .lw      => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b010 } } },
+            .lwu     => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b110 } } },
+            .lh      => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b001 } } },
+            .lhu     => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b101 } } },
+            .lb      => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b000 } } },
+            .lbu     => .{ .opcode = .LOAD, .data = .{ .fo = .{ .funct3 = 0b100 } } },
 
 
             // STORE
 
-            .sd     => .{ .opcode = .STORE, .data = .{ .fo = .{ .funct3 = 0b011 } } },
-            .sw     => .{ .opcode = .STORE, .data = .{ .fo = .{ .funct3 = 0b010 } } },
-            .sh     => .{ .opcode = .STORE, .data = .{ .fo = .{ .funct3 = 0b001 } } },
-            .sb     => .{ .opcode = .STORE, .data = .{ .fo = .{ .funct3 = 0b000 } } },
+            .sd      => .{ .opcode = .STORE, .data = .{ .fo = .{ .funct3 = 0b011 } } },
+            .sw      => .{ .opcode = .STORE, .data = .{ .fo = .{ .funct3 = 0b010 } } },
+            .sh      => .{ .opcode = .STORE, .data = .{ .fo = .{ .funct3 = 0b001 } } },
+            .sb      => .{ .opcode = .STORE, .data = .{ .fo = .{ .funct3 = 0b000 } } },
 
 
             // LOAD_FP
 
-            .fld    => .{ .opcode = .LOAD_FP, .data = .{ .fo = .{ .funct3 = 0b011 } } },
-            .flw    => .{ .opcode = .LOAD_FP, .data = .{ .fo = .{ .funct3 = 0b010 } } },
+            .fld     => .{ .opcode = .LOAD_FP, .data = .{ .fo = .{ .funct3 = 0b011 } } },
+            .flw     => .{ .opcode = .LOAD_FP, .data = .{ .fo = .{ .funct3 = 0b010 } } },
 
             // STORE_FP
 
-            .fsd    => .{ .opcode = .STORE_FP, .data = .{ .fo = .{ .funct3 = 0b011 } } },
-            .fsw    => .{ .opcode = .STORE_FP, .data = .{ .fo = .{ .funct3 = 0b010 } } },
+            .fsd     => .{ .opcode = .STORE_FP, .data = .{ .fo = .{ .funct3 = 0b011 } } },
+            .fsw     => .{ .opcode = .STORE_FP, .data = .{ .fo = .{ .funct3 = 0b010 } } },
 
 
             // JALR
 
-            .jalr   => .{ .opcode = .JALR, .data = .{ .fo = .{ .funct3 = 0b000 } } },
+            .jalr    => .{ .opcode = .JALR, .data = .{ .fo = .{ .funct3 = 0b000 } } },
 
 
             // OP_32
 
-            .sllw   => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000000 } } },
+            .sllw    => .{ .opcode = .OP_32, .data = .{ .ff = .{ .funct3 = 0b001, .funct7 = 0b0000000 } } },
 
 
             // LUI
 
-            .lui    => .{ .opcode = .LUI, .data = .{ .none = {} } },
+            .lui     => .{ .opcode = .LUI, .data = .{ .none = {} } },
 
 
             // AUIPC
 
-            .auipc  => .{ .opcode = .AUIPC, .data = .{ .none = {} } },
+            .auipc   => .{ .opcode = .AUIPC, .data = .{ .none = {} } },
 
 
             // JAL
 
-            .jal    => .{ .opcode = .JAL, .data = .{ .none = {} } },
+            .jal     => .{ .opcode = .JAL, .data = .{ .none = {} } },
 
 
             // BRANCH
 
-            .beq    => .{ .opcode = .BRANCH, .data = .{ .fo = .{ .funct3 = 0b000 } } },
+            .beq     => .{ .opcode = .BRANCH, .data = .{ .fo = .{ .funct3 = 0b000 } } },
 
 
             // SYSTEM
 
-            .ecall  => .{ .opcode = .SYSTEM, .data = .{ .fo = .{ .funct3 = 0b000 } } },
-            .ebreak => .{ .opcode = .SYSTEM, .data = .{ .fo = .{ .funct3 = 0b000 } } },
+            .ecall   => .{ .opcode = .SYSTEM, .data = .{ .fo = .{ .funct3 = 0b000 } } },
+            .ebreak  => .{ .opcode = .SYSTEM, .data = .{ .fo = .{ .funct3 = 0b000 } } },
            
 
             // NONE
             
-            .unimp  => .{ .opcode = .NONE, .data = .{ .fo = .{ .funct3 = 0b000 } } },
+            .unimp   => .{ .opcode = .NONE, .data = .{ .fo = .{ .funct3 = 0b000 } } },
 
 
             // zig fmt: on
@@ -308,6 +312,7 @@ pub const InstEnc = enum {
             .faddd,
             .feqs,
             .feqd,
+            .fsgnjns,
             => .R,
 
             .ecall,
src/arch/riscv64/Lower.zig
@@ -132,11 +132,27 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
             .pseudo_mv => {
                 const rr = inst.data.rr;
 
-                try lower.emit(.addi, &.{
-                    .{ .reg = rr.rd },
-                    .{ .reg = rr.rs },
-                    .{ .imm = Immediate.s(0) },
-                });
+                const dst_class = rr.rd.class();
+                const src_class = rr.rs.class();
+
+                assert(dst_class == src_class);
+
+                switch (dst_class) {
+                    .float => {
+                        try lower.emit(.fsgnjns, &.{
+                            .{ .reg = rr.rd },
+                            .{ .reg = rr.rs },
+                            .{ .reg = rr.rs },
+                        });
+                    },
+                    .int => {
+                        try lower.emit(.addi, &.{
+                            .{ .reg = rr.rd },
+                            .{ .reg = rr.rs },
+                            .{ .imm = Immediate.s(0) },
+                        });
+                    },
+                }
             },
 
             .pseudo_ret => {
src/codegen/llvm.zig
@@ -11148,7 +11148,6 @@ fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!Bu
                             }
                             return o.builder.structType(.normal, types[0..types_len]);
                         },
-                        .none => unreachable,
                     }
                 },
                 // TODO investigate C ABI for other architectures
@@ -11406,7 +11405,6 @@ const ParamTypeIterator = struct {
                             it.llvm_index += it.types_len - 1;
                             return .multiple_llvm_types;
                         },
-                        .none => unreachable,
                     }
                 },
                 // TODO investigate C ABI for other architectures