Commit 35e7011124

Andrew Kelley <andrew@ziglang.org>
2022-07-13 08:30:26
LLVM: implement signext/zeroext attributes
For calling convention ABI purposes, integer attributes and return values need to have an LLVM attribute signext or zeroext added sometimes. This commit implements that logic. It also implements a proof-of-concept of moving the F16T type from being a compiler_rt hack to being how the compiler lowers f16 in functions that need to match certain calling conventions. Closes #12054
1 parent efe3424
Changed files (3)
lib
compiler_rt
src
codegen
test
behavior
lib/compiler_rt/common.zig
@@ -68,7 +68,11 @@ pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) nore
 /// need for extending them to wider fp types.
 /// TODO remove this; do this type selection in the language rather than
 /// here in compiler-rt.
-pub const F16T = if (builtin.cpu.arch.isAARCH64()) f16 else u16;
+pub const F16T = switch (builtin.cpu.arch) {
+    .aarch64, .aarch64_be, .aarch64_32 => f16,
+    .riscv64 => if (builtin.zig_backend == .stage1) u16 else f16,
+    else => u16,
+};
 
 pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void {
     switch (Z) {
src/codegen/llvm.zig
@@ -717,6 +717,11 @@ pub const Object = struct {
         const ret_ptr = if (sret) llvm_func.getParam(0) else null;
         const gpa = dg.gpa;
 
+        if (ccAbiPromoteInt(fn_info.cc, target, fn_info.return_type)) |s| switch (s) {
+            .signed => dg.addAttr(llvm_func, 0, "signext"),
+            .unsigned => dg.addAttr(llvm_func, 0, "zeroext"),
+        };
+
         const err_return_tracing = fn_info.return_type.isError() and
             dg.module.comp.bin_file.options.error_return_tracing;
 
@@ -774,7 +779,10 @@ pub const Object = struct {
                                 );
                                 dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", elem_align);
                             }
-                        }
+                        } else if (ccAbiPromoteInt(fn_info.cc, target, param_ty)) |s| switch (s) {
+                            .signed => dg.addArgAttr(llvm_func, llvm_arg_i, "signext"),
+                            .unsigned => dg.addArgAttr(llvm_func, llvm_arg_i, "zeroext"),
+                        };
                     }
                     llvm_arg_i += 1;
                 },
@@ -887,6 +895,13 @@ pub const Object = struct {
                     };
                     try args.append(loaded);
                 },
+                .as_u16 => {
+                    const param = llvm_func.getParam(llvm_arg_i);
+                    llvm_arg_i += 1;
+                    const casted = builder.buildBitCast(param, dg.context.halfType(), "");
+                    try args.ensureUnusedCapacity(1);
+                    args.appendAssumeCapacity(casted);
+                },
             };
         }
 
@@ -2794,6 +2809,9 @@ pub const DeclGen = struct {
                     llvm_params.appendAssumeCapacity(big_int_ty);
                 }
             },
+            .as_u16 => {
+                try llvm_params.append(dg.context.intType(16));
+            },
         };
 
         return llvm.functionType(
@@ -4234,6 +4252,12 @@ pub const FuncGen = struct {
                     llvm_args.appendAssumeCapacity(load_inst);
                 }
             },
+            .as_u16 => {
+                const arg = args[it.zig_index - 1];
+                const llvm_arg = try self.resolveInst(arg);
+                const casted = self.builder.buildBitCast(llvm_arg, self.dg.context.intType(16), "");
+                try llvm_args.append(casted);
+            },
         };
 
         const call = self.builder.buildCall(
@@ -8965,6 +8989,7 @@ const ParamTypeIterator = struct {
         abi_sized_int,
         multiple_llvm_ints,
         slice,
+        as_u16,
     };
 
     pub fn next(it: *ParamTypeIterator) ?Lowering {
@@ -9025,6 +9050,15 @@ const ParamTypeIterator = struct {
                     else => false,
                 };
                 switch (it.target.cpu.arch) {
+                    .riscv32, .riscv64 => {
+                        it.zig_index += 1;
+                        it.llvm_index += 1;
+                        if (ty.tag() == .f16) {
+                            return .as_u16;
+                        } else {
+                            return .byval;
+                        }
+                    },
                     .mips, .mipsel => {
                         it.zig_index += 1;
                         it.llvm_index += 1;
@@ -9135,6 +9169,35 @@ fn iterateParamTypes(dg: *DeclGen, fn_info: Type.Payload.Function.Data) ParamTyp
     };
 }
 
+fn ccAbiPromoteInt(
+    cc: std.builtin.CallingConvention,
+    target: std.Target,
+    ty: Type,
+) ?std.builtin.Signedness {
+    switch (cc) {
+        .Unspecified, .Inline, .Async => return null,
+        else => {},
+    }
+    const int_info = switch (ty.zigTypeTag()) {
+        .Int, .Enum, .ErrorSet => ty.intInfo(target),
+        else => return null,
+    };
+    if (int_info.bits <= 16) return int_info.signedness;
+    switch (target.cpu.arch) {
+        .sparc64,
+        .riscv64,
+        .powerpc64,
+        .powerpc64le,
+        => {
+            if (int_info.bits < 64) {
+                return int_info.signedness;
+            }
+        },
+        else => {},
+    }
+    return null;
+}
+
 fn isByRef(ty: Type) bool {
     // For tuples and structs, if there are more than this many non-void
     // fields, then we make it byref, otherwise byval.
test/behavior/math.zig
@@ -1168,11 +1168,6 @@ test "remainder division" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
-    if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
-        // https://github.com/ziglang/zig/issues/12054
-        return error.SkipZigTest;
-    }
-
     comptime try remdiv(f16);
     comptime try remdiv(f32);
     comptime try remdiv(f64);
@@ -1204,11 +1199,6 @@ test "float remainder division using @rem" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
-    if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
-        // https://github.com/ziglang/zig/issues/12054
-        return error.SkipZigTest;
-    }
-
     comptime try frem(f16);
     comptime try frem(f32);
     comptime try frem(f64);
@@ -1251,11 +1241,6 @@ test "float modulo division using @mod" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
-    if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
-        // https://github.com/ziglang/zig/issues/12054
-        return error.SkipZigTest;
-    }
-
     comptime try fmod(f16);
     comptime try fmod(f32);
     comptime try fmod(f64);
@@ -1431,11 +1416,6 @@ test "@ceil f80" {
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
 
-    if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
-        // https://github.com/ziglang/zig/issues/12054
-        return error.SkipZigTest;
-    }
-
     try testCeil(f80, 12.0);
     comptime try testCeil(f80, 12.0);
 }
@@ -1447,11 +1427,6 @@ test "@ceil f128" {
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
 
-    if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
-        // https://github.com/ziglang/zig/issues/12054
-        return error.SkipZigTest;
-    }
-
     try testCeil(f128, 12.0);
     comptime try testCeil(f128, 12.0);
 }
@@ -1600,11 +1575,6 @@ test "NaN comparison" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
-    if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .riscv64) {
-        // https://github.com/ziglang/zig/issues/12054
-        return error.SkipZigTest;
-    }
-
     try testNanEqNan(f16);
     try testNanEqNan(f32);
     try testNanEqNan(f64);