Commit 65bea9ac07

Andrew Kelley <andrew@ziglang.org>
2024-04-26 22:40:42
LLVM 18 update: avoid passing vectors sometimes
LLVM now refuses to lower arguments and return values on x86 targets when the total vector bit size is >= 512. This code detects such a situation and uses byref instead of byval.
1 parent 21f1e76
Changed files (2)
src
src/codegen/llvm.zig
@@ -10990,12 +10990,27 @@ fn toLlvmGlobalAddressSpace(wanted_address_space: std.builtin.AddressSpace, targ
     };
 }
 
+fn returnTypeByRef(zcu: *Zcu, target: std.Target, ty: Type) bool {
+    if (isByRef(ty, zcu)) {
+        return true;
+    } else if (target.cpu.arch.isX86() and
+        !std.Target.x86.featureSetHas(target.cpu.features, .evex512) and
+        ty.totalVectorBits(zcu) >= 512)
+    {
+        // As of LLVM 18, passing a vector byval with fastcc that is 512 bits or more returns
+        // "512-bit vector arguments require 'evex512' for AVX512"
+        return true;
+    } else {
+        return false;
+    }
+}
+
 fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: std.Target) bool {
     const return_type = Type.fromInterned(fn_info.return_type);
     if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) return false;
 
     return switch (fn_info.cc) {
-        .Unspecified, .Inline => isByRef(return_type, zcu),
+        .Unspecified, .Inline => returnTypeByRef(zcu, target, return_type),
         .C => switch (target.cpu.arch) {
             .mips, .mipsel => false,
             .x86 => isByRef(return_type, zcu),
@@ -11043,7 +11058,8 @@ fn lowerFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!Bu
     switch (fn_info.cc) {
         .Unspecified,
         .Inline,
-        => return if (isByRef(return_type, mod)) .void else o.lowerType(return_type),
+        => return if (returnTypeByRef(mod, target, return_type)) .void else o.lowerType(return_type),
+
         .C => {
             switch (target.cpu.arch) {
                 .mips, .mipsel => return o.lowerType(return_type),
@@ -11266,6 +11282,13 @@ const ParamTypeIterator = struct {
                     return .slice;
                 } else if (isByRef(ty, zcu)) {
                     return .byref;
+                } else if (target.cpu.arch.isX86() and
+                    !std.Target.x86.featureSetHas(target.cpu.features, .evex512) and
+                    ty.totalVectorBits(zcu) >= 512)
+                {
+                    // As of LLVM 18, passing a vector byval with fastcc that is 512 bits or more returns
+                    // "512-bit vector arguments require 'evex512' for AVX512"
+                    return .byref;
                 } else {
                     return .byval;
                 }
src/type.zig
@@ -2804,6 +2804,13 @@ pub const Type = struct {
         return ty.zigTypeTag(mod) == .Vector;
     }
 
+    /// Returns 0 if not a vector, otherwise returns @bitSizeOf(Element) * vector_len.
+    pub fn totalVectorBits(ty: Type, zcu: *Zcu) u64 {
+        if (!ty.isVector(zcu)) return 0;
+        const v = zcu.intern_pool.indexToKey(ty.toIntern()).vector_type;
+        return v.len * Type.fromInterned(v.child).bitSize(zcu);
+    }
+
     pub fn isArrayOrVector(ty: Type, mod: *const Module) bool {
         return switch (ty.zigTypeTag(mod)) {
             .Array, .Vector => true,