diff --git a/lib/compiler/aro/aro/Target.zig b/lib/compiler/aro/aro/Target.zig index 3a871504700f666b6e74ca464d37c0770568c43d..a0c9f3be3943810313dfcd94b94f6bea0794ff2d 100644 --- a/lib/compiler/aro/aro/Target.zig +++ b/lib/compiler/aro/aro/Target.zig @@ -1559,15 +1559,15 @@ pub fn ptrBitWidth(target: *const Target) u16 { } pub fn cCharSignedness(target: *const Target) std.builtin.Signedness { - return target.toZigTarget().cCharSignedness(); + return target.toZigTarget().cCharSignedness().?; } pub fn cTypeBitSize(target: *const Target, c_type: std.Target.CType) u16 { - return target.toZigTarget().cTypeBitSize(c_type); + return target.toZigTarget().cTypeBitSize(c_type).?; } pub fn cTypeAlignment(target: *const Target, c_type: std.Target.CType) u16 { - return target.toZigTarget().cTypeAlignment(c_type); + return target.toZigTarget().cTypeAlignment(c_type).?; } pub fn standardDynamicLinkerPath(target: *const Target) std.Target.DynamicLinker { diff --git a/lib/compiler/reduce.zig b/lib/compiler/reduce.zig index 04f0c03650031d0083cd84f89fa87bfa7b8aff09..398a44a9b4e1d9ec0670db4cd0d2878c37ff6c83 100644 --- a/lib/compiler/reduce.zig +++ b/lib/compiler/reduce.zig @@ -400,7 +400,7 @@ fn parse(gpa: Allocator, io: Io, file_path: []const u8) !Ast { file_path, gpa, .limited(std.math.maxInt(u32)), - .fromByteUnits(1), + .@"1", 0, ) catch |err| { fatal("unable to open '{s}': {s}", .{ file_path, @errorName(err) }); diff --git a/lib/compiler/test_runner.zig b/lib/compiler/test_runner.zig index 4e9502656c56f1e8cc43a03529d5d2599abbace5..2827d32c496e12c3a2bf7c481b54afa50eb477fe 100644 --- a/lib/compiler/test_runner.zig +++ b/lib/compiler/test_runner.zig @@ -91,24 +91,23 @@ fn mainServer(init: std.process.Init.Minimal) !void { return std.process.exit(0); }, .query_test_metadata => { - testing.allocator_instance = .init(std.heap.page_allocator, .{}); - defer if (testing.allocator_instance.deinit() != 0) { - @panic("internal test runner memory leak"); - }; + var sa: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{}); + defer if (sa.deinit() != 0) @panic("internal test runner memory leak"); + const gpa = sa.allocator(); var string_bytes: std.ArrayList(u8) = .empty; - defer string_bytes.deinit(testing.allocator); - try string_bytes.append(testing.allocator, 0); // Reserve 0 for null. + defer string_bytes.deinit(gpa); + try string_bytes.append(gpa, 0); // Reserve 0 for null. const test_fns = builtin.test_functions; - const names = try testing.allocator.alloc(u32, test_fns.len); - defer testing.allocator.free(names); - const expected_panic_msgs = try testing.allocator.alloc(u32, test_fns.len); - defer testing.allocator.free(expected_panic_msgs); + const names = try gpa.alloc(u32, test_fns.len); + defer gpa.free(names); + const expected_panic_msgs = try gpa.alloc(u32, test_fns.len); + defer gpa.free(expected_panic_msgs); for (test_fns, names, expected_panic_msgs) |test_fn, *name, *expected_panic_msg| { name.* = @intCast(string_bytes.items.len); - try string_bytes.ensureUnusedCapacity(testing.allocator, test_fn.name.len + 1); + try string_bytes.ensureUnusedCapacity(gpa, test_fn.name.len + 1); string_bytes.appendSliceAssumeCapacity(test_fn.name); string_bytes.appendAssumeCapacity(0); expected_panic_msg.* = 0; diff --git a/lib/compiler_rt/comparef.zig b/lib/compiler_rt/comparef.zig index 7b397ba02f9aca5a534f015726bcb143a456eb63..d230e9a6b42f8ef88114e4d72b838f3f47c01cec 100644 --- a/lib/compiler_rt/comparef.zig +++ b/lib/compiler_rt/comparef.zig @@ -8,7 +8,7 @@ const Unordered = if (builtin.cpu.arch == .avr) i8 else if (builtin.cpu.arch.isAARCH64()) i32 -else if (builtin.target.cTypeBitSize(.long) >= builtin.target.ptrBitWidth()) +else if (builtin.target.cTypeBitSize(.long).? >= builtin.target.ptrBitWidth()) c_long else c_longlong; diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig index c243c8551f4917ef210159743f0d55ba749d1e6a..ebfe6cd502f399501eeafdbdf44205ca6636e8d2 100644 --- a/lib/std/Io/Writer.zig +++ b/lib/std/Io/Writer.zig @@ -2817,12 +2817,12 @@ pub const Allocating = struct { } test Allocating { - try testAllocating(.fromByteUnits(1)); - try testAllocating(.fromByteUnits(4)); - try testAllocating(.fromByteUnits(8)); - try testAllocating(.fromByteUnits(16)); - try testAllocating(.fromByteUnits(32)); - try testAllocating(.fromByteUnits(64)); + try testAllocating(.@"1"); + try testAllocating(.@"4"); + try testAllocating(.@"8"); + try testAllocating(.@"16"); + try testAllocating(.@"32"); + try testAllocating(.@"64"); } }; diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 5dc5075bada694841849a65904acdaa60471a23b..3a6cfabb42be0d191520ef33ae7cbbc190e7c644 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -3066,9 +3066,13 @@ pub fn stackGrowth(target: *const Target) StackGrowth { /// Default signedness of `char` for the native C compiler for this target /// Note that char signedness is implementation-defined and many compilers provide /// an option to override the default signedness e.g. GCC's -funsigned-char / -fsigned-char -pub fn cCharSignedness(target: *const Target) std.builtin.Signedness { +/// Returns `null` if no C ABI is defined for this target. +pub fn cCharSignedness(target: *const Target) ?std.builtin.Signedness { + switch (target.os.tag) { + .opengl => return null, + else => {}, + } if (target.os.tag.isDarwin() or target.os.tag == .windows or target.os.tag == .uefi) return .signed; - return switch (target.cpu.arch) { .aarch64, .aarch64_be, @@ -3114,7 +3118,8 @@ pub const CType = enum { longdouble, }; -pub fn cTypeByteSize(t: *const Target, c_type: CType) u16 { +/// Returns `null` if no C ABI is defined for this target. +pub fn cTypeByteSize(t: *const Target, c_type: CType) ?u16 { return switch (c_type) { .char, .short, @@ -3127,18 +3132,19 @@ pub fn cTypeByteSize(t: *const Target, c_type: CType) u16 { .ulonglong, .float, .double, - => @divExact(cTypeBitSize(t, c_type), 8), + => @divExact(cTypeBitSize(t, c_type) orelse return null, 8), - .longdouble => switch (cTypeBitSize(t, c_type)) { + .longdouble => switch (cTypeBitSize(t, c_type) orelse return null) { 64 => 8, - 80 => @intCast(std.mem.alignForward(usize, 10, cTypeAlignment(t, .longdouble))), + 80 => @intCast(std.mem.alignForward(usize, 10, cTypeAlignment(t, c_type).?)), 128 => 16, else => unreachable, }, }; } -pub fn cTypeBitSize(target: *const Target, c_type: CType) u16 { +/// Returns `null` if no C ABI is defined for this target. +pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 { switch (target.os.tag) { .freestanding, .other, @@ -3459,15 +3465,17 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) u16 { .longlong, .ulonglong, .longdouble => return 64, }, + .opengl => return null, + .ps3, .contiki, .managarm, - .opengl, => @panic("specify the C integer and float type sizes for this OS"), } } -pub fn cTypeAlignment(target: *const Target, c_type: CType) u16 { +/// Returns `null` if no C ABI is defined for this target. +pub fn cTypeAlignment(target: *const Target, c_type: CType) ?u16 { // Overrides for unusual alignments switch (target.cpu.arch) { .avr, @@ -3500,7 +3508,7 @@ pub fn cTypeAlignment(target: *const Target, c_type: CType) u16 { // Next-power-of-two-aligned, up to a maximum. return @min( - std.math.ceilPowerOfTwoAssert(u16, (cTypeBitSize(target, c_type) + 7) / 8), + std.math.ceilPowerOfTwoAssert(u16, ((cTypeBitSize(target, c_type) orelse return null) + 7) / 8), @as(u16, switch (target.cpu.arch) { .msp430, .x86_16, @@ -3598,6 +3606,11 @@ pub fn cMaxIntAlignment(target: *const Target) u16 { .xcore, => 4, + .x86 => switch (target.os.tag) { + else => 4, + .uefi, .windows => 8, + }, + .arm, .armeb, .hexagon, @@ -3616,7 +3629,6 @@ pub fn cMaxIntAlignment(target: *const Target) u16 { .sparc, .thumb, .thumbeb, - .x86, .xtensa, .xtensaeb, => 8, diff --git a/lib/std/math/gamma.zig b/lib/std/math/gamma.zig index ce9a2b07f91b2cc5e5909069813d28bd4b7e9ce6..fed7e87ceef250eb92b67d43973289635c54d06c 100644 --- a/lib/std/math/gamma.zig +++ b/lib/std/math/gamma.zig @@ -263,8 +263,6 @@ test gamma { } test "gamma.special" { - if (builtin.cpu.arch.isArm() and builtin.target.abi.float() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 - inline for (&.{ f32, f64 }) |T| { try expect(std.math.isNan(gamma(T, -std.math.nan(T)))); try expect(std.math.isNan(gamma(T, std.math.nan(T)))); diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 48ae8216b9026099894b01f95ca9505215bef869..a19a54651db7736b3a31ca926c534690ee27bf79 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -5999,7 +5999,7 @@ pub const WipFunction = struct { alignment: Alignment, name: []const u8, ) Allocator.Error!Value { - return self.loadAtomic(access_kind, ty, ptr, .system, .none, alignment, name); + return self.loadAtomic(access_kind, ty, ptr, undefined, .none, alignment, name); } pub fn loadAtomic( @@ -6043,7 +6043,7 @@ pub const WipFunction = struct { ptr: Value, alignment: Alignment, ) Allocator.Error!Instruction.Index { - return self.storeAtomic(kind, val, ptr, .system, .none, alignment); + return self.storeAtomic(kind, val, ptr, undefined, .none, alignment); } pub fn storeAtomic( diff --git a/lib/std/zig/target.zig b/lib/std/zig/target.zig index 2315405a9e89549e26a875b00b51390fc3bc1b3d..e30cae2a6728a7df71e9bef14f194f6264809604 100644 --- a/lib/std/zig/target.zig +++ b/lib/std/zig/target.zig @@ -499,31 +499,12 @@ pub fn intByteSize(target: *const std.Target, bits: u16) u16 { } pub fn intAlignment(target: *const std.Target, bits: u16) u16 { - return switch (target.cpu.arch) { - .x86 => switch (bits) { - 0...8 => 1, - 9...16 => 2, - 17...32 => 4, - 33...64 => switch (target.os.tag) { - .uefi, .windows => 8, - else => 4, - }, - else => 16, - }, - .x86_64 => switch (bits) { - 0...8 => 1, - 9...16 => 2, - 17...32 => 4, - 33...64 => 8, - else => 16, - }, - else => switch (bits) { - 0 => 1, - else => @min( - std.math.ceilPowerOfTwoPromote(u16, @intCast((@as(u17, bits) + 7) / 8)), - target.cMaxIntAlignment(), - ), - }, + return switch (bits) { + 0 => 1, + else => @min( + std.math.ceilPowerOfTwoPromote(u16, @intCast((@as(u17, bits) + 7) / 8)), + target.cMaxIntAlignment(), + ), }; } @@ -536,7 +517,10 @@ pub fn compilerRtFloatAbi(target: *const std.Target, bits: u16) std.Target.Abi.F 16 => if (target.cpu.arch.isMIPS() or target.cpu.arch.isPowerPC()) return no_c_type_available, 32, 64 => {}, 80 => if (target.cTypeBitSize(.longdouble) != 80) return no_c_type_available, - 128 => if (target.cTypeBitSize(.longdouble) <= 64) return no_c_type_available, + 128 => { + if (target.cpu.arch.isX86()) return .hard; // if (target.abi == .msvc) __m128i else __float128 + if (target.cTypeBitSize(.longdouble) != 128) return no_c_type_available; + }, } return .hard; } diff --git a/src/Sema.zig b/src/Sema.zig index bb2cd4eff32547c659ec0e7f14fddf3cc4b07d18..cb8f87836a5e2c8623670cb870f7bf2b4b05a43d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -29657,7 +29657,7 @@ fn coerceVarArgParam( .array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), .float => float: { const target = zcu.getTarget(); - const double_bits = target.cTypeBitSize(.double); + const double_bits = target.cTypeBitSize(.double) orelse break :float inst; const inst_bits = uncasted_ty.floatBits(target); if (inst_bits >= double_bits) break :float inst; switch (double_bits) { @@ -29673,21 +29673,21 @@ fn coerceVarArgParam( if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { .signed => .int, .unsigned => .uint, - })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { + }) orelse break :int inst) break :int try sema.coerce(block, switch (uncasted_info.signedness) { .signed => .c_int, .unsigned => .c_uint, }, inst, inst_src); if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { .signed => .long, .unsigned => .ulong, - })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { + }).?) break :int try sema.coerce(block, switch (uncasted_info.signedness) { .signed => .c_long, .unsigned => .c_ulong, }, inst, inst_src); if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { .signed => .longlong, .unsigned => .ulonglong, - })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { + }).?) break :int try sema.coerce(block, switch (uncasted_info.signedness) { .signed => .c_longlong, .unsigned => .c_ulonglong, }, inst, inst_src); diff --git a/src/Sema/type_resolution.zig b/src/Sema/type_resolution.zig index ef8e24c86744023e52bef9d9c2d0f1f029936852..b3de4f8433a768a6a5c4c174fb300f1a899edf64 100644 --- a/src/Sema/type_resolution.zig +++ b/src/Sema/type_resolution.zig @@ -364,7 +364,7 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { const a = struct_obj.field_aligns.get(ip)[field_idx]; if (a != .none) break :a a; } - break :a field_ty.defaultStructFieldAlignment(struct_obj.layout, zcu); + break :a field_ty.abiAlignment(zcu); }; align_out.* = field_align; if (struct_obj.field_is_comptime_bits.get(ip, field_idx)) { diff --git a/src/Type.zig b/src/Type.zig index 56b2388710b2b919a2a429fd3afa0dfa515bcf66..466a707b998294905c7d068acc1f2c98193232dc 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -957,10 +957,25 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment { if (vector_type.len == 0) return .@"1"; switch (zcu.comp.getZigBackend()) { else => { - const elem_bits: u32 = @intCast(Type.fromInterned(vector_type.child).bitSize(zcu)); + const elem_ty: Type = .fromInterned(vector_type.child); + switch (if (elem_ty.isRuntimeFloat()) + std.zig.target.compilerRtFloatAbi(target, elem_ty.floatBits(target)) + else + .hard) { + .hard => {}, + .soft => return elem_ty.abiAlignment(zcu), + } + const elem_bits: u32 = @intCast(elem_ty.bitSize(zcu)); if (elem_bits == 0) return .@"1"; const bytes = ((elem_bits * vector_type.len) + 7) / 8; - return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes)); + const arch = target.cpu.arch; + return .fromByteUnits(std.math.ceilPowerOfTwoAssert( + u32, + if (arch.isArm() or arch.isAARCH64() or arch == .s390x) + @min(bytes, target.stackAlignment()) + else + bytes, + )); }, .stage2_c, .stage2_wasm => return Type.fromInterned(vector_type.child).abiAlignment(zcu), .stage2_x86_64 => { @@ -1018,19 +1033,33 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment { .c_ulonglong => cTypeAlign(target, .ulonglong), .c_longdouble => cTypeAlign(target, .longdouble), - .f16 => .@"2", - .f32 => if (target.os.tag == .opengl) .@"4" else cTypeAlign(target, .float), - .f64 => if (target.os.tag == .opengl) .@"8" else switch (target.cTypeBitSize(.double)) { - 64 => cTypeAlign(target, .double), - else => .@"8", - }, - .f80 => switch (target.cTypeBitSize(.longdouble)) { - 80 => cTypeAlign(target, .longdouble), - else => Type.u80.abiAlignment(zcu), - }, - .f128 => switch (target.cTypeBitSize(.longdouble)) { - 128 => cTypeAlign(target, .longdouble), - else => .@"16", + .f16 => .fromByteUnits(std.zig.target.intAlignment(target, 16)), // repr: u16 + .f32 => if (target.cTypeBitSize(.float) == 32) + cTypeAlign(target, .float) // abi: c_float, + else + .fromByteUnits(std.zig.target.intAlignment(target, 32)), // repr: u32, + .f64 => if (target.cTypeBitSize(.double) == 64) + cTypeAlign(target, .double) // abi: c_double, + else + .fromByteUnits(std.zig.target.intAlignment(target, 64)), // repr: u64, + .f80 => if (target.cTypeBitSize(.longdouble) == 80) + cTypeAlign(target, .longdouble) // abi: c_longdouble, + else + .fromByteUnits(switch (std.zig.target.compilerRtFloatAbi(target, 80)) { + .hard => std.zig.target.intAlignment(target, 80), // repr: u80, + .soft => @max( + std.zig.target.intAlignment(target, 64), // mantissa: u64, + std.zig.target.intAlignment(target, 16), // exponent: u16, + ), + }), + .f128 => if (target.cTypeBitSize(.longdouble) == 128) + cTypeAlign(target, .longdouble) // abi: c_longdouble, + else switch (std.zig.target.compilerRtFloatAbi(target, 128)) { + .hard => if (target.cpu.arch.isX86()) + .@"16" // abi: c___float128, + else + .fromByteUnits(std.zig.target.intAlignment(target, 128)), // repr: u128, + .soft => .fromByteUnits(std.zig.target.intAlignment(target, 64)), // lo: u64, hi: u64, }, .generic_poison => unreachable, @@ -1111,7 +1140,13 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 { .vector_type => |vec| { const elem_ty: Type = .fromInterned(vec.child); const bytes = switch (zcu.comp.getZigBackend()) { - else => @divCeil(vec.len * elem_ty.bitSize(zcu), 8), + else => switch (if (elem_ty.isRuntimeFloat()) + std.zig.target.compilerRtFloatAbi(target, elem_ty.floatBits(target)) + else + .hard) { + .hard => @divCeil(vec.len * elem_ty.bitSize(zcu), 8), + .soft => vec.len * elem_ty.abiSize(zcu), + }, .stage2_c, .stage2_wasm => vec.len * elem_ty.abiSize(zcu), .stage2_x86_64 => switch (elem_ty.toIntern()) { .bool_type => @divCeil(vec.len, 8), @@ -1167,25 +1202,44 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 { .anyerror, .adhoc_inferred_error_set => errorAbiSize(zcu), .usize, .isize => ptrAbiSize(target), - .c_char => target.cTypeByteSize(.char), - .c_short => target.cTypeByteSize(.short), - .c_ushort => target.cTypeByteSize(.ushort), - .c_int => target.cTypeByteSize(.int), - .c_uint => target.cTypeByteSize(.uint), - .c_long => target.cTypeByteSize(.long), - .c_ulong => target.cTypeByteSize(.ulong), - .c_longlong => target.cTypeByteSize(.longlong), - .c_ulonglong => target.cTypeByteSize(.ulonglong), - .c_longdouble => target.cTypeByteSize(.longdouble), + .c_char => target.cTypeByteSize(.char).?, + .c_short => target.cTypeByteSize(.short).?, + .c_ushort => target.cTypeByteSize(.ushort).?, + .c_int => target.cTypeByteSize(.int).?, + .c_uint => target.cTypeByteSize(.uint).?, + .c_long => target.cTypeByteSize(.long).?, + .c_ulong => target.cTypeByteSize(.ulong).?, + .c_longlong => target.cTypeByteSize(.longlong).?, + .c_ulonglong => target.cTypeByteSize(.ulonglong).?, + .c_longdouble => target.cTypeByteSize(.longdouble).?, - .f16 => 2, - .f32 => 4, - .f64 => 8, - .f80 => switch (target.cTypeBitSize(.longdouble)) { - 80 => target.cTypeByteSize(.longdouble), - else => Type.u80.abiSize(zcu), + .f16 => std.zig.target.intByteSize(target, 16), // repr: u16 + .f32 => if (target.cTypeBitSize(.float) == 32) + target.cTypeByteSize(.float).? // abi: c_float, + else + std.zig.target.intByteSize(target, 32), // repr: u32, + .f64 => if (target.cTypeBitSize(.double) == 64) + target.cTypeByteSize(.double).? // abi: c_double, + else + std.zig.target.intByteSize(target, 64), // repr: u64, + .f80 => if (target.cTypeBitSize(.longdouble) == 80) + target.cTypeByteSize(.longdouble).? // abi: c_longdouble, + else switch (std.zig.target.compilerRtFloatAbi(target, 80)) { + .hard => std.zig.target.intByteSize(target, 80), // repr: u80, + .soft => ty.abiAlignment(zcu).forward( + std.zig.target.intByteSize(target, 64) + // mantissa: u64, + std.zig.target.intByteSize(target, 16), // exponent: u16 + ), + }, + .f128 => if (target.cTypeBitSize(.longdouble) == 128) + target.cTypeByteSize(.longdouble).? // abi: c_longdouble, + else switch (std.zig.target.compilerRtFloatAbi(target, 128)) { + .hard => if (target.cpu.arch.isX86()) + 16 // abi: c___float128, + else + std.zig.target.intByteSize(target, 128), // repr: u128, + .soft => std.zig.target.intByteSize(target, 64) * 2, // lo: u64, hi: u64, }, - .f128 => 16, .anyopaque => unreachable, .generic_poison => unreachable, @@ -1733,7 +1787,7 @@ pub fn isInt(self: Type, zcu: *const Zcu) bool { /// Returns true if and only if the type is a fixed-width, signed integer. pub fn isSignedInt(ty: Type, zcu: *const Zcu) bool { return switch (ty.toIntern()) { - .c_char_type => zcu.getTarget().cCharSignedness() == .signed, + .c_char_type => zcu.getTarget().cCharSignedness().? == .signed, .isize_type, .c_short_type, .c_int_type, .c_long_type, .c_longlong_type => true, else => switch (zcu.intern_pool.indexToKey(ty.toIntern())) { .int_type => |int_type| int_type.signedness == .signed, @@ -1745,7 +1799,7 @@ pub fn isSignedInt(ty: Type, zcu: *const Zcu) bool { /// Returns true if and only if the type is a fixed-width, unsigned integer. pub fn isUnsignedInt(ty: Type, zcu: *const Zcu) bool { return switch (ty.toIntern()) { - .c_char_type => zcu.getTarget().cCharSignedness() == .unsigned, + .c_char_type => zcu.getTarget().cCharSignedness().? == .unsigned, .usize_type, .c_ushort_type, .c_uint_type, .c_ulong_type, .c_ulonglong_type => true, else => switch (zcu.intern_pool.indexToKey(ty.toIntern())) { .int_type => |int_type| int_type.signedness == .unsigned, @@ -1776,15 +1830,15 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType { }, .usize_type => return .{ .signedness = .unsigned, .bits = target.ptrBitWidth() }, .isize_type => return .{ .signedness = .signed, .bits = target.ptrBitWidth() }, - .c_char_type => return .{ .signedness = zcu.getTarget().cCharSignedness(), .bits = target.cTypeBitSize(.char) }, - .c_short_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.short) }, - .c_ushort_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ushort) }, - .c_int_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.int) }, - .c_uint_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.uint) }, - .c_long_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.long) }, - .c_ulong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulong) }, - .c_longlong_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.longlong) }, - .c_ulonglong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulonglong) }, + .c_char_type => return .{ .signedness = target.cCharSignedness().?, .bits = target.cTypeBitSize(.char).? }, + .c_short_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.short).? }, + .c_ushort_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ushort).? }, + .c_int_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.int).? }, + .c_uint_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.uint).? }, + .c_long_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.long).? }, + .c_ulong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulong).? }, + .c_longlong_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.longlong).? }, + .c_ulonglong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulonglong).? }, else => switch (ip.indexToKey(ty.toIntern())) { .int_type => |int_type| return int_type, .struct_type => { @@ -1882,7 +1936,7 @@ pub fn floatBits(ty: Type, target: *const Target) u16 { .f64_type => 64, .f80_type => 80, .f128_type, .comptime_float_type => 128, - .c_longdouble_type => target.cTypeBitSize(.longdouble), + .c_longdouble_type => target.cTypeBitSize(.longdouble).?, else => unreachable, }; @@ -2147,13 +2201,6 @@ pub fn isVector(ty: Type, zcu: *const Zcu) bool { return ty.zigTypeTag(zcu) == .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, zcu: *const Zcu) bool { return switch (ty.zigTypeTag(zcu)) { .array, .vector => true, @@ -2416,34 +2463,6 @@ pub fn explicitFieldAlignment(ty: Type, index: usize, zcu: *const Zcu) Alignment }; } -/// Returns the alignment a struct field of type `field_ty` will be given if no alignment is -/// explicitly specified. However, in an `extern struct`, a higher alignment may be available due -/// to the struct's full layout (i.e. a field might coincidentally be more aligned). -/// -/// Asserts that the layout of `field_ty` is resolved. Asserts that `layout` is not `.@"packed"`. -pub fn defaultStructFieldAlignment( - field_ty: Type, - layout: std.lang.Type.ContainerLayout, - zcu: *const Zcu, -) Alignment { - const overalign_big_int = switch (layout) { - .@"packed" => unreachable, - .auto => zcu.getTarget().ofmt == .c, - .@"extern" => true, - }; - const abi_align = field_ty.abiAlignment(zcu); - assert(abi_align != .none); - // We check for anything over 64 here, because the C backend will lower e.g. u64 to a 128-bit - // integer, which has 16-byte alignment. - if (overalign_big_int and - ((field_ty.isAbiInt(zcu) and field_ty.intInfo(zcu).bits > 64) or - (field_ty.toIntern() == .f80_type and zcu.getTarget().cTypeBitSize(.longdouble) != 80))) - { - return abi_align.maxStrict(if (zcu.getTarget().cpu.arch == .s390x) .@"8" else .@"16"); - } - return abi_align; -} - pub fn structFieldDefaultValue(ty: Type, index: usize, zcu: *const Zcu) ?Value { const ip = &zcu.intern_pool; switch (ip.indexToKey(ty.toIntern())) { @@ -2961,8 +2980,7 @@ pub fn fieldPtrType(ptr_ty: Type, field_index: u32, pt: Zcu.PerThread) Allocator } const actual_field_align = switch (field_align) { .none => switch (ip.indexToKey(aggregate_ty.toIntern())) { - .tuple_type, .union_type => field_ty.abiAlignment(zcu), - .struct_type => field_ty.defaultStructFieldAlignment(.auto, zcu), + .struct_type, .tuple_type, .union_type => field_ty.abiAlignment(zcu), .ptr_type => Type.usize.abiAlignment(zcu), else => unreachable, }, @@ -3603,5 +3621,5 @@ pub fn smallestUnsignedBits(max: u64) u16 { pub const packed_struct_layout_version = 2; fn cTypeAlign(target: *const Target, c_type: Target.CType) Alignment { - return Alignment.fromByteUnits(target.cTypeAlignment(c_type)); + return .fromByteUnits(target.cTypeAlignment(c_type).?); } diff --git a/src/Value.zig b/src/Value.zig index dfc124659c3f6d9fb3be15430a5734ff2a3fe194..f6905eb4b55d11df4c1610c9c8e41475cd4fe522 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -611,12 +611,7 @@ pub fn toFloat(val: Value, comptime T: type, zcu: *const Zcu) T { return switch (zcu.intern_pool.indexToKey(val.toIntern())) { .int => |int| switch (int.storage) { .big_int => |big_int| big_int.toFloat(T, .nearest_even)[0], - inline .u64, .i64 => |x| { - if (T == f80) { - @panic("TODO we can't lower this properly on non-x86 llvm backend yet"); - } - return @floatFromInt(x); - }, + inline .u64, .i64 => |x| @floatFromInt(x), }, .float => |float| switch (float.storage) { inline else => |x| @floatCast(x), diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig index 73cd6ea1e357e38497633fbcbb5af744f1815e1a..1050b8fb0eb1425205f04b7440640ec55d6cf69a 100644 --- a/src/codegen/aarch64/Select.zig +++ b/src/codegen/aarch64/Select.zig @@ -12388,7 +12388,7 @@ pub const CallAbiIterator = struct { .f32 => .single, .f64 => .double, .f128 => .quad, - .c_longdouble => switch (zcu.getTarget().cTypeBitSize(.longdouble)) { + .c_longdouble => switch (zcu.getTarget().cTypeBitSize(.longdouble).?) { else => unreachable, 64 => .double, 80 => null, diff --git a/src/codegen/aarch64/abi.zig b/src/codegen/aarch64/abi.zig index 942e4d0660d79fd8bb0e204714586dd7878b3ebe..863a45e2d4bded294e263edff46787942cbcb31b 100644 --- a/src/codegen/aarch64/abi.zig +++ b/src/codegen/aarch64/abi.zig @@ -35,7 +35,12 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class { if (bit_size > 64) return .double_integer; return .integer; }, - .int, .@"enum", .error_set, .float, .bool => return .byval, + .int, .@"enum", .error_set, .bool => return .byval, + .float => return switch (ty.floatBits(zcu.getTarget())) { + else => unreachable, + 16, 32, 64, 128 => .byval, + 80 => .double_integer, + }, .vector => { const bit_size = ty.bitSize(zcu); // TODO is this controlled by a cpu feature? diff --git a/src/codegen/arm/abi.zig b/src/codegen/arm/abi.zig index 14acccbb7963991a3c9c201bcebaeb9218af7ff8..bd767560f1c0ac8fb3d5b8b6eed5956ea8e8253d 100644 --- a/src/codegen/arm/abi.zig +++ b/src/codegen/arm/abi.zig @@ -39,7 +39,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { const float_count = countFloats(ty, zcu, &maybe_float_bits); if (float_count <= byval_float_count) return .byval; - if (ty.abiAlignment(zcu).compare(.gt, .@"32")) { + if (ty.abiAlignment(zcu).compare(.gt, .@"4")) { return Class.arrSize(bit_size, 64); } @@ -62,7 +62,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { const float_count = countFloats(ty, zcu, &maybe_float_bits); if (float_count <= byval_float_count) return .byval; - if (union_obj.alignment.compareStrict(.gt, .@"32")) { + if (union_obj.alignment.compareStrict(.gt, .@"4")) { return Class.arrSize(bit_size, 64); } @@ -73,14 +73,16 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { } return Class.arrSize(bit_size, 32); }, - .bool, .float => return .byval, + .bool => return .byval, .int => { - // TODO this is incorrect for _BitInt(128) but implementing - // this correctly makes implementing compiler-rt impossible. - // const bit_size = ty.bitSize(zcu); - // if (bit_size > 64) return .memory; + if (ctx == .ret and ty.intInfo(zcu).bits > 64) return .memory; return .byval; }, + .float => return switch (ty.floatBits(zcu.getTarget())) { + else => unreachable, + 16, 32, 64 => .byval, + 80, 128 => .{ .i64_array = 2 }, + }, .@"enum", .error_set => { const bit_size = ty.bitSize(zcu); if (bit_size > 64) return .memory; diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index 8e86c0da93dabcda0bdfec08c406bbe0dab4f04d..41ce79a2f5ef333ad5d319f0fbbdd544d9347409 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -130,28 +130,28 @@ pub const CType = union(enum) { pub fn bits(int: Int, target: *const std.Target) u16 { return switch (int) { // zig fmt: off - .char => target.cTypeBitSize(.char), + .char => target.cTypeBitSize(.char).?, - .@"unsigned short" => target.cTypeBitSize(.ushort), - .@"unsigned int" => target.cTypeBitSize(.uint), - .@"unsigned long" => target.cTypeBitSize(.ulong), - .@"unsigned long long" => target.cTypeBitSize(.ulonglong), + .@"unsigned short" => target.cTypeBitSize(.ushort).?, + .@"unsigned int" => target.cTypeBitSize(.uint).?, + .@"unsigned long" => target.cTypeBitSize(.ulong).?, + .@"unsigned long long" => target.cTypeBitSize(.ulonglong).?, - .@"signed short" => target.cTypeBitSize(.short), - .@"signed int" => target.cTypeBitSize(.int), - .@"signed long" => target.cTypeBitSize(.long), - .@"signed long long" => target.cTypeBitSize(.longlong), + .@"signed short" => target.cTypeBitSize(.short).?, + .@"signed int" => target.cTypeBitSize(.int).?, + .@"signed long" => target.cTypeBitSize(.long).?, + .@"signed long long" => target.cTypeBitSize(.longlong).?, - .uintptr_t, .intptr_t => target.ptrBitWidth(), + .uintptr_t, .intptr_t => target.ptrBitWidth(), - .uint8_t, .int8_t => 8, - .uint16_t, .int16_t => 16, - .uint24_t, .int24_t => 24, - .uint32_t, .int32_t => 32, - .uint48_t, .int48_t => 48, - .uint64_t, .int64_t => 64, - .zig_u128, .zig_i128 => 128, - // zig fmt: on + .uint8_t, .int8_t => 8, + .uint16_t, .int16_t => 16, + .uint24_t, .int24_t => 24, + .uint32_t, .int32_t => 32, + .uint48_t, .int48_t => 48, + .uint64_t, .int64_t => 64, + .zig_u128, .zig_i128 => 128, + // zig fmt: on }; } }; diff --git a/src/codegen/c/type/render_defs.zig b/src/codegen/c/type/render_defs.zig index bfa368d3ceee36ac575898019e1b48d19a20d066..d591c09bf16d71b77c4d7b2dc3c4659665dd94a1 100644 --- a/src/codegen/c/type/render_defs.zig +++ b/src/codegen/c/type/render_defs.zig @@ -381,7 +381,7 @@ fn defineTuple( const overalign: bool = for (tuple.types.get(ip)) |field_ty_ip| { const field_ty: Type = .fromInterned(field_ty_ip); if (!field_ty.hasRuntimeBits(zcu)) continue; - const natural_align = field_ty.defaultStructFieldAlignment(.auto, zcu); + const natural_align = field_ty.abiAlignment(zcu); if (natural_align.compareStrict(.gte, tuple_align)) break false; } else true; @@ -402,15 +402,17 @@ fn defineTuple( if (zig_offset == 0 and overalign) { // This is the first field; specify its alignment to align the tuple. try writeFieldAlign(field_ty, tuple_align, w, zcu); - } else if (zig_offset > c_offset) { - // This field needs to be overaligned compared to what its offset would otherwise be. - const need_align: Alignment = .minStrict( - tuple_align, // don't make the struct more aligned than it should be - .fromLog2Units(@ctz(zig_offset)), - ); - try writeFieldAlign(field_ty, need_align, w, zcu); - c_offset = need_align.forward(c_offset); + } else switch (zig_offset - c_offset) { + 0 => {}, + else => |need_bytes| { + // This field needs to be overaligned compared to what its offset would otherwise be. + const need_align: Alignment = .fromLog2Units(std.math.log2_int(u64, need_bytes) + 1); + assert(need_align.compareStrict(.lte, tuple_align)); + try writeFieldAlign(field_ty, need_align, w, zcu); + c_offset = need_align.forward(c_offset); + }, } + assert(c_offset == zig_offset); const field_cty: CType = try .lower(field_ty, deps, arena, zcu); try w.print("{f}f{d}{f};\n", .{ field_cty.fmtDeclaratorPrefix(zcu), @@ -443,7 +445,7 @@ fn defineStruct( while (it.next()) |field_index| { const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); if (!field_ty.hasRuntimeBits(zcu)) continue; - const natural_align = field_ty.defaultStructFieldAlignment(struct_type.layout, zcu); + const natural_align = field_ty.abiAlignment(zcu); const natural_offset = natural_align.forward(offset); const actual_offset = struct_type.field_offsets.get(ip)[field_index]; if (actual_offset < natural_offset) break :pack true; @@ -464,7 +466,7 @@ fn defineStruct( while (it.next()) |field_index| { const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); if (!field_ty.hasRuntimeBits(zcu)) continue; - const natural_align = field_ty.defaultStructFieldAlignment(struct_type.layout, zcu); + const natural_align = field_ty.abiAlignment(zcu); if (natural_align.compareStrict(.gte, struct_type.alignment)) break :overalign false; } break :overalign true; @@ -481,7 +483,7 @@ fn defineStruct( while (it.next()) |field_index| { const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); if (!field_ty.hasRuntimeBits(zcu)) continue; - const natural_align = field_ty.defaultStructFieldAlignment(struct_type.layout, zcu); + const natural_align = field_ty.abiAlignment(zcu); const natural_offset = switch (pack) { true => offset, false => natural_align.forward(offset), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6d200cacafc52cc7067eefe18f07c1d1a9580768..221c9423e3a035367c010f0d6225940ab70a8ffb 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -573,6 +573,8 @@ pub const Object = struct { val: InternPool.Index, @"addrspace": std.lang.AddressSpace, }, Builder.Variable.Index), + /// Same as `uav_map` but for llvm values not originating from the frontend. + const_map: std.AutoHashMapUnmanaged(Builder.Constant, Builder.Variable.Index), /// Maps enum types to their corresponding LLVM functions for implementing the `tag_name` instruction. enum_tag_name_map: std.AutoHashMapUnmanaged(InternPool.Index, Builder.Function.Index), /// Serves the same purpose as `enum_tag_name_map` but for the `is_named_enum_value` instruction. @@ -693,6 +695,7 @@ pub const Object = struct { .zcu = zcu, .nav_map = .empty, .uav_map = .empty, + .const_map = .empty, .enum_tag_name_map = .empty, .named_enum_map = .empty, .type_map = .empty, @@ -703,21 +706,22 @@ pub const Object = struct { return obj; } - pub fn deinit(self: *Object) void { - const gpa = self.gpa; - self.type_pool.deinit(gpa); - self.lazy_abi_aligns.deinit(gpa); - self.debug_enums.deinit(gpa); - self.debug_globals.deinit(gpa); - self.debug_file_map.deinit(gpa); - self.debug_types.deinit(gpa); - self.nav_map.deinit(gpa); - self.uav_map.deinit(gpa); - self.enum_tag_name_map.deinit(gpa); - self.named_enum_map.deinit(gpa); - self.type_map.deinit(gpa); - self.builder.deinit(); - self.* = undefined; + pub fn deinit(o: *Object) void { + const gpa = o.gpa; + o.type_pool.deinit(gpa); + o.lazy_abi_aligns.deinit(gpa); + o.debug_enums.deinit(gpa); + o.debug_globals.deinit(gpa); + o.debug_file_map.deinit(gpa); + o.debug_types.deinit(gpa); + o.nav_map.deinit(gpa); + o.uav_map.deinit(gpa); + o.const_map.deinit(gpa); + o.enum_tag_name_map.deinit(gpa); + o.named_enum_map.deinit(gpa); + o.type_map.deinit(gpa); + o.builder.deinit(); + o.* = undefined; } fn genErrorNameTable(o: *Object) Allocator.Error!void { @@ -741,16 +745,16 @@ pub const Object = struct { for (llvm_errors[1..], error_name_list) |*llvm_error, name| { const name_string = try o.builder.stringNull(name.toSlice(ip)); const name_init = try o.builder.stringConst(name_string); - const name_variable_index = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); - try name_variable_index.setInitializer(name_init, &o.builder); - name_variable_index.setMutability(.constant, &o.builder); - name_variable_index.setAlignment(comptime .fromByteUnits(1), &o.builder); - const global_index = name_variable_index.ptrConst(&o.builder).global; - global_index.setLinkage(.private, &o.builder); - global_index.setUnnamedAddr(.unnamed_addr, &o.builder); + const name_llvm_variable = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); + try name_llvm_variable.setInitializer(name_init, &o.builder); + name_llvm_variable.setMutability(.constant, &o.builder); + name_llvm_variable.setAlignment(comptime .fromByteUnits(1), &o.builder); + const llvm_global = name_llvm_variable.ptrConst(&o.builder).global; + llvm_global.setLinkage(.private, &o.builder); + llvm_global.setUnnamedAddr(.unnamed_addr, &o.builder); llvm_error.* = try o.builder.structConst(llvm_slice_ty, &.{ - name_variable_index.toConst(&o.builder), + name_llvm_variable.toConst(&o.builder), try o.builder.intConst(llvm_usize_ty, name_string.slice(&o.builder).?.len - 1), }); } @@ -1199,19 +1203,33 @@ pub const Object = struct { global.dll_storage_class = .default; global.unnamed_addr = .unnamed_addr; } - llvm_function.setAlignment(switch (nav.resolved.?.@"align") { - .none => fn_ty.abiAlignment(zcu).toLlvm(), - else => |a| a.toLlvm(), - }, &o.builder); + llvm_function.setAlignment(nav.resolved.?.@"align".toLlvm(), &o.builder); llvm_function.setSection(s: { const section = nav.resolved.?.@"linksection".toSlice(ip) orelse break :s .none; break :s try o.builder.string(section); }, &o.builder); - try o.addLlvmFunctionAttributes(pt, func.owner_nav, llvm_function); - var attributes = try llvm_function.ptrConst(&o.builder).attributes.toWip(&o.builder); + var attributes: Builder.FunctionAttributes.Wip = .{}; defer attributes.deinit(&o.builder); + // Function attributes that are independent of analysis results of the function body. + try o.addCommonFnAttributes( + &attributes, + owner_mod, + // Some backends don't respect the `naked` attribute in `TargetFrameLowering::hasFP()`, + // so for these backends, LLVM will happily emit code that accesses the stack through + // the frame pointer. This is nonsensical since what the `naked` attribute does is + // suppress generation of the prologue and epilogue, and the prologue is where the + // frame pointer normally gets set up. At time of writing, this is the case for at + // least x86 and RISC-V. + owner_mod.omit_frame_pointer or fn_info.cc == .naked, + ); + + try o.addCallingConventionFnAttributes(pt, llvm_function, &attributes, if (nav.getExtern(ip)) |@"extern"| .{ + .name = nav.name.toSlice(ip), + .lib_name = @"extern".lib_name.toSlice(ip), + } else null, .fromIntern(fn_info, ip)); + const func_analysis = func.analysisUnordered(ip); if (func_analysis.is_noinline) { try attributes.addFnAttr(.@"noinline", &o.builder); @@ -1324,7 +1342,7 @@ pub const Object = struct { const counters_variable = try o.builder.addVariable(anon_name, .void, .default); try o.used.append(gpa, counters_variable.toConst(&o.builder)); counters_variable.ptrConst(&o.builder).global.setLinkage(.private, &o.builder); - counters_variable.setAlignment(comptime Builder.Alignment.fromByteUnits(1), &o.builder); + counters_variable.setAlignment(comptime .fromByteUnits(1), &o.builder); if (target.ofmt == .macho) { counters_variable.setSection(try o.builder.string("__DATA,__sancov_cntrs"), &o.builder); @@ -1507,10 +1525,6 @@ pub const Object = struct { llvm_global.ptr(&o.builder).unnamed_addr = .unnamed_addr; } - const llvm_align = switch (resolved.@"align") { - .none => nav_ty.abiAlignment(zcu).toLlvm(), - else => |a| a.toLlvm(), - }; const llvm_section: Builder.String = if (resolved.@"linksection".toSlice(ip)) |section| s: { break :s try o.builder.string(section); } else .none; @@ -1519,13 +1533,20 @@ pub const Object = struct { // can see are extern functions or other comptime function body values (e.g. undefined). Of // these, only extern functions need to be lowered to LLVM functions. if (opt_extern != null and nav_ty.zigTypeTag(zcu) == .@"fn" and nav_ty.fnHasRuntimeBits(zcu)) { + const fn_info = zcu.typeToFunc(nav_ty).?; const llvm_function: Builder.Function.Index = switch (llvm_global.ptrConst(&o.builder).kind) { .function => |function| function, // re-use existing `Builder.Function` .replaced, .alias, .variable => try llvm_global.toNewFunction(&o.builder), }; - llvm_function.setAlignment(llvm_align, &o.builder); + llvm_function.setAlignment(resolved.@"align".toLlvm(), &o.builder); llvm_function.setSection(llvm_section, &o.builder); - try o.addLlvmFunctionAttributes(pt, nav_id, llvm_function); + var attributes: Builder.FunctionAttributes.Wip = .{}; + defer attributes.deinit(&o.builder); + try o.addCallingConventionFnAttributes(pt, llvm_function, &attributes, .{ + .name = nav.name.toSlice(ip), + .lib_name = opt_extern.?.lib_name.toSlice(ip), + }, .fromIntern(fn_info, ip)); + llvm_function.setAttributes(try attributes.finish(&o.builder), &o.builder); } else { const file_scope = nav.srcInst(ip).resolveFile(ip); const mod = zcu.fileByIndex(file_scope).mod.?; @@ -1534,7 +1555,10 @@ pub const Object = struct { .variable => |variable| variable, // re-use existing `Builder.Variable` .replaced, .alias, .function => try llvm_global.toNewVariable(&o.builder), }; - llvm_variable.setAlignment(llvm_align, &o.builder); + llvm_variable.setAlignment(switch (resolved.@"align") { + .none => nav_ty.abiAlignment(zcu).toLlvm(), + else => |a| a.toLlvm(), + }, &o.builder); llvm_variable.setSection(llvm_section, &o.builder); llvm_variable.setMutability(if (resolved.@"const") .constant else .global, &o.builder); try llvm_variable.setInitializer(if (opt_extern != null) .no_init else try o.lowerValue(resolved.value, .in_memory), &o.builder); @@ -1585,7 +1609,7 @@ pub const Object = struct { const uav_ty = Value.fromInterned(uav).typeOf(zcu); const uav_ref = try o.lowerUavRef( uav, - uav_ty.abiAlignment(zcu), + uav_ty.abiAlignment(zcu).toLlvm(), target_util.defaultAddressSpace(zcu.getTarget(), .global_constant), ); break :exp .{ uav_ty, uav_ref }; @@ -1599,7 +1623,7 @@ pub const Object = struct { fn updateExportedGlobal( o: *Object, - global_index: Builder.Global.Index, + llvm_global: Builder.Global.Index, ty: Type, export_indices: []const Zcu.Export.Index, ) link.Error!void { @@ -1634,11 +1658,11 @@ pub const Object = struct { // make much sense: the linksection should be associated with the declaration itself rather // than some particular symbol it is exported as! if (export_indices[0].ptr(zcu).opts.section.toSlice(ip)) |section_slice| { - const variable = &global_index.ptrConst(&o.builder).kind.variable; + const variable = &llvm_global.ptrConst(&o.builder).kind.variable; variable.setSection(try o.builder.string(section_slice), &o.builder); } - const llvm_global_ty = global_index.typeOf(&o.builder); + const llvm_global_ty = llvm_global.typeOf(&o.builder); // All exports are represented as aliases to the original global. @@ -1661,8 +1685,8 @@ pub const Object = struct { const alias = try o.builder.addAlias( exp_name, llvm_global_ty, - global_index.ptrConst(&o.builder).addr_space, - global_index.toConst(), + llvm_global.ptrConst(&o.builder).addr_space, + llvm_global.toConst(), ); break :global alias.ptrConst(&o.builder).global; }; @@ -1671,12 +1695,9 @@ pub const Object = struct { switch (existing_global.ptrConst(&o.builder).kind) { .alias => |alias| { // We can just repurpose the existing alias. - alias.setAliasee(global_index.toConst(), &o.builder); - alias.ptrConst(&o.builder).global.ptr(&o.builder).type = global_index.typeOf(&o.builder); - // If the type the alias is pointing to can change, then - // it makes sense that we should update the address - // space too. - alias.ptrConst(&o.builder).global.ptr(&o.builder).addr_space = global_index.ptrConst(&o.builder).addr_space; + alias.setAliasee(llvm_global.toConst(), &o.builder); + alias.ptrConst(&o.builder).global.ptr(&o.builder).type = llvm_global.typeOf(&o.builder); + alias.ptrConst(&o.builder).global.ptr(&o.builder).addr_space = llvm_global.ptrConst(&o.builder).addr_space; break :global existing_global; }, .variable, .function => { @@ -1686,13 +1707,13 @@ pub const Object = struct { // We need to make a new global which is an alias. Replace this existing one // with the target global, making the name available and fixing references // to this global to point to the target. - try existing_global.replace(global_index, &o.builder); + try existing_global.replace(llvm_global, &o.builder); // The name is now free, so create an alias. const alias = try o.builder.addAlias( exp_name, llvm_global_ty, - global_index.ptrConst(&o.builder).addr_space, - global_index.toConst(), + llvm_global.ptrConst(&o.builder).addr_space, + llvm_global.toConst(), ); break :global alias.ptrConst(&o.builder).global; }, @@ -1725,11 +1746,11 @@ pub const Object = struct { pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Allocator.Error!void { _ = o.type_map.remove(ty); try o.type_pool.updateContainerType(pt, .{ .llvm = o }, ty, success); - if (o.named_enum_map.get(ty)) |function_index| { - try o.updateIsNamedEnumValueFunction(.fromInterned(ty), function_index); + if (o.named_enum_map.get(ty)) |llvm_function| { + try o.updateIsNamedEnumValueFunction(.fromInterned(ty), llvm_function); } - if (o.enum_tag_name_map.get(ty)) |function_index| { - try o.updateEnumTagNameFunction(.fromInterned(ty), function_index); + if (o.enum_tag_name_map.get(ty)) |llvm_function| { + try o.updateEnumTagNameFunction(.fromInterned(ty), llvm_function); } } @@ -2102,7 +2123,7 @@ pub const Object = struct { payload_offset * 8, ); - return try o.builder.debugStructType( + return o.builder.debugStructType( name, null, // File o.debug_compile_unit.unwrap().?, // Scope @@ -2140,7 +2161,7 @@ pub const Object = struct { defer debug_param_types.deinit(gpa); // Return type goes first. - if (try fnReturnStrat(o, fn_info) == .sret) { + if (try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type)) == .sret) { // Actual return type is void, then first arg is the sret pointer. const ptr_ty = try pt.singleMutPtrType(.fromInterned(fn_info.return_type)); debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, .void)); @@ -2575,50 +2596,114 @@ pub const Object = struct { fn namespaceToDebugScope(o: *Object, pt: Zcu.PerThread, namespace_index: InternPool.NamespaceIndex) !Builder.Metadata { const zcu = o.zcu; const namespace = zcu.namespacePtr(namespace_index); - if (namespace.parent == .none) return try o.getDebugFile(namespace.file_scope); + if (namespace.parent == .none) return o.getDebugFile(namespace.file_scope); return o.getDebugType(pt, .fromInterned(namespace.owner_type)); } - /// Sets the attributes and callconv of the given `Builder.Function`, which corresponds to the - /// given `Nav` (which is a function). - fn addLlvmFunctionAttributes( + fn addCommonFnAttributes( + o: *Object, + attributes: *Builder.FunctionAttributes.Wip, + owner_mod: *Module, + omit_frame_pointer: bool, + ) Allocator.Error!void { + if (!owner_mod.red_zone) { + try attributes.addFnAttr(.noredzone, &o.builder); + } + if (omit_frame_pointer) { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("frame-pointer"), + .value = try o.builder.string("none"), + } }, &o.builder); + } else { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("frame-pointer"), + .value = try o.builder.string("all"), + } }, &o.builder); + } + try attributes.addFnAttr(.nounwind, &o.builder); + if (owner_mod.unwind_tables != .none) { + try attributes.addFnAttr( + .{ .uwtable = if (owner_mod.unwind_tables == .async) .async else .sync }, + &o.builder, + ); + } + if (owner_mod.optimize_mode == .small) { + try attributes.addFnAttr(.minsize, &o.builder); + try attributes.addFnAttr(.optsize, &o.builder); + } + const target = &owner_mod.resolved_target.result; + if (target.cpu.model.llvm_name) |s| { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("target-cpu"), + .value = try o.builder.string(s), + } }, &o.builder); + } + if (owner_mod.resolved_target.llvm_cpu_features) |s| { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("target-features"), + .value = try o.builder.string(std.mem.span(s)), + } }, &o.builder); + } + if (target.abi.float() == .soft) { + // `use-soft-float` means "use software routines for floating point computations". In + // other words, it configures how LLVM lowers basic float instructions like `fcmp`, + // `fadd`, etc. The float calling convention is configured on `TargetMachine` and is + // mostly an orthogonal concept, although obviously we do need hardware float operations + // to actually be able to pass float values in float registers. + // + // Ideally, we would support something akin to the `-mfloat-abi=softfp` option that GCC + // and Clang support for Arm32 and CSKY. We don't currently expose such an option in + // Zig, and using CPU features as the source of truth for this makes for a miserable + // user experience since people expect e.g. `arm-linux-gnueabi` to mean full soft float + // unless the compiler has explicitly been told otherwise. (And note that our baseline + // CPU models almost all include FPU features!) + // + // Revisit this at some point. + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("use-soft-float"), + .value = try o.builder.string("true"), + } }, &o.builder); + + // This prevents LLVM from using FPU/SIMD code for things like `memcpy`. As for the + // above, this should be revisited if `softfp` support is added. + try attributes.addFnAttr(.noimplicitfloat, &o.builder); + } + } + + pub fn addCallingConventionFnAttributes( o: *Object, pt: Zcu.PerThread, - nav_id: InternPool.Nav.Index, - function_index: Builder.Function.Index, + llvm_function: Builder.Function.Index, + attributes: *Builder.FunctionAttributes.Wip, + opt_extern: ?struct { + name: []const u8, + lib_name: ?[]const u8 = null, + }, + fn_info: FuncInfo, ) Allocator.Error!void { const zcu = o.zcu; - const ip = &zcu.intern_pool; - const nav = ip.getNav(nav_id); - const owner_mod = zcu.navFileScope(nav_id).mod.?; - const ty: Type = .fromInterned(nav.resolved.?.type); - - const fn_info = zcu.typeToFunc(ty).?; - const target = &owner_mod.resolved_target.result; - - var attributes: Builder.FunctionAttributes.Wip = .{}; - defer attributes.deinit(&o.builder); - - if (target.cpu.arch.isWasm()) if (nav.getExtern(ip)) |@"extern"| { - try attributes.addFnAttr(.{ .string = .{ - .kind = try o.builder.string("wasm-import-name"), - .value = try o.builder.string(nav.name.toSlice(ip)), - } }, &o.builder); - if (@"extern".lib_name.toSlice(ip)) |lib_name_slice| { - if (!std.mem.eql(u8, lib_name_slice, "c")) try attributes.addFnAttr(.{ .string = .{ - .kind = try o.builder.string("wasm-import-module"), - .value = try o.builder.string(lib_name_slice), - } }, &o.builder); - } - }; + const target = zcu.getTarget(); if (fn_info.cc == .async) { @panic("TODO: LLVM backend lower async function"); } + if (target.cpu.arch.isWasm()) if (opt_extern) |@"extern"| { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("wasm-import-name"), + .value = try o.builder.string(@"extern".name), + } }, &o.builder); + if (@"extern".lib_name) |lib_name| { + if (!std.mem.eql(u8, lib_name, "c")) try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("wasm-import-module"), + .value = try o.builder.string(lib_name), + } }, &o.builder); + } + }; + const cc_info = toLlvmCallConv(fn_info.cc, target).?; - function_index.setCallConv(cc_info.llvm_cc, &o.builder); + llvm_function.setCallConv(cc_info.llvm_cc, &o.builder); if (cc_info.align_stack) { try attributes.addFnAttr(.{ .alignstack = .wrap(.fromByteUnits(target.stackAlignment())) }, &o.builder); @@ -2672,29 +2757,16 @@ pub const Object = struct { else => {}, } - // Function attributes that are independent of analysis results of the function body. - try o.addCommonFnAttributes( - &attributes, - owner_mod, - // Some backends don't respect the `naked` attribute in `TargetFrameLowering::hasFP()`, - // so for these backends, LLVM will happily emit code that accesses the stack through - // the frame pointer. This is nonsensical since what the `naked` attribute does is - // suppress generation of the prologue and epilogue, and the prologue is where the - // frame pointer normally gets set up. At time of writing, this is the case for at - // least x86 and RISC-V. - owner_mod.omit_frame_pointer or fn_info.cc == .naked, - ); - if (fn_info.return_type == .noreturn_type) try attributes.addFnAttr(.noreturn, &o.builder); - var it = iterateParamTypes(o, fn_info); - if (try fnReturnStrat(o, fn_info) == .sret) { - // Sret pointers must not be address 0 - try attributes.addParamAttr(it.llvm_index, .nonnull, &o.builder); - try attributes.addParamAttr(it.llvm_index, .@"noalias", &o.builder); - - const raw_llvm_ret_ty = try o.lowerType(.fromInterned(fn_info.return_type), .in_memory); - try attributes.addParamAttr(it.llvm_index, .{ .sret = raw_llvm_ret_ty }, &o.builder); + var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types); + if (try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type)) == .sret) { + try o.addSRetFnAttributes( + attributes, + try o.lowerType(.fromInterned(fn_info.return_type), .in_memory), + Type.fromInterned(fn_info.return_type).abiAlignment(zcu).toLlvm(), + .declaration, + ); it.llvm_index += 1; } else if (ccAbiPromoteInt(fn_info.cc, zcu, Type.fromInterned(fn_info.return_type))) |s| switch (s) { .signed => try attributes.addRetAttr(.signext, &o.builder), @@ -2713,9 +2785,9 @@ pub const Object = struct { while (try it.next()) |lowering| switch (lowering) { .byval => { const param_index = it.zig_index - 1; - const param_ty: Type = .fromInterned(fn_info.param_types.get(ip)[param_index]); + const param_ty: Type = .fromInterned(fn_info.param_types[param_index]); if (!isByRef(param_ty, zcu)) { - try o.addByValParamAttrs(pt, &attributes, param_ty, param_index, fn_info, it.llvm_index - 1); + try o.addByValParamAttrs(pt, attributes, param_ty, param_index, fn_info, it.llvm_index - 1); } if (remaining_inreg_int > 0 and @@ -2734,12 +2806,12 @@ pub const Object = struct { } }, .byref => { - const param_ty: Type = .fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); - try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, it.byval_attr, param_ty); + const param_ty: Type = .fromInterned(fn_info.param_types[it.zig_index - 1]); + try o.addByRefParamAttrs(attributes, it.llvm_index - 1, it.byval_attr, param_ty); }, .byref_mut => try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder), .slice => { - const param_ty: Type = .fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); + const param_ty: Type = .fromInterned(fn_info.param_types[it.zig_index - 1]); const ptr_info = param_ty.ptrInfo(zcu); const llvm_ptr_index = it.llvm_index - 2; if (std.math.cast(u5, it.zig_index - 1)) |i| { @@ -2771,78 +2843,24 @@ pub const Object = struct { .i64_array, => continue, }; - - function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); } - fn addCommonFnAttributes( + pub fn addSRetFnAttributes( o: *Object, attributes: *Builder.FunctionAttributes.Wip, - owner_mod: *Module, - omit_frame_pointer: bool, + ret_ty: Builder.Type, + ret_align: Builder.Alignment, + location: enum { declaration, callsite }, ) Allocator.Error!void { - if (!owner_mod.red_zone) { - try attributes.addFnAttr(.noredzone, &o.builder); - } - if (omit_frame_pointer) { - try attributes.addFnAttr(.{ .string = .{ - .kind = try o.builder.string("frame-pointer"), - .value = try o.builder.string("none"), - } }, &o.builder); - } else { - try attributes.addFnAttr(.{ .string = .{ - .kind = try o.builder.string("frame-pointer"), - .value = try o.builder.string("all"), - } }, &o.builder); - } - try attributes.addFnAttr(.nounwind, &o.builder); - if (owner_mod.unwind_tables != .none) { - try attributes.addFnAttr( - .{ .uwtable = if (owner_mod.unwind_tables == .async) .async else .sync }, - &o.builder, - ); - } - if (owner_mod.optimize_mode == .small) { - try attributes.addFnAttr(.minsize, &o.builder); - try attributes.addFnAttr(.optsize, &o.builder); - } - const target = &owner_mod.resolved_target.result; - if (target.cpu.model.llvm_name) |s| { - try attributes.addFnAttr(.{ .string = .{ - .kind = try o.builder.string("target-cpu"), - .value = try o.builder.string(s), - } }, &o.builder); - } - if (owner_mod.resolved_target.llvm_cpu_features) |s| { - try attributes.addFnAttr(.{ .string = .{ - .kind = try o.builder.string("target-features"), - .value = try o.builder.string(std.mem.span(s)), - } }, &o.builder); - } - if (target.abi.float() == .soft) { - // `use-soft-float` means "use software routines for floating point computations". In - // other words, it configures how LLVM lowers basic float instructions like `fcmp`, - // `fadd`, etc. The float calling convention is configured on `TargetMachine` and is - // mostly an orthogonal concept, although obviously we do need hardware float operations - // to actually be able to pass float values in float registers. - // - // Ideally, we would support something akin to the `-mfloat-abi=softfp` option that GCC - // and Clang support for Arm32 and CSKY. We don't currently expose such an option in - // Zig, and using CPU features as the source of truth for this makes for a miserable - // user experience since people expect e.g. `arm-linux-gnueabi` to mean full soft float - // unless the compiler has explicitly been told otherwise. (And note that our baseline - // CPU models almost all include FPU features!) - // - // Revisit this at some point. - try attributes.addFnAttr(.{ .string = .{ - .kind = try o.builder.string("use-soft-float"), - .value = try o.builder.string("true"), - } }, &o.builder); - - // This prevents LLVM from using FPU/SIMD code for things like `memcpy`. As for the - // above, this should be revisited if `softfp` support is added. - try attributes.addFnAttr(.noimplicitfloat, &o.builder); - } + try attributes.addParamAttr(0, .dead_on_unwind, &o.builder); + switch (location) { + .declaration => try attributes.addParamAttr(0, .@"noalias", &o.builder), + .callsite => {}, + } + try attributes.addParamAttr(0, .writeonly, &o.builder); + try attributes.addParamAttr(0, .{ .captures = .none }, &o.builder); + try attributes.addParamAttr(0, .{ .sret = ret_ty }, &o.builder); + try attributes.addParamAttr(0, .{ .@"align" = .wrap(ret_align) }, &o.builder); } pub const TypeRepr = enum { @@ -2861,6 +2879,151 @@ pub const Object = struct { }); } + pub const SoftF80Layout = struct { + alignment: InternPool.Alignment, + /// byte offset of u64 field + mantissa_offset: u64, + /// byte offset of u16 field + exponent_offset: u64, + llvm_fields_len: u32, + + pub const LlvmFieldTag = enum { mantissa, exponent, padding }; + }; + pub fn softF80Layout(o: *Object, opts: struct { + llvm_field_tags_buf: []SoftF80Layout.LlvmFieldTag = &.{}, + llvm_field_types_buf: []Builder.Type = &.{}, + }) Allocator.Error!SoftF80Layout { + const zcu = o.zcu; + const target = zcu.getTarget(); + assert(std.zig.target.compilerRtFloatAbi(target, 80) == .soft); + // Current compiler rt soft abi, which is not yet affected by endianness for simplicity: + // + // typedef struct { uint64_t mantissa; uint16_t exponent; } f80; + // + var layout: SoftF80Layout = .{ + .alignment = Type.f80.abiAlignment(zcu), + .mantissa_offset = undefined, + .exponent_offset = undefined, + .llvm_fields_len = 0, + }; + var offset: u64 = 0; + for ([2]SoftF80Layout.LlvmFieldTag{ .mantissa, .exponent }, [2]Type{ .u64, .u16 }) |field_tag, field_type| { + const field_align = field_type.abiAlignment(zcu); + assert(field_align.compareStrict(.lte, layout.alignment)); + const field_offset = field_align.forward(offset); + switch (field_offset - offset) { + 0 => {}, + else => |padding| { + if (layout.llvm_fields_len < opts.llvm_field_tags_buf.len) + opts.llvm_field_tags_buf[layout.llvm_fields_len] = .padding; + if (layout.llvm_fields_len < opts.llvm_field_types_buf.len) + opts.llvm_field_types_buf[layout.llvm_fields_len] = try o.builder.arrayType(padding, .i8); + layout.llvm_fields_len += 1; + }, + } + switch (field_tag) { + .mantissa => layout.mantissa_offset = field_offset, + .exponent => layout.exponent_offset = field_offset, + .padding => unreachable, + } + if (layout.llvm_fields_len < opts.llvm_field_tags_buf.len) + opts.llvm_field_tags_buf[layout.llvm_fields_len] = field_tag; + if (layout.llvm_fields_len < opts.llvm_field_types_buf.len) + opts.llvm_field_types_buf[layout.llvm_fields_len] = try o.lowerType(field_type, .in_memory); + layout.llvm_fields_len += 1; + offset = field_offset + field_type.abiSize(zcu); + } + const end = layout.alignment.forward(offset); + assert(end == Type.f80.abiSize(zcu)); + switch (end - offset) { + 0 => {}, + else => |padding| { + if (layout.llvm_fields_len < opts.llvm_field_tags_buf.len) + opts.llvm_field_tags_buf[layout.llvm_fields_len] = .padding; + if (layout.llvm_fields_len < opts.llvm_field_types_buf.len) + opts.llvm_field_types_buf[layout.llvm_fields_len] = try o.builder.arrayType(padding, .i8); + layout.llvm_fields_len += 1; + }, + } + return layout; + } + + pub const SoftF128Layout = struct { + alignment: InternPool.Alignment, + /// byte offset of u64 field + lo_offset: u64, + /// byte offset of u64 field + hi_offset: u64, + llvm_fields_len: u32, + + pub const LlvmFieldTag = enum { lo, hi, padding }; + }; + pub fn softF128Layout(o: *Object, opts: struct { + llvm_field_tags_buf: []SoftF128Layout.LlvmFieldTag = &.{}, + llvm_field_types_buf: []Builder.Type = &.{}, + }) Allocator.Error!SoftF128Layout { + const zcu = o.zcu; + const target = zcu.getTarget(); + assert(std.zig.target.compilerRtFloatAbi(target, 128) == .soft); + // Current compiler rt soft abi: + // + // #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + // typedef struct { uint64_t hi, lo; } f128; + // #else + // typedef struct { uint64_t lo, hi; } f128; + // #endif + // + var layout: SoftF128Layout = .{ + .alignment = Type.f128.abiAlignment(zcu), + .lo_offset = undefined, + .hi_offset = undefined, + .llvm_fields_len = 0, + }; + var offset: u64 = 0; + for (@as([2]SoftF128Layout.LlvmFieldTag, switch (target.cpu.arch.endian()) { + .big => .{ .hi, .lo }, + .little => .{ .lo, .hi }, + }), [2]Type{ .u64, .u64 }) |field_tag, field_type| { + const field_align = field_type.abiAlignment(zcu); + assert(field_align.compareStrict(.lte, layout.alignment)); + const field_offset = field_align.forward(offset); + switch (field_offset - offset) { + 0 => {}, + else => |padding| { + if (layout.llvm_fields_len < opts.llvm_field_tags_buf.len) + opts.llvm_field_tags_buf[layout.llvm_fields_len] = .padding; + if (layout.llvm_fields_len < opts.llvm_field_types_buf.len) + opts.llvm_field_types_buf[layout.llvm_fields_len] = try o.builder.arrayType(padding, .i8); + layout.llvm_fields_len += 1; + }, + } + switch (field_tag) { + .lo => layout.lo_offset = field_offset, + .hi => layout.hi_offset = field_offset, + .padding => unreachable, + } + if (layout.llvm_fields_len < opts.llvm_field_tags_buf.len) + opts.llvm_field_tags_buf[layout.llvm_fields_len] = field_tag; + if (layout.llvm_fields_len < opts.llvm_field_types_buf.len) + opts.llvm_field_types_buf[layout.llvm_fields_len] = try o.lowerType(field_type, .in_memory); + layout.llvm_fields_len += 1; + offset = field_offset + field_type.abiSize(zcu); + } + const end = layout.alignment.forward(offset); + assert(end == Type.f128.abiSize(zcu)); + switch (end - offset) { + 0 => {}, + else => |padding| { + if (layout.llvm_fields_len < opts.llvm_field_tags_buf.len) + opts.llvm_field_tags_buf[layout.llvm_fields_len] = .padding; + if (layout.llvm_fields_len < opts.llvm_field_types_buf.len) + opts.llvm_field_types_buf[layout.llvm_fields_len] = try o.builder.arrayType(padding, .i8); + layout.llvm_fields_len += 1; + }, + } + return layout; + } + pub fn lowerType(o: *Object, t: Type, repr: TypeRepr) Allocator.Error!Builder.Type { const zcu = o.zcu; const target = zcu.getTarget(); @@ -2901,7 +3064,7 @@ pub const Object = struct { .c_ulonglong_type, => |tag| try o.builder.intType(target.cTypeBitSize( @field(std.Target.CType, @tagName(tag)["c_".len .. @tagName(tag).len - "_type".len]), - )), + ).?), .c_longdouble_type, .f16_type, .f32_type, @@ -2909,11 +3072,44 @@ pub const Object = struct { .f80_type, .f128_type, => switch (t.floatBits(target)) { - 16 => if (backendSupportsF16(target)) .half else .i16, - 32 => .float, - 64 => .double, - 80 => if (backendSupportsF80(target)) .x86_fp80 else .i80, - 128 => .fp128, + 16 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { + .hard => .half, + .soft => .i16, + }, + 32 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { + .hard => .float, + .soft => .i32, + }, + 64 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { + .hard => .double, + .soft => .i64, + }, + 80 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { + .hard => .x86_fp80, + .soft => { + var llvm_field_types_buf: [5]Builder.Type = undefined; + const f80_layout = try o.softF80Layout(.{ + .llvm_field_types_buf = &llvm_field_types_buf, + }); + return o.builder.structType( + .normal, + llvm_field_types_buf[0..f80_layout.llvm_fields_len], + ); + }, + }, + 128 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { + .hard => .fp128, + .soft => { + var llvm_field_types_buf: [5]Builder.Type = undefined; + const f128_layout = try o.softF128Layout(.{ + .llvm_field_types_buf = &llvm_field_types_buf, + }); + return o.builder.structType( + .normal, + llvm_field_types_buf[0..f128_layout.llvm_fields_len], + ); + }, + }, else => unreachable, }, .anyopaque_type => { @@ -2992,11 +3188,13 @@ pub const Object = struct { array_type.lenIncludingSentinel(), try o.lowerType(.fromInterned(array_type.child), repr), ), - .vector_type => |vector_type| o.builder.vectorType( - .normal, - vector_type.len, - try o.lowerType(.fromInterned(vector_type.child), .as_value), - ), + .vector_type => |vector_type| if (isByRef(t, zcu)) { + const child_llvm_ty = try o.lowerType(.fromInterned(vector_type.child), .in_memory); + return o.builder.arrayType(vector_type.len, child_llvm_ty); + } else { + const child_llvm_ty = try o.lowerType(.fromInterned(vector_type.child), .as_value); + return o.builder.vectorType(.normal, vector_type.len, child_llvm_ty); + }, .opt_type => |child_ty| { // Must stay in sync with `opt_payload` logic in `lowerPtr`. switch (Type.fromInterned(child_ty).classify(zcu)) { @@ -3257,7 +3455,10 @@ pub const Object = struct { }, .opaque_type, .spirv_type => unreachable, // no runtime bits .enum_type => try o.lowerType(t.backingIntType(zcu), repr), - .func_type => |func_type| try o.lowerFnType(t, func_type), + .func_type => |func_type| { + assert(t.fnHasRuntimeBits(zcu)); + return o.lowerFnType(.fromIntern(func_type, ip)); + }, .error_set_type, .inferred_error_set_type => try o.errorIntType(repr), // values, not types .undef, @@ -3283,14 +3484,28 @@ pub const Object = struct { }; } - fn lowerFnType(o: *Object, fn_ty: Type, fn_info: InternPool.Key.FuncType) Allocator.Error!Builder.Type { + pub const FuncInfo = struct { + cc: std.lang.CallingConvention, + noalias_bits: u32 = 0, + param_types: []const InternPool.Index, + return_type: InternPool.Index = .void_type, + is_var_args: bool = false, + + pub fn fromIntern(fn_info: InternPool.Key.FuncType, ip: *InternPool) FuncInfo { + return .{ + .cc = fn_info.cc, + .noalias_bits = fn_info.noalias_bits, + .param_types = fn_info.param_types.get(ip), + .return_type = fn_info.return_type, + .is_var_args = fn_info.is_var_args, + }; + } + }; + pub fn lowerFnType(o: *Object, fn_info: FuncInfo) Allocator.Error!Builder.Type { const zcu = o.zcu; - const ip = &zcu.intern_pool; const target = zcu.getTarget(); - assert(fn_ty.fnHasRuntimeBits(zcu)); - - const ret_strat = try fnReturnStrat(o, fn_info); + const ret_strat = try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type)); var llvm_params: std.ArrayList(Builder.Type) = .empty; defer llvm_params.deinit(o.gpa); @@ -3305,24 +3520,24 @@ pub const Object = struct { try llvm_params.append(o.gpa, llvm_ptr_ty); } - var it = iterateParamTypes(o, fn_info); + var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types); while (try it.next()) |lowering| switch (lowering) { .no_bits => continue, .byval => { - const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); + const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]); try llvm_params.append(o.gpa, try o.lowerType(param_ty, if (isByRef(param_ty, zcu)) .in_memory else .as_value)); }, .byref, .byref_mut => { try llvm_params.append(o.gpa, .ptr); }, .abi_sized_int => { - const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); + const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]); try llvm_params.append(o.gpa, try o.builder.intType( @intCast(param_ty.abiSize(zcu) * 8), )); }, .slice => { - const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); + const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]); try llvm_params.appendSlice(o.gpa, &.{ try o.builder.ptrType(toLlvmAddressSpace(param_ty.ptrAddressSpace(zcu), target)), try o.lowerType(.usize, .as_value), @@ -3332,7 +3547,7 @@ pub const Object = struct { try llvm_params.appendSlice(o.gpa, it.types_buffer[0..it.types_len]); }, .float_array => |count| { - const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); + const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]); const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(param_ty, zcu).?, .in_memory); try llvm_params.append(o.gpa, try o.builder.arrayType(count, float_ty)); }, @@ -3460,18 +3675,12 @@ pub const Object = struct { }, .enum_tag => |enum_tag| o.lowerValue(enum_tag.int, repr), .float => switch (ty.floatBits(target)) { - 16 => if (backendSupportsF16(target)) - try o.builder.halfConst(val.toFloat(f16, zcu)) - else - try o.builder.intConst(.i16, @as(i16, @bitCast(val.toFloat(f16, zcu)))), - 32 => try o.builder.floatConst(val.toFloat(f32, zcu)), - 64 => try o.builder.doubleConst(val.toFloat(f64, zcu)), - 80 => if (backendSupportsF80(target)) - try o.builder.x86_fp80Const(val.toFloat(f80, zcu)) - else - try o.builder.intConst(.i80, @as(i80, @bitCast(val.toFloat(f80, zcu)))), - 128 => try o.builder.fp128Const(val.toFloat(f128, zcu)), else => unreachable, + 16 => try o.f16Const(val.toFloat(f16, zcu)), + 32 => try o.f32Const(val.toFloat(f32, zcu)), + 64 => try o.f64Const(val.toFloat(f64, zcu)), + 80 => try o.f80Const(val.toFloat(f80, zcu)), + 128 => try o.f128Const(val.toFloat(f128, zcu)), }, .ptr => try o.lowerPtr(arg_val, 0), .slice => |slice| return o.builder.structConst(try o.lowerType(ty, repr), &.{ @@ -3590,12 +3799,13 @@ pub const Object = struct { }, .vector_type => |vector_type| { const vector_ty = try o.lowerType(ty, repr); + const ExpectedContents = [Builder.expected_fields_len]Builder.Constant; + var bfa_buf: ExpectedContents = undefined; + var bfa: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), o.gpa); + const allocator = bfa.allocator(); + const is_by_ref = isByRef(ty, zcu); switch (aggregate.storage) { .bytes, .elems => { - const ExpectedContents = [Builder.expected_fields_len]Builder.Constant; - var bfa_buf: ExpectedContents = undefined; - var bfa: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), o.gpa); - const allocator = bfa.allocator(); const vals = try allocator.alloc(Builder.Constant, vector_type.len); defer allocator.free(vals); @@ -3604,16 +3814,21 @@ pub const Object = struct { result_val.* = try o.builder.intConst(.i8, byte); }, .elems => |elems| for (vals, elems) |*result_val, elem| { - result_val.* = try o.lowerValue(elem, .as_value); + result_val.* = try o.lowerValue(elem, if (is_by_ref) .in_memory else .as_value); }, .repeated_elem => unreachable, } - return o.builder.vectorConst(vector_ty, vals); + return if (is_by_ref) + o.builder.arrayConst(vector_ty, vals) + else + o.builder.vectorConst(vector_ty, vals); }, - .repeated_elem => |elem| return o.builder.splatConst( - vector_ty, - try o.lowerValue(elem, .as_value), - ), + .repeated_elem => |elem| if (is_by_ref) { + const vals = try allocator.alloc(Builder.Constant, vector_type.len); + defer allocator.free(vals); + @memset(vals, try o.lowerValue(elem, .in_memory)); + return o.builder.arrayConst(vector_ty, vals); + } else return o.builder.splatConst(vector_ty, try o.lowerValue(elem, .as_value)), } }, .tuple_type => |tuple| { @@ -3841,6 +4056,117 @@ pub const Object = struct { }; } + pub fn f16Const(o: *Object, val: f16) Allocator.Error!Builder.Constant { + return switch (std.zig.target.compilerRtFloatAbi(o.zcu.getTarget(), 16)) { + .hard => o.builder.halfConst(val), + .soft => o.builder.intConst(.i16, @as(u16, @bitCast(val))), + }; + } + + pub fn f32Const(o: *Object, val: f32) Allocator.Error!Builder.Constant { + return switch (std.zig.target.compilerRtFloatAbi(o.zcu.getTarget(), 32)) { + .hard => o.builder.floatConst(val), + .soft => o.builder.intConst(.i32, @as(u32, @bitCast(val))), + }; + } + + pub fn f64Const(o: *Object, val: f64) Allocator.Error!Builder.Constant { + return switch (std.zig.target.compilerRtFloatAbi(o.zcu.getTarget(), 64)) { + .hard => o.builder.doubleConst(val), + .soft => o.builder.intConst(.i64, @as(u64, @bitCast(val))), + }; + } + + pub fn f80Const(o: *Object, val: f80) Allocator.Error!Builder.Constant { + switch (std.zig.target.compilerRtFloatAbi(o.zcu.getTarget(), 80)) { + .hard => return o.builder.x86_fp80Const(val), + .soft => {}, + } + var llvm_field_tags_buf: [5]SoftF80Layout.LlvmFieldTag = undefined; + var llvm_field_types_buf: [5]Builder.Type = undefined; + const f80_layout = try o.softF80Layout(.{ + .llvm_field_tags_buf = &llvm_field_tags_buf, + .llvm_field_types_buf = &llvm_field_types_buf, + }); + const llvm_field_types = llvm_field_types_buf[0..f80_layout.llvm_fields_len]; + const f80_llvm_ty = try o.builder.structType(.normal, llvm_field_types); + const f80_repr: packed struct { mantissa: u64, exponent: u16 } = @bitCast(val); + var llvm_field_vals_buf: [5]Builder.Constant = undefined; + const llvm_field_vals = llvm_field_vals_buf[0..f80_layout.llvm_fields_len]; + for ( + llvm_field_vals, + llvm_field_tags_buf[0..f80_layout.llvm_fields_len], + llvm_field_types, + ) |*llvm_field_val, llvm_field_tag, llvm_field_type| + llvm_field_val.* = switch (llvm_field_tag) { + .mantissa => try o.builder.intConst(llvm_field_type, f80_repr.mantissa), + .exponent => try o.builder.intConst(llvm_field_type, f80_repr.exponent), + .padding => try o.builder.undefConst(llvm_field_type), + }; + return o.builder.structConst(f80_llvm_ty, llvm_field_vals); + } + + pub fn f128Const(o: *Object, val: f128) Allocator.Error!Builder.Constant { + switch (std.zig.target.compilerRtFloatAbi(o.zcu.getTarget(), 128)) { + .hard => return o.builder.fp128Const(val), + .soft => {}, + } + var llvm_field_tags_buf: [5]SoftF128Layout.LlvmFieldTag = undefined; + var llvm_field_types_buf: [5]Builder.Type = undefined; + const f128_layout = try o.softF128Layout(.{ + .llvm_field_tags_buf = &llvm_field_tags_buf, + .llvm_field_types_buf = &llvm_field_types_buf, + }); + const llvm_field_types = llvm_field_types_buf[0..f128_layout.llvm_fields_len]; + const f128_llvm_ty = try o.builder.structType(.normal, llvm_field_types); + const f128_repr: packed struct { lo: u64, hi: u64 } = @bitCast(val); + var llvm_field_vals_buf: [5]Builder.Constant = undefined; + const llvm_field_vals = llvm_field_vals_buf[0..f128_layout.llvm_fields_len]; + for ( + llvm_field_vals, + llvm_field_tags_buf[0..f128_layout.llvm_fields_len], + llvm_field_types, + ) |*llvm_field_val, llvm_field_tag, llvm_field_type| + llvm_field_val.* = switch (llvm_field_tag) { + .lo => try o.builder.intConst(llvm_field_type, f128_repr.lo), + .hi => try o.builder.intConst(llvm_field_type, f128_repr.hi), + .padding => try o.builder.undefConst(llvm_field_type), + }; + return o.builder.structConst(f128_llvm_ty, llvm_field_vals); + } + + pub fn lowerConstRef( + o: *Object, + constant: Builder.Constant, + @"align": Builder.Alignment, + ) Allocator.Error!Builder.Constant { + assert(@"align" != .default); + const zcu = o.zcu; + const gpa = zcu.comp.gpa; + const gop = try o.const_map.getOrPut(gpa, constant); + if (gop.found_existing) { + // Keep the greater of the two alignments. + const llvm_variable = gop.value_ptr.*; + const llvm_old_align = llvm_variable.getAlignment(&o.builder); + const llvm_new_align = llvm_old_align.max(@"align"); + llvm_variable.setAlignment(llvm_new_align, &o.builder); + return llvm_variable.ptrConst(&o.builder).global.toConst(); + } + errdefer assert(o.const_map.remove(constant)); + + const llvm_ty = constant.typeOf(&o.builder); + const llvm_addrspace = toLlvmAddressSpace(.generic, zcu.getTarget()); + const llvm_variable = try o.builder.addVariable(.empty, llvm_ty, llvm_addrspace); + gop.value_ptr.* = llvm_variable; + try llvm_variable.setInitializer(constant, &o.builder); + llvm_variable.setMutability(.constant, &o.builder); + llvm_variable.setAlignment(@"align", &o.builder); + const llvm_global = llvm_variable.ptrConst(&o.builder).global; + llvm_global.setLinkage(.private, &o.builder); + llvm_global.setUnnamedAddr(.unnamed_addr, &o.builder); + return llvm_global.toConst(); + } + fn lowerPtr( o: *Object, ptr_val: InternPool.Index, @@ -3860,7 +4186,7 @@ pub const Object = struct { const orig_ptr_ty: Type = .fromInterned(uav.orig_ty); const base_ptr = try o.lowerUavRef( uav.val, - orig_ptr_ty.ptrAlignment(zcu), + orig_ptr_ty.ptrAlignment(zcu).toLlvm(), orig_ptr_ty.ptrAddressSpace(zcu), ); return o.builder.gepConst(.inbounds, .i8, base_ptr, null, &.{ @@ -3912,8 +4238,8 @@ pub const Object = struct { pub fn lowerPtrToVoid( o: *Object, - /// Must not be `.none`. - @"align": InternPool.Alignment, + /// Must not be `.default`. + @"align": Builder.Alignment, @"addrspace": std.lang.AddressSpace, ) Allocator.Error!Builder.Constant { const addr: u64 = @"align".toByteUnits().?; @@ -3926,11 +4252,11 @@ pub const Object = struct { pub fn lowerUavRef( o: *Object, uav_val: InternPool.Index, - /// Must not be `.none`. - @"align": InternPool.Alignment, + /// Must not be `.default`. + @"align": Builder.Alignment, @"addrspace": std.lang.AddressSpace, ) Allocator.Error!Builder.Constant { - assert(@"align" != .none); + assert(@"align" != .default); const zcu = o.zcu; const ip = &zcu.intern_pool; @@ -3955,7 +4281,7 @@ pub const Object = struct { // Keep the greater of the two alignments. const llvm_variable = gop.value_ptr.*; const llvm_old_align = llvm_variable.getAlignment(&o.builder); - const llvm_new_align = llvm_old_align.max(@"align".toLlvm()); + const llvm_new_align = llvm_old_align.max(@"align"); llvm_variable.setAlignment(llvm_new_align, &o.builder); return llvm_variable.ptrConst(&o.builder).global.toConst(); } @@ -3967,7 +4293,7 @@ pub const Object = struct { gop.value_ptr.* = llvm_variable; try llvm_variable.setInitializer(try o.lowerValue(uav_val, .in_memory), &o.builder); llvm_variable.setMutability(.constant, &o.builder); - llvm_variable.setAlignment(@"align".toLlvm(), &o.builder); + llvm_variable.setAlignment(@"align", &o.builder); const llvm_global = llvm_variable.ptrConst(&o.builder).global; llvm_global.setLinkage(if (o.builder.strip) .private else .internal, &o.builder); llvm_global.setUnnamedAddr(.unnamed_addr, &o.builder); @@ -3986,7 +4312,7 @@ pub const Object = struct { .none => nav_ty.abiAlignment(zcu), else => |a| a, }; - return o.lowerPtrToVoid(nav_align, nav.resolved.?.@"addrspace"); + return o.lowerPtrToVoid(nav_align.toLlvm(), nav.resolved.?.@"addrspace"); } const gop = try o.nav_map.getOrPut(gpa, nav_id); @@ -4015,7 +4341,7 @@ pub const Object = struct { attributes: *Builder.FunctionAttributes.Wip, param_ty: Type, param_index: u32, - fn_info: InternPool.Key.FuncType, + fn_info: FuncInfo, llvm_arg_i: u32, ) Allocator.Error!void { const zcu = o.zcu; @@ -4075,18 +4401,18 @@ pub const Object = struct { const name = try o.builder.strtabString("__zig_error_name_table"); // TODO: Address space - const variable_index = try o.builder.addVariable(name, .ptr, .default); - variable_index.setMutability(.constant, &o.builder); - variable_index.setAlignment( + const llvm_variable = try o.builder.addVariable(name, .ptr, .default); + llvm_variable.setMutability(.constant, &o.builder); + llvm_variable.setAlignment( Type.slice_const_u8_sentinel_0.abiAlignment(o.zcu).toLlvm(), &o.builder, ); - const global_index = variable_index.ptrConst(&o.builder).global; - global_index.setLinkage(.private, &o.builder); - global_index.setUnnamedAddr(.unnamed_addr, &o.builder); + const llvm_global = llvm_variable.ptrConst(&o.builder).global; + llvm_global.setLinkage(.private, &o.builder); + llvm_global.setUnnamedAddr(.unnamed_addr, &o.builder); - o.error_name_table = variable_index; - return variable_index; + o.error_name_table = llvm_variable; + return llvm_variable; } pub fn getErrorsLen(o: *Object) Allocator.Error!Builder.Variable.Index { @@ -4094,13 +4420,13 @@ pub const Object = struct { if (o.errors_len_variable == .none) { const llvm_err_int_ty = try o.errorIntType(.in_memory); const name = try builder.strtabString("__zig_errors_len"); - const variable_index = try builder.addVariable(name, llvm_err_int_ty, .default); - variable_index.setMutability(.constant, builder); - variable_index.setAlignment(Type.errorAbiAlignment(o.zcu).toLlvm(), builder); - const global_index = variable_index.ptrConst(&o.builder).global; - global_index.setLinkage(.private, builder); - global_index.setUnnamedAddr(.unnamed_addr, builder); - o.errors_len_variable = variable_index; + const llvm_variable = try builder.addVariable(name, llvm_err_int_ty, .default); + llvm_variable.setMutability(.constant, builder); + llvm_variable.setAlignment(Type.errorAbiAlignment(o.zcu).toLlvm(), builder); + const llvm_global = llvm_variable.ptrConst(&o.builder).global; + llvm_global.setLinkage(.private, builder); + llvm_global.setUnnamedAddr(.unnamed_addr, builder); + o.errors_len_variable = llvm_variable; } return o.errors_len_variable; } @@ -4112,21 +4438,21 @@ pub const Object = struct { const gop = try o.enum_tag_name_map.getOrPut(o.gpa, enum_ty.toIntern()); if (gop.found_existing) return gop.value_ptr.*; errdefer assert(o.enum_tag_name_map.remove(enum_ty.toIntern())); - const function_index = try o.builder.addFunction( + const llvm_function = try o.builder.addFunction( // Dummy function type; `updateEnumTagNameFunction` will replace it with the correct type. // TODO: change the builder API so we don't need to do this. try o.builder.fnType(.void, &.{}, .normal), try o.builder.strtabStringFmt("__zig_tag_name_{f}", .{enum_ty.containerTypeName(ip).fmt(ip)}), toLlvmAddressSpace(.generic, zcu.getTarget()), ); - gop.value_ptr.* = function_index; - try o.updateEnumTagNameFunction(enum_ty, function_index); - return function_index; + gop.value_ptr.* = llvm_function; + try o.updateEnumTagNameFunction(enum_ty, llvm_function); + return llvm_function; } fn updateEnumTagNameFunction( o: *Object, enum_ty: Type, - function_index: Builder.Function.Index, + llvm_function: Builder.Function.Index, ) Allocator.Error!void { const zcu = o.zcu; const ip = &zcu.intern_pool; @@ -4136,19 +4462,19 @@ pub const Object = struct { const llvm_ret_ty = try o.lowerType(.slice_const_u8_sentinel_0, .as_value); const llvm_int_ty = try o.lowerType(.fromInterned(loaded_enum.int_tag_type), .as_value); - function_index.ptrConst(&o.builder).global.ptr(&o.builder).type = + llvm_function.ptrConst(&o.builder).global.ptr(&o.builder).type = try o.builder.fnType(llvm_ret_ty, &.{llvm_int_ty}, .normal); var attributes: Builder.FunctionAttributes.Wip = .{}; defer attributes.deinit(&o.builder); try o.addCommonFnAttributes(&attributes, zcu.root_mod, zcu.root_mod.omit_frame_pointer); - function_index.setLinkage(if (o.builder.strip) .private else .internal, &o.builder); - function_index.setCallConv(.fastcc, &o.builder); - function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); + llvm_function.setLinkage(if (o.builder.strip) .private else .internal, &o.builder); + llvm_function.setCallConv(.fastcc, &o.builder); + llvm_function.setAttributes(try attributes.finish(&o.builder), &o.builder); var wip = try Builder.WipFunction.init(&o.builder, .{ - .function = function_index, + .function = llvm_function, .strip = true, }); defer wip.deinit(); @@ -4167,16 +4493,16 @@ pub const Object = struct { for (0..loaded_enum.field_names.len) |field_index| { const name = try o.builder.stringNull(loaded_enum.field_names.get(ip)[field_index].toSlice(ip)); const name_init = try o.builder.stringConst(name); - const name_variable_index = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); - try name_variable_index.setInitializer(name_init, &o.builder); - name_variable_index.setMutability(.constant, &o.builder); - name_variable_index.setAlignment(comptime Builder.Alignment.fromByteUnits(1), &o.builder); - const name_global_index = name_variable_index.ptrConst(&o.builder).global; - name_global_index.setLinkage(.private, &o.builder); - name_global_index.setUnnamedAddr(.unnamed_addr, &o.builder); + const name_llvm_variable = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); + try name_llvm_variable.setInitializer(name_init, &o.builder); + name_llvm_variable.setMutability(.constant, &o.builder); + name_llvm_variable.setAlignment(comptime .fromByteUnits(1), &o.builder); + const name_llvm_global = name_llvm_variable.ptrConst(&o.builder).global; + name_llvm_global.setLinkage(.private, &o.builder); + name_llvm_global.setUnnamedAddr(.unnamed_addr, &o.builder); const name_val = try o.builder.structValue(llvm_ret_ty, &.{ - name_global_index.toConst(), + name_llvm_global.toConst(), try o.builder.intConst(llvm_usize_ty, name.slice(&o.builder).?.len - 1), }); @@ -4209,40 +4535,40 @@ pub const Object = struct { const gop = try o.named_enum_map.getOrPut(o.gpa, enum_ty.toIntern()); if (gop.found_existing) return gop.value_ptr.*; errdefer assert(o.named_enum_map.remove(enum_ty.toIntern())); - const function_index = try o.builder.addFunction( + const llvm_function = try o.builder.addFunction( // Dummy function type; `updateIsNamedEnumValue` will replace it with the correct type. // TODO: change the builder API so we don't need to do this. try o.builder.fnType(.void, &.{}, .normal), try o.builder.strtabStringFmt("__zig_is_named_enum_value_{f}", .{enum_ty.containerTypeName(ip).fmt(ip)}), toLlvmAddressSpace(.generic, zcu.getTarget()), ); - gop.value_ptr.* = function_index; - try o.updateIsNamedEnumValueFunction(enum_ty, function_index); - return function_index; + gop.value_ptr.* = llvm_function; + try o.updateIsNamedEnumValueFunction(enum_ty, llvm_function); + return llvm_function; } fn updateIsNamedEnumValueFunction( o: *Object, enum_ty: Type, - function_index: Builder.Function.Index, + llvm_function: Builder.Function.Index, ) Allocator.Error!void { const zcu = o.zcu; const ip = &zcu.intern_pool; const loaded_enum = ip.loadEnumType(enum_ty.toIntern()); const llvm_int_ty = try o.lowerType(.fromInterned(loaded_enum.int_tag_type), .as_value); - function_index.ptrConst(&o.builder).global.ptr(&o.builder).type = + llvm_function.ptrConst(&o.builder).global.ptr(&o.builder).type = try o.builder.fnType(.i1, &.{llvm_int_ty}, .normal); var attributes: Builder.FunctionAttributes.Wip = .{}; defer attributes.deinit(&o.builder); try o.addCommonFnAttributes(&attributes, zcu.root_mod, zcu.root_mod.omit_frame_pointer); - function_index.setLinkage(if (o.builder.strip) .private else .internal, &o.builder); - function_index.setCallConv(.fastcc, &o.builder); - function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); + llvm_function.setLinkage(if (o.builder.strip) .private else .internal, &o.builder); + llvm_function.setCallConv(.fastcc, &o.builder); + llvm_function.setAttributes(try attributes.finish(&o.builder), &o.builder); var wip: Builder.WipFunction = try .init(&o.builder, .{ - .function = function_index, + .function = llvm_function, .strip = true, }); defer wip.deinit(); @@ -4278,20 +4604,27 @@ pub const Object = struct { pub fn getLibcFunction( o: *Object, + pt: Zcu.PerThread, fn_name: Builder.StrtabString, - param_types: []const Builder.Type, - return_type: Builder.Type, + fn_info: FuncInfo, ) Allocator.Error!Builder.Function.Index { if (o.builder.getGlobal(fn_name)) |global| return switch (global.ptrConst(&o.builder).kind) { .alias => |alias| alias.getAliasee(&o.builder).ptrConst(&o.builder).kind.function, .function => |function| function, .variable, .replaced => unreachable, }; - return o.builder.addFunction( - try o.builder.fnType(return_type, param_types, .normal), + const llvm_function = try o.builder.addFunction( + try o.lowerFnType(fn_info), fn_name, toLlvmAddressSpace(.generic, o.zcu.getTarget()), ); + var attributes: Builder.FunctionAttributes.Wip = .{}; + defer attributes.deinit(&o.builder); + try o.addCallingConventionFnAttributes(pt, llvm_function, &attributes, .{ + .name = fn_name.slice(&o.builder).?, + }, fn_info); + llvm_function.setAttributes(try attributes.finish(&o.builder), &o.builder); + return llvm_function; } }; @@ -4585,47 +4918,6 @@ fn toLlvmGlobalAddressSpace(wanted_address_space: std.lang.AddressSpace, target: }; } -/// This function returns true if we expect LLVM to lower f16 correctly -/// and false if we expect LLVM to crash if it encounters an f16 type, -/// or if it produces miscompilations. -pub fn backendSupportsF16(target: *const std.Target) bool { - return switch (target.cpu.arch) { - .arm, - .armeb, - .thumb, - .thumbeb, - => target.abi.float() == .soft or target.cpu.has(.arm, .fullfp16), - else => true, - }; -} - -/// This function returns true if we expect LLVM to lower x86_fp80 correctly -/// and false if we expect LLVM to crash if it encounters an x86_fp80 type, -/// or if it produces miscompilations. -pub fn backendSupportsF80(target: *const std.Target) bool { - return switch (target.cpu.arch) { - .x86, .x86_64 => !target.cpu.has(.x86, .soft_float), - else => false, - }; -} - -/// This function returns true if we expect LLVM to lower f128 correctly, -/// and false if we expect LLVM to crash if it encounters an f128 type, -/// or if it produces miscompilations. -pub fn backendSupportsF128(target: *const std.Target) bool { - return switch (target.cpu.arch) { - // https://github.com/llvm/llvm-project/issues/121122 - .amdgcn, - => false, - .arm, - .armeb, - .thumb, - .thumbeb, - => target.abi.float() == .soft or target.cpu.has(.arm, .fp_armv8), - else => true, - }; -} - /// We need to insert extra padding if LLVM's isn't enough. /// However we don't want to ever call LLVMABIAlignmentOfType or /// LLVMABISizeOfType because these functions will trip assertions diff --git a/src/codegen/llvm/FuncGen.zig b/src/codegen/llvm/FuncGen.zig index 156aa875926d6d3d55341b2eac6d1e8f93e18972..5bdf5029d1b4a639c41652f0792d56aba08e927a 100644 --- a/src/codegen/llvm/FuncGen.zig +++ b/src/codegen/llvm/FuncGen.zig @@ -169,7 +169,7 @@ fn resolveValue(self: *FuncGen, val: Value) Allocator.Error!Builder.Constant { // We need a pointer to a global constant, i.e. a UAV. return o.lowerUavRef( val.toIntern(), - ty.abiAlignment(zcu), + ty.abiAlignment(zcu).toLlvm(), target_util.defaultAddressSpace(zcu.getTarget(), .global_constant), ); } @@ -190,10 +190,10 @@ pub fn genMainBody(fg: *FuncGen) TodoError!void { const fn_info = zcu.typeToFunc(fn_ty).?; const param_types = fn_info.param_types.get(ip); - var it = iterateParamTypes(o, fn_info); + var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types.get(ip)); // Populate `fg.ret_ptr`... - fg.ret_ptr = switch (try fnReturnStrat(o, fn_info)) { + fg.ret_ptr = switch (try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type))) { .sret => rp: { defer it.llvm_index += 1; break :rp fg.wip.arg(it.llvm_index); @@ -721,29 +721,19 @@ fn genBodyDebugScope( try self.genBody(body, coverage_point); } -const CallAttr = enum { - Auto, - NeverTail, - NeverInline, - AlwaysTail, - AlwaysInline, -}; - -fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) Allocator.Error!Builder.Value { - const air_call = self.air.unwrapCall(inst); +fn airCall(fg: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) Allocator.Error!Builder.Value { + const o = fg.object; + const zcu = o.zcu; + const air_call = fg.air.unwrapCall(inst); const args = air_call.args; - const o = self.object; - const pt = self.pt; - const zcu = o.zcu; const ip = &zcu.intern_pool; - const callee_ty = self.typeOf(air_call.callee); + const callee_ty = fg.typeOf(air_call.callee); const zig_fn_ty = switch (callee_ty.zigTypeTag(zcu)) { .@"fn" => callee_ty, .pointer => callee_ty.childType(zcu), else => unreachable, }; const fn_info = zcu.typeToFunc(zig_fn_ty).?; - const return_type: Type = .fromInterned(fn_info.return_type); const llvm_fn = llvm_fn: { // If the callee is a function *body*, we need to use a pointer to the global. if (air_call.callee.toInterned()) |ip_index| switch (ip.indexToKey(ip_index)) { @@ -752,22 +742,54 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier else => {}, }; // Otherwise, the operand is already a function pointer (possibly runtime-known). - break :llvm_fn try self.resolveInst(air_call.callee); + break :llvm_fn try fg.resolveInst(air_call.callee); }; + + const arg_types = try fg.gpa.alloc(InternPool.Index, args.len); + defer fg.gpa.free(arg_types); + const arg_values = try fg.gpa.alloc(Builder.Value, args.len); + defer fg.gpa.free(arg_values); + for (arg_types, arg_values, args) |*arg_type, *arg_value, arg| { + const arg_ty = fg.typeOf(arg); + arg_type.* = arg_ty.toIntern(); + arg_value.* = if (arg_ty.hasRuntimeBits(zcu)) try fg.resolveInst(arg) else .none; + } + return fg.buildCall(.{ + .is_unused = fg.liveness.isUnused(inst), + .modifier = modifier, + }, try o.lowerType(zig_fn_ty, .as_value), llvm_fn, .fromIntern(fn_info, ip), arg_types, arg_values); +} + +fn buildCall( + fg: *FuncGen, + opts: struct { + is_unused: bool = false, + modifier: std.lang.CallModifier = .auto, + }, + llvm_fn_ty: Builder.Type, + llvm_fn: Builder.Value, + fn_info: Object.FuncInfo, + arg_types: []const InternPool.Index, + arg_values: []const Builder.Value, +) Allocator.Error!Builder.Value { + const o = fg.object; + const pt = fg.pt; + const zcu = o.zcu; + const return_type: Type = .fromInterned(fn_info.return_type); const target = zcu.getTarget(); - const ret_strat = try fnReturnStrat(o, fn_info); + const ret_strat = try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type)); - var llvm_args = std.array_list.Managed(Builder.Value).init(self.gpa); - defer llvm_args.deinit(); + var llvm_args: std.ArrayList(Builder.Value) = .empty; + defer llvm_args.deinit(fg.gpa); var attributes: Builder.FunctionAttributes.Wip = .{}; defer attributes.deinit(&o.builder); - if (self.disable_intrinsics) { + if (fg.disable_intrinsics) { try attributes.addFnAttr(.nobuiltin, &o.builder); } - switch (modifier) { + switch (opts.modifier) { .auto, .always_tail => {}, .never_tail, .never_inline => try attributes.addFnAttr(.@"noinline", &o.builder), .no_suspend, .always_inline, .compile_time => unreachable, @@ -775,10 +797,11 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier const sret_alloc: ?Builder.Value = switch (ret_strat) { .sret => sret_alloc: { - try attributes.addParamAttr(0, .{ .sret = try o.lowerType(return_type, .in_memory) }, &o.builder); + const alignment = return_type.abiAlignment(zcu).toLlvm(); + try o.addSRetFnAttributes(&attributes, try o.lowerType(return_type, .in_memory), alignment, .callsite); - const ptr = try self.buildZigAlloca(return_type, .none); - try llvm_args.append(ptr); + const ptr = try fg.buildZigAlloca(return_type, .none); + try llvm_args.append(fg.gpa, ptr); break :sret_alloc ptr; }, else => sret_alloc: { @@ -792,132 +815,111 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing; if (err_return_tracing) { - assert(self.err_ret_trace != .none); - try llvm_args.append(self.err_ret_trace); + assert(fg.err_ret_trace != .none); + try llvm_args.append(fg.gpa, fg.err_ret_trace); } - var it = iterateParamTypes(o, fn_info); - while (try it.nextCall(self, args)) |lowering| switch (lowering) { - .no_bits => continue, - .byval => { - const arg = args[it.zig_index - 1]; - const param_ty = self.typeOf(arg); - const llvm_arg = try self.resolveInst(arg); - if (isByRef(param_ty, zcu)) { - const alignment = param_ty.abiAlignment(zcu).toLlvm(); - // We don't need to handle non-ABI-sized integer types in memory here since they are - // never by-ref. - const llvm_param_ty = try o.lowerType(param_ty, .in_memory); - const loaded = try self.wip.load(.normal, llvm_param_ty, llvm_arg, alignment, ""); - try llvm_args.append(loaded); - } else { - try llvm_args.append(llvm_arg); - } - }, - .byref => { - const arg = args[it.zig_index - 1]; - const param_ty = self.typeOf(arg); - const llvm_arg = try self.resolveInst(arg); - if (isByRef(param_ty, zcu)) { - try llvm_args.append(llvm_arg); - } else { - const arg_ptr = try self.buildZigAlloca(param_ty, .none); - try self.store(arg_ptr, .none, llvm_arg, param_ty, .normal); - try llvm_args.append(arg_ptr); - } - }, - .byref_mut => { - const arg = args[it.zig_index - 1]; - const param_ty = self.typeOf(arg); - const llvm_arg = try self.resolveInst(arg); + var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types); + while (try it.nextCall(arg_types)) |lowering| { + const arg_ty: Type = .fromInterned(arg_types[it.zig_index - 1]); + const arg_val = arg_values[it.zig_index - 1]; + switch (lowering) { + .no_bits => continue, + .byval => { + if (isByRef(arg_ty, zcu)) { + const alignment = arg_ty.abiAlignment(zcu).toLlvm(); + // We don't need to handle non-ABI-sized integer types in memory here since they are + // never by-ref. + const llvm_arg_ty = try o.lowerType(arg_ty, .in_memory); + const loaded = try fg.wip.load(.normal, llvm_arg_ty, arg_val, alignment, ""); + try llvm_args.append(fg.gpa, loaded); + } else { + try llvm_args.append(fg.gpa, arg_val); + } + }, + .byref => { + if (isByRef(arg_ty, zcu)) { + try llvm_args.append(fg.gpa, arg_val); + } else { + const arg_ptr = try fg.buildZigAlloca(arg_ty, .none); + try fg.store(arg_ptr, .none, arg_val, arg_ty, .normal); + try llvm_args.append(fg.gpa, arg_ptr); + } + }, + .byref_mut => { + const arg_ptr = try fg.buildZigAlloca(arg_ty, .none); + try fg.store(arg_ptr, .none, arg_val, arg_ty, .normal); + try llvm_args.append(fg.gpa, arg_ptr); + }, + .abi_sized_int => { + const int_llvm_ty = try o.builder.intType(@intCast(arg_ty.abiSize(zcu) * 8)); - const arg_ptr = try self.buildZigAlloca(param_ty, .none); - try self.store(arg_ptr, .none, llvm_arg, param_ty, .normal); - try llvm_args.append(arg_ptr); - }, - .abi_sized_int => { - const arg = args[it.zig_index - 1]; - const param_ty = self.typeOf(arg); - const llvm_arg = try self.resolveInst(arg); - const int_llvm_ty = try o.builder.intType(@intCast(param_ty.abiSize(zcu) * 8)); + if (isByRef(arg_ty, zcu)) { + const alignment = arg_ty.abiAlignment(zcu).toLlvm(); + const loaded = try fg.wip.load(.normal, int_llvm_ty, arg_val, alignment, ""); + try llvm_args.append(fg.gpa, loaded); + } else { + // LLVM does not allow bitcasting structs so we must allocate + // a local, store as one type, and then load as another type. + const alignment = arg_ty.abiAlignment(zcu).toLlvm(); + const ptr = try fg.buildAlloca(int_llvm_ty, alignment); + try fg.store(ptr, .none, arg_val, arg_ty, .normal); + const loaded = try fg.wip.load(.normal, int_llvm_ty, ptr, alignment, ""); + try llvm_args.append(fg.gpa, loaded); + } + }, + .slice => { + const ptr = try fg.wip.extractValue(arg_val, &.{0}, ""); + const len = try fg.wip.extractValue(arg_val, &.{1}, ""); + try llvm_args.appendSlice(fg.gpa, &.{ ptr, len }); + }, + .multiple_llvm_types => { + const arg_alignment = arg_ty.abiAlignment(zcu); + const llvm_ty = try o.builder.arrayType(it.offsets_buffer[it.types_len], .i8); + const arg_ptr = try fg.buildAlloca(llvm_ty, arg_alignment.toLlvm()); + try fg.store(arg_ptr, .none, arg_val, arg_ty, .normal); - if (isByRef(param_ty, zcu)) { - const alignment = param_ty.abiAlignment(zcu).toLlvm(); - const loaded = try self.wip.load(.normal, int_llvm_ty, llvm_arg, alignment, ""); - try llvm_args.append(loaded); - } else { - // LLVM does not allow bitcasting structs so we must allocate - // a local, store as one type, and then load as another type. - const alignment = param_ty.abiAlignment(zcu).toLlvm(); - const ptr = try self.buildAlloca(int_llvm_ty, alignment); - try self.store(ptr, .none, llvm_arg, param_ty, .normal); - const loaded = try self.wip.load(.normal, int_llvm_ty, ptr, alignment, ""); - try llvm_args.append(loaded); - } - }, - .slice => { - const arg = args[it.zig_index - 1]; - const llvm_arg = try self.resolveInst(arg); - const ptr = try self.wip.extractValue(llvm_arg, &.{0}, ""); - const len = try self.wip.extractValue(llvm_arg, &.{1}, ""); - try llvm_args.appendSlice(&.{ ptr, len }); - }, - .multiple_llvm_types => { - const arg = args[it.zig_index - 1]; - const param_ty = self.typeOf(arg); - const llvm_arg = try self.resolveInst(arg); - const param_alignment = param_ty.abiAlignment(zcu); - const llvm_ty = try o.builder.arrayType(it.offsets_buffer[it.types_len], .i8); - const arg_ptr = try self.buildAlloca(llvm_ty, param_alignment.toLlvm()); - try self.store(arg_ptr, .none, llvm_arg, param_ty, .normal); + try llvm_args.ensureUnusedCapacity(fg.gpa, it.types_len); + for (it.types_buffer[0..it.types_len], it.offsets_buffer[0..it.types_len]) |field_ty, offset| { + const field_ptr = try fg.ptraddConst(arg_ptr, offset); + const loaded = try fg.wip.load(.normal, field_ty, field_ptr, arg_alignment.offset(offset).toLlvm(), ""); + llvm_args.appendAssumeCapacity(loaded); + } + }, + .float_array => |count| { + const arg_ptr: Builder.Value = if (!isByRef(arg_ty, zcu)) ptr: { + const ptr = try fg.buildZigAlloca(arg_ty, .none); + try fg.store(ptr, .none, arg_val, arg_ty, .normal); + break :ptr ptr; + } else arg_val; - try llvm_args.ensureUnusedCapacity(it.types_len); - for (it.types_buffer[0..it.types_len], it.offsets_buffer[0..it.types_len]) |field_ty, offset| { - const field_ptr = try self.ptraddConst(arg_ptr, offset); - const loaded = try self.wip.load(.normal, field_ty, field_ptr, param_alignment.offset(offset).toLlvm(), ""); - llvm_args.appendAssumeCapacity(loaded); - } - }, - .float_array => |count| { - const arg = args[it.zig_index - 1]; - const arg_ty = self.typeOf(arg); - const arg_val = try self.resolveInst(arg); + const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(arg_ty, zcu).?, .in_memory); + const array_ty = try o.builder.arrayType(count, float_ty); - const arg_ptr: Builder.Value = if (!isByRef(arg_ty, zcu)) ptr: { - const ptr = try self.buildZigAlloca(arg_ty, .none); - try self.store(ptr, .none, arg_val, arg_ty, .normal); - break :ptr ptr; - } else arg_val; + const loaded = try fg.wip.load(.normal, array_ty, arg_ptr, arg_ty.abiAlignment(zcu).toLlvm(), ""); + try llvm_args.append(fg.gpa, loaded); + }, + .i32_array, .i64_array => |arr_len| { + const elem_size: u8 = if (lowering == .i32_array) 32 else 64; - const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(arg_ty, zcu).?, .in_memory); - const array_ty = try o.builder.arrayType(count, float_ty); + const arg_ptr: Builder.Value = if (!isByRef(arg_ty, zcu)) ptr: { + const ptr = try fg.buildZigAlloca(arg_ty, .none); + try fg.store(ptr, .none, arg_val, arg_ty, .normal); + break :ptr ptr; + } else arg_val; - const loaded = try self.wip.load(.normal, array_ty, arg_ptr, arg_ty.abiAlignment(zcu).toLlvm(), ""); - try llvm_args.append(loaded); - }, - .i32_array, .i64_array => |arr_len| { - const elem_size: u8 = if (lowering == .i32_array) 32 else 64; - const arg = args[it.zig_index - 1]; - const arg_ty = self.typeOf(arg); - const arg_val = try self.resolveInst(arg); - - const arg_ptr: Builder.Value = if (!isByRef(arg_ty, zcu)) ptr: { - const ptr = try self.buildZigAlloca(arg_ty, .none); - try self.store(ptr, .none, arg_val, arg_ty, .normal); - break :ptr ptr; - } else arg_val; - - const array_ty = try o.builder.arrayType(arr_len, try o.builder.intType(@intCast(elem_size))); - const loaded = try self.wip.load(.normal, array_ty, arg_ptr, arg_ty.abiAlignment(zcu).toLlvm(), ""); - try llvm_args.append(loaded); - }, - }; + const array_ty = try o.builder.arrayType(arr_len, try o.builder.intType(@intCast(elem_size))); + const loaded = try fg.wip.load(.normal, array_ty, arg_ptr, arg_ty.abiAlignment(zcu).toLlvm(), ""); + try llvm_args.append(fg.gpa, loaded); + }, + } + } const cc_info = llvm.toLlvmCallConv(fn_info.cc, target).?; { // Add argument attributes. - it = iterateParamTypes(o, fn_info); + it = iterateParamTypes(o, fn_info.cc, fn_info.param_types); it.llvm_index += @intFromBool(ret_strat == .sret); it.llvm_index += @intFromBool(err_return_tracing); var remaining_inreg_int = cc_info.inreg_int_params; @@ -925,7 +927,7 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier while (try it.next()) |lowering| switch (lowering) { .byval => { const param_index = it.zig_index - 1; - const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); + const param_ty = Type.fromInterned(fn_info.param_types[param_index]); if (!isByRef(param_ty, zcu)) { try o.addByValParamAttrs(pt, &attributes, param_ty, param_index, fn_info, it.llvm_index - 1); } @@ -947,7 +949,7 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier }, .byref => { const param_index = it.zig_index - 1; - const param_ty: Type = .fromInterned(fn_info.param_types.get(ip)[param_index]); + const param_ty: Type = .fromInterned(fn_info.param_types[param_index]); try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, it.byval_attr, param_ty); }, .byref_mut => try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder), @@ -962,7 +964,7 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier .slice => { assert(!it.byval_attr); - const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); + const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]); const ptr_info = param_ty.ptrInfo(zcu); const llvm_arg_i = it.llvm_index - 2; @@ -989,8 +991,8 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier }; } - const call = try self.wip.call( - switch (modifier) { + const call = try fg.wip.call( + switch (opts.modifier) { .auto, .never_inline => .normal, .never_tail => .notail, .always_tail => .musttail, @@ -998,19 +1000,14 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier }, cc_info.llvm_cc, try attributes.finish(&o.builder), - try o.lowerType(zig_fn_ty, .as_value), + llvm_fn_ty, llvm_fn, llvm_args.items, "", ); - if (fn_info.return_type == .noreturn_type and modifier != .always_tail) { - return .none; - } - - if (self.liveness.isUnused(inst)) { - return .none; - } + if (opts.is_unused) return .none; + if (fn_info.return_type == .noreturn_type and opts.modifier != .always_tail) return .none; // We exit this `switch` if we have a pointer to the return value. const ret_val_ptr: Builder.Value = switch (ret_strat) { @@ -1020,15 +1017,15 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier .sret => sret_alloc.?, .mem_cast => |llvm_ret_ty| ret_val_ptr: { const alignment = return_type.abiAlignment(zcu).toLlvm(); - const ptr = try self.buildAlloca(llvm_ret_ty, alignment); - _ = try self.wip.store(.normal, call, ptr, alignment); + const ptr = try fg.buildAlloca(llvm_ret_ty, alignment); + _ = try fg.wip.store(.normal, call, ptr, alignment); break :ret_val_ptr ptr; }, }; if (isByRef(return_type, zcu)) { return ret_val_ptr; } else { - return self.load(ret_val_ptr, .none, return_type, .normal); + return fg.load(ret_val_ptr, .none, return_type, .normal); } } @@ -1067,7 +1064,7 @@ fn airRet(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!vo const fn_info = zcu.typeToFunc(Type.fromInterned(ip.getNav(self.nav_index).resolved.?.type)).?; - const ret_strat = try fnReturnStrat(o, fn_info); + const ret_strat = try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type)); const val_is_undef = if (un_op.toInterned()) |i| Value.fromInterned(i).isUndef(zcu) else false; const ret_ty_align = ret_ty.abiAlignment(zcu); @@ -1141,7 +1138,7 @@ fn airRetLoad(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!void { const ret_ty = ptr_ty.childType(zcu); const fn_info = zcu.typeToFunc(.fromInterned(ip.getNav(self.nav_index).resolved.?.type)).?; const ptr = try self.resolveInst(un_op); - switch (try fnReturnStrat(o, fn_info)) { + switch (try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type))) { .void => _ = try self.wip.retVoid(), .sret => { assert(self.ret_ptr != .none); @@ -2028,135 +2025,95 @@ fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder return self.wip.buildAggregate(slice_llvm_ty, &.{ operand, len }, ""); } -fn airFloatFromInt(self: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value { - const o = self.object; +fn airFloatFromInt(fg: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value { + const o = fg.object; const zcu = o.zcu; - const ty_op = self.air.instructions.items(.data)[@backingInt(inst)].ty_op; + const ty_op = fg.air.instructions.items(.data)[@backingInt(inst)].ty_op; - const operand = try self.resolveInst(ty_op.operand); - const operand_ty = self.typeOf(ty_op.operand); + const operand = try fg.resolveInst(ty_op.operand); + const operand_ty = fg.typeOf(ty_op.operand); const operand_scalar_ty = operand_ty.scalarType(zcu); - const is_signed_int = operand_scalar_ty.isSignedInt(zcu); + const operand_scalar_info = operand_scalar_ty.intInfo(zcu); - const dest_ty = self.typeOfIndex(inst); + const dest_ty = fg.typeOfIndex(inst); const dest_scalar_ty = dest_ty.scalarType(zcu); - const dest_llvm_ty = try o.lowerType(dest_ty, .as_value); const target = zcu.getTarget(); - if (intrinsicsAllowed(dest_scalar_ty, target)) return self.wip.conv( - if (is_signed_int) .signed else .unsigned, - operand, - dest_llvm_ty, - "", - ); + if (intrinsicsAllowed(dest_scalar_ty, target)) + return fg.wip.conv(.fromStdLang(operand_scalar_info.signedness), operand, try o.lowerType(dest_ty, .as_value), ""); - const rt_int_bits = compilerRtIntBits(@intCast(operand_scalar_ty.bitSize(zcu))) orelse { - return self.todo("float_from_int on {d} bit integer", .{operand_scalar_ty.bitSize(zcu)}); + const rt_int_ty = compilerRtPromoteInt(operand_scalar_info) orelse { + return fg.todo("float_from_int on {d} bit integer", .{operand_scalar_info.bits}); }; - const rt_int_ty = try o.builder.intType(rt_int_bits); - var extended = try self.wip.conv( - if (is_signed_int) .signed else .unsigned, + const vector_len = if (operand_ty.isVector(zcu)) operand_ty.vectorLen(zcu) else null; + const rt_llvm_int_ty = try o.lowerType(rt_int_ty, .as_value); + const extended = try fg.wip.conv( + .fromStdLang(operand_scalar_info.signedness), operand, - rt_int_ty, + if (vector_len) |len| + try o.builder.vectorType(.normal, len, rt_llvm_int_ty) + else + rt_llvm_int_ty, "", ); - const dest_bits = dest_scalar_ty.floatBits(target); - const compiler_rt_operand_abbrev = compilerRtIntAbbrev(rt_int_bits); - const compiler_rt_dest_abbrev = compilerRtFloatAbbrev(dest_bits); - const sign_prefix = if (is_signed_int) "" else "un"; const fn_name = try o.builder.strtabStringFmt("__float{s}{s}i{s}f", .{ - sign_prefix, - compiler_rt_operand_abbrev, - compiler_rt_dest_abbrev, + switch (operand_scalar_info.signedness) { + .signed => "", + .unsigned => "un", + }, + compilerRtIntAbbrev(rt_int_ty.intInfo(zcu).bits), + compilerRtFloatAbbrev(target, dest_scalar_ty.floatBits(target)), }); - - var param_type = rt_int_ty; - if (rt_int_bits == 128 and (target.os.tag == .windows and target.cpu.arch == .x86_64)) { - // On Windows x86-64, "ti" functions must use Vector(2, u64) instead of the standard - // i128 calling convention to adhere to the ABI that LLVM expects compiler-rt to have. - param_type = try o.builder.vectorType(.normal, 2, .i64); - extended = try self.wip.cast(.bitcast, extended, param_type, ""); - } - - const libc_fn = try o.getLibcFunction(fn_name, &.{param_type}, dest_llvm_ty); - return self.wip.call( - .normal, - .ccc, - .none, - libc_fn.typeOf(&o.builder), - libc_fn.toValue(&o.builder), - &.{extended}, - "", - ); + return fg.buildElementwiseCall(fn_name, .{ + .cc = target.cCallingConvention().?, + .param_types = &.{rt_int_ty.toIntern()}, + .return_type = dest_scalar_ty.toIntern(), + }, &.{extended}, vector_len); } fn airIntFromFloat( - self: *FuncGen, + fg: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind, ) TodoError!Builder.Value { _ = fast; - const o = self.object; + const o = fg.object; const zcu = o.zcu; const target = zcu.getTarget(); - const ty_op = self.air.instructions.items(.data)[@backingInt(inst)].ty_op; + const ty_op = fg.air.instructions.items(.data)[@backingInt(inst)].ty_op; - const operand = try self.resolveInst(ty_op.operand); - const operand_ty = self.typeOf(ty_op.operand); + const operand = try fg.resolveInst(ty_op.operand); + const operand_ty = fg.typeOf(ty_op.operand); const operand_scalar_ty = operand_ty.scalarType(zcu); - const dest_ty = self.typeOfIndex(inst); + const dest_ty = fg.typeOfIndex(inst); const dest_scalar_ty = dest_ty.scalarType(zcu); const dest_llvm_ty = try o.lowerType(dest_ty, .as_value); + const dest_scalar_info = dest_scalar_ty.intInfo(zcu); if (intrinsicsAllowed(operand_scalar_ty, target)) { // TODO set fast math flag - return self.wip.conv( - if (dest_scalar_ty.isSignedInt(zcu)) .signed else .unsigned, - operand, - dest_llvm_ty, - "", - ); + return fg.wip.conv(.fromStdLang(dest_scalar_info.signedness), operand, dest_llvm_ty, ""); } - const rt_int_bits = compilerRtIntBits(@intCast(dest_scalar_ty.bitSize(zcu))) orelse { - return self.todo("int_from_float to {d} bit integer", .{dest_scalar_ty.bitSize(zcu)}); + const rt_int_ty = compilerRtPromoteInt(dest_scalar_info) orelse { + return fg.todo("int_from_float to {d} bit integer", .{dest_scalar_info.bits}); }; - const ret_ty = try o.builder.intType(rt_int_bits); - const libc_ret_ty = if (rt_int_bits == 128 and (target.os.tag == .windows and target.cpu.arch == .x86_64)) b: { - // On Windows x86-64, "ti" functions must use Vector(2, u64) instead of the standard - // i128 calling convention to adhere to the ABI that LLVM expects compiler-rt to have. - break :b try o.builder.vectorType(.normal, 2, .i64); - } else ret_ty; - - const operand_bits = operand_scalar_ty.floatBits(target); - const compiler_rt_operand_abbrev = compilerRtFloatAbbrev(operand_bits); - - const compiler_rt_dest_abbrev = compilerRtIntAbbrev(rt_int_bits); - const sign_prefix = if (dest_scalar_ty.isSignedInt(zcu)) "" else "uns"; - const fn_name = try o.builder.strtabStringFmt("__fix{s}{s}f{s}i", .{ - sign_prefix, - compiler_rt_operand_abbrev, - compiler_rt_dest_abbrev, + switch (dest_scalar_info.signedness) { + .signed => "", + .unsigned => "uns", + }, + compilerRtFloatAbbrev(target, operand_scalar_ty.floatBits(target)), + compilerRtIntAbbrev(rt_int_ty.intInfo(zcu).bits), }); - - const operand_llvm_ty = try o.lowerType(operand_ty, .as_value); - const libc_fn = try o.getLibcFunction(fn_name, &.{operand_llvm_ty}, libc_ret_ty); - var result = try self.wip.call( - .normal, - .ccc, - .none, - libc_fn.typeOf(&o.builder), - libc_fn.toValue(&o.builder), - &.{operand}, - "", - ); - - if (libc_ret_ty != ret_ty) result = try self.wip.cast(.bitcast, result, ret_ty, ""); - if (ret_ty != dest_llvm_ty) result = try self.wip.cast(.trunc, result, dest_llvm_ty, ""); - return result; + const result = try fg.buildElementwiseCall(fn_name, .{ + .cc = target.cCallingConvention().?, + .param_types = &.{operand_scalar_ty.toIntern()}, + .return_type = rt_int_ty.toIntern(), + }, &.{operand}, if (operand_ty.isVector(zcu)) operand_ty.vectorLen(zcu) else null); + return fg.wip.cast(.trunc, result, try o.lowerType(dest_ty, .as_value), ""); } fn sliceOrArrayPtr(fg: *FuncGen, ptr: Builder.Value, ty: Type) Allocator.Error!Builder.Value { @@ -3692,15 +3649,17 @@ fn airMod(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allo const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.typeOfIndex(inst); - const inst_llvm_ty = try o.lowerType(inst_ty, .as_value); const scalar_ty = inst_ty.scalarType(zcu); if (scalar_ty.isRuntimeFloat()) { const a = try self.buildFloatOp(.fmod, fast, inst_ty, 2, .{ lhs, rhs }); const b = try self.buildFloatOp(.add, fast, inst_ty, 2, .{ a, rhs }); const c = try self.buildFloatOp(.fmod, fast, inst_ty, 2, .{ b, rhs }); - const zero = try o.builder.zeroInitValue(inst_llvm_ty); - const ltz = try self.buildFloatCmp(fast, .lt, inst_ty, .{ lhs, zero }); + const zero = if (isByRef(inst_ty, zcu)) zero: { + const zero = try o.builder.zeroInitConst(try o.lowerType(inst_ty, .in_memory)); + break :zero try o.lowerConstRef(zero, inst_ty.abiAlignment(zcu).toLlvm()); + } else try o.builder.zeroInitConst(try o.lowerType(inst_ty, .as_value)); + const ltz = try self.buildFloatCmp(fast, .lt, inst_ty, .{ lhs, zero.toValue() }); return self.wip.select(fast, ltz, c, a, ""); } if (scalar_ty.isSignedInt(zcu)) { @@ -3709,6 +3668,7 @@ fn airMod(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allo var bfa: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), self.gpa); const allocator = bfa.allocator(); + const inst_llvm_ty = try o.lowerType(inst_ty, .as_value); const scalar_bits = scalar_ty.intInfo(zcu).bits; var smin_big_int: std.math.big.int.Mutable = .{ .limbs = try allocator.alloc( @@ -3818,34 +3778,97 @@ fn airOverflow( } fn buildElementwiseCall( - self: *FuncGen, - llvm_fn: Builder.Function.Index, - args_vectors: []const Builder.Value, - result_vector: Builder.Value, - vector_len: usize, + fg: *FuncGen, + fn_name: Builder.StrtabString, + fn_info: Object.FuncInfo, + arg_values: []const Builder.Value, + vector_len: ?u32, ) Allocator.Error!Builder.Value { - const o = self.object; - assert(args_vectors.len <= 3); + const o = fg.object; + const zcu = o.zcu; + const llvm_fn = try fg.object.getLibcFunction(fg.pt, fn_name, fn_info); - var i: usize = 0; - var result = result_vector; - while (i < vector_len) : (i += 1) { - const index_i32 = try o.builder.intValue(.i32, i); + const iterations = vector_len orelse 1; + const ret_ty: Type = .fromInterned(fn_info.return_type); + const ret_is_by_ref = isByRef(ret_ty, zcu); + if (iterations > 1 and (fn_info.return_type == .void_type or ret_is_by_ref) and + for (fn_info.param_types) |param_type| { + if (!isByRef(.fromInterned(param_type), zcu)) break false; + } else true) + { + const entry_block = fg.wip.cursor.block; + const loop_block = try fg.wip.block(2, "elementwise.loop"); + const done_block = try fg.wip.block(1, "elementwise.done"); - var args: [3]Builder.Value = undefined; - for (args[0..args_vectors.len], args_vectors) |*arg_elem, arg_vector| { - arg_elem.* = try self.wip.extractElement(arg_vector, index_i32, ""); + const result_ptr = if (fn_info.return_type == .void_type) .none else result_ptr: { + const ret_llvm_ty = try o.lowerType(ret_ty, .in_memory); + break :result_ptr try fg.buildAlloca( + if (vector_len) |len| try o.builder.arrayType(len, ret_llvm_ty) else ret_llvm_ty, + ret_ty.abiAlignment(zcu).toLlvm(), + ); + }; + _ = try fg.wip.br(loop_block); + + fg.wip.cursor = .{ .block = loop_block }; + const index = try fg.wip.phi(.i32, "elementwise.index"); + + var arg_elems_buf: [3]Builder.Value = undefined; + const arg_elems = arg_elems_buf[0..arg_values.len]; + for (arg_elems, fn_info.param_types, arg_values) |*arg_elem, param_type, arg_value| { + const arg_elem_ptr = try fg.ptraddScaled(arg_value, index.toValue(), Type.fromInterned(param_type).abiSize(zcu)); + arg_elem.* = try fg.load(arg_elem_ptr, .none, .fromInterned(param_type), .normal); + } + const result_elem = try fg.buildCall(.{}, llvm_fn.typeOf(&o.builder), llvm_fn.toValue(&o.builder), fn_info, fn_info.param_types, arg_elems); + if (fn_info.return_type == .void_type) { + assert(result_elem == .none); + } else if (result_elem != .none) { + const result_elem_ptr = try fg.ptraddScaled(result_ptr, index.toValue(), ret_ty.abiSize(zcu)); + try fg.store(result_elem_ptr, .none, result_elem, ret_ty, .normal); } - const result_elem = try self.wip.call( - .normal, - .ccc, - .none, - llvm_fn.typeOf(&o.builder), - llvm_fn.toValue(&o.builder), - args[0..args_vectors.len], - "", + + const next_index = try fg.wip.bin(.@"add nuw", index.toValue(), try o.builder.intValue(.i32, 1), "elementwise.next_index"); + index.finish(&.{ try o.builder.intValue(.i32, 0), next_index }, &.{ entry_block, loop_block }, &fg.wip); + const is_done = try fg.wip.icmp(.eq, next_index, try o.builder.intValue(.i32, iterations), "elementwise.is_done"); + _ = try fg.wip.brCond(is_done, done_block, loop_block, .none); + + fg.wip.cursor = .{ .block = done_block }; + return result_ptr; + } + + var result = if (fn_info.return_type == .void_type) .none else if (ret_is_by_ref) result: { + const ret_llvm_ty = try o.lowerType(ret_ty, .in_memory); + break :result try fg.buildAlloca( + if (vector_len) |len| try o.builder.arrayType(len, ret_llvm_ty) else ret_llvm_ty, + ret_ty.abiAlignment(zcu).toLlvm(), ); - result = try self.wip.insertElement(result, result_elem, index_i32, ""); + } else if (vector_len) |len| try o.builder.poisonValue( + try o.builder.vectorType(.normal, len, try o.lowerType(ret_ty, .as_value)), + ) else .none; + for (0..iterations) |index| { + const index_value = try o.builder.intValue(.i32, index); + var arg_elems_buf: [3]Builder.Value = undefined; + const arg_elems = arg_elems_buf[0..arg_values.len]; + for (arg_elems, fn_info.param_types, arg_values) |*arg_elem_value, param_type, arg_value| { + const arg_ty: Type = .fromInterned(param_type); + if (isByRef(arg_ty, zcu)) { + const arg_elem_ptr = try fg.ptraddConst(arg_value, index * arg_ty.abiSize(zcu)); + arg_elem_value.* = try fg.load(arg_elem_ptr, .none, .fromInterned(param_type), .normal); + } else if (vector_len) |_| { + arg_elem_value.* = try fg.wip.extractElement(arg_value, index_value, "elementwise.arg_elem"); + } else arg_elem_value.* = arg_value; + } + const result_elem = try fg.buildCall(.{}, llvm_fn.typeOf(&o.builder), llvm_fn.toValue(&o.builder), fn_info, fn_info.param_types, arg_elems); + if (fn_info.return_type == .void_type) { + assert(result_elem == .none); + } else if (ret_is_by_ref) { + const result_elem_ptr = try fg.ptraddConst(result, index * ret_ty.abiSize(zcu)); + try fg.store(result_elem_ptr, .none, result_elem, ret_ty, .normal); + } else if (vector_len) |_| { + result = try fg.wip.insertElement(result, result_elem, index_value, "elementwise.result"); + } else { + assert(result == .none); + result = result_elem; + } } return result; } @@ -3853,17 +3876,16 @@ fn buildElementwiseCall( /// Creates a floating point comparison by lowering to the appropriate /// hardware instruction or softfloat routine for the target fn buildFloatCmp( - self: *FuncGen, + fg: *FuncGen, fast: Builder.FastMathKind, pred: math.CompareOperator, ty: Type, params: [2]Builder.Value, ) Allocator.Error!Builder.Value { - const o = self.object; + const o = fg.object; const zcu = o.zcu; const target = zcu.getTarget(); const scalar_ty = ty.scalarType(zcu); - const scalar_llvm_ty = try o.lowerType(scalar_ty, .as_value); if (intrinsicsAllowed(scalar_ty, target)) { const cond: Builder.FloatCondition = switch (pred) { @@ -3874,53 +3896,33 @@ fn buildFloatCmp( .gt => .ogt, .gte => .oge, }; - return self.wip.fcmp(fast, cond, params[0], params[1], ""); + return fg.wip.fcmp(fast, cond, params[0], params[1], ""); } - const float_bits = scalar_ty.floatBits(target); - const compiler_rt_float_abbrev = compilerRtFloatAbbrev(float_bits); - const fn_base_name = switch (pred) { - .neq => "ne", - .eq => "eq", - .lt => "lt", - .lte => "le", - .gt => "gt", - .gte => "ge", - }; - const fn_name = try o.builder.strtabStringFmt("__{s}{s}f2", .{ fn_base_name, compiler_rt_float_abbrev }); - - const libc_fn = try o.getLibcFunction(fn_name, &.{ scalar_llvm_ty, scalar_llvm_ty }, .i32); - - const int_cond: Builder.IntegerCondition = switch (pred) { + const fn_name = try o.builder.strtabStringFmt("__{s}{s}f2", .{ + switch (pred) { + .neq => "ne", + .eq => "eq", + .lt => "lt", + .lte => "le", + .gt => "gt", + .gte => "ge", + }, + compilerRtFloatAbbrev(target, scalar_ty.floatBits(target)), + }); + const result = try fg.buildElementwiseCall(fn_name, .{ + .cc = target.cCallingConvention().?, + .param_types = &.{ scalar_ty.toIntern(), scalar_ty.toIntern() }, + .return_type = .i32_type, + }, ¶ms, if (ty.isVector(zcu)) ty.vectorLen(zcu) else null); + return fg.wip.icmp(switch (pred) { .eq => .eq, .neq => .ne, .lt => .slt, .lte => .sle, .gt => .sgt, .gte => .sge, - }; - - if (ty.zigTypeTag(zcu) == .vector) { - const vec_len = ty.vectorLen(zcu); - const vector_result_ty = try o.builder.vectorType(.normal, vec_len, .i32); - - const init = try o.builder.poisonValue(vector_result_ty); - const result = try self.buildElementwiseCall(libc_fn, ¶ms, init, vec_len); - - const zero_vector = try o.builder.splatValue(vector_result_ty, .@"0"); - return self.wip.icmp(int_cond, result, zero_vector, ""); - } - - const result = try self.wip.call( - .normal, - .ccc, - .none, - libc_fn.typeOf(&o.builder), - libc_fn.toValue(&o.builder), - ¶ms, - "", - ); - return self.wip.icmp(int_cond, result, .@"0", ""); + }, result, try o.builder.splatValue(result.typeOfWip(&fg.wip), .@"0"), ""); } const FloatOp = enum { @@ -3949,32 +3951,26 @@ const FloatOp = enum { trunc, }; -const FloatOpStrat = union(enum) { - intrinsic: []const u8, - libc: Builder.String, -}; - /// Creates a floating point operation (add, sub, fma, sqrt, exp, etc.) /// by lowering to the appropriate hardware instruction or softfloat /// routine for the target fn buildFloatOp( - self: *FuncGen, + fg: *FuncGen, comptime op: FloatOp, fast: Builder.FastMathKind, ty: Type, comptime params_len: usize, params: [params_len]Builder.Value, ) Allocator.Error!Builder.Value { - const o = self.object; + const o = fg.object; const zcu = o.zcu; const target = zcu.getTarget(); const scalar_ty = ty.scalarType(zcu); - const llvm_ty = try o.lowerType(ty, .as_value); if (op != .tan and intrinsicsAllowed(scalar_ty, target)) switch (op) { // Some operations are dedicated LLVM instructions, not available as intrinsics - .neg => return self.wip.un(.fneg, params[0], ""), - .add, .sub, .mul, .div, .fmod => return self.wip.bin(switch (fast) { + .neg => return fg.wip.un(.fneg, params[0], ""), + .add, .sub, .mul, .div, .fmod => return fg.wip.bin(switch (fast) { .normal => switch (op) { .add => .fadd, .sub => .fsub, @@ -4008,7 +4004,7 @@ fn buildFloatOp( .sqrt, .trunc, .fma, - => return self.wip.callIntrinsic(fast, .none, switch (op) { + => return fg.wip.callIntrinsic(fast, .none, switch (op) { .fmax => .maxnum, .fmin => .minnum, .ceil => .ceil, @@ -4026,36 +4022,152 @@ fn buildFloatOp( .trunc => .trunc, .fma => .fma, else => unreachable, - }, &.{llvm_ty}, ¶ms, ""), + }, &.{try o.lowerType(ty, .as_value)}, ¶ms, ""), .tan => unreachable, }; const float_bits = scalar_ty.floatBits(target); const fn_name = switch (op) { - .neg => { - // In this case we can generate a softfloat negation by XORing the - // bits with a constant. + // In these cases we can generate a softfloat operation by modifying the sign bit using a bitwise operation. + .neg, .fabs => if (isByRef(scalar_ty, zcu)) { + const is_vector = ty.toIntern() != scalar_ty.toIntern(); + const result_ptr = try fg.buildZigAlloca(ty, .none); + const entry_block = fg.wip.cursor.block; + const loop_block, const done_block, const llvm_usize_ty, const offset, const elem, const result_elem = if (is_vector) loop: { + const loop_block = try fg.wip.block(2, "neg_fabs.loop"); + const done_block = try fg.wip.block(1, "neg_fabs.done"); + _ = try fg.wip.br(loop_block); + + fg.wip.cursor = .{ .block = loop_block }; + const llvm_usize_ty = try o.lowerType(.usize, .as_value); + const offset = try fg.wip.phi(llvm_usize_ty, "neg_fabs.offset"); + break :loop .{ + loop_block, + done_block, + llvm_usize_ty, + offset, + try fg.ptraddScaled(params[0], offset.toValue(), 1), + try fg.ptraddScaled(result_ptr, offset.toValue(), 1), + }; + } else .{ undefined, undefined, undefined, undefined, params[0], result_ptr }; + switch (scalar_ty.floatBits(target)) { + else => unreachable, + 80 => { + const f80_layout = o.softF80Layout(.{}) catch unreachable; + const mantissa = try fg.load( + try fg.ptraddConst(elem, f80_layout.mantissa_offset), + f80_layout.alignment.offset(f80_layout.mantissa_offset), + .u64, + .normal, + ); + const exponent = try fg.load( + try fg.ptraddConst(elem, f80_layout.exponent_offset), + f80_layout.alignment.offset(f80_layout.exponent_offset), + .u16, + .normal, + ); + const exponent_sign_bit: u16 = 1 << (16 - 1); + const updated_exponent = try fg.wip.bin(switch (op) { + else => unreachable, + .neg => .xor, + .fabs => .@"and", + }, exponent, try o.builder.intValue(.i16, switch (op) { + else => unreachable, + .neg => exponent_sign_bit, + .fabs => exponent_sign_bit - 1, + }), "neg_fabs.updated_exponent"); + try fg.store( + try fg.ptraddConst(result_elem, f80_layout.mantissa_offset), + f80_layout.alignment.offset(f80_layout.mantissa_offset), + mantissa, + .u64, + .normal, + ); + try fg.store( + try fg.ptraddConst(result_elem, f80_layout.exponent_offset), + f80_layout.alignment.offset(f80_layout.exponent_offset), + updated_exponent, + .u16, + .normal, + ); + }, + 128 => { + const f128_layout = o.softF128Layout(.{}) catch unreachable; + const lo = try fg.load( + try fg.ptraddConst(elem, f128_layout.lo_offset), + f128_layout.alignment.offset(f128_layout.lo_offset), + .u64, + .normal, + ); + const hi = try fg.load( + try fg.ptraddConst(elem, f128_layout.hi_offset), + f128_layout.alignment.offset(f128_layout.hi_offset), + .u64, + .normal, + ); + const hi_sign_bit: u64 = 1 << (64 - 1); + const updated_hi = try fg.wip.bin(switch (op) { + else => unreachable, + .neg => .xor, + .fabs => .@"and", + }, hi, try o.builder.intValue(.i64, switch (op) { + else => unreachable, + .neg => hi_sign_bit, + .fabs => hi_sign_bit - 1, + }), "neg_fabs.updated_hi"); + try fg.store( + try fg.ptraddConst(result_elem, f128_layout.lo_offset), + f128_layout.alignment.offset(f128_layout.lo_offset), + lo, + .u64, + .normal, + ); + try fg.store( + try fg.ptraddConst(result_elem, f128_layout.hi_offset), + f128_layout.alignment.offset(f128_layout.hi_offset), + updated_hi, + .u64, + .normal, + ); + }, + } + if (is_vector) { + const next_offset = try fg.wip.bin(.@"add nuw", offset.toValue(), try o.builder.intValue(llvm_usize_ty, scalar_ty.abiSize(zcu)), "neg_fabs.next_offset"); + offset.finish(&.{ try o.builder.intValue(llvm_usize_ty, 0), next_offset }, &.{ entry_block, loop_block }, &fg.wip); + const is_done = try fg.wip.icmp(.eq, next_offset, try o.builder.intValue(llvm_usize_ty, ty.abiSize(zcu)), "neg_fabs.is_done"); + _ = try fg.wip.brCond(is_done, done_block, loop_block, .none); + + fg.wip.cursor = .{ .block = done_block }; + } + return result_ptr; + } else { const int_ty = try o.builder.intType(@intCast(float_bits)); const cast_ty = switch (ty.zigTypeTag(zcu)) { .vector => try o.builder.vectorType(.normal, ty.vectorLen(zcu), int_ty), else => int_ty, }; - const sign_mask = try o.builder.splatValue( - cast_ty, - try o.builder.intConst(int_ty, @as(u128, 1) << @intCast(float_bits - 1)), - ); - const bitcasted_operand = try self.wip.cast(.bitcast, params[0], cast_ty, ""); - const result = try self.wip.bin(.xor, bitcasted_operand, sign_mask, ""); - return self.wip.cast(.bitcast, result, llvm_ty, ""); + const sign_bit = @as(u128, 1) << @intCast(float_bits - 1); + const bitwise_rhs = try o.builder.splatValue(cast_ty, try o.builder.intConst(int_ty, switch (op) { + else => unreachable, + .neg => sign_bit, + .fabs => sign_bit - 1, + })); + const bitcasted_operand = try fg.wip.cast(.bitcast, params[0], cast_ty, ""); + const result = try fg.wip.bin(switch (op) { + else => unreachable, + .neg => .xor, + .fabs => .@"and", + }, bitcasted_operand, bitwise_rhs, ""); + const llvm_ty = try o.lowerType(ty, .as_value); + return fg.wip.cast(.bitcast, result, llvm_ty, ""); }, .add, .sub, .div, .mul => try o.builder.strtabStringFmt("__{s}{s}f3", .{ - @tagName(op), compilerRtFloatAbbrev(float_bits), + @tagName(op), compilerRtFloatAbbrev(target, float_bits), }), .ceil, .cos, .exp, .exp2, - .fabs, .floor, .fma, .fmax, @@ -4073,27 +4185,27 @@ fn buildFloatOp( libcFloatPrefix(float_bits), @tagName(op), libcFloatSuffix(float_bits), }), }; + return fg.buildElementwiseCall(fn_name, .{ + .cc = target.cCallingConvention().?, + .param_types = &@as([params_len]InternPool.Index, @splat(scalar_ty.toIntern())), + .return_type = scalar_ty.toIntern(), + }, ¶ms, if (ty.isVector(zcu)) ty.vectorLen(zcu) else null); +} - const scalar_llvm_ty = try o.lowerType(scalar_ty, .as_value); - const libc_fn = try o.getLibcFunction( - fn_name, - @as([3]Builder.Type, @splat(scalar_llvm_ty))[0..params.len], - scalar_llvm_ty, - ); - if (ty.zigTypeTag(zcu) == .vector) { - const result = try o.builder.poisonValue(llvm_ty); - return self.buildElementwiseCall(libc_fn, ¶ms, result, ty.vectorLen(zcu)); - } - - return self.wip.call( - fast.toCallKind(), - .ccc, - .none, - libc_fn.typeOf(&o.builder), - libc_fn.toValue(&o.builder), - ¶ms, - "", - ); +/// Creates a floating point cast operation by lowering to the specified softfloat routine. +fn buildFloatCastCall( + fg: *FuncGen, + dest_ty: Type, + fn_name: Builder.StrtabString, + operand_ty: Type, + operand: Builder.Value, +) Allocator.Error!Builder.Value { + const zcu = fg.object.zcu; + return fg.buildElementwiseCall(fn_name, .{ + .cc = zcu.getTarget().cCallingConvention().?, + .param_types = &.{operand_ty.scalarType(zcu).toIntern()}, + .return_type = dest_ty.scalarType(zcu).toIntern(), + }, &.{operand}, if (operand_ty.isVector(zcu)) operand_ty.vectorLen(zcu) else null); } fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { @@ -4471,32 +4583,19 @@ fn airFptrunc(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Valu const ty_op = self.air.instructions.items(.data)[@backingInt(inst)].ty_op; const operand = try self.resolveInst(ty_op.operand); const operand_ty = self.typeOf(ty_op.operand); + const operand_scalar_ty = operand_ty.scalarType(zcu); const dest_ty = self.typeOfIndex(inst); + const dest_scalar_ty = dest_ty.scalarType(zcu); const target = zcu.getTarget(); - if (intrinsicsAllowed(dest_ty, target) and intrinsicsAllowed(operand_ty, target)) { + if (intrinsicsAllowed(dest_scalar_ty, target) and intrinsicsAllowed(operand_scalar_ty, target)) return self.wip.cast(.fptrunc, operand, try o.lowerType(dest_ty, .as_value), ""); - } else { - const operand_llvm_ty = try o.lowerType(operand_ty, .as_value); - const dest_llvm_ty = try o.lowerType(dest_ty, .as_value); - - const dest_bits = dest_ty.floatBits(target); - const src_bits = operand_ty.floatBits(target); - const fn_name = try o.builder.strtabStringFmt("__trunc{s}f{s}f2", .{ - compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits), - }); - - const libc_fn = try o.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); - return self.wip.call( - .normal, - .ccc, - .none, - libc_fn.typeOf(&o.builder), - libc_fn.toValue(&o.builder), - &.{operand}, - "", - ); - } + const dest_bits = dest_scalar_ty.floatBits(target); + const src_bits = operand_scalar_ty.floatBits(target); + const fn_name = try o.builder.strtabStringFmt("__trunc{s}f{s}f2", .{ + compilerRtFloatAbbrev(target, src_bits), compilerRtFloatAbbrev(target, dest_bits), + }); + return self.buildFloatCastCall(dest_ty, fn_name, operand_ty, operand); } fn airFpext(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { @@ -4505,38 +4604,19 @@ fn airFpext(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value const ty_op = self.air.instructions.items(.data)[@backingInt(inst)].ty_op; const operand = try self.resolveInst(ty_op.operand); const operand_ty = self.typeOf(ty_op.operand); + const operand_scalar_ty = operand_ty.scalarType(zcu); const dest_ty = self.typeOfIndex(inst); + const dest_scalar_ty = dest_ty.scalarType(zcu); const target = zcu.getTarget(); - if (intrinsicsAllowed(dest_ty, target) and intrinsicsAllowed(operand_ty, target)) { + if (intrinsicsAllowed(dest_scalar_ty, target) and intrinsicsAllowed(operand_scalar_ty, target)) return self.wip.cast(.fpext, operand, try o.lowerType(dest_ty, .as_value), ""); - } else { - const operand_llvm_ty = try o.lowerType(operand_ty, .as_value); - const dest_llvm_ty = try o.lowerType(dest_ty, .as_value); - - const dest_bits = dest_ty.scalarType(zcu).floatBits(target); - const src_bits = operand_ty.scalarType(zcu).floatBits(target); - const fn_name = try o.builder.strtabStringFmt("__extend{s}f{s}f2", .{ - compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits), - }); - - const libc_fn = try o.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); - if (dest_ty.isVector(zcu)) return self.buildElementwiseCall( - libc_fn, - &.{operand}, - try o.builder.poisonValue(dest_llvm_ty), - dest_ty.vectorLen(zcu), - ); - return self.wip.call( - .normal, - .ccc, - .none, - libc_fn.typeOf(&o.builder), - libc_fn.toValue(&o.builder), - &.{operand}, - "", - ); - } + const dest_bits = dest_scalar_ty.floatBits(target); + const src_bits = operand_scalar_ty.floatBits(target); + const fn_name = try o.builder.strtabStringFmt("__extend{s}f{s}f2", .{ + compilerRtFloatAbbrev(target, src_bits), compilerRtFloatAbbrev(target, dest_bits), + }); + return self.buildFloatCastCall(dest_ty, fn_name, operand_ty, operand); } fn airBitCast(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Builder.Value { @@ -4558,10 +4638,143 @@ fn airBitCast(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error! // * bool/int/float <-> bool/int/float // * `@Vector(n, A)` <-> `@Vector(n, B)` // - // All of these cases can be handled by LLVM's `bitcast` instruction. + // Most of these cases can be handled by LLVM's `bitcast` instruction, except when + // a non-native type like `f80` is used. - assert(!isByRef(operand_ty, zcu)); - assert(!isByRef(dest_ty, zcu)); + if (isByRef(operand_ty, zcu)) { + const operand_scalar_ty = operand_ty.scalarType(zcu); + const target = zcu.getTarget(); + const bits = operand_scalar_ty.floatBits(target); + const dest_scalar_ty = dest_ty.scalarType(zcu); + if (isByRef(dest_ty, zcu)) { + assert(dest_scalar_ty.floatBits(target) == bits); + return operand; + } + assert(dest_scalar_ty.intInfo(zcu).bits == bits); + + const len = if (operand_ty.toIntern() != operand_scalar_ty.toIntern()) + operand_ty.vectorLen(zcu) + else + null; + const operand_scalar_size = operand_scalar_ty.abiSize(zcu); + var result = if (len) |_| + try o.builder.poisonValue(try o.lowerType(dest_ty, .as_value)) + else + undefined; + for (0..len orelse 1) |index| { + const result_elem = result_elem: switch (bits) { + else => unreachable, + 80 => { + const f80_layout = o.softF80Layout(.{}) catch unreachable; + const mantissa = try fg.load( + try fg.ptraddConst(operand, operand_scalar_size * index + f80_layout.mantissa_offset), + f80_layout.alignment.offset(f80_layout.mantissa_offset), + .u64, + .normal, + ); + const exponent = try fg.load( + try fg.ptraddConst(operand, operand_scalar_size * index + f80_layout.exponent_offset), + f80_layout.alignment.offset(f80_layout.exponent_offset), + .u16, + .normal, + ); + const casted_mantissa = try fg.wip.cast(.zext, mantissa, .i80, "bitCast.casted_mantissa"); + const casted_exponent = try fg.wip.cast(.zext, exponent, .i80, "bitCast.casted_exponent"); + const shifted_exponent = try fg.wip.bin(.@"shl nuw", casted_exponent, try o.builder.intValue(.i80, 64), "bitCast.shifted_exponent"); + break :result_elem try fg.wip.bin(.@"or", casted_mantissa, shifted_exponent, "bitCast.result_elem"); + }, + 128 => { + const f128_layout = o.softF128Layout(.{}) catch unreachable; + const lo = try fg.load( + try fg.ptraddConst(operand, operand_scalar_size * index + f128_layout.lo_offset), + f128_layout.alignment.offset(f128_layout.lo_offset), + .u64, + .normal, + ); + const hi = try fg.load( + try fg.ptraddConst(operand, operand_scalar_size * index + f128_layout.hi_offset), + f128_layout.alignment.offset(f128_layout.hi_offset), + .u64, + .normal, + ); + const casted_lo = try fg.wip.cast(.zext, lo, .i128, "bitCast.casted_lo"); + const casted_hi = try fg.wip.cast(.zext, hi, .i128, "bitCast.casted_hi"); + const shifted_hi = try fg.wip.bin(.@"shl nuw", casted_hi, try o.builder.intValue(.i128, 64), "bitCast.shifted_hi"); + break :result_elem try fg.wip.bin(.@"or", casted_lo, shifted_hi, "bitCast.result_elem"); + }, + }; + result = if (len) |_| + try fg.wip.insertElement(result, result_elem, try o.builder.intValue(.i32, index), "elementwise.result") + else + result_elem; + } + return result; + } + + if (isByRef(dest_ty, zcu)) { + const dest_scalar_ty = dest_ty.scalarType(zcu); + const bits = dest_scalar_ty.floatBits(zcu.getTarget()); + assert(dest_scalar_ty.isRuntimeFloat()); + const operand_scalar_ty = operand_ty.scalarType(zcu); + assert(operand_scalar_ty.intInfo(zcu).bits == bits); + + const len = if (operand_ty.toIntern() != operand_scalar_ty.toIntern()) + operand_ty.vectorLen(zcu) + else + null; + const operand_scalar_size = operand_scalar_ty.abiSize(zcu); + const result_ptr = try fg.buildZigAlloca(dest_ty, .none); + for (0..len orelse 1) |index| { + const operand_elem = if (len) |_| + try fg.wip.extractElement(operand, try o.builder.intValue(.i32, index), "elementwise.operand_elem") + else + operand; + switch (bits) { + else => unreachable, + 80 => { + const f80_layout = o.softF80Layout(.{}) catch unreachable; + const mantissa = try fg.wip.cast(.trunc, operand_elem, .i64, "bitCast.mantissa"); + const shifted_exponent = try fg.wip.bin(.lshr, operand_elem, try o.builder.intValue(.i80, 64), "bitCast.shifted_exponent"); + const exponent = try fg.wip.cast(.@"trunc nuw", shifted_exponent, .i16, "bitCast.exponent"); + try fg.store( + try fg.ptraddConst(result_ptr, operand_scalar_size * index + f80_layout.mantissa_offset), + f80_layout.alignment.offset(f80_layout.mantissa_offset), + mantissa, + .u64, + .normal, + ); + try fg.store( + try fg.ptraddConst(result_ptr, operand_scalar_size * index + f80_layout.exponent_offset), + f80_layout.alignment.offset(f80_layout.exponent_offset), + exponent, + .u16, + .normal, + ); + }, + 128 => { + const f128_layout = o.softF128Layout(.{}) catch unreachable; + const lo = try fg.wip.cast(.trunc, operand_elem, .i64, "bitCast.lo"); + const shifted_hi = try fg.wip.bin(.lshr, operand_elem, try o.builder.intValue(.i128, 64), "bitCast.shifted_hi"); + const hi = try fg.wip.cast(.@"trunc nuw", shifted_hi, .i64, "bitCast.hi"); + try fg.store( + try fg.ptraddConst(result_ptr, operand_scalar_size * index + f128_layout.lo_offset), + f128_layout.alignment.offset(f128_layout.lo_offset), + lo, + .u64, + .normal, + ); + try fg.store( + try fg.ptraddConst(result_ptr, operand_scalar_size * index + f128_layout.hi_offset), + f128_layout.alignment.offset(f128_layout.hi_offset), + hi, + .u64, + .normal, + ); + }, + } + } + return result_ptr; + } const llvm_dest_ty = try o.lowerType(dest_ty, .as_value); const result = try fg.wip.cast(.bitcast, operand, llvm_dest_ty, ""); @@ -4730,7 +4943,7 @@ fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value const ptr_align = ptr_ty.ptrAlignment(zcu); const elem_ty = ptr_ty.childType(zcu); if (!elem_ty.hasRuntimeBits(zcu)) { - return (try o.lowerPtrToVoid(ptr_align, ptr_ty.ptrAddressSpace(zcu))).toValue(); + return (try o.lowerPtrToVoid(ptr_align.toLlvm(), ptr_ty.ptrAddressSpace(zcu))).toValue(); } return self.buildZigAlloca(elem_ty, ptr_align); } @@ -4743,7 +4956,7 @@ fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value const ptr_align = ptr_ty.ptrAlignment(zcu); const elem_ty = ptr_ty.childType(zcu); if (!elem_ty.hasRuntimeBits(zcu)) { - return (try o.lowerPtrToVoid(ptr_align, ptr_ty.ptrAddressSpace(zcu))).toValue(); + return (try o.lowerPtrToVoid(ptr_align.toLlvm(), ptr_ty.ptrAddressSpace(zcu))).toValue(); } return self.buildZigAlloca(elem_ty, ptr_align); } @@ -4850,17 +5063,24 @@ fn airStore(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Bu const elem = try fg.resolveInst(bin_op.rhs); if (ptr_info.flags.vector_index != .none) { - // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. - const vec_ty = try fg.pt.vectorType(.{ - .len = ptr_info.packed_offset.host_size, - .child = elem_ty.toIntern(), - }); + if (isByRef(elem_ty, zcu)) { + const offset = @backingInt(ptr_info.flags.vector_index) * elem_ty.abiSize(zcu); + const elem_ptr = try fg.ptraddConst(ptr, offset); + try fg.store(elem_ptr, ptr_alignment.offset(offset), elem, elem_ty, access_kind); + } else { + // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. + const vec_ty = try fg.pt.vectorType(.{ + .len = ptr_info.packed_offset.host_size, + .child = elem_ty.toIntern(), + }); - const loaded_vector = try fg.load(ptr, ptr_alignment, vec_ty, access_kind); - const index_val = try o.builder.intValue(.i32, ptr_info.flags.vector_index); - const modified_vector = try fg.wip.insertElement(loaded_vector, elem, index_val, ""); + const loaded_vector = try fg.load(ptr, ptr_alignment, vec_ty, access_kind); + const index_val = try o.builder.intValue(.i32, ptr_info.flags.vector_index); + const modified_vector = try fg.wip.insertElement(loaded_vector, elem, index_val, ""); + + try fg.store(ptr, ptr_alignment, modified_vector, vec_ty, access_kind); + } - try fg.store(ptr, ptr_alignment, modified_vector, vec_ty, access_kind); return .none; } @@ -4927,22 +5147,27 @@ fn airLoad(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { if (ptr_info.flags.is_volatile) .@"volatile" else .normal; if (ptr_info.flags.vector_index != .none) { - // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. - const vec_ty = try fg.pt.vectorType(.{ - .len = ptr_info.packed_offset.host_size, - .child = elem_ty.toIntern(), - }); - const vector_val = try fg.load(ptr, ptr_align, vec_ty, access_kind); - const index_val = try o.builder.intValue(.i32, ptr_info.flags.vector_index); - return fg.wip.extractElement(vector_val, index_val, ""); + if (isByRef(elem_ty, zcu)) { + const elem_size = elem_ty.abiSize(zcu); + const offset = @backingInt(ptr_info.flags.vector_index) * elem_size; + const elem_ptr = try fg.ptraddConst(ptr, offset); + return fg.load(elem_ptr, ptr_align.offset(offset), elem_ty, access_kind); + } else { + // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. + const vec_ty = try fg.pt.vectorType(.{ + .len = ptr_info.packed_offset.host_size, + .child = elem_ty.toIntern(), + }); + const vector_val = try fg.load(ptr, ptr_align, vec_ty, access_kind); + const index_val = try o.builder.intValue(.i32, ptr_info.flags.vector_index); + return fg.wip.extractElement(vector_val, index_val, ""); + } } if (ptr_info.packed_offset.host_size == 0) { return fg.load(ptr, ptr_align, elem_ty, access_kind); } - assert(!isByRef(elem_ty, zcu)); // all packable types are by-val - // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. const backing_int_ty = try fg.pt.intType(.unsigned, @intCast(ptr_info.packed_offset.host_size * 8)); const llvm_backing_int_ty = try o.lowerType(backing_int_ty, .as_value); @@ -4952,6 +5177,67 @@ fn airLoad(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { const elem_bits = ptr_ty.childType(zcu).bitSize(zcu); const shift_amt = try o.builder.intValue(llvm_backing_int_ty, ptr_info.packed_offset.bit_offset); const shifted_value = try fg.wip.bin(.lshr, backing_int_val, shift_amt, ""); + + if (isByRef(elem_ty, zcu)) { + const result_ptr = try fg.buildZigAlloca(elem_ty, .none); + switch (elem_ty.floatBits(zcu.getTarget())) { + else => unreachable, + 80 => { + const f80_layout = o.softF80Layout(.{}) catch unreachable; + const mantissa = try fg.wip.cast(.trunc, shifted_value, .i64, "load.mantissa"); + const shifted_exponent = try fg.wip.bin( + .lshr, + backing_int_val, + try o.builder.intValue(llvm_backing_int_ty, ptr_info.packed_offset.bit_offset + 64), + "load.shifted_exponent", + ); + const exponent = try fg.wip.cast(.trunc, shifted_exponent, .i16, "load.exponent"); + + try fg.store( + try fg.ptraddConst(result_ptr, f80_layout.mantissa_offset), + f80_layout.alignment.offset(f80_layout.mantissa_offset), + mantissa, + .u64, + .normal, + ); + try fg.store( + try fg.ptraddConst(result_ptr, f80_layout.exponent_offset), + f80_layout.alignment.offset(f80_layout.exponent_offset), + exponent, + .u16, + .normal, + ); + }, + 128 => { + const f128_layout = o.softF128Layout(.{}) catch unreachable; + const lo = try fg.wip.cast(.trunc, shifted_value, .i64, "load.lo"); + const shifted_hi = try fg.wip.bin( + .lshr, + backing_int_val, + try o.builder.intValue(llvm_backing_int_ty, ptr_info.packed_offset.bit_offset + 64), + "load.shifted_hi", + ); + const hi = try fg.wip.cast(.trunc, shifted_hi, .i64, "load.hi"); + + try fg.store( + try fg.ptraddConst(result_ptr, f128_layout.lo_offset), + f128_layout.alignment.offset(f128_layout.lo_offset), + lo, + .u64, + .normal, + ); + try fg.store( + try fg.ptraddConst(result_ptr, f128_layout.hi_offset), + f128_layout.alignment.offset(f128_layout.hi_offset), + hi, + .u64, + .normal, + ); + }, + } + return result_ptr; + } + const elem_llvm_ty = try o.lowerType(elem_ty, .as_value); if (elem_ty.zigTypeTag(zcu) == .float or elem_ty.zigTypeTag(zcu) == .vector) { @@ -5848,95 +6134,25 @@ fn airShuffleTwo(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Val ); } -/// Reduce a vector by repeatedly applying `llvm_fn` to produce an accumulated result. -/// -/// Equivalent to: -/// ``` -/// var accum: T = init; -/// for (0..i) |i| { -/// accum = llvm_fn(accum, vec[i]); -/// } -/// // result is 'accum' -/// ``` -fn buildReducedCall( - self: *FuncGen, - llvm_fn: Builder.Function.Index, - operand_vector: Builder.Value, - vector_len: usize, - accum_init: Builder.Value, -) Allocator.Error!Builder.Value { - const o = self.object; - const llvm_usize_ty = try o.lowerType(.usize, .as_value); - const llvm_vector_len = try o.builder.intValue(llvm_usize_ty, vector_len); - const llvm_result_ty = accum_init.typeOfWip(&self.wip); - - const entry_block = self.wip.cursor.block; - - const cond_block = try self.wip.block(2, "ReduceLoopCond"); - const body_block = try self.wip.block(1, "ReduceLoopBody"); - const exit_block = try self.wip.block(1, "ReduceLoopExit"); - - _ = try self.wip.br(cond_block); - - // ReduceLoopCond: - // %index = phi iN [0, %Entry], [%new_index, %ReduceLoopBody] - // %accum = phi T [%accum_init, %Entry], [%new_accum, %ReduceLoopBody] - // %cond = icmp ult iN %index, %vector_len - // br i1 %cond, label %ReduceLoopBody, label %ReduceLoopExit - self.wip.cursor = .{ .block = cond_block }; - const index = try self.wip.phi(llvm_usize_ty, ""); - const accum = try self.wip.phi(llvm_result_ty, ""); - const cond = try self.wip.icmp(.ult, index.toValue(), llvm_vector_len, ""); - _ = try self.wip.brCond(cond, body_block, exit_block, .none); - - // ReduceLoopBody: - // %elem = extractelement %operand_vec, iN %index - // %new_accum = call T @llvm_fn(T %accum, T %elem) - // %new_index = add nuw iN %index, 1 - // br label %ReduceLoopCond - self.wip.cursor = .{ .block = body_block }; - const elem = try self.wip.extractElement(operand_vector, index.toValue(), ""); - const new_accum = try self.wip.call( - .normal, - .ccc, - .none, - llvm_fn.typeOf(&o.builder), - llvm_fn.toValue(&o.builder), - &.{ accum.toValue(), elem }, - "", - ); - const new_index = try self.wip.bin(.@"add nuw", index.toValue(), try o.builder.intValue(llvm_usize_ty, 1), ""); - _ = try self.wip.br(cond_block); - - const index_init = try o.builder.intValue(llvm_usize_ty, 0); - index.finish(&.{ index_init, new_index }, &.{ entry_block, body_block }, &self.wip); - accum.finish(&.{ accum_init, new_accum }, &.{ entry_block, body_block }, &self.wip); - - self.wip.cursor = .{ .block = exit_block }; - return accum.toValue(); -} - -fn airReduce(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value { - const o = self.object; +fn airReduce(fg: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value { + const o = fg.object; const zcu = o.zcu; const target = zcu.getTarget(); - const reduce = self.air.instructions.items(.data)[@backingInt(inst)].reduce; - const operand = try self.resolveInst(reduce.operand); - const operand_ty = self.typeOf(reduce.operand); - const llvm_operand_ty = try o.lowerType(operand_ty, .as_value); - const scalar_ty = self.typeOfIndex(inst); - const llvm_scalar_ty = try o.lowerType(scalar_ty, .as_value); + const reduce = fg.air.instructions.items(.data)[@backingInt(inst)].reduce; + const operand = try fg.resolveInst(reduce.operand); + const operand_ty = fg.typeOf(reduce.operand); + const scalar_ty = fg.typeOfIndex(inst); switch (reduce.operation) { - .And, .Or, .Xor => return self.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { + .And, .Or, .Xor => return fg.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { .And => .@"vector.reduce.and", .Or => .@"vector.reduce.or", .Xor => .@"vector.reduce.xor", else => unreachable, - }, &.{llvm_operand_ty}, &.{operand}, ""), + }, &.{try o.lowerType(operand_ty, .as_value)}, &.{operand}, ""), .Min, .Max => switch (scalar_ty.zigTypeTag(zcu)) { - .int => return self.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { + .int => return fg.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { .Min => if (scalar_ty.isSignedInt(zcu)) .@"vector.reduce.smin" else @@ -5946,29 +6162,29 @@ fn airReduce(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) A else .@"vector.reduce.umax", else => unreachable, - }, &.{llvm_operand_ty}, &.{operand}, ""), + }, &.{try o.lowerType(operand_ty, .as_value)}, &.{operand}, ""), .float => if (intrinsicsAllowed(scalar_ty, target)) - return self.wip.callIntrinsic(fast, .none, switch (reduce.operation) { + return fg.wip.callIntrinsic(fast, .none, switch (reduce.operation) { .Min => .@"vector.reduce.fmin", .Max => .@"vector.reduce.fmax", else => unreachable, - }, &.{llvm_operand_ty}, &.{operand}, ""), + }, &.{try o.lowerType(operand_ty, .as_value)}, &.{operand}, ""), else => unreachable, }, .Add, .Mul => switch (scalar_ty.zigTypeTag(zcu)) { - .int => return self.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { + .int => return fg.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { .Add => .@"vector.reduce.add", .Mul => .@"vector.reduce.mul", else => unreachable, - }, &.{llvm_operand_ty}, &.{operand}, ""), + }, &.{try o.lowerType(operand_ty, .as_value)}, &.{operand}, ""), .float => if (intrinsicsAllowed(scalar_ty, target)) - return self.wip.callIntrinsic(fast, .none, switch (reduce.operation) { + return fg.wip.callIntrinsic(fast, .none, switch (reduce.operation) { .Add => .@"vector.reduce.fadd", .Mul => .@"vector.reduce.fmul", else => unreachable, - }, &.{llvm_operand_ty}, &.{ switch (reduce.operation) { - .Add => try o.builder.fpValue(llvm_scalar_ty, -0.0), - .Mul => try o.builder.fpValue(llvm_scalar_ty, 1.0), + }, &.{try o.lowerType(operand_ty, .as_value)}, &.{ switch (reduce.operation) { + .Add => try o.builder.fpValue(try o.lowerType(scalar_ty, .as_value), -0.0), + .Mul => try o.builder.fpValue(try o.lowerType(scalar_ty, .as_value), 1.0), else => unreachable, }, operand }, ""), else => unreachable, @@ -5986,62 +6202,119 @@ fn airReduce(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) A libcFloatPrefix(float_bits), libcFloatSuffix(float_bits), }), .Add => try o.builder.strtabStringFmt("__add{s}f3", .{ - compilerRtFloatAbbrev(float_bits), + compilerRtFloatAbbrev(target, float_bits), }), .Mul => try o.builder.strtabStringFmt("__mul{s}f3", .{ - compilerRtFloatAbbrev(float_bits), + compilerRtFloatAbbrev(target, float_bits), }), else => unreachable, }; - - const libc_fn = try o.getLibcFunction(fn_name, &.{ llvm_scalar_ty, llvm_scalar_ty }, llvm_scalar_ty); - const init_val = switch (llvm_scalar_ty) { - .i16 => try o.builder.intValue(.i16, @as(i16, @bitCast( - @as(f16, switch (reduce.operation) { - .Min, .Max => std.math.nan(f16), - .Add => -0.0, - .Mul => 1.0, - else => unreachable, - }), - ))), - .i80 => try o.builder.intValue(.i80, @as(i80, @bitCast( - @as(f80, switch (reduce.operation) { - .Min, .Max => std.math.nan(f80), - .Add => -0.0, - .Mul => 1.0, - else => unreachable, - }), - ))), - .i128 => try o.builder.intValue(.i128, @as(i128, @bitCast( - @as(f128, switch (reduce.operation) { - .Min, .Max => std.math.nan(f128), - .Add => -0.0, - .Mul => 1.0, - else => unreachable, - }), - ))), + const fn_info: Object.FuncInfo = .{ + .cc = target.cCallingConvention().?, + .param_types = &.{ scalar_ty.toIntern(), scalar_ty.toIntern() }, + .return_type = scalar_ty.toIntern(), + }; + const llvm_fn = try fg.object.getLibcFunction(fg.pt, fn_name, fn_info); + const init = switch (float_bits) { else => unreachable, + 16 => try o.f16Const(switch (reduce.operation) { + else => unreachable, + .Min, .Max => std.math.nan(f16), + .Add => -0.0, + .Mul => 1.0, + }), + 32 => try o.f32Const(switch (reduce.operation) { + else => unreachable, + .Min, .Max => std.math.nan(f32), + .Add => -0.0, + .Mul => 1.0, + }), + 64 => try o.f64Const(switch (reduce.operation) { + else => unreachable, + .Min, .Max => std.math.nan(f64), + .Add => -0.0, + .Mul => 1.0, + }), + 80 => try o.f80Const(switch (reduce.operation) { + else => unreachable, + .Min, .Max => std.math.nan(f80), + .Add => -0.0, + .Mul => 1.0, + }), + 128 => try o.f128Const(switch (reduce.operation) { + else => unreachable, + .Min, .Max => std.math.nan(f128), + .Add => -0.0, + .Mul => 1.0, + }), }; - return self.buildReducedCall(libc_fn, operand, operand_ty.vectorLen(zcu), init_val); + const iterations = operand_ty.vectorLen(zcu); + const is_by_ref = isByRef(operand_ty, zcu); + if (iterations > 1 and is_by_ref) { + const init_ref = try o.lowerConstRef(init, scalar_ty.abiAlignment(zcu).toLlvm()); + + const entry_block = fg.wip.cursor.block; + const loop_block = try fg.wip.block(2, "reduce.loop"); + const done_block = try fg.wip.block(1, "reduce.loop"); + + _ = try fg.wip.br(loop_block); + + fg.wip.cursor = .{ .block = loop_block }; + const index = try fg.wip.phi(.i32, "reduce.index"); + const result = try fg.wip.phi(.ptr, "reduce.result"); + + const rhs_elem_ptr = try fg.ptraddScaled(operand, index.toValue(), scalar_ty.abiSize(zcu)); + const rhs_elem = try fg.load(rhs_elem_ptr, .none, scalar_ty, .normal); + const next_result = try fg.buildCall(.{}, llvm_fn.typeOf(&o.builder), llvm_fn.toValue(&o.builder), fn_info, fn_info.param_types, &.{ result.toValue(), rhs_elem }); + + const next_index = try fg.wip.bin(.@"add nuw", index.toValue(), try o.builder.intValue(.i32, 1), "reduce.next_index"); + index.finish(&.{ try o.builder.intValue(.i32, 0), next_index }, &.{ entry_block, loop_block }, &fg.wip); + result.finish(&.{ init_ref.toValue(), next_result }, &.{ entry_block, loop_block }, &fg.wip); + const is_done = try fg.wip.icmp(.eq, next_index, try o.builder.intValue(.i32, iterations), "reduce.is_done"); + _ = try fg.wip.brCond(is_done, done_block, loop_block, .none); + + fg.wip.cursor = .{ .block = done_block }; + return next_result; + } + var result = init.toValue(); + for (0..iterations) |index| { + const index_value = try o.builder.intValue(.i32, index); + const rhs_elem = if (is_by_ref) rhs_elem: { + const rhs_elem_ptr = try fg.ptraddConst(operand, index * scalar_ty.abiSize(zcu)); + break :rhs_elem try fg.load(rhs_elem_ptr, .none, scalar_ty, .normal); + } else try fg.wip.extractElement(operand, index_value, "reduce.rhs_elem"); + result = try fg.buildCall(.{}, llvm_fn.typeOf(&o.builder), llvm_fn.toValue(&o.builder), fn_info, fn_info.param_types, &.{ result, rhs_elem }); + } + return result; } -fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { - const o = self.object; +fn airAggregateInit(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { + const o = fg.object; const zcu = o.zcu; const ip = &zcu.intern_pool; - const ty_pl = self.air.instructions.items(.data)[@backingInt(inst)].ty_pl; - const result_ty = self.typeOfIndex(inst); + const ty_pl = fg.air.instructions.items(.data)[@backingInt(inst)].ty_pl; + const result_ty = fg.typeOfIndex(inst); const len: usize = @intCast(result_ty.arrayLen(zcu)); - const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[ty_pl.payload..][0..len]); + const elements: []const Air.Inst.Ref = @ptrCast(fg.air.extra.items[ty_pl.payload..][0..len]); switch (result_ty.zigTypeTag(zcu)) { - .vector => { + .vector => if (isByRef(result_ty, zcu)) { + const elem_ty = result_ty.childType(zcu); + const elem_size = elem_ty.abiSize(zcu); + const result_ptr = try fg.buildZigAlloca(result_ty, .none); + for (elements, 0..) |elem, elem_index| { + const elem_ptr = try fg.ptraddConst(result_ptr, elem_index * elem_size); + const llvm_elem = try fg.resolveInst(elem); + try fg.store(elem_ptr, .none, llvm_elem, elem_ty, .normal); + } + return result_ptr; + } else { const llvm_result_ty = try o.lowerType(result_ty, .as_value); var vector = try o.builder.poisonValue(llvm_result_ty); - for (elements, 0..) |elem, i| { - const index_u32 = try o.builder.intValue(.i32, i); - const llvm_elem = try self.resolveInst(elem); - vector = try self.wip.insertElement(vector, llvm_elem, index_u32, ""); + for (elements, 0..) |elem, elem_index| { + const elem_index_val = try o.builder.intValue(.i32, elem_index); + const llvm_elem = try fg.resolveInst(elem); + vector = try fg.wip.insertElement(vector, llvm_elem, elem_index_val, ""); } return vector; }, @@ -6057,18 +6330,18 @@ fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builde for (elements, struct_type.field_types.get(ip)) |elem, field_ty| { if (!Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; - const non_int_val = try self.resolveInst(elem); + const non_int_val = try fg.resolveInst(elem); const ty_bit_size: u16 = @intCast(Type.fromInterned(field_ty).bitSize(zcu)); const small_int_ty = try o.builder.intType(ty_bit_size); const small_int_val = if (Type.fromInterned(field_ty).isPtrAtRuntime(zcu)) - try self.wip.cast(.ptrtoint, non_int_val, small_int_ty, "") + try fg.wip.cast(.ptrtoint, non_int_val, small_int_ty, "") else - try self.wip.cast(.bitcast, non_int_val, small_int_ty, ""); + try fg.wip.cast(.bitcast, non_int_val, small_int_ty, ""); const shift_rhs = try o.builder.intValue(int_ty, running_bits); const extended_int_val = - try self.wip.conv(.unsigned, small_int_val, int_ty, ""); - const shifted = try self.wip.bin(.shl, extended_int_val, shift_rhs, ""); - running_int = try self.wip.bin(.@"or", running_int, shifted, ""); + try fg.wip.conv(.unsigned, small_int_val, int_ty, ""); + const shifted = try fg.wip.bin(.shl, extended_int_val, shift_rhs, ""); + running_int = try fg.wip.bin(.@"or", running_int, shifted, ""); running_bits += ty_bit_size; } return running_int; @@ -6078,19 +6351,19 @@ fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builde // TODO in debug builds init to undef so that the padding will be 0xaa // even if we fully populate the fields. const struct_align = result_ty.abiAlignment(zcu); - const alloca_inst = try self.buildZigAlloca(result_ty, .none); + const alloca_inst = try fg.buildZigAlloca(result_ty, .none); for (elements, 0..) |elem, field_index| { if (result_ty.structFieldIsComptime(field_index, zcu)) continue; const field_ty = result_ty.fieldType(field_index, zcu); if (!field_ty.hasRuntimeBits(zcu)) continue; const offset = result_ty.structFieldOffset(field_index, zcu); - const field_ptr = try self.ptraddConst(alloca_inst, offset); + const field_ptr = try fg.ptraddConst(alloca_inst, offset); const field_ptr_align = struct_align.offset(offset); - const llvm_field_val = try self.resolveInst(elem); + const llvm_field_val = try fg.resolveInst(elem); - try self.store(field_ptr, field_ptr_align, llvm_field_val, field_ty, .normal); + try fg.store(field_ptr, field_ptr_align, llvm_field_val, field_ty, .normal); } return alloca_inst; @@ -6099,21 +6372,21 @@ fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builde .array => { assert(isByRef(result_ty, zcu)); - const alloca_inst = try self.buildZigAlloca(result_ty, .none); + const alloca_inst = try fg.buildZigAlloca(result_ty, .none); const array_info = result_ty.arrayInfo(zcu); const elem_size = array_info.elem_type.abiSize(zcu); for (elements, 0..) |elem, i| { - const elem_ptr = try self.ptraddConst(alloca_inst, elem_size * i); - const llvm_elem = try self.resolveInst(elem); - try self.store(elem_ptr, .none, llvm_elem, array_info.elem_type, .normal); + const elem_ptr = try fg.ptraddConst(alloca_inst, elem_size * i); + const llvm_elem = try fg.resolveInst(elem); + try fg.store(elem_ptr, .none, llvm_elem, array_info.elem_type, .normal); } if (array_info.sentinel) |sent_val| { - const elem_ptr = try self.ptraddConst(alloca_inst, elem_size * array_info.len); - const llvm_elem = try self.resolveValue(sent_val); - try self.store(elem_ptr, .none, llvm_elem.toValue(), array_info.elem_type, .normal); + const elem_ptr = try fg.ptraddConst(alloca_inst, elem_size * array_info.len); + const llvm_elem = try fg.resolveValue(sent_val); + try fg.store(elem_ptr, .none, llvm_elem.toValue(), array_info.elem_type, .normal); } return alloca_inst; @@ -6469,26 +6742,12 @@ fn store( .unsigned => .zext, .signed => .sext, }, elem, llvm_memory_ty, ""); - _ = try fg.wip.storeAtomic( - access_kind, - extended, - ptr, - fg.sync_scope, - .none, - llvm_ptr_align, - ); + _ = try fg.wip.store(access_kind, extended, ptr, llvm_ptr_align); return; } // `elem_ty` is a simple by-val type which requires no special handling. - _ = try fg.wip.storeAtomic( - access_kind, - elem, - ptr, - fg.sync_scope, - .none, - llvm_ptr_align, - ); + _ = try fg.wip.store(access_kind, elem, ptr, llvm_ptr_align); } fn valgrindMarkUndef(fg: *FuncGen, ptr: Builder.Value, len: Builder.Value) Allocator.Error!void { @@ -6650,7 +6909,8 @@ fn typeOfIndex(fg: *FuncGen, inst: Air.Inst.Index) Type { const ParamTypeIterator = struct { object: *Object, - fn_info: InternPool.Key.FuncType, + cc: std.lang.CallingConvention, + param_types: []const InternPool.Index, zig_index: u32, llvm_index: u32, types_len: u32, @@ -6672,63 +6932,66 @@ const ParamTypeIterator = struct { }; pub fn next(it: *ParamTypeIterator) Allocator.Error!?Lowering { - if (it.zig_index >= it.fn_info.param_types.len) return null; - const ip = &it.object.zcu.intern_pool; - const ty = it.fn_info.param_types.get(ip)[it.zig_index]; + if (it.zig_index >= it.param_types.len) return null; + const ty = it.param_types[it.zig_index]; it.byval_attr = false; return nextInner(it, Type.fromInterned(ty)); } /// `airCall` uses this instead of `next` so that it can take into account variadic functions. - fn nextCall(it: *ParamTypeIterator, fg: *FuncGen, args: []const Air.Inst.Ref) Allocator.Error!?Lowering { - const ip = &it.object.zcu.intern_pool; - if (it.zig_index >= it.fn_info.param_types.len) { - if (it.zig_index >= args.len) { + fn nextCall(it: *ParamTypeIterator, arg_types: []const InternPool.Index) Allocator.Error!?Lowering { + if (it.zig_index >= it.param_types.len) { + if (it.zig_index >= arg_types.len) { return null; } else { - return nextInner(it, fg.typeOf(args[it.zig_index])); + return nextInner(it, .fromInterned(arg_types[it.zig_index])); } } else { - return nextInner(it, Type.fromInterned(it.fn_info.param_types.get(ip)[it.zig_index])); + return nextInner(it, .fromInterned(it.param_types[it.zig_index])); } } fn nextInner(it: *ParamTypeIterator, ty: Type) Allocator.Error!?Lowering { const zcu = it.object.zcu; - const target = zcu.getTarget(); - + ty.assertHasLayout(zcu); if (!ty.hasRuntimeBits(zcu)) { it.zig_index += 1; return .no_bits; } - switch (it.fn_info.cc) { + switch (it.cc) { .@"inline" => unreachable, .auto => { it.zig_index += 1; it.llvm_index += 1; + + // Match the c calling convention in some cases to avoid llvm bugs. + const target = zcu.getTarget(); + if (target.cpu.arch == .x86_64 and ty.isVector(zcu) and ty.childType(zcu).toIntern() == .bool_type) return switch (ty.vectorLen(zcu)) { + 0 => .no_bits, + 1...32 => .abi_sized_int, + 33...64 => { + it.types_buffer[0..1].* = .{.double}; + it.offsets_buffer[0..2].* = .{ 0, 8 }; + it.types_len = 1; + return .multiple_llvm_types; + }, + else => .byval, + }; + if (ty.isSlice(zcu) or (ty.zigTypeTag(zcu) == .optional and ty.optionalChild(zcu).isSlice(zcu) and !ty.ptrAllowsZero(zcu))) { it.llvm_index += 1; return .slice; - } else if (isByRef(ty, zcu)) { - return .byref; - } else if (target.cpu.arch.isX86() and - !target.cpu.has(.x86, .avx512f) 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 'avx512f' for AVX512" - return .byref; - } else { - return .byval; } + if (isByRef(ty, zcu)) return .byref; + return .byval; }, .async => { @panic("TODO implement async function lowering in the LLVM backend"); }, - .x86_64_sysv, .x86_64_x32 => return it.nextSystemV(ty), - .x86_64_win => return it.nextWin64(ty), + .x86_64_sysv, .x86_64_x32 => return try it.next_x86_64_sysv(ty), + .x86_64_win => return it.next_x86_64_win(ty), .x86_stdcall => { it.zig_index += 1; it.llvm_index += 1; @@ -6748,9 +7011,9 @@ const ParamTypeIterator = struct { .float_array => |len| return Lowering{ .float_array = len }, .byval => return .byval, .integer => { - it.types_len = 1; it.types_buffer[0..1].* = .{.i64}; it.offsets_buffer[0..2].* = .{ 0, 8 }; + it.types_len = 1; return .multiple_llvm_types; }, .double_integer => return Lowering{ .i64_array = 2 }, @@ -6857,7 +7120,7 @@ const ParamTypeIterator = struct { } } - fn nextWin64(it: *ParamTypeIterator, ty: Type) ?Lowering { + fn next_x86_64_win(it: *ParamTypeIterator, ty: Type) Lowering { const zcu = it.object.zcu; switch (x86_64_abi.classifyWindows(ty, zcu, zcu.getTarget(), .arg)) { .integer => { @@ -6898,113 +7161,108 @@ const ParamTypeIterator = struct { } } - fn nextSystemV(it: *ParamTypeIterator, ty: Type) Allocator.Error!?Lowering { - const zcu = it.object.zcu; - const ip = &zcu.intern_pool; - ty.assertHasLayout(zcu); - const classes = x86_64_abi.classifySystemV(ty, zcu, zcu.getTarget(), .arg); - if (classes[0] == .memory) { - it.zig_index += 1; - it.llvm_index += 1; - it.byval_attr = true; - return .byref; - } - if (isScalar(zcu, ty)) { - it.zig_index += 1; - it.llvm_index += 1; - return .byval; - } - var types_index: u32 = 0; - var offset: u64 = 0; - for (classes) |class| { - switch (class) { - .integer => { - it.types_buffer[types_index] = .i64; - it.offsets_buffer[types_index] = offset; - types_index += 1; - }, - .sse => { - it.types_buffer[types_index] = .double; - it.offsets_buffer[types_index] = offset; - types_index += 1; - }, - .sseup => { - if (it.types_buffer[types_index - 1] == .double) { - it.types_buffer[types_index - 1] = .fp128; - } else { - it.types_buffer[types_index] = .double; - it.offsets_buffer[types_index] = offset; - types_index += 1; + fn next_x86_64_sysv(it: *ParamTypeIterator, ty: Type) Allocator.Error!Lowering { + const o = it.object; + const zcu = o.zcu; + const target = zcu.getTarget(); + const classes = x86_64_abi.classifySystemV(ty, zcu, target, .arg); + var types_len: u32 = 0; + const classes_len = for (classes, 0..) |class, class_index| switch (class) { + .integer => { + it.types_buffer[types_len] = try o.builder.intType(@min(8 * ty.abiSize(zcu) - 64 * class_index, 64)); + it.offsets_buffer[types_len] = 8 * class_index; + types_len += 1; + }, + .sse => { + it.types_buffer[types_len] = .double; + it.offsets_buffer[types_len] = 8 * class_index; + types_len += 1; + }, + .sseup => { + if (it.types_buffer[types_len - 1] == .double) { + if (ty.isVector(zcu)) { + it.zig_index += 1; + it.llvm_index += 1; + return .byval; } - }, - .float => { - it.types_buffer[types_index] = .float; - it.offsets_buffer[types_index] = offset; - types_index += 1; - }, - .float_combine => { - it.types_buffer[types_index] = try it.object.builder.vectorType(.normal, 2, .float); - it.offsets_buffer[types_index] = offset; - types_index += 1; - }, - .x87 => { - it.zig_index += 1; - it.llvm_index += 1; - it.byval_attr = true; - return .byref; - }, - .x87up => unreachable, - .none => break, - .memory => unreachable, // handled above - .win_i128 => unreachable, // windows only - .bool_vector_mask, - .integer_per_element, - .sse_per_element, - .sse_sse_x87_per_qword, - .sse_per_xword, - .sse_per_yword, - .sse_per_zword, - => unreachable, // vectors already handled by `isScalar` above - } - offset += 8; - } - const first_non_integer = std.mem.indexOfNone(x86_64_abi.Class, &classes, &.{.integer}); - if (first_non_integer == null or classes[first_non_integer.?] == .none) { - assert(first_non_integer orelse classes.len == types_index); - if (types_index == 1) { + it.types_buffer[types_len - 1] = .fp128; + } else { + it.types_buffer[types_len] = .double; + it.offsets_buffer[types_len] = 8 * class_index; + types_len += 1; + } + }, + .float => { + it.types_buffer[types_len] = .float; + it.offsets_buffer[types_len] = 8 * class_index; + types_len += 1; + }, + .float_combine => { + it.types_buffer[types_len] = try it.object.builder.vectorType(.normal, 2, .float); + it.offsets_buffer[types_len] = 8 * class_index; + types_len += 1; + }, + .x87 => { it.zig_index += 1; it.llvm_index += 1; - return .abi_sized_int; - } - if (it.llvm_index + types_index > 6) { + it.byval_attr = true; + return .byref; + }, + .x87up => unreachable, + .none => break class_index, + .memory => { + it.zig_index += 1; + it.llvm_index += 1; + it.byval_attr = true; + return .byref; + }, + .win_i128 => unreachable, // windows only + .bool_vector_mask, + .integer_per_element, + .sse_per_element, + .sse_sse_x87_per_qword, + .sse_per_xword, + .sse_per_yword, + .sse_per_zword, + => { + it.zig_index += 1; + it.llvm_index += 1; + return .byval; + }, + } else classes.len; + if (types_len > 1) { + if (it.llvm_index + classes_len > 6) { it.zig_index += 1; it.llvm_index += 1; it.byval_attr = true; return .byref; } - switch (ip.indexToKey(ty.toIntern())) { - .struct_type => { - const size = ty.abiSize(zcu); - assert(@divCeil(size, 8) == types_index); - if (size % 8 > 0) { - it.types_buffer[types_index - 1] = - try it.object.builder.intType(@intCast(size % 8 * 8)); - } - }, - else => {}, + } else if (!isByRef(ty, zcu)) { + const llvm_ty = try o.lowerType(ty, .as_value); + if (it.types_buffer[0] == llvm_ty or + (it.types_buffer[0] == .i64 and llvm_ty.isPointer(&o.builder))) + { + it.zig_index += 1; + it.llvm_index += 1; + return .byval; } } - it.offsets_buffer[types_index] = offset; - it.types_len = types_index; - it.llvm_index += types_index; + it.offsets_buffer[types_len] = 8 * classes_len; + it.types_len = types_len; + it.llvm_index += types_len; it.zig_index += 1; return .multiple_llvm_types; } }; -pub fn iterateParamTypes(object: *Object, fn_info: InternPool.Key.FuncType) ParamTypeIterator { +pub fn iterateParamTypes( + object: *Object, + cc: std.lang.CallingConvention, + param_types: []const InternPool.Index, +) ParamTypeIterator { return .{ .object = object, - .fn_info = fn_info, + .cc = cc, + .param_types = param_types, .zig_index = 0, .llvm_index = 0, .types_len = undefined, @@ -7035,35 +7293,34 @@ pub const FnReturnStrat = union(enum) { /// In order to support the C calling convention, some return types need to be lowered /// completely differently in the function prototype to honor the C ABI, and then /// be effectively bitcasted to the actual return type. -pub fn fnReturnStrat(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!FnReturnStrat { +pub fn fnReturnStrat(o: *Object, cc: std.lang.CallingConvention, ret_ty: Type) Allocator.Error!FnReturnStrat { const zcu = o.zcu; - const ret_ty: Type = .fromInterned(fn_info.return_type); ret_ty.assertHasLayout(zcu); if (!ret_ty.hasRuntimeBits(zcu)) return .void; - switch (fn_info.cc) { + switch (cc) { .@"inline" => unreachable, .auto => { + // Match the c calling convention in some cases to avoid llvm bugs. + const target = zcu.getTarget(); + if (target.cpu.arch == .x86_64 and ret_ty.isVector(zcu) and ret_ty.childType(zcu).toIntern() == .bool_type) return switch (ret_ty.vectorLen(zcu)) { + 0 => .void, + 1...8 => .{ .mem_cast = .i8 }, + 9...16 => .{ .mem_cast = .i16 }, + 17...32 => .{ .mem_cast = .i32 }, + 33...64 => .{ .mem_cast = .double }, + else => .by_val, + }; + if (isByRef(ret_ty, zcu)) return .sret; - - const target = zcu.getTarget(); - if (target.cpu.arch.isX86() and - !target.cpu.has(.x86, .avx512f) and - ret_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 'avx512f' for AVX512" - return .sret; - } - return .by_val; }, - .x86_64_sysv, .x86_64_x32 => return lowerSystemVFnRetTy(o, fn_info), - .x86_64_win => return lowerWin64FnRetTy(o, fn_info), + .x86_64_sysv, .x86_64_x32 => return fnReturnStrat_x86_64_sysv(o, ret_ty), + .x86_64_win => return fnReturnStrat_x86_64_win(o, ret_ty), .x86_stdcall => if (isScalar(zcu, ret_ty)) { assert(!isByRef(ret_ty, zcu)); return .by_val; } else return .sret, - .x86_fastcall => return lowerX86FastcallFnRetTy(o, zcu, ret_ty), + .x86_fastcall => return fnReturnStrat_x86_fastcall(o, zcu, ret_ty), .x86_sysv, .x86_win => return if (isByRef(ret_ty, zcu)) .sret else .by_val, .aarch64_aapcs, .aarch64_aapcs_darwin, .aarch64_aapcs_win => switch (aarch64_c_abi.classifyType(ret_ty, zcu)) { .memory => return .sret, @@ -7124,7 +7381,7 @@ pub fn fnReturnStrat(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Err } } -fn lowerX86FastcallFnRetTy(o: *Object, zcu: *Zcu, ty: Type) Allocator.Error!FnReturnStrat { +fn fnReturnStrat_x86_fastcall(o: *Object, zcu: *Zcu, ty: Type) Allocator.Error!FnReturnStrat { if (isScalar(zcu, ty)) { assert(!isByRef(ty, zcu)); return .by_val; @@ -7139,9 +7396,8 @@ fn lowerX86FastcallFnRetTy(o: *Object, zcu: *Zcu, ty: Type) Allocator.Error!FnRe return .sret; } -fn lowerWin64FnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!FnReturnStrat { +fn fnReturnStrat_x86_64_win(o: *Object, ret_ty: Type) Allocator.Error!FnReturnStrat { const zcu = o.zcu; - const ret_ty = Type.fromInterned(fn_info.return_type); switch (x86_64_abi.classifyWindows(ret_ty, zcu, zcu.getTarget(), .ret)) { .integer => if (isScalar(zcu, ret_ty)) { assert(!isByRef(ret_ty, zcu)); @@ -7174,78 +7430,65 @@ fn lowerWin64FnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Err } } -fn lowerSystemVFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!FnReturnStrat { +fn fnReturnStrat_x86_64_sysv(o: *Object, ret_ty: Type) Allocator.Error!FnReturnStrat { const zcu = o.zcu; - const ip = &zcu.intern_pool; - const ret_ty = Type.fromInterned(fn_info.return_type); - if (isScalar(zcu, ret_ty)) { - assert(!isByRef(ret_ty, zcu)); - return .by_val; - } const classes = x86_64_abi.classifySystemV(ret_ty, zcu, zcu.getTarget(), .ret); - var types_index: u32 = 0; var types_buffer: [8]Builder.Type = undefined; - for (classes) |class| { - switch (class) { - .integer => { - types_buffer[types_index] = .i64; - types_index += 1; - }, - .sse => { - types_buffer[types_index] = .double; - types_index += 1; - }, - .sseup => { - if (types_buffer[types_index - 1] == .double) { - types_buffer[types_index - 1] = .fp128; - } else { - types_buffer[types_index] = .double; - types_index += 1; - } - }, - .float => { - types_buffer[types_index] = .float; - types_index += 1; - }, - .float_combine => { - types_buffer[types_index] = try o.builder.vectorType(.normal, 2, .float); - types_index += 1; - }, - .x87 => { - if (types_index != 0 or classes[2] != .none) return .sret; - types_buffer[types_index] = .x86_fp80; - types_index += 1; - }, - .x87up => continue, - .none => break, - .memory => return .sret, - .win_i128 => unreachable, // windows only - .bool_vector_mask, - .integer_per_element, - .sse_per_element, - .sse_sse_x87_per_qword, - .sse_per_xword, - .sse_per_yword, - .sse_per_zword, - => unreachable, // vectors already handled by `isScalar` above - } + var types_len: u32 = 0; + for (classes, 0..) |class, class_index| switch (class) { + .integer => { + types_buffer[types_len] = try o.builder.intType(@min(8 * ret_ty.abiSize(zcu) - 64 * class_index, 64)); + types_len += 1; + }, + .sse => { + types_buffer[types_len] = .double; + types_len += 1; + }, + .sseup => { + if (types_buffer[types_len - 1] == .double) { + if (ret_ty.isVector(zcu)) return .by_val; + types_buffer[types_len - 1] = .fp128; + } else { + types_buffer[types_len] = .double; + types_len += 1; + } + }, + .float => { + types_buffer[types_len] = .float; + types_len += 1; + }, + .float_combine => { + types_buffer[types_len] = try o.builder.vectorType(.normal, 2, .float); + types_len += 1; + }, + .x87 => { + if (types_len > 0 or classes[2] != .none) return .sret; + types_buffer[types_len] = .x86_fp80; + types_len += 1; + }, + .x87up => continue, + .none => break, + .memory => return if (ret_ty.isVector(zcu)) .by_val else .sret, + .win_i128 => unreachable, // windows only + .bool_vector_mask, + .integer_per_element, + .sse_per_element, + .sse_sse_x87_per_qword, + .sse_per_xword, + .sse_per_yword, + .sse_per_zword, + => return .by_val, + }; + if (types_len > 1) return .{ .mem_cast = try o.builder.structType(.normal, types_buffer[0..types_len]) }; + if (!isByRef(ret_ty, zcu)) { + const llvm_ty = try o.lowerType(ret_ty, .as_value); + if (types_buffer[0] == llvm_ty) return .by_val; + if (types_buffer[0] == .i64 and llvm_ty.isPointer(&o.builder)) return .by_val; + if (types_buffer[0] == .double and llvm_ty.isVector(&o.builder) and + llvm_ty.vectorLen(&o.builder) == 1 and + llvm_ty.scalarType(&o.builder) == .double) return .by_val; } - const first_non_integer = std.mem.indexOfNone(x86_64_abi.Class, &classes, &.{.integer}); - if (first_non_integer == null or classes[first_non_integer.?] == .none) { - assert(first_non_integer orelse classes.len == types_index); - switch (ip.indexToKey(ret_ty.toIntern())) { - .struct_type => { - const size = ret_ty.abiSize(zcu); - assert(@divCeil(size, 8) == types_index); - if (size % 8 > 0) { - types_buffer[types_index - 1] = try o.builder.intType(@intCast(size % 8 * 8)); - } - }, - else => {}, - } - if (types_index == 1) return .{ .mem_cast = types_buffer[0] }; - } - return .{ .mem_cast = try o.builder.structType(.normal, types_buffer[0..types_index]) }; + return .{ .mem_cast = types_buffer[0] }; } /// This function deliberately does not handle `_BitInt` because it typically @@ -7258,15 +7501,22 @@ pub fn ccAbiPromoteInt(cc: std.lang.CallingConvention, zcu: *Zcu, ty: Type) ?std else => {}, } - const ty_tag = ty.zigTypeTag(zcu); - const int_info = switch (ty_tag) { - .bool => Type.u1.intInfo(zcu), - else => if (ty.isAbiInt(zcu)) ty.intInfo(zcu) else return null, - }; - - assert(int_info.bits == 0 or (int_info.bits == 1 and ty_tag == .bool) or std.math.isPowerOfTwo(int_info.bits)); - const target = zcu.getTarget(); + const int_info: std.lang.Type.Int = if (ty.toIntern() == .bool_type) + .{ .signedness = .unsigned, .bits = 1 } + else if (ty.isAbiInt(zcu)) + ty.intInfo(zcu) + else if (ty.isRuntimeFloat()) switch (ty.floatBits(target)) { + else => unreachable, + 16, 32, 64 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { + .hard => return null, + .soft => .{ .signedness = .unsigned, .bits = bits }, + }, + 80, 128 => return null, + } else return null; + + assert(int_info.bits == 0 or (int_info.bits == 1 and ty.toIntern() == .bool_type) or std.math.isPowerOfTwo(int_info.bits)); + return switch (target.cpu.arch) { .aarch64, .aarch64_be, @@ -7362,15 +7612,26 @@ pub fn isByRef(ty: Type, zcu: *const Zcu) bool { .void, .bool, .int, - .float, .pointer, .error_set, .@"fn", .@"enum", - .vector, .@"anyframe", => false, + .float, .vector => { + const target = zcu.getTarget(); + const scalar_ty = ty.scalarType(zcu); + return if (scalar_ty.isRuntimeFloat()) switch (scalar_ty.floatBits(target)) { + else => unreachable, + 16, 32, 64 => false, + 80, 128 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { + .hard => false, + .soft => true, + }, + } else false; + }, + .array, .frame, => ty.hasRuntimeBits(zcu), @@ -7431,12 +7692,19 @@ fn ptraddScaled(fg: *FuncGen, ptr: Builder.Value, index: Builder.Value, scale: u return fg.wip.gep(.inbounds, llvm_scale_ty, ptr, &.{index}, ""); } -fn compilerRtIntBits(bits: u16) ?u16 { - inline for (.{ 32, 64, 128 }) |b| { - if (bits <= b) { - return b; - } - } +fn compilerRtPromoteInt(int_info: InternPool.Key.IntType) ?Type { + if (int_info.bits <= 32) return switch (int_info.signedness) { + .signed => .i32, + .unsigned => .u32, + }; + if (int_info.bits <= 64) return switch (int_info.signedness) { + .signed => .i64, + .unsigned => .u64, + }; + if (int_info.bits <= 128) return switch (int_info.signedness) { + .signed => .i128, + .unsigned => .u128, + }; return null; } @@ -7495,13 +7763,12 @@ fn appendConstraints( } /// LLVM does not support all relevant intrinsics for all targets, so we -/// may need to manually generate a compiler-rt call. +/// may need to manually generate a compiler-rt call using a soft type. fn intrinsicsAllowed(scalar_ty: Type, target: *const std.Target) bool { - return switch (scalar_ty.toIntern()) { - .f16_type => llvm.backendSupportsF16(target), - .f80_type => (target.cTypeBitSize(.longdouble) == 80) and llvm.backendSupportsF80(target), - .f128_type => (target.cTypeBitSize(.longdouble) == 128) and llvm.backendSupportsF128(target), - else => true, + if (!scalar_ty.isRuntimeFloat()) return true; + return switch (std.zig.target.compilerRtFloatAbi(target, scalar_ty.floatBits(target))) { + .hard => true, + .soft => false, }; } diff --git a/src/codegen/mips/abi.zig b/src/codegen/mips/abi.zig index f512f1e6db98031dd581bc9cb19ef7be42b7ae29..e7c07582030cc16d73f07b0d56d56f09f0607a0f 100644 --- a/src/codegen/mips/abi.zig +++ b/src/codegen/mips/abi.zig @@ -38,7 +38,14 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { return .byval; }, .bool => return .byval, - .float => return .byval, + .float => return switch (ty.floatBits(target)) { + else => unreachable, + 16, 32, 64 => .byval, + 80, 128 => switch (max_direct_size) { + else => unreachable, + 64 => .memory, + }, + }, .int, .@"enum", .error_set => { return .byval; }, diff --git a/src/codegen/riscv64/CodeGen.zig b/src/codegen/riscv64/CodeGen.zig index 4ee131d079c666a52516e480137782e44f45a553..aed925be2953ddfb56581b9bb5eef521e8b78507 100644 --- a/src/codegen/riscv64/CodeGen.zig +++ b/src/codegen/riscv64/CodeGen.zig @@ -5036,7 +5036,7 @@ fn airRet(func: *Func, inst: Air.Inst.Index, safety: bool) !void { .register_pair, => { if (ret_ty.isVector(zcu)) { - const bit_size = ret_ty.totalVectorBits(zcu); + const bit_size = ret_ty.bitSize(zcu); // set the vtype to hold the entire vector's contents in a single element try func.setVl(.zero, 0, .{ @@ -6871,7 +6871,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! // size to the total size of the vector, and vmv.x.s will work then if (src_reg.class() == .vector) { try func.setVl(.zero, 0, .{ - .vsew = switch (ty.totalVectorBits(zcu)) { + .vsew = switch (ty.bitSize(zcu)) { 8 => .@"8", 16 => .@"16", 32 => .@"32", diff --git a/src/codegen/riscv64/abi.zig b/src/codegen/riscv64/abi.zig index 5c89a35f7bd4e718e8e032093856f6068b751c9b..154118c50c98572fea63fcc00986c7cc338b3ca6 100644 --- a/src/codegen/riscv64/abi.zig +++ b/src/codegen/riscv64/abi.zig @@ -56,12 +56,20 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class { return .integer; }, .bool => return .integer, - .float => return .byval, .int, .@"enum", .error_set => { const bit_size = ty.bitSize(zcu); if (bit_size > max_byval_size) return .memory; return .byval; }, + .float => return switch (ty.floatBits(target)) { + else => unreachable, + 16, 32, 64, 128 => .byval, + 80 => switch (max_byval_size) { + else => unreachable, + 64 => .memory, + 128 => .double_integer, + }, + }, .vector => { const bit_size = ty.bitSize(zcu); if (bit_size > max_byval_size) return .memory; @@ -190,7 +198,7 @@ pub fn classifySystem(ty: Type, zcu: *Zcu) [8]SystemClass { }, .vector => { // we pass vectors through integer registers if they are small enough to fit. - const vec_bits = ty.totalVectorBits(zcu); + const vec_bits = ty.bitSize(zcu); if (vec_bits <= 64) { result[0] = .integer; return result; diff --git a/src/codegen/s390x/abi.zig b/src/codegen/s390x/abi.zig index 6fb81d3e6b8792566b9c69156ca63a67ed788299..7b35245fdad37be5e062bbcc873c116a78a99152 100644 --- a/src/codegen/s390x/abi.zig +++ b/src/codegen/s390x/abi.zig @@ -38,9 +38,11 @@ pub fn classifyType(ty: Type, context: Context, zcu: *Zcu) Class { 1...64 => .simple, else => .pointer, }, - .float => return switch (ty.floatBits(zcu.getTarget())) { - 16, 32, 64 => .double_or_float, - else => .pointer, + .float => switch (ty.floatBits(zcu.getTarget())) { + else => unreachable, + 16, 32, 64 => return .double_or_float, + 80 => {}, + 128 => return .pointer, }, .pointer, .optional => return .simple, .array => switch (ty.arrayLen(zcu)) { diff --git a/src/codegen/wasm/CodeGen.zig b/src/codegen/wasm/CodeGen.zig index 3dc36619686e53a6112e3cc81ec9ccd3f902e0c6..2848a5f2573c75c73036aa2ff19f583403a9085f 100644 --- a/src/codegen/wasm/CodeGen.zig +++ b/src/codegen/wasm/CodeGen.zig @@ -24,12 +24,6 @@ const Alignment = InternPool.Alignment; const errUnionPayloadOffset = codegen.errUnionPayloadOffset; const errUnionErrorOffset = codegen.errUnionErrorOffset; -const target_util = @import("../../target.zig"); -const libcFloatPrefix = target_util.libcFloatPrefix; -const libcFloatSuffix = target_util.libcFloatSuffix; -const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev; -const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; - pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { return comptime &.initMany(&.{ .expand_bit_cast_safe, @@ -2515,15 +2509,15 @@ const IntType = struct { .anyerror, .adhoc_inferred_error_set => .{ .is_signed = false, .bits = zcu.errorSetBits() }, .isize => .{ .is_signed = true, .bits = cg.target.ptrBitWidth() }, .usize => .{ .is_signed = false, .bits = cg.target.ptrBitWidth() }, - .c_char => .{ .is_signed = cg.target.cCharSignedness() == .signed, .bits = cg.target.cTypeBitSize(.char) }, - .c_short => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.short) }, - .c_ushort => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.short) }, - .c_int => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.int) }, - .c_uint => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.int) }, - .c_long => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.long) }, - .c_ulong => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.long) }, - .c_longlong => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.longlong) }, - .c_ulonglong => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.longlong) }, + .c_char => .{ .is_signed = cg.target.cCharSignedness().? == .signed, .bits = cg.target.cTypeBitSize(.char).? }, + .c_short => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.short).? }, + .c_ushort => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.short).? }, + .c_int => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.int).? }, + .c_uint => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.int).? }, + .c_long => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.long).? }, + .c_ulong => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.long).? }, + .c_longlong => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.longlong).? }, + .c_ulonglong => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.longlong).? }, .f16, .f32, .f64, .f80, .f128, .c_longdouble => unreachable, .anyopaque, .void, .type, .comptime_int, .comptime_float, .noreturn, .null, .undefined, .enum_literal, .generic_poison => unreachable, }, diff --git a/src/codegen/wasm/abi.zig b/src/codegen/wasm/abi.zig index 7a643e8dc7a755937f096f9826e5d770c2394b42..244b2c7719476ffa1542ce5863c11ac62c3c36de 100644 --- a/src/codegen/wasm/abi.zig +++ b/src/codegen/wasm/abi.zig @@ -25,7 +25,11 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class { assert(ty.hasRuntimeBits(zcu)); switch (ty.zigTypeTag(zcu)) { .int, .@"enum", .error_set => return .{ .direct = ty }, - .float => return .{ .direct = ty }, + .float => return switch (ty.floatBits(zcu.getTarget())) { + else => unreachable, + 16, 32, 64, 128 => .{ .direct = ty }, + 80 => .indirect, + }, .bool => return .{ .direct = ty }, .vector => return .{ .direct = ty }, .array => return .indirect, diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig index da82d324608abdc1a1dabc4c691c41b4f9c470d5..662fc600b1e863b89bdfc0f8719545786ac64dac 100644 --- a/src/codegen/x86_64/CodeGen.zig +++ b/src/codegen/x86_64/CodeGen.zig @@ -182636,15 +182636,15 @@ fn intInfo(cg: *CodeGen, ty: Type) ?std.lang.Type.Int { .anyerror => .{ .signedness = .unsigned, .bits = zcu.errorSetBits() }, .isize => .{ .signedness = .signed, .bits = cg.target.ptrBitWidth() }, .usize => .{ .signedness = .unsigned, .bits = cg.target.ptrBitWidth() }, - .c_char => .{ .signedness = cg.target.cCharSignedness(), .bits = cg.target.cTypeBitSize(.char) }, - .c_short => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.short) }, - .c_ushort => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.short) }, - .c_int => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.int) }, - .c_uint => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.int) }, - .c_long => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.long) }, - .c_ulong => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.long) }, - .c_longlong => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.longlong) }, - .c_ulonglong => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.longlong) }, + .c_char => .{ .signedness = cg.target.cCharSignedness().?, .bits = cg.target.cTypeBitSize(.char).? }, + .c_short => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.short).? }, + .c_ushort => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.short).? }, + .c_int => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.int).? }, + .c_uint => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.int).? }, + .c_long => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.long).? }, + .c_ulong => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.long).? }, + .c_longlong => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.longlong).? }, + .c_ulonglong => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.longlong).? }, .f16, .f32, .f64, .f80, .f128, .c_longdouble => null, .anyopaque, .void, diff --git a/src/codegen/x86_64/abi.zig b/src/codegen/x86_64/abi.zig index 3e7a9a548d54baee16d64a54de9a42c4650f7ea5..0bff5bd60a2f7e918dda9fd5835d653378686cff 100644 --- a/src/codegen/x86_64/abi.zig +++ b/src/codegen/x86_64/abi.zig @@ -133,7 +133,7 @@ pub fn classifyWindows(init_ty: Type, zcu: *Zcu, target: *const std.Target, ctx: .float => switch (ty.floatBits(target)) { 16, 32, 64 => .sse, 80 => .memory, - 128 => if (ctx == .arg) .memory else .sse, + 128 => .win_i128, else => unreachable, }, .vector => { @@ -238,16 +238,18 @@ pub fn classifySystemV(ty: Type, zcu: *Zcu, target: *const std.Target, ctx: Cont }; const unaligned_size = elem_ty.abiSize(zcu) * len; if (unaligned_size <= 4) return Class.one_integer; - if (ctx == .arg and unaligned_size == 8 * 1 * 1 and len == 1 and - elem_ty.isRuntimeFloat()) return Class.stack; // what + if (unaligned_size == 8 * 1 * 1 and len == 1) { + if (ctx == .arg and elem_ty.isRuntimeFloat()) return Class.stack; // what? + if (ctx != .other and !elem_ty.isRuntimeFloat() and target.os.tag == .freebsd) return Class.one_integer; // who? + } if (unaligned_size <= 8 * 1) return .{ .sse, .none, .none, .none, .none, .none, .none, .none }; if (unaligned_size <= 8 * 2) return .{ .sse, .sseup, .none, .none, .none, .none, .none, .none }; if (!target.cpu.has(.x86, .avx)) { if (ctx == .ret) switch (unaligned_size) { else => {}, 8 * 3 => if (len == 3) return if (elem_ty.isRuntimeFloat()) .{ - .sse_sse_x87_per_qword, .none, .none, .none, .none, .none, .none, .none, // how - } else Class.len_integers, // why + .sse_sse_x87_per_qword, .none, .none, .none, .none, .none, .none, .none, // how? + } else Class.len_integers, // why? 8 * 2 * 2, 8 * 2 * 4 => return .{ .sse_per_xword, .none, .none, .none, .none, .none, .none, .none }, }; return Class.stack; diff --git a/src/libs/mingw/Preprocessor.zig b/src/libs/mingw/Preprocessor.zig index dcf8e01d043dbc6790aeff839718fd65b5e621cd..9098b6013a4179ba15de85707d70d290f09bd116 100644 --- a/src/libs/mingw/Preprocessor.zig +++ b/src/libs/mingw/Preprocessor.zig @@ -91,9 +91,9 @@ fn addTokenAssumeCapacity(pp: *Preprocessor, tok: Token) void { fn defineBuiltins(pp: *Preprocessor) !void { var buf: [5]u8 = undefined; - var val = std.fmt.bufPrint(&buf, "{d}", .{pp.target.cTypeBitSize(.longdouble)}) catch unreachable; + var val = std.fmt.bufPrint(&buf, "{d}", .{pp.target.cTypeByteSize(.longdouble).?}) catch unreachable; try pp.defineBuiltinValue("__SIZEOF_LONG_DOUBLE__", val, .pp_num); - val = std.fmt.bufPrint(&buf, "{d}", .{pp.target.cTypeBitSize(.double)}) catch unreachable; + val = std.fmt.bufPrint(&buf, "{d}", .{pp.target.cTypeByteSize(.double).?}) catch unreachable; try pp.defineBuiltinValue("__SIZEOF_DOUBLE__", val, .pp_num); if (pp.target.abi.isGnu()) { diff --git a/src/target.zig b/src/target.zig index e249be39947d8d99f6f9eef6bd15b271887625fe..b4dc16b3185bb4e60ec99d93e2a39595f815eeed 100644 --- a/src/target.zig +++ b/src/target.zig @@ -881,13 +881,13 @@ pub fn libcFloatSuffix(float_bits: u16) []const u8 { }; } -pub fn compilerRtFloatAbbrev(float_bits: u16) []const u8 { +pub fn compilerRtFloatAbbrev(target: *const std.Target, float_bits: u16) []const u8 { return switch (float_bits) { 16 => "h", 32 => "s", 64 => "d", 80 => "x", - 128 => "t", + 128 => if (target.cpu.arch.isPowerPC()) "k" else "t", else => unreachable, }; } diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 957ac6b1796c3ce962c139fc3669c4760c8ede87..d79d2d07ba75c4660478753dc4d9a23d48fdfe2c 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -129,8 +129,7 @@ test "alignment and size of structs with 128-bit fields" { y: u8, }; const expected = switch (builtin.cpu.arch) { - .s390x, - => .{ + .s390x => .{ .a_align = 8, .a_size = 16, @@ -142,7 +141,32 @@ test "alignment and size of structs with 128-bit fields" { .u129_align = 8, .u129_size = 24, }, + .x86 => switch (builtin.os.tag) { + else => .{ + .a_align = 4, + .a_size = 16, + .b_align = 4, + .b_size = 20, + + .u128_align = 4, + .u128_size = 16, + .u129_align = 4, + .u129_size = 20, + }, + .uefi, .windows => .{ + .a_align = 8, + .a_size = 16, + + .b_align = 8, + .b_size = 24, + + .u128_align = 8, + .u128_size = 16, + .u129_align = 8, + .u129_size = 24, + }, + }, .amdgcn, .arm, .armeb, @@ -155,12 +179,13 @@ test "alignment and size of structs with 128-bit fields" { .powerpc, .powerpcle, .riscv32, + .sparc, => .{ .a_align = 8, .a_size = 16, - .b_align = 16, - .b_size = 32, + .b_align = 8, + .b_size = 24, .u128_align = 8, .u128_size = 16, @@ -178,12 +203,10 @@ test "alignment and size of structs with 128-bit fields" { .nvptx64, .powerpc64, .powerpc64le, - .sparc, .sparc64, .riscv64, .wasm32, .wasm64, - .x86, .x86_64, => .{ .a_align = 16, @@ -200,12 +223,11 @@ test "alignment and size of structs with 128-bit fields" { else => return error.SkipZigTest, }; - const min_struct_align = if (builtin.zig_backend == .stage2_c) if (builtin.cpu.arch == .s390x) 8 else 16 else 0; comptime { - assert(@alignOf(A) == @max(expected.a_align, min_struct_align)); + assert(@alignOf(A) == expected.a_align); assert(@sizeOf(A) == expected.a_size); - assert(@alignOf(B) == @max(expected.b_align, min_struct_align)); + assert(@alignOf(B) == expected.b_align); assert(@sizeOf(B) == expected.b_size); assert(@alignOf(u128) == expected.u128_align); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 4cbe2f1aed593d513d58691ec7baf5613494f9e7..697da63db23d14bb9e583745c80c8aca350d8a8d 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -181,6 +181,7 @@ test "@floatFromInt(f80)" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; const S = struct { fn doTheTest(comptime Int: type) !void { @@ -204,7 +205,7 @@ test "@floatFromInt(f80)" { try S.doTheTest(i64); try S.doTheTest(i80); try S.doTheTest(i128); - // try S.doTheTest(i256); // TODO missing compiler_rt symbols + try S.doTheTest(i256); try comptime S.doTheTest(i31); try comptime S.doTheTest(i32); try comptime S.doTheTest(i45); diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 7e9f58e46b0cfa6632234c2136344da6240f468f..ea01c8a88fe3ebf590d71027a7a5516cd57eeedb 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -118,7 +118,6 @@ fn testMul(comptime T: type) !void { test "cmp f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.cpu.arch.isArm() and builtin.target.abi.float() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 try testCmp(f16); try comptime testCmp(f16); @@ -127,7 +126,6 @@ test "cmp f16" { test "cmp f32" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.cpu.arch.isArm() and builtin.target.abi.float() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 try testCmp(f32); try comptime testCmp(f32); @@ -1173,11 +1171,6 @@ test "@floor f80/f128/c_longdouble" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { - // https://github.com/ziglang/zig/issues/12602 - return error.SkipZigTest; - } - try testFloor(f80); try comptime testFloor(f80); try testFloor(f128); @@ -1261,11 +1254,6 @@ test "@ceil f80/f128/c_longdouble" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { - // https://github.com/ziglang/zig/issues/12602 - return error.SkipZigTest; - } - try testCeil(f80); try comptime testCeil(f80); try testCeil(f128); @@ -1280,11 +1268,6 @@ test "@ceil f80 maxInt(u64)" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { - // https://github.com/ziglang/zig/issues/12602 - return error.SkipZigTest; - } - var x: u64 = std.math.maxInt(u64); x = x; const float: f80 = @floatFromInt(x); @@ -1366,11 +1349,6 @@ test "@trunc f80/f128/c_longdouble" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { - // https://github.com/ziglang/zig/issues/12602 - return error.SkipZigTest; - } - try testTrunc(f80); try comptime testTrunc(f80); try testTrunc(f128); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 8750c7723149a9c772ec2dae6aaeda0b4ba03f8b..e76ca9850f3ce2bc9ca356910e76407dfae07fb1 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -2142,11 +2142,6 @@ test "remainder division" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { - // https://github.com/ziglang/zig/issues/12602 - return error.SkipZigTest; - } - if (builtin.zig_backend == .stage2_x86_64 and builtin.object_format == .coff and builtin.abi != .gnu) return error.SkipZigTest; try comptime remdiv(f16); @@ -2337,7 +2332,6 @@ test "NaN comparison" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.cpu.arch.isArm() and builtin.target.abi.float() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 try testNanEqNan(f16); try testNanEqNan(f32); diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 9f8d277774f278dd2e7ee138e5d54e52fbfaa491..5838231e75ed214364a9a1af0527e5b9e480eaa9 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -774,6 +774,8 @@ test "vector reduce operation" { try testReduce(.Add, [4]f16{ -1.9, 5.1, -60.3, 100.0 }, @as(f16, 42.9)); try testReduce(.Add, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 42.9)); try testReduce(.Add, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 42.9)); + try testReduce(.Add, [4]f80{ -1.9, 5.1, -60.3, 100.0 }, @as(f80, 42.9)); + try testReduce(.Add, [4]f128{ -1.9, 5.1, -60.3, 100.0 }, @as(f128, 42.9)); try testReduce(.And, [4]bool{ true, false, true, true }, @as(bool, false)); try testReduce(.And, [4]u1{ 1, 0, 1, 1 }, @as(u1, 0)); @@ -792,6 +794,8 @@ test "vector reduce operation" { try testReduce(.Min, [4]f16{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f16, -100.0)); try testReduce(.Min, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, -100.0)); try testReduce(.Min, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, -100.0)); + try testReduce(.Min, [4]f80{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f80, -100.0)); + try testReduce(.Min, [4]f128{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f128, -100.0)); try testReduce(.Max, [4]i16{ -1, 2, 3, 4 }, @as(i16, 4)); try testReduce(.Max, [4]u16{ 1, 2, 3, 4 }, @as(u16, 4)); @@ -804,6 +808,8 @@ test "vector reduce operation" { try testReduce(.Max, [4]f16{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f16, 10.0e9)); try testReduce(.Max, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, 10.0e9)); try testReduce(.Max, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, 10.0e9)); + try testReduce(.Max, [4]f80{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f80, 10.0e9)); + try testReduce(.Max, [4]f128{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f128, 10.0e9)); try testReduce(.Mul, [4]i16{ -1, 2, 3, 4 }, @as(i16, -24)); try testReduce(.Mul, [4]u16{ 1, 2, 3, 4 }, @as(u16, 24)); @@ -816,6 +822,8 @@ test "vector reduce operation" { try testReduce(.Mul, [4]f16{ -1.9, 5.1, -60.3, 100.0 }, @as(f16, 58430.7)); try testReduce(.Mul, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 58430.7)); try testReduce(.Mul, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 58430.7)); + try testReduce(.Mul, [4]f80{ -1.9, 5.1, -60.3, 100.0 }, @as(f80, 58430.7)); + try testReduce(.Mul, [4]f128{ -1.9, 5.1, -60.3, 100.0 }, @as(f128, 58430.7)); try testReduce(.Or, [4]bool{ false, true, false, false }, @as(bool, true)); try testReduce(.Or, [4]u1{ 0, 1, 0, 0 }, @as(u1, 1)); @@ -823,6 +831,7 @@ test "vector reduce operation" { try testReduce(.Or, [4]u32{ 0xffff0000, 0xff00, 0xf0, 0xf }, ~@as(u32, 0)); try testReduce(.Or, [4]u64{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u64, 0xffffffff)); try testReduce(.Or, [4]u128{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u128, 0xffffffff)); + try testReduce(.Or, [4]u80{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u80, 0xffffffff)); try testReduce(.Xor, [4]bool{ true, true, true, false }, @as(bool, true)); try testReduce(.Xor, [4]u1{ 1, 1, 1, 0 }, @as(u1, 1)); @@ -835,22 +844,32 @@ test "vector reduce operation" { const f16_nan = math.nan(f16); const f32_nan = math.nan(f32); const f64_nan = math.nan(f64); + const f80_nan = math.nan(f80); + const f128_nan = math.nan(f128); try testReduce(.Add, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); try testReduce(.Add, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); try testReduce(.Add, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); + try testReduce(.Add, [4]f80{ -1.9, 5.1, f80_nan, 100.0 }, f80_nan); + try testReduce(.Add, [4]f128{ -1.9, 5.1, f128_nan, 100.0 }, f128_nan); try testReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, @as(f16, -1.9)); try testReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, @as(f32, -1.9)); try testReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, @as(f64, -1.9)); + try testReduce(.Min, [4]f80{ -1.9, 5.1, f80_nan, 100.0 }, @as(f80, -1.9)); + try testReduce(.Min, [4]f128{ -1.9, 5.1, f128_nan, 100.0 }, @as(f128, -1.9)); try testReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, @as(f16, 100.0)); try testReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, @as(f32, 100.0)); try testReduce(.Max, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, @as(f64, 100.0)); + try testReduce(.Max, [4]f80{ -1.9, 5.1, f80_nan, 100.0 }, @as(f80, 100.0)); + try testReduce(.Max, [4]f128{ -1.9, 5.1, f128_nan, 100.0 }, @as(f128, 100.0)); try testReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); try testReduce(.Mul, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); try testReduce(.Mul, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); + try testReduce(.Mul, [4]f80{ -1.9, 5.1, f80_nan, 100.0 }, f80_nan); + try testReduce(.Mul, [4]f128{ -1.9, 5.1, f128_nan, 100.0 }, f128_nan); } }; @@ -1319,11 +1338,6 @@ test "byte vector initialized in inline function" { if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.cpu.arch == .hexagon and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and comptime builtin.cpu.has(.x86, .avx512f)) { - // TODO https://github.com/ziglang/zig/issues/13279 - return error.SkipZigTest; - } - const S = struct { fn boolx4(e0: bool, e1: bool, e2: bool, e3: bool) @Vector(4, bool) { return .{ e0, e1, e2, e3 }; diff --git a/test/c_abi/cfuncs.c b/test/c_abi/cfuncs.c index acd8c258649e3cccadadbd4390d8f9b0cf0999a6..101e07c97ccf7a4c9c2cd702c545b7898f858082 100644 --- a/test/c_abi/cfuncs.c +++ b/test/c_abi/cfuncs.c @@ -408,7 +408,11 @@ void c_test_longdouble(void) { zig_8_longdouble(0, 1, 2, 3, 4, 5, 6, 7, 10, 9); } -#if defined(ZIG_BACKEND_STAGE2_X86_64) || defined(ZIG_PPC32) || defined(__wasm__) +#ifndef __hexagon__ +#ifndef __loongarch__ +#ifndef __mips__ +#ifndef ZIG_PPC64 +#if !(defined(__i386__) && defined(_WIN32)) typedef bool Vector_2_bool __attribute__((ext_vector_type(2))); @@ -4657,6 +4661,10 @@ void c_test_vector_512_bool(void) { }); } +#endif +#endif +#endif +#endif #endif typedef uint8_t Vector_1_u8 __attribute__((vector_size(1 * sizeof(uint8_t)))); diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index 387eeb88c20b4ebfa9646ff10fde7322db299302..1f9b5a9f2ab04f4d3269a1ff6058d1d15ae2a63c 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -451,8 +451,11 @@ test "long double" { comptime { skip: { - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .hexagon) break :skip; + if (builtin.cpu.arch == .loongarch64) break :skip; + if (builtin.cpu.arch.isMIPS()) break :skip; + if (builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; _ = struct { export fn zig_ret_vector_2_bool() @Vector(2, bool) { @@ -474,7 +477,13 @@ extern fn c_vector_2_bool(@Vector(2, bool)) void; extern fn c_test_vector_2_bool() void; test "@Vector(2, bool)" { - if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; + if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; + if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; const vec = c_ret_vector_2_bool(); try expect(vec[0] == true); @@ -488,8 +497,11 @@ test "@Vector(2, bool)" { comptime { skip: { - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .hexagon) break :skip; + if (builtin.cpu.arch == .loongarch64) break :skip; + if (builtin.cpu.arch.isMIPS()) break :skip; + if (builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; _ = struct { export fn zig_ret_vector_4_bool() @Vector(4, bool) { @@ -515,7 +527,13 @@ extern fn c_vector_4_bool(@Vector(4, bool)) void; extern fn c_test_vector_4_bool() void; test "@Vector(4, bool)" { - if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; + if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; + if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; const vec = c_ret_vector_4_bool(); try expect(vec[0] == true); @@ -533,8 +551,11 @@ test "@Vector(4, bool)" { comptime { skip: { - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .hexagon) break :skip; + if (builtin.cpu.arch == .loongarch64) break :skip; + if (builtin.cpu.arch.isMIPS()) break :skip; + if (builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; _ = struct { export fn zig_ret_vector_8_bool() @Vector(8, bool) { @@ -568,7 +589,13 @@ extern fn c_vector_8_bool(@Vector(8, bool)) void; extern fn c_test_vector_8_bool() void; test "@Vector(8, bool)" { - if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; + if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; + if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; const vec = c_ret_vector_8_bool(); try expect(vec[0] == false); @@ -594,8 +621,11 @@ test "@Vector(8, bool)" { comptime { skip: { - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .hexagon) break :skip; + if (builtin.cpu.arch == .loongarch64) break :skip; + if (builtin.cpu.arch.isMIPS()) break :skip; + if (builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; _ = struct { export fn zig_ret_vector_16_bool() @Vector(16, bool) { @@ -645,7 +675,13 @@ extern fn c_vector_16_bool(@Vector(16, bool)) void; extern fn c_test_vector_16_bool() void; test "@Vector(16, bool)" { - if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; + if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; + if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; const vec = c_ret_vector_16_bool(); try expect(vec[0] == true); @@ -687,8 +723,11 @@ test "@Vector(16, bool)" { comptime { skip: { - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .hexagon) break :skip; + if (builtin.cpu.arch == .loongarch64) break :skip; + if (builtin.cpu.arch.isMIPS()) break :skip; + if (builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; _ = struct { export fn zig_ret_vector_32_bool() @Vector(32, bool) { @@ -770,7 +809,13 @@ extern fn c_vector_32_bool(@Vector(32, bool)) void; extern fn c_test_vector_32_bool() void; test "@Vector(32, bool)" { - if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; + if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; + if (builtin.cpu.arch.isArm()) return error.SkipZigTest; + if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; + if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; const vec = c_ret_vector_32_bool(); try expect(vec[0] == true); @@ -844,8 +889,11 @@ test "@Vector(32, bool)" { comptime { skip: { - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .hexagon) break :skip; + if (builtin.cpu.arch == .loongarch64) break :skip; + if (builtin.cpu.arch.isMIPS()) break :skip; + if (builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; _ = struct { export fn zig_ret_vector_64_bool() @Vector(64, bool) { @@ -991,7 +1039,11 @@ extern fn c_vector_64_bool(@Vector(64, bool)) void; extern fn c_test_vector_64_bool() void; test "@Vector(64, bool)" { - if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; + if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; + if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; + if (builtin.cpu.arch == .x86) return error.SkipZigTest; const vec = c_ret_vector_64_bool(); try expect(vec[0] == false); @@ -1129,8 +1181,11 @@ test "@Vector(64, bool)" { comptime { skip: { - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .hexagon) break :skip; + if (builtin.cpu.arch == .loongarch64) break :skip; + if (builtin.cpu.arch.isMIPS()) break :skip; + if (builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; _ = struct { export fn zig_ret_vector_128_bool() @Vector(128, bool) { @@ -1404,7 +1459,11 @@ extern fn c_vector_128_bool(@Vector(128, bool)) void; extern fn c_test_vector_128_bool() void; test "@Vector(128, bool)" { - if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; + if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; + if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; const vec = c_ret_vector_128_bool(); try expect(vec[0] == false); @@ -1670,8 +1729,11 @@ test "@Vector(128, bool)" { comptime { skip: { - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .hexagon) break :skip; + if (builtin.cpu.arch == .loongarch64) break :skip; + if (builtin.cpu.arch.isMIPS()) break :skip; + if (builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; _ = struct { export fn zig_ret_vector_256_bool() @Vector(256, bool) { @@ -2201,7 +2263,11 @@ extern fn c_vector_256_bool(@Vector(256, bool)) void; extern fn c_test_vector_256_bool() void; test "@Vector(256, bool)" { - if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; + if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; + if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; const vec = c_ret_vector_256_bool(); try expect(vec[0] == true); @@ -2723,8 +2789,11 @@ test "@Vector(256, bool)" { comptime { skip: { - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .hexagon) break :skip; + if (builtin.cpu.arch == .loongarch64) break :skip; + if (builtin.cpu.arch.isMIPS()) break :skip; + if (builtin.cpu.arch.isPowerPC64()) break :skip; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; _ = struct { export fn zig_ret_vector_512_bool() @Vector(512, bool) { @@ -3766,7 +3835,11 @@ extern fn c_vector_512_bool(@Vector(512, bool)) void; extern fn c_test_vector_512_bool() void; test "@Vector(512, bool)" { - if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; + if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; + if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; + if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; + if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; const vec = c_ret_vector_512_bool(); try expect(vec[0] == false); @@ -4840,7 +4913,7 @@ test "@Vector(2, u8)" { if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; const v = c_ret_vector_2_u8(); try expect(v[0] == 9); @@ -4869,7 +4942,6 @@ test "@Vector(3, u8)" { if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag != .windows) return error.SkipZigTest; const v = c_ret_vector_3_u8(); try expect(v[0] == 19); @@ -4912,7 +4984,7 @@ test "@Vector(4, u8)" { if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; const v = c_ret_vector_4_u8(); try expect(v[0] == 41); @@ -4946,7 +5018,6 @@ test "@Vector(6, u8)" { if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag != .windows) return error.SkipZigTest; const v = c_ret_vector_6_u8(); try expect(v[0] == 53); @@ -9063,7 +9134,7 @@ test "@Vector(2, u16)" { if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; const v = c_ret_vector_2_u16(); try expect(v[0] == 9); @@ -9091,7 +9162,6 @@ test "@Vector(3, u16)" { if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag != .windows) return error.SkipZigTest; const v = c_ret_vector_3_u16(); try expect(v[0] == 19); @@ -12564,8 +12634,6 @@ extern fn c_vector_1_u64(@Vector(1, u64), usize) void; extern fn c_test_vector_1_u64() void; test "@Vector(1, u64)" { - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag != .windows) return error.SkipZigTest; - const v = c_ret_vector_1_u64(); try expect(v[0] == 3); c_vector_1_u64(.{4}, 1); @@ -13291,7 +13359,6 @@ test "@Vector(1, f32)" { if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag != .windows) return error.SkipZigTest; const v = c_ret_vector_1_f32(); try expect(v[0] == 3); @@ -14633,7 +14700,6 @@ test "@Vector(4, f64)" { if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; if (builtin.cpu.arch.isMIPS32()) return error.SkipZigTest; if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; - if (builtin.cpu.arch.isArm()) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35899 const v = c_ret_vector_4_f64(); try expect(v[0] == 33); @@ -14701,7 +14767,6 @@ test "@Vector(8, f64)" { if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; if (builtin.cpu.arch.isMIPS32()) return error.SkipZigTest; if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; - if (builtin.cpu.arch.isArm()) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35899 const v = c_ret_vector_8_f64(); try expect(v[0] == 81); diff --git a/test/tests.zig b/test/tests.zig index 849267617f0cfe5e8c8f227a3758a397ba047083..d823d2add16c8a4486decb3671025cd60cbc16e2 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -2015,7 +2015,6 @@ const c_abi_targets = blk: { .abi = .musl, }, .use_llvm = false, - .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, }, .{ .target = .{ @@ -2026,7 +2025,6 @@ const c_abi_targets = blk: { }, .use_llvm = false, .strip = true, - .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, }, .{ .target = .{ @@ -2037,7 +2035,6 @@ const c_abi_targets = blk: { }, .use_llvm = false, .pic = true, - .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, }, .{ .target = .{ @@ -2082,7 +2079,6 @@ const c_abi_targets = blk: { .abi = .gnu, }, .use_llvm = false, - .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, }, .{ .target = .{ @@ -2092,7 +2088,6 @@ const c_abi_targets = blk: { .abi = .gnu, }, .use_llvm = false, - .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, }, .{ .target = .{ @@ -2102,7 +2097,6 @@ const c_abi_targets = blk: { .abi = .gnu, }, .use_llvm = false, - .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, }, .{ .target = .{