| author | |
| committer | |
| log | b425e6869452ebeaf9b15ceb5739ce4b4bb5b87d |
| tree | 47b3425719a1d13a17935ec1c50d65f6e19644a6 |
| parent | 865f329e4f9fd097bb9739566045f9b7179521c4 |
Closes #12602
Closes #21234
Closes #35899
Closes #3612738 files changed, 2145 insertions(+), 1478 deletions(-)
lib/compiler/aro/aro/Target.zig+3-3| ... | ... | @@ -1559,15 +1559,15 @@ pub fn ptrBitWidth(target: *const Target) u16 { |
| 1559 | 1559 | } |
| 1560 | 1560 | |
| 1561 | 1561 | pub fn cCharSignedness(target: *const Target) std.builtin.Signedness { |
| 1562 | return target.toZigTarget().cCharSignedness(); | |
| 1562 | return target.toZigTarget().cCharSignedness().?; | |
| 1563 | 1563 | } |
| 1564 | 1564 | |
| 1565 | 1565 | pub fn cTypeBitSize(target: *const Target, c_type: std.Target.CType) u16 { |
| 1566 | return target.toZigTarget().cTypeBitSize(c_type); | |
| 1566 | return target.toZigTarget().cTypeBitSize(c_type).?; | |
| 1567 | 1567 | } |
| 1568 | 1568 | |
| 1569 | 1569 | pub fn cTypeAlignment(target: *const Target, c_type: std.Target.CType) u16 { |
| 1570 | return target.toZigTarget().cTypeAlignment(c_type); | |
| 1570 | return target.toZigTarget().cTypeAlignment(c_type).?; | |
| 1571 | 1571 | } |
| 1572 | 1572 | |
| 1573 | 1573 | pub fn standardDynamicLinkerPath(target: *const Target) std.Target.DynamicLinker { |
lib/compiler/reduce.zig+1-1| ... | ... | @@ -400,7 +400,7 @@ fn parse(gpa: Allocator, io: Io, file_path: []const u8) !Ast { |
| 400 | 400 | file_path, |
| 401 | 401 | gpa, |
| 402 | 402 | .limited(std.math.maxInt(u32)), |
| 403 | .fromByteUnits(1), | |
| 403 | .@"1", | |
| 404 | 404 | 0, |
| 405 | 405 | ) catch |err| { |
| 406 | 406 | fatal("unable to open '{s}': {s}", .{ file_path, @errorName(err) }); |
lib/compiler/test_runner.zig+10-11| ... | ... | @@ -91,24 +91,23 @@ fn mainServer(init: std.process.Init.Minimal) !void { |
| 91 | 91 | return std.process.exit(0); |
| 92 | 92 | }, |
| 93 | 93 | .query_test_metadata => { |
| 94 | testing.allocator_instance = .init(std.heap.page_allocator, .{}); | |
| 95 | defer if (testing.allocator_instance.deinit() != 0) { | |
| 96 | @panic("internal test runner memory leak"); | |
| 97 | }; | |
| 94 | var sa: std.heap.SafeAllocator = .init(std.heap.page_allocator, .{}); | |
| 95 | defer if (sa.deinit() != 0) @panic("internal test runner memory leak"); | |
| 96 | const gpa = sa.allocator(); | |
| 98 | 97 | |
| 99 | 98 | var string_bytes: std.ArrayList(u8) = .empty; |
| 100 | defer string_bytes.deinit(testing.allocator); | |
| 101 | try string_bytes.append(testing.allocator, 0); // Reserve 0 for null. | |
| 99 | defer string_bytes.deinit(gpa); | |
| 100 | try string_bytes.append(gpa, 0); // Reserve 0 for null. | |
| 102 | 101 | |
| 103 | 102 | const test_fns = builtin.test_functions; |
| 104 | const names = try testing.allocator.alloc(u32, test_fns.len); | |
| 105 | defer testing.allocator.free(names); | |
| 106 | const expected_panic_msgs = try testing.allocator.alloc(u32, test_fns.len); | |
| 107 | defer testing.allocator.free(expected_panic_msgs); | |
| 103 | const names = try gpa.alloc(u32, test_fns.len); | |
| 104 | defer gpa.free(names); | |
| 105 | const expected_panic_msgs = try gpa.alloc(u32, test_fns.len); | |
| 106 | defer gpa.free(expected_panic_msgs); | |
| 108 | 107 | |
| 109 | 108 | for (test_fns, names, expected_panic_msgs) |test_fn, *name, *expected_panic_msg| { |
| 110 | 109 | name.* = @intCast(string_bytes.items.len); |
| 111 | try string_bytes.ensureUnusedCapacity(testing.allocator, test_fn.name.len + 1); | |
| 110 | try string_bytes.ensureUnusedCapacity(gpa, test_fn.name.len + 1); | |
| 112 | 111 | string_bytes.appendSliceAssumeCapacity(test_fn.name); |
| 113 | 112 | string_bytes.appendAssumeCapacity(0); |
| 114 | 113 | expected_panic_msg.* = 0; |
lib/compiler_rt/comparef.zig+1-1| ... | ... | @@ -8,7 +8,7 @@ const Unordered = if (builtin.cpu.arch == .avr) |
| 8 | 8 | i8 |
| 9 | 9 | else if (builtin.cpu.arch.isAARCH64()) |
| 10 | 10 | i32 |
| 11 | else if (builtin.target.cTypeBitSize(.long) >= builtin.target.ptrBitWidth()) | |
| 11 | else if (builtin.target.cTypeBitSize(.long).? >= builtin.target.ptrBitWidth()) | |
| 12 | 12 | c_long |
| 13 | 13 | else |
| 14 | 14 | c_longlong; |
lib/std/Io/Writer.zig+6-6| ... | ... | @@ -2817,12 +2817,12 @@ pub const Allocating = struct { |
| 2817 | 2817 | } |
| 2818 | 2818 | |
| 2819 | 2819 | test Allocating { |
| 2820 | try testAllocating(.fromByteUnits(1)); | |
| 2821 | try testAllocating(.fromByteUnits(4)); | |
| 2822 | try testAllocating(.fromByteUnits(8)); | |
| 2823 | try testAllocating(.fromByteUnits(16)); | |
| 2824 | try testAllocating(.fromByteUnits(32)); | |
| 2825 | try testAllocating(.fromByteUnits(64)); | |
| 2820 | try testAllocating(.@"1"); | |
| 2821 | try testAllocating(.@"4"); | |
| 2822 | try testAllocating(.@"8"); | |
| 2823 | try testAllocating(.@"16"); | |
| 2824 | try testAllocating(.@"32"); | |
| 2825 | try testAllocating(.@"64"); | |
| 2826 | 2826 | } |
| 2827 | 2827 | }; |
| 2828 | 2828 |
lib/std/Target.zig+23-11| ... | ... | @@ -3066,9 +3066,13 @@ pub fn stackGrowth(target: *const Target) StackGrowth { |
| 3066 | 3066 | /// Default signedness of `char` for the native C compiler for this target |
| 3067 | 3067 | /// Note that char signedness is implementation-defined and many compilers provide |
| 3068 | 3068 | /// an option to override the default signedness e.g. GCC's -funsigned-char / -fsigned-char |
| 3069 | pub fn cCharSignedness(target: *const Target) std.builtin.Signedness { | |
| 3069 | /// Returns `null` if no C ABI is defined for this target. | |
| 3070 | pub fn cCharSignedness(target: *const Target) ?std.builtin.Signedness { | |
| 3071 | switch (target.os.tag) { | |
| 3072 | .opengl => return null, | |
| 3073 | else => {}, | |
| 3074 | } | |
| 3070 | 3075 | if (target.os.tag.isDarwin() or target.os.tag == .windows or target.os.tag == .uefi) return .signed; |
| 3071 | ||
| 3072 | 3076 | return switch (target.cpu.arch) { |
| 3073 | 3077 | .aarch64, |
| 3074 | 3078 | .aarch64_be, |
| ... | ... | @@ -3114,7 +3118,8 @@ pub const CType = enum { |
| 3114 | 3118 | longdouble, |
| 3115 | 3119 | }; |
| 3116 | 3120 | |
| 3117 | pub fn cTypeByteSize(t: *const Target, c_type: CType) u16 { | |
| 3121 | /// Returns `null` if no C ABI is defined for this target. | |
| 3122 | pub fn cTypeByteSize(t: *const Target, c_type: CType) ?u16 { | |
| 3118 | 3123 | return switch (c_type) { |
| 3119 | 3124 | .char, |
| 3120 | 3125 | .short, |
| ... | ... | @@ -3127,18 +3132,19 @@ pub fn cTypeByteSize(t: *const Target, c_type: CType) u16 { |
| 3127 | 3132 | .ulonglong, |
| 3128 | 3133 | .float, |
| 3129 | 3134 | .double, |
| 3130 | => @divExact(cTypeBitSize(t, c_type), 8), | |
| 3135 | => @divExact(cTypeBitSize(t, c_type) orelse return null, 8), | |
| 3131 | 3136 | |
| 3132 | .longdouble => switch (cTypeBitSize(t, c_type)) { | |
| 3137 | .longdouble => switch (cTypeBitSize(t, c_type) orelse return null) { | |
| 3133 | 3138 | 64 => 8, |
| 3134 | 80 => @intCast(std.mem.alignForward(usize, 10, cTypeAlignment(t, .longdouble))), | |
| 3139 | 80 => @intCast(std.mem.alignForward(usize, 10, cTypeAlignment(t, c_type).?)), | |
| 3135 | 3140 | 128 => 16, |
| 3136 | 3141 | else => unreachable, |
| 3137 | 3142 | }, |
| 3138 | 3143 | }; |
| 3139 | 3144 | } |
| 3140 | 3145 | |
| 3141 | pub fn cTypeBitSize(target: *const Target, c_type: CType) u16 { | |
| 3146 | /// Returns `null` if no C ABI is defined for this target. | |
| 3147 | pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 { | |
| 3142 | 3148 | switch (target.os.tag) { |
| 3143 | 3149 | .freestanding, |
| 3144 | 3150 | .other, |
| ... | ... | @@ -3459,15 +3465,17 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) u16 { |
| 3459 | 3465 | .longlong, .ulonglong, .longdouble => return 64, |
| 3460 | 3466 | }, |
| 3461 | 3467 | |
| 3468 | .opengl => return null, | |
| 3469 | ||
| 3462 | 3470 | .ps3, |
| 3463 | 3471 | .contiki, |
| 3464 | 3472 | .managarm, |
| 3465 | .opengl, | |
| 3466 | 3473 | => @panic("specify the C integer and float type sizes for this OS"), |
| 3467 | 3474 | } |
| 3468 | 3475 | } |
| 3469 | 3476 | |
| 3470 | pub fn cTypeAlignment(target: *const Target, c_type: CType) u16 { | |
| 3477 | /// Returns `null` if no C ABI is defined for this target. | |
| 3478 | pub fn cTypeAlignment(target: *const Target, c_type: CType) ?u16 { | |
| 3471 | 3479 | // Overrides for unusual alignments |
| 3472 | 3480 | switch (target.cpu.arch) { |
| 3473 | 3481 | .avr, |
| ... | ... | @@ -3500,7 +3508,7 @@ pub fn cTypeAlignment(target: *const Target, c_type: CType) u16 { |
| 3500 | 3508 | |
| 3501 | 3509 | // Next-power-of-two-aligned, up to a maximum. |
| 3502 | 3510 | return @min( |
| 3503 | std.math.ceilPowerOfTwoAssert(u16, (cTypeBitSize(target, c_type) + 7) / 8), | |
| 3511 | std.math.ceilPowerOfTwoAssert(u16, ((cTypeBitSize(target, c_type) orelse return null) + 7) / 8), | |
| 3504 | 3512 | @as(u16, switch (target.cpu.arch) { |
| 3505 | 3513 | .msp430, |
| 3506 | 3514 | .x86_16, |
| ... | ... | @@ -3598,6 +3606,11 @@ pub fn cMaxIntAlignment(target: *const Target) u16 { |
| 3598 | 3606 | .xcore, |
| 3599 | 3607 | => 4, |
| 3600 | 3608 | |
| 3609 | .x86 => switch (target.os.tag) { | |
| 3610 | else => 4, | |
| 3611 | .uefi, .windows => 8, | |
| 3612 | }, | |
| 3613 | ||
| 3601 | 3614 | .arm, |
| 3602 | 3615 | .armeb, |
| 3603 | 3616 | .hexagon, |
| ... | ... | @@ -3616,7 +3629,6 @@ pub fn cMaxIntAlignment(target: *const Target) u16 { |
| 3616 | 3629 | .sparc, |
| 3617 | 3630 | .thumb, |
| 3618 | 3631 | .thumbeb, |
| 3619 | .x86, | |
| 3620 | 3632 | .xtensa, |
| 3621 | 3633 | .xtensaeb, |
| 3622 | 3634 | => 8, |
lib/std/math/gamma.zig-2| ... | ... | @@ -263,8 +263,6 @@ test gamma { |
| 263 | 263 | } |
| 264 | 264 | |
| 265 | 265 | test "gamma.special" { |
| 266 | if (builtin.cpu.arch.isArm() and builtin.target.abi.float() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 | |
| 267 | ||
| 268 | 266 | inline for (&.{ f32, f64 }) |T| { |
| 269 | 267 | try expect(std.math.isNan(gamma(T, -std.math.nan(T)))); |
| 270 | 268 | try expect(std.math.isNan(gamma(T, std.math.nan(T)))); |
lib/std/zig/llvm/Builder.zig+2-2| ... | ... | @@ -5999,7 +5999,7 @@ pub const WipFunction = struct { |
| 5999 | 5999 | alignment: Alignment, |
| 6000 | 6000 | name: []const u8, |
| 6001 | 6001 | ) Allocator.Error!Value { |
| 6002 | return self.loadAtomic(access_kind, ty, ptr, .system, .none, alignment, name); | |
| 6002 | return self.loadAtomic(access_kind, ty, ptr, undefined, .none, alignment, name); | |
| 6003 | 6003 | } |
| 6004 | 6004 | |
| 6005 | 6005 | pub fn loadAtomic( |
| ... | ... | @@ -6043,7 +6043,7 @@ pub const WipFunction = struct { |
| 6043 | 6043 | ptr: Value, |
| 6044 | 6044 | alignment: Alignment, |
| 6045 | 6045 | ) Allocator.Error!Instruction.Index { |
| 6046 | return self.storeAtomic(kind, val, ptr, .system, .none, alignment); | |
| 6046 | return self.storeAtomic(kind, val, ptr, undefined, .none, alignment); | |
| 6047 | 6047 | } |
| 6048 | 6048 | |
| 6049 | 6049 | pub fn storeAtomic( |
lib/std/zig/target.zig+10-26| ... | ... | @@ -499,31 +499,12 @@ pub fn intByteSize(target: *const std.Target, bits: u16) u16 { |
| 499 | 499 | } |
| 500 | 500 | |
| 501 | 501 | pub fn intAlignment(target: *const std.Target, bits: u16) u16 { |
| 502 | return switch (target.cpu.arch) { | |
| 503 | .x86 => switch (bits) { | |
| 504 | 0...8 => 1, | |
| 505 | 9...16 => 2, | |
| 506 | 17...32 => 4, | |
| 507 | 33...64 => switch (target.os.tag) { | |
| 508 | .uefi, .windows => 8, | |
| 509 | else => 4, | |
| 510 | }, | |
| 511 | else => 16, | |
| 512 | }, | |
| 513 | .x86_64 => switch (bits) { | |
| 514 | 0...8 => 1, | |
| 515 | 9...16 => 2, | |
| 516 | 17...32 => 4, | |
| 517 | 33...64 => 8, | |
| 518 | else => 16, | |
| 519 | }, | |
| 520 | else => switch (bits) { | |
| 521 | 0 => 1, | |
| 522 | else => @min( | |
| 523 | std.math.ceilPowerOfTwoPromote(u16, @intCast((@as(u17, bits) + 7) / 8)), | |
| 524 | target.cMaxIntAlignment(), | |
| 525 | ), | |
| 526 | }, | |
| 502 | return switch (bits) { | |
| 503 | 0 => 1, | |
| 504 | else => @min( | |
| 505 | std.math.ceilPowerOfTwoPromote(u16, @intCast((@as(u17, bits) + 7) / 8)), | |
| 506 | target.cMaxIntAlignment(), | |
| 507 | ), | |
| 527 | 508 | }; |
| 528 | 509 | } |
| 529 | 510 | |
| ... | ... | @@ -536,7 +517,10 @@ pub fn compilerRtFloatAbi(target: *const std.Target, bits: u16) std.Target.Abi.F |
| 536 | 517 | 16 => if (target.cpu.arch.isMIPS() or target.cpu.arch.isPowerPC()) return no_c_type_available, |
| 537 | 518 | 32, 64 => {}, |
| 538 | 519 | 80 => if (target.cTypeBitSize(.longdouble) != 80) return no_c_type_available, |
| 539 | 128 => if (target.cTypeBitSize(.longdouble) <= 64) return no_c_type_available, | |
| 520 | 128 => { | |
| 521 | if (target.cpu.arch.isX86()) return .hard; // if (target.abi == .msvc) __m128i else __float128 | |
| 522 | if (target.cTypeBitSize(.longdouble) != 128) return no_c_type_available; | |
| 523 | }, | |
| 540 | 524 | } |
| 541 | 525 | return .hard; |
| 542 | 526 | } |
src/Sema.zig+4-4| ... | ... | @@ -29657,7 +29657,7 @@ fn coerceVarArgParam( |
| 29657 | 29657 | .array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), |
| 29658 | 29658 | .float => float: { |
| 29659 | 29659 | const target = zcu.getTarget(); |
| 29660 | const double_bits = target.cTypeBitSize(.double); | |
| 29660 | const double_bits = target.cTypeBitSize(.double) orelse break :float inst; | |
| 29661 | 29661 | const inst_bits = uncasted_ty.floatBits(target); |
| 29662 | 29662 | if (inst_bits >= double_bits) break :float inst; |
| 29663 | 29663 | switch (double_bits) { |
| ... | ... | @@ -29673,21 +29673,21 @@ fn coerceVarArgParam( |
| 29673 | 29673 | if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { |
| 29674 | 29674 | .signed => .int, |
| 29675 | 29675 | .unsigned => .uint, |
| 29676 | })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { | |
| 29676 | }) orelse break :int inst) break :int try sema.coerce(block, switch (uncasted_info.signedness) { | |
| 29677 | 29677 | .signed => .c_int, |
| 29678 | 29678 | .unsigned => .c_uint, |
| 29679 | 29679 | }, inst, inst_src); |
| 29680 | 29680 | if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { |
| 29681 | 29681 | .signed => .long, |
| 29682 | 29682 | .unsigned => .ulong, |
| 29683 | })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { | |
| 29683 | }).?) break :int try sema.coerce(block, switch (uncasted_info.signedness) { | |
| 29684 | 29684 | .signed => .c_long, |
| 29685 | 29685 | .unsigned => .c_ulong, |
| 29686 | 29686 | }, inst, inst_src); |
| 29687 | 29687 | if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { |
| 29688 | 29688 | .signed => .longlong, |
| 29689 | 29689 | .unsigned => .ulonglong, |
| 29690 | })) break :int try sema.coerce(block, switch (uncasted_info.signedness) { | |
| 29690 | }).?) break :int try sema.coerce(block, switch (uncasted_info.signedness) { | |
| 29691 | 29691 | .signed => .c_longlong, |
| 29692 | 29692 | .unsigned => .c_ulonglong, |
| 29693 | 29693 | }, inst, inst_src); |
src/Sema/type_resolution.zig+1-1| ... | ... | @@ -364,7 +364,7 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void { |
| 364 | 364 | const a = struct_obj.field_aligns.get(ip)[field_idx]; |
| 365 | 365 | if (a != .none) break :a a; |
| 366 | 366 | } |
| 367 | break :a field_ty.defaultStructFieldAlignment(struct_obj.layout, zcu); | |
| 367 | break :a field_ty.abiAlignment(zcu); | |
| 368 | 368 | }; |
| 369 | 369 | align_out.* = field_align; |
| 370 | 370 | if (struct_obj.field_is_comptime_bits.get(ip, field_idx)) { |
src/Type.zig+102-84| ... | ... | @@ -957,10 +957,25 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment { |
| 957 | 957 | if (vector_type.len == 0) return .@"1"; |
| 958 | 958 | switch (zcu.comp.getZigBackend()) { |
| 959 | 959 | else => { |
| 960 | const elem_bits: u32 = @intCast(Type.fromInterned(vector_type.child).bitSize(zcu)); | |
| 960 | const elem_ty: Type = .fromInterned(vector_type.child); | |
| 961 | switch (if (elem_ty.isRuntimeFloat()) | |
| 962 | std.zig.target.compilerRtFloatAbi(target, elem_ty.floatBits(target)) | |
| 963 | else | |
| 964 | .hard) { | |
| 965 | .hard => {}, | |
| 966 | .soft => return elem_ty.abiAlignment(zcu), | |
| 967 | } | |
| 968 | const elem_bits: u32 = @intCast(elem_ty.bitSize(zcu)); | |
| 961 | 969 | if (elem_bits == 0) return .@"1"; |
| 962 | 970 | const bytes = ((elem_bits * vector_type.len) + 7) / 8; |
| 963 | return .fromByteUnits(std.math.ceilPowerOfTwoAssert(u32, bytes)); | |
| 971 | const arch = target.cpu.arch; | |
| 972 | return .fromByteUnits(std.math.ceilPowerOfTwoAssert( | |
| 973 | u32, | |
| 974 | if (arch.isArm() or arch.isAARCH64() or arch == .s390x) | |
| 975 | @min(bytes, target.stackAlignment()) | |
| 976 | else | |
| 977 | bytes, | |
| 978 | )); | |
| 964 | 979 | }, |
| 965 | 980 | .stage2_c, .stage2_wasm => return Type.fromInterned(vector_type.child).abiAlignment(zcu), |
| 966 | 981 | .stage2_x86_64 => { |
| ... | ... | @@ -1018,19 +1033,33 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment { |
| 1018 | 1033 | .c_ulonglong => cTypeAlign(target, .ulonglong), |
| 1019 | 1034 | .c_longdouble => cTypeAlign(target, .longdouble), |
| 1020 | 1035 | |
| 1021 | .f16 => .@"2", | |
| 1022 | .f32 => if (target.os.tag == .opengl) .@"4" else cTypeAlign(target, .float), | |
| 1023 | .f64 => if (target.os.tag == .opengl) .@"8" else switch (target.cTypeBitSize(.double)) { | |
| 1024 | 64 => cTypeAlign(target, .double), | |
| 1025 | else => .@"8", | |
| 1026 | }, | |
| 1027 | .f80 => switch (target.cTypeBitSize(.longdouble)) { | |
| 1028 | 80 => cTypeAlign(target, .longdouble), | |
| 1029 | else => Type.u80.abiAlignment(zcu), | |
| 1030 | }, | |
| 1031 | .f128 => switch (target.cTypeBitSize(.longdouble)) { | |
| 1032 | 128 => cTypeAlign(target, .longdouble), | |
| 1033 | else => .@"16", | |
| 1036 | .f16 => .fromByteUnits(std.zig.target.intAlignment(target, 16)), // repr: u16 | |
| 1037 | .f32 => if (target.cTypeBitSize(.float) == 32) | |
| 1038 | cTypeAlign(target, .float) // abi: c_float, | |
| 1039 | else | |
| 1040 | .fromByteUnits(std.zig.target.intAlignment(target, 32)), // repr: u32, | |
| 1041 | .f64 => if (target.cTypeBitSize(.double) == 64) | |
| 1042 | cTypeAlign(target, .double) // abi: c_double, | |
| 1043 | else | |
| 1044 | .fromByteUnits(std.zig.target.intAlignment(target, 64)), // repr: u64, | |
| 1045 | .f80 => if (target.cTypeBitSize(.longdouble) == 80) | |
| 1046 | cTypeAlign(target, .longdouble) // abi: c_longdouble, | |
| 1047 | else | |
| 1048 | .fromByteUnits(switch (std.zig.target.compilerRtFloatAbi(target, 80)) { | |
| 1049 | .hard => std.zig.target.intAlignment(target, 80), // repr: u80, | |
| 1050 | .soft => @max( | |
| 1051 | std.zig.target.intAlignment(target, 64), // mantissa: u64, | |
| 1052 | std.zig.target.intAlignment(target, 16), // exponent: u16, | |
| 1053 | ), | |
| 1054 | }), | |
| 1055 | .f128 => if (target.cTypeBitSize(.longdouble) == 128) | |
| 1056 | cTypeAlign(target, .longdouble) // abi: c_longdouble, | |
| 1057 | else switch (std.zig.target.compilerRtFloatAbi(target, 128)) { | |
| 1058 | .hard => if (target.cpu.arch.isX86()) | |
| 1059 | .@"16" // abi: c___float128, | |
| 1060 | else | |
| 1061 | .fromByteUnits(std.zig.target.intAlignment(target, 128)), // repr: u128, | |
| 1062 | .soft => .fromByteUnits(std.zig.target.intAlignment(target, 64)), // lo: u64, hi: u64, | |
| 1034 | 1063 | }, |
| 1035 | 1064 | |
| 1036 | 1065 | .generic_poison => unreachable, |
| ... | ... | @@ -1111,7 +1140,13 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 { |
| 1111 | 1140 | .vector_type => |vec| { |
| 1112 | 1141 | const elem_ty: Type = .fromInterned(vec.child); |
| 1113 | 1142 | const bytes = switch (zcu.comp.getZigBackend()) { |
| 1114 | else => @divCeil(vec.len * elem_ty.bitSize(zcu), 8), | |
| 1143 | else => switch (if (elem_ty.isRuntimeFloat()) | |
| 1144 | std.zig.target.compilerRtFloatAbi(target, elem_ty.floatBits(target)) | |
| 1145 | else | |
| 1146 | .hard) { | |
| 1147 | .hard => @divCeil(vec.len * elem_ty.bitSize(zcu), 8), | |
| 1148 | .soft => vec.len * elem_ty.abiSize(zcu), | |
| 1149 | }, | |
| 1115 | 1150 | .stage2_c, .stage2_wasm => vec.len * elem_ty.abiSize(zcu), |
| 1116 | 1151 | .stage2_x86_64 => switch (elem_ty.toIntern()) { |
| 1117 | 1152 | .bool_type => @divCeil(vec.len, 8), |
| ... | ... | @@ -1167,25 +1202,44 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 { |
| 1167 | 1202 | .anyerror, .adhoc_inferred_error_set => errorAbiSize(zcu), |
| 1168 | 1203 | .usize, .isize => ptrAbiSize(target), |
| 1169 | 1204 | |
| 1170 | .c_char => target.cTypeByteSize(.char), | |
| 1171 | .c_short => target.cTypeByteSize(.short), | |
| 1172 | .c_ushort => target.cTypeByteSize(.ushort), | |
| 1173 | .c_int => target.cTypeByteSize(.int), | |
| 1174 | .c_uint => target.cTypeByteSize(.uint), | |
| 1175 | .c_long => target.cTypeByteSize(.long), | |
| 1176 | .c_ulong => target.cTypeByteSize(.ulong), | |
| 1177 | .c_longlong => target.cTypeByteSize(.longlong), | |
| 1178 | .c_ulonglong => target.cTypeByteSize(.ulonglong), | |
| 1179 | .c_longdouble => target.cTypeByteSize(.longdouble), | |
| 1180 | ||
| 1181 | .f16 => 2, | |
| 1182 | .f32 => 4, | |
| 1183 | .f64 => 8, | |
| 1184 | .f80 => switch (target.cTypeBitSize(.longdouble)) { | |
| 1185 | 80 => target.cTypeByteSize(.longdouble), | |
| 1186 | else => Type.u80.abiSize(zcu), | |
| 1205 | .c_char => target.cTypeByteSize(.char).?, | |
| 1206 | .c_short => target.cTypeByteSize(.short).?, | |
| 1207 | .c_ushort => target.cTypeByteSize(.ushort).?, | |
| 1208 | .c_int => target.cTypeByteSize(.int).?, | |
| 1209 | .c_uint => target.cTypeByteSize(.uint).?, | |
| 1210 | .c_long => target.cTypeByteSize(.long).?, | |
| 1211 | .c_ulong => target.cTypeByteSize(.ulong).?, | |
| 1212 | .c_longlong => target.cTypeByteSize(.longlong).?, | |
| 1213 | .c_ulonglong => target.cTypeByteSize(.ulonglong).?, | |
| 1214 | .c_longdouble => target.cTypeByteSize(.longdouble).?, | |
| 1215 | ||
| 1216 | .f16 => std.zig.target.intByteSize(target, 16), // repr: u16 | |
| 1217 | .f32 => if (target.cTypeBitSize(.float) == 32) | |
| 1218 | target.cTypeByteSize(.float).? // abi: c_float, | |
| 1219 | else | |
| 1220 | std.zig.target.intByteSize(target, 32), // repr: u32, | |
| 1221 | .f64 => if (target.cTypeBitSize(.double) == 64) | |
| 1222 | target.cTypeByteSize(.double).? // abi: c_double, | |
| 1223 | else | |
| 1224 | std.zig.target.intByteSize(target, 64), // repr: u64, | |
| 1225 | .f80 => if (target.cTypeBitSize(.longdouble) == 80) | |
| 1226 | target.cTypeByteSize(.longdouble).? // abi: c_longdouble, | |
| 1227 | else switch (std.zig.target.compilerRtFloatAbi(target, 80)) { | |
| 1228 | .hard => std.zig.target.intByteSize(target, 80), // repr: u80, | |
| 1229 | .soft => ty.abiAlignment(zcu).forward( | |
| 1230 | std.zig.target.intByteSize(target, 64) + // mantissa: u64, | |
| 1231 | std.zig.target.intByteSize(target, 16), // exponent: u16 | |
| 1232 | ), | |
| 1233 | }, | |
| 1234 | .f128 => if (target.cTypeBitSize(.longdouble) == 128) | |
| 1235 | target.cTypeByteSize(.longdouble).? // abi: c_longdouble, | |
| 1236 | else switch (std.zig.target.compilerRtFloatAbi(target, 128)) { | |
| 1237 | .hard => if (target.cpu.arch.isX86()) | |
| 1238 | 16 // abi: c___float128, | |
| 1239 | else | |
| 1240 | std.zig.target.intByteSize(target, 128), // repr: u128, | |
| 1241 | .soft => std.zig.target.intByteSize(target, 64) * 2, // lo: u64, hi: u64, | |
| 1187 | 1242 | }, |
| 1188 | .f128 => 16, | |
| 1189 | 1243 | |
| 1190 | 1244 | .anyopaque => unreachable, |
| 1191 | 1245 | .generic_poison => unreachable, |
| ... | ... | @@ -1733,7 +1787,7 @@ pub fn isInt(self: Type, zcu: *const Zcu) bool { |
| 1733 | 1787 | /// Returns true if and only if the type is a fixed-width, signed integer. |
| 1734 | 1788 | pub fn isSignedInt(ty: Type, zcu: *const Zcu) bool { |
| 1735 | 1789 | return switch (ty.toIntern()) { |
| 1736 | .c_char_type => zcu.getTarget().cCharSignedness() == .signed, | |
| 1790 | .c_char_type => zcu.getTarget().cCharSignedness().? == .signed, | |
| 1737 | 1791 | .isize_type, .c_short_type, .c_int_type, .c_long_type, .c_longlong_type => true, |
| 1738 | 1792 | else => switch (zcu.intern_pool.indexToKey(ty.toIntern())) { |
| 1739 | 1793 | .int_type => |int_type| int_type.signedness == .signed, |
| ... | ... | @@ -1745,7 +1799,7 @@ pub fn isSignedInt(ty: Type, zcu: *const Zcu) bool { |
| 1745 | 1799 | /// Returns true if and only if the type is a fixed-width, unsigned integer. |
| 1746 | 1800 | pub fn isUnsignedInt(ty: Type, zcu: *const Zcu) bool { |
| 1747 | 1801 | return switch (ty.toIntern()) { |
| 1748 | .c_char_type => zcu.getTarget().cCharSignedness() == .unsigned, | |
| 1802 | .c_char_type => zcu.getTarget().cCharSignedness().? == .unsigned, | |
| 1749 | 1803 | .usize_type, .c_ushort_type, .c_uint_type, .c_ulong_type, .c_ulonglong_type => true, |
| 1750 | 1804 | else => switch (zcu.intern_pool.indexToKey(ty.toIntern())) { |
| 1751 | 1805 | .int_type => |int_type| int_type.signedness == .unsigned, |
| ... | ... | @@ -1776,15 +1830,15 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType { |
| 1776 | 1830 | }, |
| 1777 | 1831 | .usize_type => return .{ .signedness = .unsigned, .bits = target.ptrBitWidth() }, |
| 1778 | 1832 | .isize_type => return .{ .signedness = .signed, .bits = target.ptrBitWidth() }, |
| 1779 | .c_char_type => return .{ .signedness = zcu.getTarget().cCharSignedness(), .bits = target.cTypeBitSize(.char) }, | |
| 1780 | .c_short_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.short) }, | |
| 1781 | .c_ushort_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ushort) }, | |
| 1782 | .c_int_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.int) }, | |
| 1783 | .c_uint_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.uint) }, | |
| 1784 | .c_long_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.long) }, | |
| 1785 | .c_ulong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulong) }, | |
| 1786 | .c_longlong_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.longlong) }, | |
| 1787 | .c_ulonglong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulonglong) }, | |
| 1833 | .c_char_type => return .{ .signedness = target.cCharSignedness().?, .bits = target.cTypeBitSize(.char).? }, | |
| 1834 | .c_short_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.short).? }, | |
| 1835 | .c_ushort_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ushort).? }, | |
| 1836 | .c_int_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.int).? }, | |
| 1837 | .c_uint_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.uint).? }, | |
| 1838 | .c_long_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.long).? }, | |
| 1839 | .c_ulong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulong).? }, | |
| 1840 | .c_longlong_type => return .{ .signedness = .signed, .bits = target.cTypeBitSize(.longlong).? }, | |
| 1841 | .c_ulonglong_type => return .{ .signedness = .unsigned, .bits = target.cTypeBitSize(.ulonglong).? }, | |
| 1788 | 1842 | else => switch (ip.indexToKey(ty.toIntern())) { |
| 1789 | 1843 | .int_type => |int_type| return int_type, |
| 1790 | 1844 | .struct_type => { |
| ... | ... | @@ -1882,7 +1936,7 @@ pub fn floatBits(ty: Type, target: *const Target) u16 { |
| 1882 | 1936 | .f64_type => 64, |
| 1883 | 1937 | .f80_type => 80, |
| 1884 | 1938 | .f128_type, .comptime_float_type => 128, |
| 1885 | .c_longdouble_type => target.cTypeBitSize(.longdouble), | |
| 1939 | .c_longdouble_type => target.cTypeBitSize(.longdouble).?, | |
| 1886 | 1940 | |
| 1887 | 1941 | else => unreachable, |
| 1888 | 1942 | }; |
| ... | ... | @@ -2147,13 +2201,6 @@ pub fn isVector(ty: Type, zcu: *const Zcu) bool { |
| 2147 | 2201 | return ty.zigTypeTag(zcu) == .vector; |
| 2148 | 2202 | } |
| 2149 | 2203 | |
| 2150 | /// Returns 0 if not a vector, otherwise returns @bitSizeOf(Element) * vector_len. | |
| 2151 | pub fn totalVectorBits(ty: Type, zcu: *Zcu) u64 { | |
| 2152 | if (!ty.isVector(zcu)) return 0; | |
| 2153 | const v = zcu.intern_pool.indexToKey(ty.toIntern()).vector_type; | |
| 2154 | return v.len * Type.fromInterned(v.child).bitSize(zcu); | |
| 2155 | } | |
| 2156 | ||
| 2157 | 2204 | pub fn isArrayOrVector(ty: Type, zcu: *const Zcu) bool { |
| 2158 | 2205 | return switch (ty.zigTypeTag(zcu)) { |
| 2159 | 2206 | .array, .vector => true, |
| ... | ... | @@ -2416,34 +2463,6 @@ pub fn explicitFieldAlignment(ty: Type, index: usize, zcu: *const Zcu) Alignment |
| 2416 | 2463 | }; |
| 2417 | 2464 | } |
| 2418 | 2465 | |
| 2419 | /// Returns the alignment a struct field of type `field_ty` will be given if no alignment is | |
| 2420 | /// explicitly specified. However, in an `extern struct`, a higher alignment may be available due | |
| 2421 | /// to the struct's full layout (i.e. a field might coincidentally be more aligned). | |
| 2422 | /// | |
| 2423 | /// Asserts that the layout of `field_ty` is resolved. Asserts that `layout` is not `.@"packed"`. | |
| 2424 | pub fn defaultStructFieldAlignment( | |
| 2425 | field_ty: Type, | |
| 2426 | layout: std.lang.Type.ContainerLayout, | |
| 2427 | zcu: *const Zcu, | |
| 2428 | ) Alignment { | |
| 2429 | const overalign_big_int = switch (layout) { | |
| 2430 | .@"packed" => unreachable, | |
| 2431 | .auto => zcu.getTarget().ofmt == .c, | |
| 2432 | .@"extern" => true, | |
| 2433 | }; | |
| 2434 | const abi_align = field_ty.abiAlignment(zcu); | |
| 2435 | assert(abi_align != .none); | |
| 2436 | // We check for anything over 64 here, because the C backend will lower e.g. u64 to a 128-bit | |
| 2437 | // integer, which has 16-byte alignment. | |
| 2438 | if (overalign_big_int and | |
| 2439 | ((field_ty.isAbiInt(zcu) and field_ty.intInfo(zcu).bits > 64) or | |
| 2440 | (field_ty.toIntern() == .f80_type and zcu.getTarget().cTypeBitSize(.longdouble) != 80))) | |
| 2441 | { | |
| 2442 | return abi_align.maxStrict(if (zcu.getTarget().cpu.arch == .s390x) .@"8" else .@"16"); | |
| 2443 | } | |
| 2444 | return abi_align; | |
| 2445 | } | |
| 2446 | ||
| 2447 | 2466 | pub fn structFieldDefaultValue(ty: Type, index: usize, zcu: *const Zcu) ?Value { |
| 2448 | 2467 | const ip = &zcu.intern_pool; |
| 2449 | 2468 | switch (ip.indexToKey(ty.toIntern())) { |
| ... | ... | @@ -2961,8 +2980,7 @@ pub fn fieldPtrType(ptr_ty: Type, field_index: u32, pt: Zcu.PerThread) Allocator |
| 2961 | 2980 | } |
| 2962 | 2981 | const actual_field_align = switch (field_align) { |
| 2963 | 2982 | .none => switch (ip.indexToKey(aggregate_ty.toIntern())) { |
| 2964 | .tuple_type, .union_type => field_ty.abiAlignment(zcu), | |
| 2965 | .struct_type => field_ty.defaultStructFieldAlignment(.auto, zcu), | |
| 2983 | .struct_type, .tuple_type, .union_type => field_ty.abiAlignment(zcu), | |
| 2966 | 2984 | .ptr_type => Type.usize.abiAlignment(zcu), |
| 2967 | 2985 | else => unreachable, |
| 2968 | 2986 | }, |
| ... | ... | @@ -3603,5 +3621,5 @@ pub fn smallestUnsignedBits(max: u64) u16 { |
| 3603 | 3621 | pub const packed_struct_layout_version = 2; |
| 3604 | 3622 | |
| 3605 | 3623 | fn cTypeAlign(target: *const Target, c_type: Target.CType) Alignment { |
| 3606 | return Alignment.fromByteUnits(target.cTypeAlignment(c_type)); | |
| 3624 | return .fromByteUnits(target.cTypeAlignment(c_type).?); | |
| 3607 | 3625 | } |
src/Value.zig+1-6| ... | ... | @@ -611,12 +611,7 @@ pub fn toFloat(val: Value, comptime T: type, zcu: *const Zcu) T { |
| 611 | 611 | return switch (zcu.intern_pool.indexToKey(val.toIntern())) { |
| 612 | 612 | .int => |int| switch (int.storage) { |
| 613 | 613 | .big_int => |big_int| big_int.toFloat(T, .nearest_even)[0], |
| 614 | inline .u64, .i64 => |x| { | |
| 615 | if (T == f80) { | |
| 616 | @panic("TODO we can't lower this properly on non-x86 llvm backend yet"); | |
| 617 | } | |
| 618 | return @floatFromInt(x); | |
| 619 | }, | |
| 614 | inline .u64, .i64 => |x| @floatFromInt(x), | |
| 620 | 615 | }, |
| 621 | 616 | .float => |float| switch (float.storage) { |
| 622 | 617 | inline else => |x| @floatCast(x), |
src/codegen/aarch64/Select.zig+1-1| ... | ... | @@ -12388,7 +12388,7 @@ pub const CallAbiIterator = struct { |
| 12388 | 12388 | .f32 => .single, |
| 12389 | 12389 | .f64 => .double, |
| 12390 | 12390 | .f128 => .quad, |
| 12391 | .c_longdouble => switch (zcu.getTarget().cTypeBitSize(.longdouble)) { | |
| 12391 | .c_longdouble => switch (zcu.getTarget().cTypeBitSize(.longdouble).?) { | |
| 12392 | 12392 | else => unreachable, |
| 12393 | 12393 | 64 => .double, |
| 12394 | 12394 | 80 => null, |
src/codegen/aarch64/abi.zig+6-1| ... | ... | @@ -35,7 +35,12 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class { |
| 35 | 35 | if (bit_size > 64) return .double_integer; |
| 36 | 36 | return .integer; |
| 37 | 37 | }, |
| 38 | .int, .@"enum", .error_set, .float, .bool => return .byval, | |
| 38 | .int, .@"enum", .error_set, .bool => return .byval, | |
| 39 | .float => return switch (ty.floatBits(zcu.getTarget())) { | |
| 40 | else => unreachable, | |
| 41 | 16, 32, 64, 128 => .byval, | |
| 42 | 80 => .double_integer, | |
| 43 | }, | |
| 39 | 44 | .vector => { |
| 40 | 45 | const bit_size = ty.bitSize(zcu); |
| 41 | 46 | // TODO is this controlled by a cpu feature? |
src/codegen/arm/abi.zig+9-7| ... | ... | @@ -39,7 +39,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { |
| 39 | 39 | const float_count = countFloats(ty, zcu, &maybe_float_bits); |
| 40 | 40 | if (float_count <= byval_float_count) return .byval; |
| 41 | 41 | |
| 42 | if (ty.abiAlignment(zcu).compare(.gt, .@"32")) { | |
| 42 | if (ty.abiAlignment(zcu).compare(.gt, .@"4")) { | |
| 43 | 43 | return Class.arrSize(bit_size, 64); |
| 44 | 44 | } |
| 45 | 45 | |
| ... | ... | @@ -62,7 +62,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { |
| 62 | 62 | const float_count = countFloats(ty, zcu, &maybe_float_bits); |
| 63 | 63 | if (float_count <= byval_float_count) return .byval; |
| 64 | 64 | |
| 65 | if (union_obj.alignment.compareStrict(.gt, .@"32")) { | |
| 65 | if (union_obj.alignment.compareStrict(.gt, .@"4")) { | |
| 66 | 66 | return Class.arrSize(bit_size, 64); |
| 67 | 67 | } |
| 68 | 68 | |
| ... | ... | @@ -73,14 +73,16 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { |
| 73 | 73 | } |
| 74 | 74 | return Class.arrSize(bit_size, 32); |
| 75 | 75 | }, |
| 76 | .bool, .float => return .byval, | |
| 76 | .bool => return .byval, | |
| 77 | 77 | .int => { |
| 78 | // TODO this is incorrect for _BitInt(128) but implementing | |
| 79 | // this correctly makes implementing compiler-rt impossible. | |
| 80 | // const bit_size = ty.bitSize(zcu); | |
| 81 | // if (bit_size > 64) return .memory; | |
| 78 | if (ctx == .ret and ty.intInfo(zcu).bits > 64) return .memory; | |
| 82 | 79 | return .byval; |
| 83 | 80 | }, |
| 81 | .float => return switch (ty.floatBits(zcu.getTarget())) { | |
| 82 | else => unreachable, | |
| 83 | 16, 32, 64 => .byval, | |
| 84 | 80, 128 => .{ .i64_array = 2 }, | |
| 85 | }, | |
| 84 | 86 | .@"enum", .error_set => { |
| 85 | 87 | const bit_size = ty.bitSize(zcu); |
| 86 | 88 | if (bit_size > 64) return .memory; |
src/codegen/c/type.zig+22-22| ... | ... | @@ -130,28 +130,28 @@ pub const CType = union(enum) { |
| 130 | 130 | pub fn bits(int: Int, target: *const std.Target) u16 { |
| 131 | 131 | return switch (int) { |
| 132 | 132 | // zig fmt: off |
| 133 | .char => target.cTypeBitSize(.char), | |
| 134 | ||
| 135 | .@"unsigned short" => target.cTypeBitSize(.ushort), | |
| 136 | .@"unsigned int" => target.cTypeBitSize(.uint), | |
| 137 | .@"unsigned long" => target.cTypeBitSize(.ulong), | |
| 138 | .@"unsigned long long" => target.cTypeBitSize(.ulonglong), | |
| 139 | ||
| 140 | .@"signed short" => target.cTypeBitSize(.short), | |
| 141 | .@"signed int" => target.cTypeBitSize(.int), | |
| 142 | .@"signed long" => target.cTypeBitSize(.long), | |
| 143 | .@"signed long long" => target.cTypeBitSize(.longlong), | |
| 144 | ||
| 145 | .uintptr_t, .intptr_t => target.ptrBitWidth(), | |
| 146 | ||
| 147 | .uint8_t, .int8_t => 8, | |
| 148 | .uint16_t, .int16_t => 16, | |
| 149 | .uint24_t, .int24_t => 24, | |
| 150 | .uint32_t, .int32_t => 32, | |
| 151 | .uint48_t, .int48_t => 48, | |
| 152 | .uint64_t, .int64_t => 64, | |
| 153 | .zig_u128, .zig_i128 => 128, | |
| 154 | // zig fmt: on | |
| 133 | .char => target.cTypeBitSize(.char).?, | |
| 134 | ||
| 135 | .@"unsigned short" => target.cTypeBitSize(.ushort).?, | |
| 136 | .@"unsigned int" => target.cTypeBitSize(.uint).?, | |
| 137 | .@"unsigned long" => target.cTypeBitSize(.ulong).?, | |
| 138 | .@"unsigned long long" => target.cTypeBitSize(.ulonglong).?, | |
| 139 | ||
| 140 | .@"signed short" => target.cTypeBitSize(.short).?, | |
| 141 | .@"signed int" => target.cTypeBitSize(.int).?, | |
| 142 | .@"signed long" => target.cTypeBitSize(.long).?, | |
| 143 | .@"signed long long" => target.cTypeBitSize(.longlong).?, | |
| 144 | ||
| 145 | .uintptr_t, .intptr_t => target.ptrBitWidth(), | |
| 146 | ||
| 147 | .uint8_t, .int8_t => 8, | |
| 148 | .uint16_t, .int16_t => 16, | |
| 149 | .uint24_t, .int24_t => 24, | |
| 150 | .uint32_t, .int32_t => 32, | |
| 151 | .uint48_t, .int48_t => 48, | |
| 152 | .uint64_t, .int64_t => 64, | |
| 153 | .zig_u128, .zig_i128 => 128, | |
| 154 | // zig fmt: on | |
| 155 | 155 | }; |
| 156 | 156 | } |
| 157 | 157 | }; |
src/codegen/c/type/render_defs.zig+14-12| ... | ... | @@ -381,7 +381,7 @@ fn defineTuple( |
| 381 | 381 | const overalign: bool = for (tuple.types.get(ip)) |field_ty_ip| { |
| 382 | 382 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 383 | 383 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 384 | const natural_align = field_ty.defaultStructFieldAlignment(.auto, zcu); | |
| 384 | const natural_align = field_ty.abiAlignment(zcu); | |
| 385 | 385 | if (natural_align.compareStrict(.gte, tuple_align)) break false; |
| 386 | 386 | } else true; |
| 387 | 387 | |
| ... | ... | @@ -402,15 +402,17 @@ fn defineTuple( |
| 402 | 402 | if (zig_offset == 0 and overalign) { |
| 403 | 403 | // This is the first field; specify its alignment to align the tuple. |
| 404 | 404 | try writeFieldAlign(field_ty, tuple_align, w, zcu); |
| 405 | } else if (zig_offset > c_offset) { | |
| 406 | // This field needs to be overaligned compared to what its offset would otherwise be. | |
| 407 | const need_align: Alignment = .minStrict( | |
| 408 | tuple_align, // don't make the struct more aligned than it should be | |
| 409 | .fromLog2Units(@ctz(zig_offset)), | |
| 410 | ); | |
| 411 | try writeFieldAlign(field_ty, need_align, w, zcu); | |
| 412 | c_offset = need_align.forward(c_offset); | |
| 405 | } else switch (zig_offset - c_offset) { | |
| 406 | 0 => {}, | |
| 407 | else => |need_bytes| { | |
| 408 | // This field needs to be overaligned compared to what its offset would otherwise be. | |
| 409 | const need_align: Alignment = .fromLog2Units(std.math.log2_int(u64, need_bytes) + 1); | |
| 410 | assert(need_align.compareStrict(.lte, tuple_align)); | |
| 411 | try writeFieldAlign(field_ty, need_align, w, zcu); | |
| 412 | c_offset = need_align.forward(c_offset); | |
| 413 | }, | |
| 413 | 414 | } |
| 415 | assert(c_offset == zig_offset); | |
| 414 | 416 | const field_cty: CType = try .lower(field_ty, deps, arena, zcu); |
| 415 | 417 | try w.print("{f}f{d}{f};\n", .{ |
| 416 | 418 | field_cty.fmtDeclaratorPrefix(zcu), |
| ... | ... | @@ -443,7 +445,7 @@ fn defineStruct( |
| 443 | 445 | while (it.next()) |field_index| { |
| 444 | 446 | const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); |
| 445 | 447 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 446 | const natural_align = field_ty.defaultStructFieldAlignment(struct_type.layout, zcu); | |
| 448 | const natural_align = field_ty.abiAlignment(zcu); | |
| 447 | 449 | const natural_offset = natural_align.forward(offset); |
| 448 | 450 | const actual_offset = struct_type.field_offsets.get(ip)[field_index]; |
| 449 | 451 | if (actual_offset < natural_offset) break :pack true; |
| ... | ... | @@ -464,7 +466,7 @@ fn defineStruct( |
| 464 | 466 | while (it.next()) |field_index| { |
| 465 | 467 | const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); |
| 466 | 468 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 467 | const natural_align = field_ty.defaultStructFieldAlignment(struct_type.layout, zcu); | |
| 469 | const natural_align = field_ty.abiAlignment(zcu); | |
| 468 | 470 | if (natural_align.compareStrict(.gte, struct_type.alignment)) break :overalign false; |
| 469 | 471 | } |
| 470 | 472 | break :overalign true; |
| ... | ... | @@ -481,7 +483,7 @@ fn defineStruct( |
| 481 | 483 | while (it.next()) |field_index| { |
| 482 | 484 | const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); |
| 483 | 485 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 484 | const natural_align = field_ty.defaultStructFieldAlignment(struct_type.layout, zcu); | |
| 486 | const natural_align = field_ty.abiAlignment(zcu); | |
| 485 | 487 | const natural_offset = switch (pack) { |
| 486 | 488 | true => offset, |
| 487 | 489 | false => natural_align.forward(offset), |
src/codegen/llvm.zig+608-316| ... | ... | @@ -573,6 +573,8 @@ pub const Object = struct { |
| 573 | 573 | val: InternPool.Index, |
| 574 | 574 | @"addrspace": std.lang.AddressSpace, |
| 575 | 575 | }, Builder.Variable.Index), |
| 576 | /// Same as `uav_map` but for llvm values not originating from the frontend. | |
| 577 | const_map: std.AutoHashMapUnmanaged(Builder.Constant, Builder.Variable.Index), | |
| 576 | 578 | /// Maps enum types to their corresponding LLVM functions for implementing the `tag_name` instruction. |
| 577 | 579 | enum_tag_name_map: std.AutoHashMapUnmanaged(InternPool.Index, Builder.Function.Index), |
| 578 | 580 | /// 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 { |
| 693 | 695 | .zcu = zcu, |
| 694 | 696 | .nav_map = .empty, |
| 695 | 697 | .uav_map = .empty, |
| 698 | .const_map = .empty, | |
| 696 | 699 | .enum_tag_name_map = .empty, |
| 697 | 700 | .named_enum_map = .empty, |
| 698 | 701 | .type_map = .empty, |
| ... | ... | @@ -703,21 +706,22 @@ pub const Object = struct { |
| 703 | 706 | return obj; |
| 704 | 707 | } |
| 705 | 708 | |
| 706 | pub fn deinit(self: *Object) void { | |
| 707 | const gpa = self.gpa; | |
| 708 | self.type_pool.deinit(gpa); | |
| 709 | self.lazy_abi_aligns.deinit(gpa); | |
| 710 | self.debug_enums.deinit(gpa); | |
| 711 | self.debug_globals.deinit(gpa); | |
| 712 | self.debug_file_map.deinit(gpa); | |
| 713 | self.debug_types.deinit(gpa); | |
| 714 | self.nav_map.deinit(gpa); | |
| 715 | self.uav_map.deinit(gpa); | |
| 716 | self.enum_tag_name_map.deinit(gpa); | |
| 717 | self.named_enum_map.deinit(gpa); | |
| 718 | self.type_map.deinit(gpa); | |
| 719 | self.builder.deinit(); | |
| 720 | self.* = undefined; | |
| 709 | pub fn deinit(o: *Object) void { | |
| 710 | const gpa = o.gpa; | |
| 711 | o.type_pool.deinit(gpa); | |
| 712 | o.lazy_abi_aligns.deinit(gpa); | |
| 713 | o.debug_enums.deinit(gpa); | |
| 714 | o.debug_globals.deinit(gpa); | |
| 715 | o.debug_file_map.deinit(gpa); | |
| 716 | o.debug_types.deinit(gpa); | |
| 717 | o.nav_map.deinit(gpa); | |
| 718 | o.uav_map.deinit(gpa); | |
| 719 | o.const_map.deinit(gpa); | |
| 720 | o.enum_tag_name_map.deinit(gpa); | |
| 721 | o.named_enum_map.deinit(gpa); | |
| 722 | o.type_map.deinit(gpa); | |
| 723 | o.builder.deinit(); | |
| 724 | o.* = undefined; | |
| 721 | 725 | } |
| 722 | 726 | |
| 723 | 727 | fn genErrorNameTable(o: *Object) Allocator.Error!void { |
| ... | ... | @@ -741,16 +745,16 @@ pub const Object = struct { |
| 741 | 745 | for (llvm_errors[1..], error_name_list) |*llvm_error, name| { |
| 742 | 746 | const name_string = try o.builder.stringNull(name.toSlice(ip)); |
| 743 | 747 | const name_init = try o.builder.stringConst(name_string); |
| 744 | const name_variable_index = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); | |
| 745 | try name_variable_index.setInitializer(name_init, &o.builder); | |
| 746 | name_variable_index.setMutability(.constant, &o.builder); | |
| 747 | name_variable_index.setAlignment(comptime .fromByteUnits(1), &o.builder); | |
| 748 | const global_index = name_variable_index.ptrConst(&o.builder).global; | |
| 749 | global_index.setLinkage(.private, &o.builder); | |
| 750 | global_index.setUnnamedAddr(.unnamed_addr, &o.builder); | |
| 748 | const name_llvm_variable = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); | |
| 749 | try name_llvm_variable.setInitializer(name_init, &o.builder); | |
| 750 | name_llvm_variable.setMutability(.constant, &o.builder); | |
| 751 | name_llvm_variable.setAlignment(comptime .fromByteUnits(1), &o.builder); | |
| 752 | const llvm_global = name_llvm_variable.ptrConst(&o.builder).global; | |
| 753 | llvm_global.setLinkage(.private, &o.builder); | |
| 754 | llvm_global.setUnnamedAddr(.unnamed_addr, &o.builder); | |
| 751 | 755 | |
| 752 | 756 | llvm_error.* = try o.builder.structConst(llvm_slice_ty, &.{ |
| 753 | name_variable_index.toConst(&o.builder), | |
| 757 | name_llvm_variable.toConst(&o.builder), | |
| 754 | 758 | try o.builder.intConst(llvm_usize_ty, name_string.slice(&o.builder).?.len - 1), |
| 755 | 759 | }); |
| 756 | 760 | } |
| ... | ... | @@ -1199,19 +1203,33 @@ pub const Object = struct { |
| 1199 | 1203 | global.dll_storage_class = .default; |
| 1200 | 1204 | global.unnamed_addr = .unnamed_addr; |
| 1201 | 1205 | } |
| 1202 | llvm_function.setAlignment(switch (nav.resolved.?.@"align") { | |
| 1203 | .none => fn_ty.abiAlignment(zcu).toLlvm(), | |
| 1204 | else => |a| a.toLlvm(), | |
| 1205 | }, &o.builder); | |
| 1206 | llvm_function.setAlignment(nav.resolved.?.@"align".toLlvm(), &o.builder); | |
| 1206 | 1207 | llvm_function.setSection(s: { |
| 1207 | 1208 | const section = nav.resolved.?.@"linksection".toSlice(ip) orelse break :s .none; |
| 1208 | 1209 | break :s try o.builder.string(section); |
| 1209 | 1210 | }, &o.builder); |
| 1210 | try o.addLlvmFunctionAttributes(pt, func.owner_nav, llvm_function); | |
| 1211 | 1211 | |
| 1212 | var attributes = try llvm_function.ptrConst(&o.builder).attributes.toWip(&o.builder); | |
| 1212 | var attributes: Builder.FunctionAttributes.Wip = .{}; | |
| 1213 | 1213 | defer attributes.deinit(&o.builder); |
| 1214 | 1214 | |
| 1215 | // Function attributes that are independent of analysis results of the function body. | |
| 1216 | try o.addCommonFnAttributes( | |
| 1217 | &attributes, | |
| 1218 | owner_mod, | |
| 1219 | // Some backends don't respect the `naked` attribute in `TargetFrameLowering::hasFP()`, | |
| 1220 | // so for these backends, LLVM will happily emit code that accesses the stack through | |
| 1221 | // the frame pointer. This is nonsensical since what the `naked` attribute does is | |
| 1222 | // suppress generation of the prologue and epilogue, and the prologue is where the | |
| 1223 | // frame pointer normally gets set up. At time of writing, this is the case for at | |
| 1224 | // least x86 and RISC-V. | |
| 1225 | owner_mod.omit_frame_pointer or fn_info.cc == .naked, | |
| 1226 | ); | |
| 1227 | ||
| 1228 | try o.addCallingConventionFnAttributes(pt, llvm_function, &attributes, if (nav.getExtern(ip)) |@"extern"| .{ | |
| 1229 | .name = nav.name.toSlice(ip), | |
| 1230 | .lib_name = @"extern".lib_name.toSlice(ip), | |
| 1231 | } else null, .fromIntern(fn_info, ip)); | |
| 1232 | ||
| 1215 | 1233 | const func_analysis = func.analysisUnordered(ip); |
| 1216 | 1234 | if (func_analysis.is_noinline) { |
| 1217 | 1235 | try attributes.addFnAttr(.@"noinline", &o.builder); |
| ... | ... | @@ -1324,7 +1342,7 @@ pub const Object = struct { |
| 1324 | 1342 | const counters_variable = try o.builder.addVariable(anon_name, .void, .default); |
| 1325 | 1343 | try o.used.append(gpa, counters_variable.toConst(&o.builder)); |
| 1326 | 1344 | counters_variable.ptrConst(&o.builder).global.setLinkage(.private, &o.builder); |
| 1327 | counters_variable.setAlignment(comptime Builder.Alignment.fromByteUnits(1), &o.builder); | |
| 1345 | counters_variable.setAlignment(comptime .fromByteUnits(1), &o.builder); | |
| 1328 | 1346 | |
| 1329 | 1347 | if (target.ofmt == .macho) { |
| 1330 | 1348 | counters_variable.setSection(try o.builder.string("__DATA,__sancov_cntrs"), &o.builder); |
| ... | ... | @@ -1507,10 +1525,6 @@ pub const Object = struct { |
| 1507 | 1525 | llvm_global.ptr(&o.builder).unnamed_addr = .unnamed_addr; |
| 1508 | 1526 | } |
| 1509 | 1527 | |
| 1510 | const llvm_align = switch (resolved.@"align") { | |
| 1511 | .none => nav_ty.abiAlignment(zcu).toLlvm(), | |
| 1512 | else => |a| a.toLlvm(), | |
| 1513 | }; | |
| 1514 | 1528 | const llvm_section: Builder.String = if (resolved.@"linksection".toSlice(ip)) |section| s: { |
| 1515 | 1529 | break :s try o.builder.string(section); |
| 1516 | 1530 | } else .none; |
| ... | ... | @@ -1519,13 +1533,20 @@ pub const Object = struct { |
| 1519 | 1533 | // can see are extern functions or other comptime function body values (e.g. undefined). Of |
| 1520 | 1534 | // these, only extern functions need to be lowered to LLVM functions. |
| 1521 | 1535 | if (opt_extern != null and nav_ty.zigTypeTag(zcu) == .@"fn" and nav_ty.fnHasRuntimeBits(zcu)) { |
| 1536 | const fn_info = zcu.typeToFunc(nav_ty).?; | |
| 1522 | 1537 | const llvm_function: Builder.Function.Index = switch (llvm_global.ptrConst(&o.builder).kind) { |
| 1523 | 1538 | .function => |function| function, // re-use existing `Builder.Function` |
| 1524 | 1539 | .replaced, .alias, .variable => try llvm_global.toNewFunction(&o.builder), |
| 1525 | 1540 | }; |
| 1526 | llvm_function.setAlignment(llvm_align, &o.builder); | |
| 1541 | llvm_function.setAlignment(resolved.@"align".toLlvm(), &o.builder); | |
| 1527 | 1542 | llvm_function.setSection(llvm_section, &o.builder); |
| 1528 | try o.addLlvmFunctionAttributes(pt, nav_id, llvm_function); | |
| 1543 | var attributes: Builder.FunctionAttributes.Wip = .{}; | |
| 1544 | defer attributes.deinit(&o.builder); | |
| 1545 | try o.addCallingConventionFnAttributes(pt, llvm_function, &attributes, .{ | |
| 1546 | .name = nav.name.toSlice(ip), | |
| 1547 | .lib_name = opt_extern.?.lib_name.toSlice(ip), | |
| 1548 | }, .fromIntern(fn_info, ip)); | |
| 1549 | llvm_function.setAttributes(try attributes.finish(&o.builder), &o.builder); | |
| 1529 | 1550 | } else { |
| 1530 | 1551 | const file_scope = nav.srcInst(ip).resolveFile(ip); |
| 1531 | 1552 | const mod = zcu.fileByIndex(file_scope).mod.?; |
| ... | ... | @@ -1534,7 +1555,10 @@ pub const Object = struct { |
| 1534 | 1555 | .variable => |variable| variable, // re-use existing `Builder.Variable` |
| 1535 | 1556 | .replaced, .alias, .function => try llvm_global.toNewVariable(&o.builder), |
| 1536 | 1557 | }; |
| 1537 | llvm_variable.setAlignment(llvm_align, &o.builder); | |
| 1558 | llvm_variable.setAlignment(switch (resolved.@"align") { | |
| 1559 | .none => nav_ty.abiAlignment(zcu).toLlvm(), | |
| 1560 | else => |a| a.toLlvm(), | |
| 1561 | }, &o.builder); | |
| 1538 | 1562 | llvm_variable.setSection(llvm_section, &o.builder); |
| 1539 | 1563 | llvm_variable.setMutability(if (resolved.@"const") .constant else .global, &o.builder); |
| 1540 | 1564 | 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 { |
| 1585 | 1609 | const uav_ty = Value.fromInterned(uav).typeOf(zcu); |
| 1586 | 1610 | const uav_ref = try o.lowerUavRef( |
| 1587 | 1611 | uav, |
| 1588 | uav_ty.abiAlignment(zcu), | |
| 1612 | uav_ty.abiAlignment(zcu).toLlvm(), | |
| 1589 | 1613 | target_util.defaultAddressSpace(zcu.getTarget(), .global_constant), |
| 1590 | 1614 | ); |
| 1591 | 1615 | break :exp .{ uav_ty, uav_ref }; |
| ... | ... | @@ -1599,7 +1623,7 @@ pub const Object = struct { |
| 1599 | 1623 | |
| 1600 | 1624 | fn updateExportedGlobal( |
| 1601 | 1625 | o: *Object, |
| 1602 | global_index: Builder.Global.Index, | |
| 1626 | llvm_global: Builder.Global.Index, | |
| 1603 | 1627 | ty: Type, |
| 1604 | 1628 | export_indices: []const Zcu.Export.Index, |
| 1605 | 1629 | ) link.Error!void { |
| ... | ... | @@ -1634,11 +1658,11 @@ pub const Object = struct { |
| 1634 | 1658 | // make much sense: the linksection should be associated with the declaration itself rather |
| 1635 | 1659 | // than some particular symbol it is exported as! |
| 1636 | 1660 | if (export_indices[0].ptr(zcu).opts.section.toSlice(ip)) |section_slice| { |
| 1637 | const variable = &global_index.ptrConst(&o.builder).kind.variable; | |
| 1661 | const variable = &llvm_global.ptrConst(&o.builder).kind.variable; | |
| 1638 | 1662 | variable.setSection(try o.builder.string(section_slice), &o.builder); |
| 1639 | 1663 | } |
| 1640 | 1664 | |
| 1641 | const llvm_global_ty = global_index.typeOf(&o.builder); | |
| 1665 | const llvm_global_ty = llvm_global.typeOf(&o.builder); | |
| 1642 | 1666 | |
| 1643 | 1667 | // All exports are represented as aliases to the original global. |
| 1644 | 1668 | |
| ... | ... | @@ -1661,8 +1685,8 @@ pub const Object = struct { |
| 1661 | 1685 | const alias = try o.builder.addAlias( |
| 1662 | 1686 | exp_name, |
| 1663 | 1687 | llvm_global_ty, |
| 1664 | global_index.ptrConst(&o.builder).addr_space, | |
| 1665 | global_index.toConst(), | |
| 1688 | llvm_global.ptrConst(&o.builder).addr_space, | |
| 1689 | llvm_global.toConst(), | |
| 1666 | 1690 | ); |
| 1667 | 1691 | break :global alias.ptrConst(&o.builder).global; |
| 1668 | 1692 | }; |
| ... | ... | @@ -1671,12 +1695,9 @@ pub const Object = struct { |
| 1671 | 1695 | switch (existing_global.ptrConst(&o.builder).kind) { |
| 1672 | 1696 | .alias => |alias| { |
| 1673 | 1697 | // We can just repurpose the existing alias. |
| 1674 | alias.setAliasee(global_index.toConst(), &o.builder); | |
| 1675 | alias.ptrConst(&o.builder).global.ptr(&o.builder).type = global_index.typeOf(&o.builder); | |
| 1676 | // If the type the alias is pointing to can change, then | |
| 1677 | // it makes sense that we should update the address | |
| 1678 | // space too. | |
| 1679 | alias.ptrConst(&o.builder).global.ptr(&o.builder).addr_space = global_index.ptrConst(&o.builder).addr_space; | |
| 1698 | alias.setAliasee(llvm_global.toConst(), &o.builder); | |
| 1699 | alias.ptrConst(&o.builder).global.ptr(&o.builder).type = llvm_global.typeOf(&o.builder); | |
| 1700 | alias.ptrConst(&o.builder).global.ptr(&o.builder).addr_space = llvm_global.ptrConst(&o.builder).addr_space; | |
| 1680 | 1701 | break :global existing_global; |
| 1681 | 1702 | }, |
| 1682 | 1703 | .variable, .function => { |
| ... | ... | @@ -1686,13 +1707,13 @@ pub const Object = struct { |
| 1686 | 1707 | // We need to make a new global which is an alias. Replace this existing one |
| 1687 | 1708 | // with the target global, making the name available and fixing references |
| 1688 | 1709 | // to this global to point to the target. |
| 1689 | try existing_global.replace(global_index, &o.builder); | |
| 1710 | try existing_global.replace(llvm_global, &o.builder); | |
| 1690 | 1711 | // The name is now free, so create an alias. |
| 1691 | 1712 | const alias = try o.builder.addAlias( |
| 1692 | 1713 | exp_name, |
| 1693 | 1714 | llvm_global_ty, |
| 1694 | global_index.ptrConst(&o.builder).addr_space, | |
| 1695 | global_index.toConst(), | |
| 1715 | llvm_global.ptrConst(&o.builder).addr_space, | |
| 1716 | llvm_global.toConst(), | |
| 1696 | 1717 | ); |
| 1697 | 1718 | break :global alias.ptrConst(&o.builder).global; |
| 1698 | 1719 | }, |
| ... | ... | @@ -1725,11 +1746,11 @@ pub const Object = struct { |
| 1725 | 1746 | pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Allocator.Error!void { |
| 1726 | 1747 | _ = o.type_map.remove(ty); |
| 1727 | 1748 | try o.type_pool.updateContainerType(pt, .{ .llvm = o }, ty, success); |
| 1728 | if (o.named_enum_map.get(ty)) |function_index| { | |
| 1729 | try o.updateIsNamedEnumValueFunction(.fromInterned(ty), function_index); | |
| 1749 | if (o.named_enum_map.get(ty)) |llvm_function| { | |
| 1750 | try o.updateIsNamedEnumValueFunction(.fromInterned(ty), llvm_function); | |
| 1730 | 1751 | } |
| 1731 | if (o.enum_tag_name_map.get(ty)) |function_index| { | |
| 1732 | try o.updateEnumTagNameFunction(.fromInterned(ty), function_index); | |
| 1752 | if (o.enum_tag_name_map.get(ty)) |llvm_function| { | |
| 1753 | try o.updateEnumTagNameFunction(.fromInterned(ty), llvm_function); | |
| 1733 | 1754 | } |
| 1734 | 1755 | } |
| 1735 | 1756 | |
| ... | ... | @@ -2102,7 +2123,7 @@ pub const Object = struct { |
| 2102 | 2123 | payload_offset * 8, |
| 2103 | 2124 | ); |
| 2104 | 2125 | |
| 2105 | return try o.builder.debugStructType( | |
| 2126 | return o.builder.debugStructType( | |
| 2106 | 2127 | name, |
| 2107 | 2128 | null, // File |
| 2108 | 2129 | o.debug_compile_unit.unwrap().?, // Scope |
| ... | ... | @@ -2140,7 +2161,7 @@ pub const Object = struct { |
| 2140 | 2161 | defer debug_param_types.deinit(gpa); |
| 2141 | 2162 | |
| 2142 | 2163 | // Return type goes first. |
| 2143 | if (try fnReturnStrat(o, fn_info) == .sret) { | |
| 2164 | if (try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type)) == .sret) { | |
| 2144 | 2165 | // Actual return type is void, then first arg is the sret pointer. |
| 2145 | 2166 | const ptr_ty = try pt.singleMutPtrType(.fromInterned(fn_info.return_type)); |
| 2146 | 2167 | debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, .void)); |
| ... | ... | @@ -2575,50 +2596,114 @@ pub const Object = struct { |
| 2575 | 2596 | fn namespaceToDebugScope(o: *Object, pt: Zcu.PerThread, namespace_index: InternPool.NamespaceIndex) !Builder.Metadata { |
| 2576 | 2597 | const zcu = o.zcu; |
| 2577 | 2598 | const namespace = zcu.namespacePtr(namespace_index); |
| 2578 | if (namespace.parent == .none) return try o.getDebugFile(namespace.file_scope); | |
| 2599 | if (namespace.parent == .none) return o.getDebugFile(namespace.file_scope); | |
| 2579 | 2600 | return o.getDebugType(pt, .fromInterned(namespace.owner_type)); |
| 2580 | 2601 | } |
| 2581 | 2602 | |
| 2582 | /// Sets the attributes and callconv of the given `Builder.Function`, which corresponds to the | |
| 2583 | /// given `Nav` (which is a function). | |
| 2584 | fn addLlvmFunctionAttributes( | |
| 2603 | fn addCommonFnAttributes( | |
| 2604 | o: *Object, | |
| 2605 | attributes: *Builder.FunctionAttributes.Wip, | |
| 2606 | owner_mod: *Module, | |
| 2607 | omit_frame_pointer: bool, | |
| 2608 | ) Allocator.Error!void { | |
| 2609 | if (!owner_mod.red_zone) { | |
| 2610 | try attributes.addFnAttr(.noredzone, &o.builder); | |
| 2611 | } | |
| 2612 | if (omit_frame_pointer) { | |
| 2613 | try attributes.addFnAttr(.{ .string = .{ | |
| 2614 | .kind = try o.builder.string("frame-pointer"), | |
| 2615 | .value = try o.builder.string("none"), | |
| 2616 | } }, &o.builder); | |
| 2617 | } else { | |
| 2618 | try attributes.addFnAttr(.{ .string = .{ | |
| 2619 | .kind = try o.builder.string("frame-pointer"), | |
| 2620 | .value = try o.builder.string("all"), | |
| 2621 | } }, &o.builder); | |
| 2622 | } | |
| 2623 | try attributes.addFnAttr(.nounwind, &o.builder); | |
| 2624 | if (owner_mod.unwind_tables != .none) { | |
| 2625 | try attributes.addFnAttr( | |
| 2626 | .{ .uwtable = if (owner_mod.unwind_tables == .async) .async else .sync }, | |
| 2627 | &o.builder, | |
| 2628 | ); | |
| 2629 | } | |
| 2630 | if (owner_mod.optimize_mode == .small) { | |
| 2631 | try attributes.addFnAttr(.minsize, &o.builder); | |
| 2632 | try attributes.addFnAttr(.optsize, &o.builder); | |
| 2633 | } | |
| 2634 | const target = &owner_mod.resolved_target.result; | |
| 2635 | if (target.cpu.model.llvm_name) |s| { | |
| 2636 | try attributes.addFnAttr(.{ .string = .{ | |
| 2637 | .kind = try o.builder.string("target-cpu"), | |
| 2638 | .value = try o.builder.string(s), | |
| 2639 | } }, &o.builder); | |
| 2640 | } | |
| 2641 | if (owner_mod.resolved_target.llvm_cpu_features) |s| { | |
| 2642 | try attributes.addFnAttr(.{ .string = .{ | |
| 2643 | .kind = try o.builder.string("target-features"), | |
| 2644 | .value = try o.builder.string(std.mem.span(s)), | |
| 2645 | } }, &o.builder); | |
| 2646 | } | |
| 2647 | if (target.abi.float() == .soft) { | |
| 2648 | // `use-soft-float` means "use software routines for floating point computations". In | |
| 2649 | // other words, it configures how LLVM lowers basic float instructions like `fcmp`, | |
| 2650 | // `fadd`, etc. The float calling convention is configured on `TargetMachine` and is | |
| 2651 | // mostly an orthogonal concept, although obviously we do need hardware float operations | |
| 2652 | // to actually be able to pass float values in float registers. | |
| 2653 | // | |
| 2654 | // Ideally, we would support something akin to the `-mfloat-abi=softfp` option that GCC | |
| 2655 | // and Clang support for Arm32 and CSKY. We don't currently expose such an option in | |
| 2656 | // Zig, and using CPU features as the source of truth for this makes for a miserable | |
| 2657 | // user experience since people expect e.g. `arm-linux-gnueabi` to mean full soft float | |
| 2658 | // unless the compiler has explicitly been told otherwise. (And note that our baseline | |
| 2659 | // CPU models almost all include FPU features!) | |
| 2660 | // | |
| 2661 | // Revisit this at some point. | |
| 2662 | try attributes.addFnAttr(.{ .string = .{ | |
| 2663 | .kind = try o.builder.string("use-soft-float"), | |
| 2664 | .value = try o.builder.string("true"), | |
| 2665 | } }, &o.builder); | |
| 2666 | ||
| 2667 | // This prevents LLVM from using FPU/SIMD code for things like `memcpy`. As for the | |
| 2668 | // above, this should be revisited if `softfp` support is added. | |
| 2669 | try attributes.addFnAttr(.noimplicitfloat, &o.builder); | |
| 2670 | } | |
| 2671 | } | |
| 2672 | ||
| 2673 | pub fn addCallingConventionFnAttributes( | |
| 2585 | 2674 | o: *Object, |
| 2586 | 2675 | pt: Zcu.PerThread, |
| 2587 | nav_id: InternPool.Nav.Index, | |
| 2588 | function_index: Builder.Function.Index, | |
| 2676 | llvm_function: Builder.Function.Index, | |
| 2677 | attributes: *Builder.FunctionAttributes.Wip, | |
| 2678 | opt_extern: ?struct { | |
| 2679 | name: []const u8, | |
| 2680 | lib_name: ?[]const u8 = null, | |
| 2681 | }, | |
| 2682 | fn_info: FuncInfo, | |
| 2589 | 2683 | ) Allocator.Error!void { |
| 2590 | 2684 | const zcu = o.zcu; |
| 2591 | const ip = &zcu.intern_pool; | |
| 2592 | const nav = ip.getNav(nav_id); | |
| 2593 | const owner_mod = zcu.navFileScope(nav_id).mod.?; | |
| 2594 | const ty: Type = .fromInterned(nav.resolved.?.type); | |
| 2595 | ||
| 2596 | const fn_info = zcu.typeToFunc(ty).?; | |
| 2597 | const target = &owner_mod.resolved_target.result; | |
| 2685 | const target = zcu.getTarget(); | |
| 2598 | 2686 | |
| 2599 | var attributes: Builder.FunctionAttributes.Wip = .{}; | |
| 2600 | defer attributes.deinit(&o.builder); | |
| 2687 | if (fn_info.cc == .async) { | |
| 2688 | @panic("TODO: LLVM backend lower async function"); | |
| 2689 | } | |
| 2601 | 2690 | |
| 2602 | if (target.cpu.arch.isWasm()) if (nav.getExtern(ip)) |@"extern"| { | |
| 2691 | if (target.cpu.arch.isWasm()) if (opt_extern) |@"extern"| { | |
| 2603 | 2692 | try attributes.addFnAttr(.{ .string = .{ |
| 2604 | 2693 | .kind = try o.builder.string("wasm-import-name"), |
| 2605 | .value = try o.builder.string(nav.name.toSlice(ip)), | |
| 2694 | .value = try o.builder.string(@"extern".name), | |
| 2606 | 2695 | } }, &o.builder); |
| 2607 | if (@"extern".lib_name.toSlice(ip)) |lib_name_slice| { | |
| 2608 | if (!std.mem.eql(u8, lib_name_slice, "c")) try attributes.addFnAttr(.{ .string = .{ | |
| 2696 | if (@"extern".lib_name) |lib_name| { | |
| 2697 | if (!std.mem.eql(u8, lib_name, "c")) try attributes.addFnAttr(.{ .string = .{ | |
| 2609 | 2698 | .kind = try o.builder.string("wasm-import-module"), |
| 2610 | .value = try o.builder.string(lib_name_slice), | |
| 2699 | .value = try o.builder.string(lib_name), | |
| 2611 | 2700 | } }, &o.builder); |
| 2612 | 2701 | } |
| 2613 | 2702 | }; |
| 2614 | 2703 | |
| 2615 | if (fn_info.cc == .async) { | |
| 2616 | @panic("TODO: LLVM backend lower async function"); | |
| 2617 | } | |
| 2618 | ||
| 2619 | 2704 | const cc_info = toLlvmCallConv(fn_info.cc, target).?; |
| 2620 | 2705 | |
| 2621 | function_index.setCallConv(cc_info.llvm_cc, &o.builder); | |
| 2706 | llvm_function.setCallConv(cc_info.llvm_cc, &o.builder); | |
| 2622 | 2707 | |
| 2623 | 2708 | if (cc_info.align_stack) { |
| 2624 | 2709 | try attributes.addFnAttr(.{ .alignstack = .wrap(.fromByteUnits(target.stackAlignment())) }, &o.builder); |
| ... | ... | @@ -2672,29 +2757,16 @@ pub const Object = struct { |
| 2672 | 2757 | else => {}, |
| 2673 | 2758 | } |
| 2674 | 2759 | |
| 2675 | // Function attributes that are independent of analysis results of the function body. | |
| 2676 | try o.addCommonFnAttributes( | |
| 2677 | &attributes, | |
| 2678 | owner_mod, | |
| 2679 | // Some backends don't respect the `naked` attribute in `TargetFrameLowering::hasFP()`, | |
| 2680 | // so for these backends, LLVM will happily emit code that accesses the stack through | |
| 2681 | // the frame pointer. This is nonsensical since what the `naked` attribute does is | |
| 2682 | // suppress generation of the prologue and epilogue, and the prologue is where the | |
| 2683 | // frame pointer normally gets set up. At time of writing, this is the case for at | |
| 2684 | // least x86 and RISC-V. | |
| 2685 | owner_mod.omit_frame_pointer or fn_info.cc == .naked, | |
| 2686 | ); | |
| 2687 | ||
| 2688 | 2760 | if (fn_info.return_type == .noreturn_type) try attributes.addFnAttr(.noreturn, &o.builder); |
| 2689 | 2761 | |
| 2690 | var it = iterateParamTypes(o, fn_info); | |
| 2691 | if (try fnReturnStrat(o, fn_info) == .sret) { | |
| 2692 | // Sret pointers must not be address 0 | |
| 2693 | try attributes.addParamAttr(it.llvm_index, .nonnull, &o.builder); | |
| 2694 | try attributes.addParamAttr(it.llvm_index, .@"noalias", &o.builder); | |
| 2695 | ||
| 2696 | const raw_llvm_ret_ty = try o.lowerType(.fromInterned(fn_info.return_type), .in_memory); | |
| 2697 | try attributes.addParamAttr(it.llvm_index, .{ .sret = raw_llvm_ret_ty }, &o.builder); | |
| 2762 | var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types); | |
| 2763 | if (try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type)) == .sret) { | |
| 2764 | try o.addSRetFnAttributes( | |
| 2765 | attributes, | |
| 2766 | try o.lowerType(.fromInterned(fn_info.return_type), .in_memory), | |
| 2767 | Type.fromInterned(fn_info.return_type).abiAlignment(zcu).toLlvm(), | |
| 2768 | .declaration, | |
| 2769 | ); | |
| 2698 | 2770 | it.llvm_index += 1; |
| 2699 | 2771 | } else if (ccAbiPromoteInt(fn_info.cc, zcu, Type.fromInterned(fn_info.return_type))) |s| switch (s) { |
| 2700 | 2772 | .signed => try attributes.addRetAttr(.signext, &o.builder), |
| ... | ... | @@ -2713,9 +2785,9 @@ pub const Object = struct { |
| 2713 | 2785 | while (try it.next()) |lowering| switch (lowering) { |
| 2714 | 2786 | .byval => { |
| 2715 | 2787 | const param_index = it.zig_index - 1; |
| 2716 | const param_ty: Type = .fromInterned(fn_info.param_types.get(ip)[param_index]); | |
| 2788 | const param_ty: Type = .fromInterned(fn_info.param_types[param_index]); | |
| 2717 | 2789 | if (!isByRef(param_ty, zcu)) { |
| 2718 | try o.addByValParamAttrs(pt, &attributes, param_ty, param_index, fn_info, it.llvm_index - 1); | |
| 2790 | try o.addByValParamAttrs(pt, attributes, param_ty, param_index, fn_info, it.llvm_index - 1); | |
| 2719 | 2791 | } |
| 2720 | 2792 | |
| 2721 | 2793 | if (remaining_inreg_int > 0 and |
| ... | ... | @@ -2734,12 +2806,12 @@ pub const Object = struct { |
| 2734 | 2806 | } |
| 2735 | 2807 | }, |
| 2736 | 2808 | .byref => { |
| 2737 | const param_ty: Type = .fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); | |
| 2738 | try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, it.byval_attr, param_ty); | |
| 2809 | const param_ty: Type = .fromInterned(fn_info.param_types[it.zig_index - 1]); | |
| 2810 | try o.addByRefParamAttrs(attributes, it.llvm_index - 1, it.byval_attr, param_ty); | |
| 2739 | 2811 | }, |
| 2740 | 2812 | .byref_mut => try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder), |
| 2741 | 2813 | .slice => { |
| 2742 | const param_ty: Type = .fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); | |
| 2814 | const param_ty: Type = .fromInterned(fn_info.param_types[it.zig_index - 1]); | |
| 2743 | 2815 | const ptr_info = param_ty.ptrInfo(zcu); |
| 2744 | 2816 | const llvm_ptr_index = it.llvm_index - 2; |
| 2745 | 2817 | if (std.math.cast(u5, it.zig_index - 1)) |i| { |
| ... | ... | @@ -2771,78 +2843,24 @@ pub const Object = struct { |
| 2771 | 2843 | .i64_array, |
| 2772 | 2844 | => continue, |
| 2773 | 2845 | }; |
| 2774 | ||
| 2775 | function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); | |
| 2776 | 2846 | } |
| 2777 | 2847 | |
| 2778 | fn addCommonFnAttributes( | |
| 2848 | pub fn addSRetFnAttributes( | |
| 2779 | 2849 | o: *Object, |
| 2780 | 2850 | attributes: *Builder.FunctionAttributes.Wip, |
| 2781 | owner_mod: *Module, | |
| 2782 | omit_frame_pointer: bool, | |
| 2851 | ret_ty: Builder.Type, | |
| 2852 | ret_align: Builder.Alignment, | |
| 2853 | location: enum { declaration, callsite }, | |
| 2783 | 2854 | ) Allocator.Error!void { |
| 2784 | if (!owner_mod.red_zone) { | |
| 2785 | try attributes.addFnAttr(.noredzone, &o.builder); | |
| 2786 | } | |
| 2787 | if (omit_frame_pointer) { | |
| 2788 | try attributes.addFnAttr(.{ .string = .{ | |
| 2789 | .kind = try o.builder.string("frame-pointer"), | |
| 2790 | .value = try o.builder.string("none"), | |
| 2791 | } }, &o.builder); | |
| 2792 | } else { | |
| 2793 | try attributes.addFnAttr(.{ .string = .{ | |
| 2794 | .kind = try o.builder.string("frame-pointer"), | |
| 2795 | .value = try o.builder.string("all"), | |
| 2796 | } }, &o.builder); | |
| 2797 | } | |
| 2798 | try attributes.addFnAttr(.nounwind, &o.builder); | |
| 2799 | if (owner_mod.unwind_tables != .none) { | |
| 2800 | try attributes.addFnAttr( | |
| 2801 | .{ .uwtable = if (owner_mod.unwind_tables == .async) .async else .sync }, | |
| 2802 | &o.builder, | |
| 2803 | ); | |
| 2804 | } | |
| 2805 | if (owner_mod.optimize_mode == .small) { | |
| 2806 | try attributes.addFnAttr(.minsize, &o.builder); | |
| 2807 | try attributes.addFnAttr(.optsize, &o.builder); | |
| 2808 | } | |
| 2809 | const target = &owner_mod.resolved_target.result; | |
| 2810 | if (target.cpu.model.llvm_name) |s| { | |
| 2811 | try attributes.addFnAttr(.{ .string = .{ | |
| 2812 | .kind = try o.builder.string("target-cpu"), | |
| 2813 | .value = try o.builder.string(s), | |
| 2814 | } }, &o.builder); | |
| 2815 | } | |
| 2816 | if (owner_mod.resolved_target.llvm_cpu_features) |s| { | |
| 2817 | try attributes.addFnAttr(.{ .string = .{ | |
| 2818 | .kind = try o.builder.string("target-features"), | |
| 2819 | .value = try o.builder.string(std.mem.span(s)), | |
| 2820 | } }, &o.builder); | |
| 2821 | } | |
| 2822 | if (target.abi.float() == .soft) { | |
| 2823 | // `use-soft-float` means "use software routines for floating point computations". In | |
| 2824 | // other words, it configures how LLVM lowers basic float instructions like `fcmp`, | |
| 2825 | // `fadd`, etc. The float calling convention is configured on `TargetMachine` and is | |
| 2826 | // mostly an orthogonal concept, although obviously we do need hardware float operations | |
| 2827 | // to actually be able to pass float values in float registers. | |
| 2828 | // | |
| 2829 | // Ideally, we would support something akin to the `-mfloat-abi=softfp` option that GCC | |
| 2830 | // and Clang support for Arm32 and CSKY. We don't currently expose such an option in | |
| 2831 | // Zig, and using CPU features as the source of truth for this makes for a miserable | |
| 2832 | // user experience since people expect e.g. `arm-linux-gnueabi` to mean full soft float | |
| 2833 | // unless the compiler has explicitly been told otherwise. (And note that our baseline | |
| 2834 | // CPU models almost all include FPU features!) | |
| 2835 | // | |
| 2836 | // Revisit this at some point. | |
| 2837 | try attributes.addFnAttr(.{ .string = .{ | |
| 2838 | .kind = try o.builder.string("use-soft-float"), | |
| 2839 | .value = try o.builder.string("true"), | |
| 2840 | } }, &o.builder); | |
| 2841 | ||
| 2842 | // This prevents LLVM from using FPU/SIMD code for things like `memcpy`. As for the | |
| 2843 | // above, this should be revisited if `softfp` support is added. | |
| 2844 | try attributes.addFnAttr(.noimplicitfloat, &o.builder); | |
| 2855 | try attributes.addParamAttr(0, .dead_on_unwind, &o.builder); | |
| 2856 | switch (location) { | |
| 2857 | .declaration => try attributes.addParamAttr(0, .@"noalias", &o.builder), | |
| 2858 | .callsite => {}, | |
| 2845 | 2859 | } |
| 2860 | try attributes.addParamAttr(0, .writeonly, &o.builder); | |
| 2861 | try attributes.addParamAttr(0, .{ .captures = .none }, &o.builder); | |
| 2862 | try attributes.addParamAttr(0, .{ .sret = ret_ty }, &o.builder); | |
| 2863 | try attributes.addParamAttr(0, .{ .@"align" = .wrap(ret_align) }, &o.builder); | |
| 2846 | 2864 | } |
| 2847 | 2865 | |
| 2848 | 2866 | pub const TypeRepr = enum { |
| ... | ... | @@ -2861,6 +2879,151 @@ pub const Object = struct { |
| 2861 | 2879 | }); |
| 2862 | 2880 | } |
| 2863 | 2881 | |
| 2882 | pub const SoftF80Layout = struct { | |
| 2883 | alignment: InternPool.Alignment, | |
| 2884 | /// byte offset of u64 field | |
| 2885 | mantissa_offset: u64, | |
| 2886 | /// byte offset of u16 field | |
| 2887 | exponent_offset: u64, | |
| 2888 | llvm_fields_len: u32, | |
| 2889 | ||
| 2890 | pub const LlvmFieldTag = enum { mantissa, exponent, padding }; | |
| 2891 | }; | |
| 2892 | pub fn softF80Layout(o: *Object, opts: struct { | |
| 2893 | llvm_field_tags_buf: []SoftF80Layout.LlvmFieldTag = &.{}, | |
| 2894 | llvm_field_types_buf: []Builder.Type = &.{}, | |
| 2895 | }) Allocator.Error!SoftF80Layout { | |
| 2896 | const zcu = o.zcu; | |
| 2897 | const target = zcu.getTarget(); | |
| 2898 | assert(std.zig.target.compilerRtFloatAbi(target, 80) == .soft); | |
| 2899 | // Current compiler rt soft abi, which is not yet affected by endianness for simplicity: | |
| 2900 | // | |
| 2901 | // typedef struct { uint64_t mantissa; uint16_t exponent; } f80; | |
| 2902 | // | |
| 2903 | var layout: SoftF80Layout = .{ | |
| 2904 | .alignment = Type.f80.abiAlignment(zcu), | |
| 2905 | .mantissa_offset = undefined, | |
| 2906 | .exponent_offset = undefined, | |
| 2907 | .llvm_fields_len = 0, | |
| 2908 | }; | |
| 2909 | var offset: u64 = 0; | |
| 2910 | for ([2]SoftF80Layout.LlvmFieldTag{ .mantissa, .exponent }, [2]Type{ .u64, .u16 }) |field_tag, field_type| { | |
| 2911 | const field_align = field_type.abiAlignment(zcu); | |
| 2912 | assert(field_align.compareStrict(.lte, layout.alignment)); | |
| 2913 | const field_offset = field_align.forward(offset); | |
| 2914 | switch (field_offset - offset) { | |
| 2915 | 0 => {}, | |
| 2916 | else => |padding| { | |
| 2917 | if (layout.llvm_fields_len < opts.llvm_field_tags_buf.len) | |
| 2918 | opts.llvm_field_tags_buf[layout.llvm_fields_len] = .padding; | |
| 2919 | if (layout.llvm_fields_len < opts.llvm_field_types_buf.len) | |
| 2920 | opts.llvm_field_types_buf[layout.llvm_fields_len] = try o.builder.arrayType(padding, .i8); | |
| 2921 | layout.llvm_fields_len += 1; | |
| 2922 | }, | |
| 2923 | } | |
| 2924 | switch (field_tag) { | |
| 2925 | .mantissa => layout.mantissa_offset = field_offset, | |
| 2926 | .exponent => layout.exponent_offset = field_offset, | |
| 2927 | .padding => unreachable, | |
| 2928 | } | |
| 2929 | if (layout.llvm_fields_len < opts.llvm_field_tags_buf.len) | |
| 2930 | opts.llvm_field_tags_buf[layout.llvm_fields_len] = field_tag; | |
| 2931 | if (layout.llvm_fields_len < opts.llvm_field_types_buf.len) | |
| 2932 | opts.llvm_field_types_buf[layout.llvm_fields_len] = try o.lowerType(field_type, .in_memory); | |
| 2933 | layout.llvm_fields_len += 1; | |
| 2934 | offset = field_offset + field_type.abiSize(zcu); | |
| 2935 | } | |
| 2936 | const end = layout.alignment.forward(offset); | |
| 2937 | assert(end == Type.f80.abiSize(zcu)); | |
| 2938 | switch (end - offset) { | |
| 2939 | 0 => {}, | |
| 2940 | else => |padding| { | |
| 2941 | if (layout.llvm_fields_len < opts.llvm_field_tags_buf.len) | |
| 2942 | opts.llvm_field_tags_buf[layout.llvm_fields_len] = .padding; | |
| 2943 | if (layout.llvm_fields_len < opts.llvm_field_types_buf.len) | |
| 2944 | opts.llvm_field_types_buf[layout.llvm_fields_len] = try o.builder.arrayType(padding, .i8); | |
| 2945 | layout.llvm_fields_len += 1; | |
| 2946 | }, | |
| 2947 | } | |
| 2948 | return layout; | |
| 2949 | } | |
| 2950 | ||
| 2951 | pub const SoftF128Layout = struct { | |
| 2952 | alignment: InternPool.Alignment, | |
| 2953 | /// byte offset of u64 field | |
| 2954 | lo_offset: u64, | |
| 2955 | /// byte offset of u64 field | |
| 2956 | hi_offset: u64, | |
| 2957 | llvm_fields_len: u32, | |
| 2958 | ||
| 2959 | pub const LlvmFieldTag = enum { lo, hi, padding }; | |
| 2960 | }; | |
| 2961 | pub fn softF128Layout(o: *Object, opts: struct { | |
| 2962 | llvm_field_tags_buf: []SoftF128Layout.LlvmFieldTag = &.{}, | |
| 2963 | llvm_field_types_buf: []Builder.Type = &.{}, | |
| 2964 | }) Allocator.Error!SoftF128Layout { | |
| 2965 | const zcu = o.zcu; | |
| 2966 | const target = zcu.getTarget(); | |
| 2967 | assert(std.zig.target.compilerRtFloatAbi(target, 128) == .soft); | |
| 2968 | // Current compiler rt soft abi: | |
| 2969 | // | |
| 2970 | // #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | |
| 2971 | // typedef struct { uint64_t hi, lo; } f128; | |
| 2972 | // #else | |
| 2973 | // typedef struct { uint64_t lo, hi; } f128; | |
| 2974 | // #endif | |
| 2975 | // | |
| 2976 | var layout: SoftF128Layout = .{ | |
| 2977 | .alignment = Type.f128.abiAlignment(zcu), | |
| 2978 | .lo_offset = undefined, | |
| 2979 | .hi_offset = undefined, | |
| 2980 | .llvm_fields_len = 0, | |
| 2981 | }; | |
| 2982 | var offset: u64 = 0; | |
| 2983 | for (@as([2]SoftF128Layout.LlvmFieldTag, switch (target.cpu.arch.endian()) { | |
| 2984 | .big => .{ .hi, .lo }, | |
| 2985 | .little => .{ .lo, .hi }, | |
| 2986 | }), [2]Type{ .u64, .u64 }) |field_tag, field_type| { | |
| 2987 | const field_align = field_type.abiAlignment(zcu); | |
| 2988 | assert(field_align.compareStrict(.lte, layout.alignment)); | |
| 2989 | const field_offset = field_align.forward(offset); | |
| 2990 | switch (field_offset - offset) { | |
| 2991 | 0 => {}, | |
| 2992 | else => |padding| { | |
| 2993 | if (layout.llvm_fields_len < opts.llvm_field_tags_buf.len) | |
| 2994 | opts.llvm_field_tags_buf[layout.llvm_fields_len] = .padding; | |
| 2995 | if (layout.llvm_fields_len < opts.llvm_field_types_buf.len) | |
| 2996 | opts.llvm_field_types_buf[layout.llvm_fields_len] = try o.builder.arrayType(padding, .i8); | |
| 2997 | layout.llvm_fields_len += 1; | |
| 2998 | }, | |
| 2999 | } | |
| 3000 | switch (field_tag) { | |
| 3001 | .lo => layout.lo_offset = field_offset, | |
| 3002 | .hi => layout.hi_offset = field_offset, | |
| 3003 | .padding => unreachable, | |
| 3004 | } | |
| 3005 | if (layout.llvm_fields_len < opts.llvm_field_tags_buf.len) | |
| 3006 | opts.llvm_field_tags_buf[layout.llvm_fields_len] = field_tag; | |
| 3007 | if (layout.llvm_fields_len < opts.llvm_field_types_buf.len) | |
| 3008 | opts.llvm_field_types_buf[layout.llvm_fields_len] = try o.lowerType(field_type, .in_memory); | |
| 3009 | layout.llvm_fields_len += 1; | |
| 3010 | offset = field_offset + field_type.abiSize(zcu); | |
| 3011 | } | |
| 3012 | const end = layout.alignment.forward(offset); | |
| 3013 | assert(end == Type.f128.abiSize(zcu)); | |
| 3014 | switch (end - offset) { | |
| 3015 | 0 => {}, | |
| 3016 | else => |padding| { | |
| 3017 | if (layout.llvm_fields_len < opts.llvm_field_tags_buf.len) | |
| 3018 | opts.llvm_field_tags_buf[layout.llvm_fields_len] = .padding; | |
| 3019 | if (layout.llvm_fields_len < opts.llvm_field_types_buf.len) | |
| 3020 | opts.llvm_field_types_buf[layout.llvm_fields_len] = try o.builder.arrayType(padding, .i8); | |
| 3021 | layout.llvm_fields_len += 1; | |
| 3022 | }, | |
| 3023 | } | |
| 3024 | return layout; | |
| 3025 | } | |
| 3026 | ||
| 2864 | 3027 | pub fn lowerType(o: *Object, t: Type, repr: TypeRepr) Allocator.Error!Builder.Type { |
| 2865 | 3028 | const zcu = o.zcu; |
| 2866 | 3029 | const target = zcu.getTarget(); |
| ... | ... | @@ -2901,7 +3064,7 @@ pub const Object = struct { |
| 2901 | 3064 | .c_ulonglong_type, |
| 2902 | 3065 | => |tag| try o.builder.intType(target.cTypeBitSize( |
| 2903 | 3066 | @field(std.Target.CType, @tagName(tag)["c_".len .. @tagName(tag).len - "_type".len]), |
| 2904 | )), | |
| 3067 | ).?), | |
| 2905 | 3068 | .c_longdouble_type, |
| 2906 | 3069 | .f16_type, |
| 2907 | 3070 | .f32_type, |
| ... | ... | @@ -2909,11 +3072,44 @@ pub const Object = struct { |
| 2909 | 3072 | .f80_type, |
| 2910 | 3073 | .f128_type, |
| 2911 | 3074 | => switch (t.floatBits(target)) { |
| 2912 | 16 => if (backendSupportsF16(target)) .half else .i16, | |
| 2913 | 32 => .float, | |
| 2914 | 64 => .double, | |
| 2915 | 80 => if (backendSupportsF80(target)) .x86_fp80 else .i80, | |
| 2916 | 128 => .fp128, | |
| 3075 | 16 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { | |
| 3076 | .hard => .half, | |
| 3077 | .soft => .i16, | |
| 3078 | }, | |
| 3079 | 32 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { | |
| 3080 | .hard => .float, | |
| 3081 | .soft => .i32, | |
| 3082 | }, | |
| 3083 | 64 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { | |
| 3084 | .hard => .double, | |
| 3085 | .soft => .i64, | |
| 3086 | }, | |
| 3087 | 80 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { | |
| 3088 | .hard => .x86_fp80, | |
| 3089 | .soft => { | |
| 3090 | var llvm_field_types_buf: [5]Builder.Type = undefined; | |
| 3091 | const f80_layout = try o.softF80Layout(.{ | |
| 3092 | .llvm_field_types_buf = &llvm_field_types_buf, | |
| 3093 | }); | |
| 3094 | return o.builder.structType( | |
| 3095 | .normal, | |
| 3096 | llvm_field_types_buf[0..f80_layout.llvm_fields_len], | |
| 3097 | ); | |
| 3098 | }, | |
| 3099 | }, | |
| 3100 | 128 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { | |
| 3101 | .hard => .fp128, | |
| 3102 | .soft => { | |
| 3103 | var llvm_field_types_buf: [5]Builder.Type = undefined; | |
| 3104 | const f128_layout = try o.softF128Layout(.{ | |
| 3105 | .llvm_field_types_buf = &llvm_field_types_buf, | |
| 3106 | }); | |
| 3107 | return o.builder.structType( | |
| 3108 | .normal, | |
| 3109 | llvm_field_types_buf[0..f128_layout.llvm_fields_len], | |
| 3110 | ); | |
| 3111 | }, | |
| 3112 | }, | |
| 2917 | 3113 | else => unreachable, |
| 2918 | 3114 | }, |
| 2919 | 3115 | .anyopaque_type => { |
| ... | ... | @@ -2992,11 +3188,13 @@ pub const Object = struct { |
| 2992 | 3188 | array_type.lenIncludingSentinel(), |
| 2993 | 3189 | try o.lowerType(.fromInterned(array_type.child), repr), |
| 2994 | 3190 | ), |
| 2995 | .vector_type => |vector_type| o.builder.vectorType( | |
| 2996 | .normal, | |
| 2997 | vector_type.len, | |
| 2998 | try o.lowerType(.fromInterned(vector_type.child), .as_value), | |
| 2999 | ), | |
| 3191 | .vector_type => |vector_type| if (isByRef(t, zcu)) { | |
| 3192 | const child_llvm_ty = try o.lowerType(.fromInterned(vector_type.child), .in_memory); | |
| 3193 | return o.builder.arrayType(vector_type.len, child_llvm_ty); | |
| 3194 | } else { | |
| 3195 | const child_llvm_ty = try o.lowerType(.fromInterned(vector_type.child), .as_value); | |
| 3196 | return o.builder.vectorType(.normal, vector_type.len, child_llvm_ty); | |
| 3197 | }, | |
| 3000 | 3198 | .opt_type => |child_ty| { |
| 3001 | 3199 | // Must stay in sync with `opt_payload` logic in `lowerPtr`. |
| 3002 | 3200 | switch (Type.fromInterned(child_ty).classify(zcu)) { |
| ... | ... | @@ -3257,7 +3455,10 @@ pub const Object = struct { |
| 3257 | 3455 | }, |
| 3258 | 3456 | .opaque_type, .spirv_type => unreachable, // no runtime bits |
| 3259 | 3457 | .enum_type => try o.lowerType(t.backingIntType(zcu), repr), |
| 3260 | .func_type => |func_type| try o.lowerFnType(t, func_type), | |
| 3458 | .func_type => |func_type| { | |
| 3459 | assert(t.fnHasRuntimeBits(zcu)); | |
| 3460 | return o.lowerFnType(.fromIntern(func_type, ip)); | |
| 3461 | }, | |
| 3261 | 3462 | .error_set_type, .inferred_error_set_type => try o.errorIntType(repr), |
| 3262 | 3463 | // values, not types |
| 3263 | 3464 | .undef, |
| ... | ... | @@ -3283,14 +3484,28 @@ pub const Object = struct { |
| 3283 | 3484 | }; |
| 3284 | 3485 | } |
| 3285 | 3486 | |
| 3286 | fn lowerFnType(o: *Object, fn_ty: Type, fn_info: InternPool.Key.FuncType) Allocator.Error!Builder.Type { | |
| 3487 | pub const FuncInfo = struct { | |
| 3488 | cc: std.lang.CallingConvention, | |
| 3489 | noalias_bits: u32 = 0, | |
| 3490 | param_types: []const InternPool.Index, | |
| 3491 | return_type: InternPool.Index = .void_type, | |
| 3492 | is_var_args: bool = false, | |
| 3493 | ||
| 3494 | pub fn fromIntern(fn_info: InternPool.Key.FuncType, ip: *InternPool) FuncInfo { | |
| 3495 | return .{ | |
| 3496 | .cc = fn_info.cc, | |
| 3497 | .noalias_bits = fn_info.noalias_bits, | |
| 3498 | .param_types = fn_info.param_types.get(ip), | |
| 3499 | .return_type = fn_info.return_type, | |
| 3500 | .is_var_args = fn_info.is_var_args, | |
| 3501 | }; | |
| 3502 | } | |
| 3503 | }; | |
| 3504 | pub fn lowerFnType(o: *Object, fn_info: FuncInfo) Allocator.Error!Builder.Type { | |
| 3287 | 3505 | const zcu = o.zcu; |
| 3288 | const ip = &zcu.intern_pool; | |
| 3289 | 3506 | const target = zcu.getTarget(); |
| 3290 | 3507 | |
| 3291 | assert(fn_ty.fnHasRuntimeBits(zcu)); | |
| 3292 | ||
| 3293 | const ret_strat = try fnReturnStrat(o, fn_info); | |
| 3508 | const ret_strat = try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type)); | |
| 3294 | 3509 | |
| 3295 | 3510 | var llvm_params: std.ArrayList(Builder.Type) = .empty; |
| 3296 | 3511 | defer llvm_params.deinit(o.gpa); |
| ... | ... | @@ -3305,24 +3520,24 @@ pub const Object = struct { |
| 3305 | 3520 | try llvm_params.append(o.gpa, llvm_ptr_ty); |
| 3306 | 3521 | } |
| 3307 | 3522 | |
| 3308 | var it = iterateParamTypes(o, fn_info); | |
| 3523 | var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types); | |
| 3309 | 3524 | while (try it.next()) |lowering| switch (lowering) { |
| 3310 | 3525 | .no_bits => continue, |
| 3311 | 3526 | .byval => { |
| 3312 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); | |
| 3527 | const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]); | |
| 3313 | 3528 | try llvm_params.append(o.gpa, try o.lowerType(param_ty, if (isByRef(param_ty, zcu)) .in_memory else .as_value)); |
| 3314 | 3529 | }, |
| 3315 | 3530 | .byref, .byref_mut => { |
| 3316 | 3531 | try llvm_params.append(o.gpa, .ptr); |
| 3317 | 3532 | }, |
| 3318 | 3533 | .abi_sized_int => { |
| 3319 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); | |
| 3534 | const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]); | |
| 3320 | 3535 | try llvm_params.append(o.gpa, try o.builder.intType( |
| 3321 | 3536 | @intCast(param_ty.abiSize(zcu) * 8), |
| 3322 | 3537 | )); |
| 3323 | 3538 | }, |
| 3324 | 3539 | .slice => { |
| 3325 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); | |
| 3540 | const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]); | |
| 3326 | 3541 | try llvm_params.appendSlice(o.gpa, &.{ |
| 3327 | 3542 | try o.builder.ptrType(toLlvmAddressSpace(param_ty.ptrAddressSpace(zcu), target)), |
| 3328 | 3543 | try o.lowerType(.usize, .as_value), |
| ... | ... | @@ -3332,7 +3547,7 @@ pub const Object = struct { |
| 3332 | 3547 | try llvm_params.appendSlice(o.gpa, it.types_buffer[0..it.types_len]); |
| 3333 | 3548 | }, |
| 3334 | 3549 | .float_array => |count| { |
| 3335 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); | |
| 3550 | const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]); | |
| 3336 | 3551 | const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(param_ty, zcu).?, .in_memory); |
| 3337 | 3552 | try llvm_params.append(o.gpa, try o.builder.arrayType(count, float_ty)); |
| 3338 | 3553 | }, |
| ... | ... | @@ -3460,18 +3675,12 @@ pub const Object = struct { |
| 3460 | 3675 | }, |
| 3461 | 3676 | .enum_tag => |enum_tag| o.lowerValue(enum_tag.int, repr), |
| 3462 | 3677 | .float => switch (ty.floatBits(target)) { |
| 3463 | 16 => if (backendSupportsF16(target)) | |
| 3464 | try o.builder.halfConst(val.toFloat(f16, zcu)) | |
| 3465 | else | |
| 3466 | try o.builder.intConst(.i16, @as(i16, @bitCast(val.toFloat(f16, zcu)))), | |
| 3467 | 32 => try o.builder.floatConst(val.toFloat(f32, zcu)), | |
| 3468 | 64 => try o.builder.doubleConst(val.toFloat(f64, zcu)), | |
| 3469 | 80 => if (backendSupportsF80(target)) | |
| 3470 | try o.builder.x86_fp80Const(val.toFloat(f80, zcu)) | |
| 3471 | else | |
| 3472 | try o.builder.intConst(.i80, @as(i80, @bitCast(val.toFloat(f80, zcu)))), | |
| 3473 | 128 => try o.builder.fp128Const(val.toFloat(f128, zcu)), | |
| 3474 | 3678 | else => unreachable, |
| 3679 | 16 => try o.f16Const(val.toFloat(f16, zcu)), | |
| 3680 | 32 => try o.f32Const(val.toFloat(f32, zcu)), | |
| 3681 | 64 => try o.f64Const(val.toFloat(f64, zcu)), | |
| 3682 | 80 => try o.f80Const(val.toFloat(f80, zcu)), | |
| 3683 | 128 => try o.f128Const(val.toFloat(f128, zcu)), | |
| 3475 | 3684 | }, |
| 3476 | 3685 | .ptr => try o.lowerPtr(arg_val, 0), |
| 3477 | 3686 | .slice => |slice| return o.builder.structConst(try o.lowerType(ty, repr), &.{ |
| ... | ... | @@ -3590,12 +3799,13 @@ pub const Object = struct { |
| 3590 | 3799 | }, |
| 3591 | 3800 | .vector_type => |vector_type| { |
| 3592 | 3801 | const vector_ty = try o.lowerType(ty, repr); |
| 3802 | const ExpectedContents = [Builder.expected_fields_len]Builder.Constant; | |
| 3803 | var bfa_buf: ExpectedContents = undefined; | |
| 3804 | var bfa: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), o.gpa); | |
| 3805 | const allocator = bfa.allocator(); | |
| 3806 | const is_by_ref = isByRef(ty, zcu); | |
| 3593 | 3807 | switch (aggregate.storage) { |
| 3594 | 3808 | .bytes, .elems => { |
| 3595 | const ExpectedContents = [Builder.expected_fields_len]Builder.Constant; | |
| 3596 | var bfa_buf: ExpectedContents = undefined; | |
| 3597 | var bfa: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), o.gpa); | |
| 3598 | const allocator = bfa.allocator(); | |
| 3599 | 3809 | const vals = try allocator.alloc(Builder.Constant, vector_type.len); |
| 3600 | 3810 | defer allocator.free(vals); |
| 3601 | 3811 | |
| ... | ... | @@ -3604,16 +3814,21 @@ pub const Object = struct { |
| 3604 | 3814 | result_val.* = try o.builder.intConst(.i8, byte); |
| 3605 | 3815 | }, |
| 3606 | 3816 | .elems => |elems| for (vals, elems) |*result_val, elem| { |
| 3607 | result_val.* = try o.lowerValue(elem, .as_value); | |
| 3817 | result_val.* = try o.lowerValue(elem, if (is_by_ref) .in_memory else .as_value); | |
| 3608 | 3818 | }, |
| 3609 | 3819 | .repeated_elem => unreachable, |
| 3610 | 3820 | } |
| 3611 | return o.builder.vectorConst(vector_ty, vals); | |
| 3821 | return if (is_by_ref) | |
| 3822 | o.builder.arrayConst(vector_ty, vals) | |
| 3823 | else | |
| 3824 | o.builder.vectorConst(vector_ty, vals); | |
| 3612 | 3825 | }, |
| 3613 | .repeated_elem => |elem| return o.builder.splatConst( | |
| 3614 | vector_ty, | |
| 3615 | try o.lowerValue(elem, .as_value), | |
| 3616 | ), | |
| 3826 | .repeated_elem => |elem| if (is_by_ref) { | |
| 3827 | const vals = try allocator.alloc(Builder.Constant, vector_type.len); | |
| 3828 | defer allocator.free(vals); | |
| 3829 | @memset(vals, try o.lowerValue(elem, .in_memory)); | |
| 3830 | return o.builder.arrayConst(vector_ty, vals); | |
| 3831 | } else return o.builder.splatConst(vector_ty, try o.lowerValue(elem, .as_value)), | |
| 3617 | 3832 | } |
| 3618 | 3833 | }, |
| 3619 | 3834 | .tuple_type => |tuple| { |
| ... | ... | @@ -3841,6 +4056,117 @@ pub const Object = struct { |
| 3841 | 4056 | }; |
| 3842 | 4057 | } |
| 3843 | 4058 | |
| 4059 | pub fn f16Const(o: *Object, val: f16) Allocator.Error!Builder.Constant { | |
| 4060 | return switch (std.zig.target.compilerRtFloatAbi(o.zcu.getTarget(), 16)) { | |
| 4061 | .hard => o.builder.halfConst(val), | |
| 4062 | .soft => o.builder.intConst(.i16, @as(u16, @bitCast(val))), | |
| 4063 | }; | |
| 4064 | } | |
| 4065 | ||
| 4066 | pub fn f32Const(o: *Object, val: f32) Allocator.Error!Builder.Constant { | |
| 4067 | return switch (std.zig.target.compilerRtFloatAbi(o.zcu.getTarget(), 32)) { | |
| 4068 | .hard => o.builder.floatConst(val), | |
| 4069 | .soft => o.builder.intConst(.i32, @as(u32, @bitCast(val))), | |
| 4070 | }; | |
| 4071 | } | |
| 4072 | ||
| 4073 | pub fn f64Const(o: *Object, val: f64) Allocator.Error!Builder.Constant { | |
| 4074 | return switch (std.zig.target.compilerRtFloatAbi(o.zcu.getTarget(), 64)) { | |
| 4075 | .hard => o.builder.doubleConst(val), | |
| 4076 | .soft => o.builder.intConst(.i64, @as(u64, @bitCast(val))), | |
| 4077 | }; | |
| 4078 | } | |
| 4079 | ||
| 4080 | pub fn f80Const(o: *Object, val: f80) Allocator.Error!Builder.Constant { | |
| 4081 | switch (std.zig.target.compilerRtFloatAbi(o.zcu.getTarget(), 80)) { | |
| 4082 | .hard => return o.builder.x86_fp80Const(val), | |
| 4083 | .soft => {}, | |
| 4084 | } | |
| 4085 | var llvm_field_tags_buf: [5]SoftF80Layout.LlvmFieldTag = undefined; | |
| 4086 | var llvm_field_types_buf: [5]Builder.Type = undefined; | |
| 4087 | const f80_layout = try o.softF80Layout(.{ | |
| 4088 | .llvm_field_tags_buf = &llvm_field_tags_buf, | |
| 4089 | .llvm_field_types_buf = &llvm_field_types_buf, | |
| 4090 | }); | |
| 4091 | const llvm_field_types = llvm_field_types_buf[0..f80_layout.llvm_fields_len]; | |
| 4092 | const f80_llvm_ty = try o.builder.structType(.normal, llvm_field_types); | |
| 4093 | const f80_repr: packed struct { mantissa: u64, exponent: u16 } = @bitCast(val); | |
| 4094 | var llvm_field_vals_buf: [5]Builder.Constant = undefined; | |
| 4095 | const llvm_field_vals = llvm_field_vals_buf[0..f80_layout.llvm_fields_len]; | |
| 4096 | for ( | |
| 4097 | llvm_field_vals, | |
| 4098 | llvm_field_tags_buf[0..f80_layout.llvm_fields_len], | |
| 4099 | llvm_field_types, | |
| 4100 | ) |*llvm_field_val, llvm_field_tag, llvm_field_type| | |
| 4101 | llvm_field_val.* = switch (llvm_field_tag) { | |
| 4102 | .mantissa => try o.builder.intConst(llvm_field_type, f80_repr.mantissa), | |
| 4103 | .exponent => try o.builder.intConst(llvm_field_type, f80_repr.exponent), | |
| 4104 | .padding => try o.builder.undefConst(llvm_field_type), | |
| 4105 | }; | |
| 4106 | return o.builder.structConst(f80_llvm_ty, llvm_field_vals); | |
| 4107 | } | |
| 4108 | ||
| 4109 | pub fn f128Const(o: *Object, val: f128) Allocator.Error!Builder.Constant { | |
| 4110 | switch (std.zig.target.compilerRtFloatAbi(o.zcu.getTarget(), 128)) { | |
| 4111 | .hard => return o.builder.fp128Const(val), | |
| 4112 | .soft => {}, | |
| 4113 | } | |
| 4114 | var llvm_field_tags_buf: [5]SoftF128Layout.LlvmFieldTag = undefined; | |
| 4115 | var llvm_field_types_buf: [5]Builder.Type = undefined; | |
| 4116 | const f128_layout = try o.softF128Layout(.{ | |
| 4117 | .llvm_field_tags_buf = &llvm_field_tags_buf, | |
| 4118 | .llvm_field_types_buf = &llvm_field_types_buf, | |
| 4119 | }); | |
| 4120 | const llvm_field_types = llvm_field_types_buf[0..f128_layout.llvm_fields_len]; | |
| 4121 | const f128_llvm_ty = try o.builder.structType(.normal, llvm_field_types); | |
| 4122 | const f128_repr: packed struct { lo: u64, hi: u64 } = @bitCast(val); | |
| 4123 | var llvm_field_vals_buf: [5]Builder.Constant = undefined; | |
| 4124 | const llvm_field_vals = llvm_field_vals_buf[0..f128_layout.llvm_fields_len]; | |
| 4125 | for ( | |
| 4126 | llvm_field_vals, | |
| 4127 | llvm_field_tags_buf[0..f128_layout.llvm_fields_len], | |
| 4128 | llvm_field_types, | |
| 4129 | ) |*llvm_field_val, llvm_field_tag, llvm_field_type| | |
| 4130 | llvm_field_val.* = switch (llvm_field_tag) { | |
| 4131 | .lo => try o.builder.intConst(llvm_field_type, f128_repr.lo), | |
| 4132 | .hi => try o.builder.intConst(llvm_field_type, f128_repr.hi), | |
| 4133 | .padding => try o.builder.undefConst(llvm_field_type), | |
| 4134 | }; | |
| 4135 | return o.builder.structConst(f128_llvm_ty, llvm_field_vals); | |
| 4136 | } | |
| 4137 | ||
| 4138 | pub fn lowerConstRef( | |
| 4139 | o: *Object, | |
| 4140 | constant: Builder.Constant, | |
| 4141 | @"align": Builder.Alignment, | |
| 4142 | ) Allocator.Error!Builder.Constant { | |
| 4143 | assert(@"align" != .default); | |
| 4144 | const zcu = o.zcu; | |
| 4145 | const gpa = zcu.comp.gpa; | |
| 4146 | const gop = try o.const_map.getOrPut(gpa, constant); | |
| 4147 | if (gop.found_existing) { | |
| 4148 | // Keep the greater of the two alignments. | |
| 4149 | const llvm_variable = gop.value_ptr.*; | |
| 4150 | const llvm_old_align = llvm_variable.getAlignment(&o.builder); | |
| 4151 | const llvm_new_align = llvm_old_align.max(@"align"); | |
| 4152 | llvm_variable.setAlignment(llvm_new_align, &o.builder); | |
| 4153 | return llvm_variable.ptrConst(&o.builder).global.toConst(); | |
| 4154 | } | |
| 4155 | errdefer assert(o.const_map.remove(constant)); | |
| 4156 | ||
| 4157 | const llvm_ty = constant.typeOf(&o.builder); | |
| 4158 | const llvm_addrspace = toLlvmAddressSpace(.generic, zcu.getTarget()); | |
| 4159 | const llvm_variable = try o.builder.addVariable(.empty, llvm_ty, llvm_addrspace); | |
| 4160 | gop.value_ptr.* = llvm_variable; | |
| 4161 | try llvm_variable.setInitializer(constant, &o.builder); | |
| 4162 | llvm_variable.setMutability(.constant, &o.builder); | |
| 4163 | llvm_variable.setAlignment(@"align", &o.builder); | |
| 4164 | const llvm_global = llvm_variable.ptrConst(&o.builder).global; | |
| 4165 | llvm_global.setLinkage(.private, &o.builder); | |
| 4166 | llvm_global.setUnnamedAddr(.unnamed_addr, &o.builder); | |
| 4167 | return llvm_global.toConst(); | |
| 4168 | } | |
| 4169 | ||
| 3844 | 4170 | fn lowerPtr( |
| 3845 | 4171 | o: *Object, |
| 3846 | 4172 | ptr_val: InternPool.Index, |
| ... | ... | @@ -3860,7 +4186,7 @@ pub const Object = struct { |
| 3860 | 4186 | const orig_ptr_ty: Type = .fromInterned(uav.orig_ty); |
| 3861 | 4187 | const base_ptr = try o.lowerUavRef( |
| 3862 | 4188 | uav.val, |
| 3863 | orig_ptr_ty.ptrAlignment(zcu), | |
| 4189 | orig_ptr_ty.ptrAlignment(zcu).toLlvm(), | |
| 3864 | 4190 | orig_ptr_ty.ptrAddressSpace(zcu), |
| 3865 | 4191 | ); |
| 3866 | 4192 | return o.builder.gepConst(.inbounds, .i8, base_ptr, null, &.{ |
| ... | ... | @@ -3912,8 +4238,8 @@ pub const Object = struct { |
| 3912 | 4238 | |
| 3913 | 4239 | pub fn lowerPtrToVoid( |
| 3914 | 4240 | o: *Object, |
| 3915 | /// Must not be `.none`. | |
| 3916 | @"align": InternPool.Alignment, | |
| 4241 | /// Must not be `.default`. | |
| 4242 | @"align": Builder.Alignment, | |
| 3917 | 4243 | @"addrspace": std.lang.AddressSpace, |
| 3918 | 4244 | ) Allocator.Error!Builder.Constant { |
| 3919 | 4245 | const addr: u64 = @"align".toByteUnits().?; |
| ... | ... | @@ -3926,11 +4252,11 @@ pub const Object = struct { |
| 3926 | 4252 | pub fn lowerUavRef( |
| 3927 | 4253 | o: *Object, |
| 3928 | 4254 | uav_val: InternPool.Index, |
| 3929 | /// Must not be `.none`. | |
| 3930 | @"align": InternPool.Alignment, | |
| 4255 | /// Must not be `.default`. | |
| 4256 | @"align": Builder.Alignment, | |
| 3931 | 4257 | @"addrspace": std.lang.AddressSpace, |
| 3932 | 4258 | ) Allocator.Error!Builder.Constant { |
| 3933 | assert(@"align" != .none); | |
| 4259 | assert(@"align" != .default); | |
| 3934 | 4260 | |
| 3935 | 4261 | const zcu = o.zcu; |
| 3936 | 4262 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -3955,7 +4281,7 @@ pub const Object = struct { |
| 3955 | 4281 | // Keep the greater of the two alignments. |
| 3956 | 4282 | const llvm_variable = gop.value_ptr.*; |
| 3957 | 4283 | const llvm_old_align = llvm_variable.getAlignment(&o.builder); |
| 3958 | const llvm_new_align = llvm_old_align.max(@"align".toLlvm()); | |
| 4284 | const llvm_new_align = llvm_old_align.max(@"align"); | |
| 3959 | 4285 | llvm_variable.setAlignment(llvm_new_align, &o.builder); |
| 3960 | 4286 | return llvm_variable.ptrConst(&o.builder).global.toConst(); |
| 3961 | 4287 | } |
| ... | ... | @@ -3967,7 +4293,7 @@ pub const Object = struct { |
| 3967 | 4293 | gop.value_ptr.* = llvm_variable; |
| 3968 | 4294 | try llvm_variable.setInitializer(try o.lowerValue(uav_val, .in_memory), &o.builder); |
| 3969 | 4295 | llvm_variable.setMutability(.constant, &o.builder); |
| 3970 | llvm_variable.setAlignment(@"align".toLlvm(), &o.builder); | |
| 4296 | llvm_variable.setAlignment(@"align", &o.builder); | |
| 3971 | 4297 | const llvm_global = llvm_variable.ptrConst(&o.builder).global; |
| 3972 | 4298 | llvm_global.setLinkage(if (o.builder.strip) .private else .internal, &o.builder); |
| 3973 | 4299 | llvm_global.setUnnamedAddr(.unnamed_addr, &o.builder); |
| ... | ... | @@ -3986,7 +4312,7 @@ pub const Object = struct { |
| 3986 | 4312 | .none => nav_ty.abiAlignment(zcu), |
| 3987 | 4313 | else => |a| a, |
| 3988 | 4314 | }; |
| 3989 | return o.lowerPtrToVoid(nav_align, nav.resolved.?.@"addrspace"); | |
| 4315 | return o.lowerPtrToVoid(nav_align.toLlvm(), nav.resolved.?.@"addrspace"); | |
| 3990 | 4316 | } |
| 3991 | 4317 | |
| 3992 | 4318 | const gop = try o.nav_map.getOrPut(gpa, nav_id); |
| ... | ... | @@ -4015,7 +4341,7 @@ pub const Object = struct { |
| 4015 | 4341 | attributes: *Builder.FunctionAttributes.Wip, |
| 4016 | 4342 | param_ty: Type, |
| 4017 | 4343 | param_index: u32, |
| 4018 | fn_info: InternPool.Key.FuncType, | |
| 4344 | fn_info: FuncInfo, | |
| 4019 | 4345 | llvm_arg_i: u32, |
| 4020 | 4346 | ) Allocator.Error!void { |
| 4021 | 4347 | const zcu = o.zcu; |
| ... | ... | @@ -4075,18 +4401,18 @@ pub const Object = struct { |
| 4075 | 4401 | |
| 4076 | 4402 | const name = try o.builder.strtabString("__zig_error_name_table"); |
| 4077 | 4403 | // TODO: Address space |
| 4078 | const variable_index = try o.builder.addVariable(name, .ptr, .default); | |
| 4079 | variable_index.setMutability(.constant, &o.builder); | |
| 4080 | variable_index.setAlignment( | |
| 4404 | const llvm_variable = try o.builder.addVariable(name, .ptr, .default); | |
| 4405 | llvm_variable.setMutability(.constant, &o.builder); | |
| 4406 | llvm_variable.setAlignment( | |
| 4081 | 4407 | Type.slice_const_u8_sentinel_0.abiAlignment(o.zcu).toLlvm(), |
| 4082 | 4408 | &o.builder, |
| 4083 | 4409 | ); |
| 4084 | const global_index = variable_index.ptrConst(&o.builder).global; | |
| 4085 | global_index.setLinkage(.private, &o.builder); | |
| 4086 | global_index.setUnnamedAddr(.unnamed_addr, &o.builder); | |
| 4410 | const llvm_global = llvm_variable.ptrConst(&o.builder).global; | |
| 4411 | llvm_global.setLinkage(.private, &o.builder); | |
| 4412 | llvm_global.setUnnamedAddr(.unnamed_addr, &o.builder); | |
| 4087 | 4413 | |
| 4088 | o.error_name_table = variable_index; | |
| 4089 | return variable_index; | |
| 4414 | o.error_name_table = llvm_variable; | |
| 4415 | return llvm_variable; | |
| 4090 | 4416 | } |
| 4091 | 4417 | |
| 4092 | 4418 | pub fn getErrorsLen(o: *Object) Allocator.Error!Builder.Variable.Index { |
| ... | ... | @@ -4094,13 +4420,13 @@ pub const Object = struct { |
| 4094 | 4420 | if (o.errors_len_variable == .none) { |
| 4095 | 4421 | const llvm_err_int_ty = try o.errorIntType(.in_memory); |
| 4096 | 4422 | const name = try builder.strtabString("__zig_errors_len"); |
| 4097 | const variable_index = try builder.addVariable(name, llvm_err_int_ty, .default); | |
| 4098 | variable_index.setMutability(.constant, builder); | |
| 4099 | variable_index.setAlignment(Type.errorAbiAlignment(o.zcu).toLlvm(), builder); | |
| 4100 | const global_index = variable_index.ptrConst(&o.builder).global; | |
| 4101 | global_index.setLinkage(.private, builder); | |
| 4102 | global_index.setUnnamedAddr(.unnamed_addr, builder); | |
| 4103 | o.errors_len_variable = variable_index; | |
| 4423 | const llvm_variable = try builder.addVariable(name, llvm_err_int_ty, .default); | |
| 4424 | llvm_variable.setMutability(.constant, builder); | |
| 4425 | llvm_variable.setAlignment(Type.errorAbiAlignment(o.zcu).toLlvm(), builder); | |
| 4426 | const llvm_global = llvm_variable.ptrConst(&o.builder).global; | |
| 4427 | llvm_global.setLinkage(.private, builder); | |
| 4428 | llvm_global.setUnnamedAddr(.unnamed_addr, builder); | |
| 4429 | o.errors_len_variable = llvm_variable; | |
| 4104 | 4430 | } |
| 4105 | 4431 | return o.errors_len_variable; |
| 4106 | 4432 | } |
| ... | ... | @@ -4112,21 +4438,21 @@ pub const Object = struct { |
| 4112 | 4438 | const gop = try o.enum_tag_name_map.getOrPut(o.gpa, enum_ty.toIntern()); |
| 4113 | 4439 | if (gop.found_existing) return gop.value_ptr.*; |
| 4114 | 4440 | errdefer assert(o.enum_tag_name_map.remove(enum_ty.toIntern())); |
| 4115 | const function_index = try o.builder.addFunction( | |
| 4441 | const llvm_function = try o.builder.addFunction( | |
| 4116 | 4442 | // Dummy function type; `updateEnumTagNameFunction` will replace it with the correct type. |
| 4117 | 4443 | // TODO: change the builder API so we don't need to do this. |
| 4118 | 4444 | try o.builder.fnType(.void, &.{}, .normal), |
| 4119 | 4445 | try o.builder.strtabStringFmt("__zig_tag_name_{f}", .{enum_ty.containerTypeName(ip).fmt(ip)}), |
| 4120 | 4446 | toLlvmAddressSpace(.generic, zcu.getTarget()), |
| 4121 | 4447 | ); |
| 4122 | gop.value_ptr.* = function_index; | |
| 4123 | try o.updateEnumTagNameFunction(enum_ty, function_index); | |
| 4124 | return function_index; | |
| 4448 | gop.value_ptr.* = llvm_function; | |
| 4449 | try o.updateEnumTagNameFunction(enum_ty, llvm_function); | |
| 4450 | return llvm_function; | |
| 4125 | 4451 | } |
| 4126 | 4452 | fn updateEnumTagNameFunction( |
| 4127 | 4453 | o: *Object, |
| 4128 | 4454 | enum_ty: Type, |
| 4129 | function_index: Builder.Function.Index, | |
| 4455 | llvm_function: Builder.Function.Index, | |
| 4130 | 4456 | ) Allocator.Error!void { |
| 4131 | 4457 | const zcu = o.zcu; |
| 4132 | 4458 | const ip = &zcu.intern_pool; |
| ... | ... | @@ -4136,19 +4462,19 @@ pub const Object = struct { |
| 4136 | 4462 | const llvm_ret_ty = try o.lowerType(.slice_const_u8_sentinel_0, .as_value); |
| 4137 | 4463 | const llvm_int_ty = try o.lowerType(.fromInterned(loaded_enum.int_tag_type), .as_value); |
| 4138 | 4464 | |
| 4139 | function_index.ptrConst(&o.builder).global.ptr(&o.builder).type = | |
| 4465 | llvm_function.ptrConst(&o.builder).global.ptr(&o.builder).type = | |
| 4140 | 4466 | try o.builder.fnType(llvm_ret_ty, &.{llvm_int_ty}, .normal); |
| 4141 | 4467 | |
| 4142 | 4468 | var attributes: Builder.FunctionAttributes.Wip = .{}; |
| 4143 | 4469 | defer attributes.deinit(&o.builder); |
| 4144 | 4470 | try o.addCommonFnAttributes(&attributes, zcu.root_mod, zcu.root_mod.omit_frame_pointer); |
| 4145 | 4471 | |
| 4146 | function_index.setLinkage(if (o.builder.strip) .private else .internal, &o.builder); | |
| 4147 | function_index.setCallConv(.fastcc, &o.builder); | |
| 4148 | function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); | |
| 4472 | llvm_function.setLinkage(if (o.builder.strip) .private else .internal, &o.builder); | |
| 4473 | llvm_function.setCallConv(.fastcc, &o.builder); | |
| 4474 | llvm_function.setAttributes(try attributes.finish(&o.builder), &o.builder); | |
| 4149 | 4475 | |
| 4150 | 4476 | var wip = try Builder.WipFunction.init(&o.builder, .{ |
| 4151 | .function = function_index, | |
| 4477 | .function = llvm_function, | |
| 4152 | 4478 | .strip = true, |
| 4153 | 4479 | }); |
| 4154 | 4480 | defer wip.deinit(); |
| ... | ... | @@ -4167,16 +4493,16 @@ pub const Object = struct { |
| 4167 | 4493 | for (0..loaded_enum.field_names.len) |field_index| { |
| 4168 | 4494 | const name = try o.builder.stringNull(loaded_enum.field_names.get(ip)[field_index].toSlice(ip)); |
| 4169 | 4495 | const name_init = try o.builder.stringConst(name); |
| 4170 | const name_variable_index = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); | |
| 4171 | try name_variable_index.setInitializer(name_init, &o.builder); | |
| 4172 | name_variable_index.setMutability(.constant, &o.builder); | |
| 4173 | name_variable_index.setAlignment(comptime Builder.Alignment.fromByteUnits(1), &o.builder); | |
| 4174 | const name_global_index = name_variable_index.ptrConst(&o.builder).global; | |
| 4175 | name_global_index.setLinkage(.private, &o.builder); | |
| 4176 | name_global_index.setUnnamedAddr(.unnamed_addr, &o.builder); | |
| 4496 | const name_llvm_variable = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); | |
| 4497 | try name_llvm_variable.setInitializer(name_init, &o.builder); | |
| 4498 | name_llvm_variable.setMutability(.constant, &o.builder); | |
| 4499 | name_llvm_variable.setAlignment(comptime .fromByteUnits(1), &o.builder); | |
| 4500 | const name_llvm_global = name_llvm_variable.ptrConst(&o.builder).global; | |
| 4501 | name_llvm_global.setLinkage(.private, &o.builder); | |
| 4502 | name_llvm_global.setUnnamedAddr(.unnamed_addr, &o.builder); | |
| 4177 | 4503 | |
| 4178 | 4504 | const name_val = try o.builder.structValue(llvm_ret_ty, &.{ |
| 4179 | name_global_index.toConst(), | |
| 4505 | name_llvm_global.toConst(), | |
| 4180 | 4506 | try o.builder.intConst(llvm_usize_ty, name.slice(&o.builder).?.len - 1), |
| 4181 | 4507 | }); |
| 4182 | 4508 | |
| ... | ... | @@ -4209,40 +4535,40 @@ pub const Object = struct { |
| 4209 | 4535 | const gop = try o.named_enum_map.getOrPut(o.gpa, enum_ty.toIntern()); |
| 4210 | 4536 | if (gop.found_existing) return gop.value_ptr.*; |
| 4211 | 4537 | errdefer assert(o.named_enum_map.remove(enum_ty.toIntern())); |
| 4212 | const function_index = try o.builder.addFunction( | |
| 4538 | const llvm_function = try o.builder.addFunction( | |
| 4213 | 4539 | // Dummy function type; `updateIsNamedEnumValue` will replace it with the correct type. |
| 4214 | 4540 | // TODO: change the builder API so we don't need to do this. |
| 4215 | 4541 | try o.builder.fnType(.void, &.{}, .normal), |
| 4216 | 4542 | try o.builder.strtabStringFmt("__zig_is_named_enum_value_{f}", .{enum_ty.containerTypeName(ip).fmt(ip)}), |
| 4217 | 4543 | toLlvmAddressSpace(.generic, zcu.getTarget()), |
| 4218 | 4544 | ); |
| 4219 | gop.value_ptr.* = function_index; | |
| 4220 | try o.updateIsNamedEnumValueFunction(enum_ty, function_index); | |
| 4221 | return function_index; | |
| 4545 | gop.value_ptr.* = llvm_function; | |
| 4546 | try o.updateIsNamedEnumValueFunction(enum_ty, llvm_function); | |
| 4547 | return llvm_function; | |
| 4222 | 4548 | } |
| 4223 | 4549 | fn updateIsNamedEnumValueFunction( |
| 4224 | 4550 | o: *Object, |
| 4225 | 4551 | enum_ty: Type, |
| 4226 | function_index: Builder.Function.Index, | |
| 4552 | llvm_function: Builder.Function.Index, | |
| 4227 | 4553 | ) Allocator.Error!void { |
| 4228 | 4554 | const zcu = o.zcu; |
| 4229 | 4555 | const ip = &zcu.intern_pool; |
| 4230 | 4556 | const loaded_enum = ip.loadEnumType(enum_ty.toIntern()); |
| 4231 | 4557 | |
| 4232 | 4558 | const llvm_int_ty = try o.lowerType(.fromInterned(loaded_enum.int_tag_type), .as_value); |
| 4233 | function_index.ptrConst(&o.builder).global.ptr(&o.builder).type = | |
| 4559 | llvm_function.ptrConst(&o.builder).global.ptr(&o.builder).type = | |
| 4234 | 4560 | try o.builder.fnType(.i1, &.{llvm_int_ty}, .normal); |
| 4235 | 4561 | |
| 4236 | 4562 | var attributes: Builder.FunctionAttributes.Wip = .{}; |
| 4237 | 4563 | defer attributes.deinit(&o.builder); |
| 4238 | 4564 | try o.addCommonFnAttributes(&attributes, zcu.root_mod, zcu.root_mod.omit_frame_pointer); |
| 4239 | 4565 | |
| 4240 | function_index.setLinkage(if (o.builder.strip) .private else .internal, &o.builder); | |
| 4241 | function_index.setCallConv(.fastcc, &o.builder); | |
| 4242 | function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); | |
| 4566 | llvm_function.setLinkage(if (o.builder.strip) .private else .internal, &o.builder); | |
| 4567 | llvm_function.setCallConv(.fastcc, &o.builder); | |
| 4568 | llvm_function.setAttributes(try attributes.finish(&o.builder), &o.builder); | |
| 4243 | 4569 | |
| 4244 | 4570 | var wip: Builder.WipFunction = try .init(&o.builder, .{ |
| 4245 | .function = function_index, | |
| 4571 | .function = llvm_function, | |
| 4246 | 4572 | .strip = true, |
| 4247 | 4573 | }); |
| 4248 | 4574 | defer wip.deinit(); |
| ... | ... | @@ -4278,20 +4604,27 @@ pub const Object = struct { |
| 4278 | 4604 | |
| 4279 | 4605 | pub fn getLibcFunction( |
| 4280 | 4606 | o: *Object, |
| 4607 | pt: Zcu.PerThread, | |
| 4281 | 4608 | fn_name: Builder.StrtabString, |
| 4282 | param_types: []const Builder.Type, | |
| 4283 | return_type: Builder.Type, | |
| 4609 | fn_info: FuncInfo, | |
| 4284 | 4610 | ) Allocator.Error!Builder.Function.Index { |
| 4285 | 4611 | if (o.builder.getGlobal(fn_name)) |global| return switch (global.ptrConst(&o.builder).kind) { |
| 4286 | 4612 | .alias => |alias| alias.getAliasee(&o.builder).ptrConst(&o.builder).kind.function, |
| 4287 | 4613 | .function => |function| function, |
| 4288 | 4614 | .variable, .replaced => unreachable, |
| 4289 | 4615 | }; |
| 4290 | return o.builder.addFunction( | |
| 4291 | try o.builder.fnType(return_type, param_types, .normal), | |
| 4616 | const llvm_function = try o.builder.addFunction( | |
| 4617 | try o.lowerFnType(fn_info), | |
| 4292 | 4618 | fn_name, |
| 4293 | 4619 | toLlvmAddressSpace(.generic, o.zcu.getTarget()), |
| 4294 | 4620 | ); |
| 4621 | var attributes: Builder.FunctionAttributes.Wip = .{}; | |
| 4622 | defer attributes.deinit(&o.builder); | |
| 4623 | try o.addCallingConventionFnAttributes(pt, llvm_function, &attributes, .{ | |
| 4624 | .name = fn_name.slice(&o.builder).?, | |
| 4625 | }, fn_info); | |
| 4626 | llvm_function.setAttributes(try attributes.finish(&o.builder), &o.builder); | |
| 4627 | return llvm_function; | |
| 4295 | 4628 | } |
| 4296 | 4629 | }; |
| 4297 | 4630 | |
| ... | ... | @@ -4585,47 +4918,6 @@ fn toLlvmGlobalAddressSpace(wanted_address_space: std.lang.AddressSpace, target: |
| 4585 | 4918 | }; |
| 4586 | 4919 | } |
| 4587 | 4920 | |
| 4588 | /// This function returns true if we expect LLVM to lower f16 correctly | |
| 4589 | /// and false if we expect LLVM to crash if it encounters an f16 type, | |
| 4590 | /// or if it produces miscompilations. | |
| 4591 | pub fn backendSupportsF16(target: *const std.Target) bool { | |
| 4592 | return switch (target.cpu.arch) { | |
| 4593 | .arm, | |
| 4594 | .armeb, | |
| 4595 | .thumb, | |
| 4596 | .thumbeb, | |
| 4597 | => target.abi.float() == .soft or target.cpu.has(.arm, .fullfp16), | |
| 4598 | else => true, | |
| 4599 | }; | |
| 4600 | } | |
| 4601 | ||
| 4602 | /// This function returns true if we expect LLVM to lower x86_fp80 correctly | |
| 4603 | /// and false if we expect LLVM to crash if it encounters an x86_fp80 type, | |
| 4604 | /// or if it produces miscompilations. | |
| 4605 | pub fn backendSupportsF80(target: *const std.Target) bool { | |
| 4606 | return switch (target.cpu.arch) { | |
| 4607 | .x86, .x86_64 => !target.cpu.has(.x86, .soft_float), | |
| 4608 | else => false, | |
| 4609 | }; | |
| 4610 | } | |
| 4611 | ||
| 4612 | /// This function returns true if we expect LLVM to lower f128 correctly, | |
| 4613 | /// and false if we expect LLVM to crash if it encounters an f128 type, | |
| 4614 | /// or if it produces miscompilations. | |
| 4615 | pub fn backendSupportsF128(target: *const std.Target) bool { | |
| 4616 | return switch (target.cpu.arch) { | |
| 4617 | // https://github.com/llvm/llvm-project/issues/121122 | |
| 4618 | .amdgcn, | |
| 4619 | => false, | |
| 4620 | .arm, | |
| 4621 | .armeb, | |
| 4622 | .thumb, | |
| 4623 | .thumbeb, | |
| 4624 | => target.abi.float() == .soft or target.cpu.has(.arm, .fp_armv8), | |
| 4625 | else => true, | |
| 4626 | }; | |
| 4627 | } | |
| 4628 | ||
| 4629 | 4921 | /// We need to insert extra padding if LLVM's isn't enough. |
| 4630 | 4922 | /// However we don't want to ever call LLVMABIAlignmentOfType or |
| 4631 | 4923 | /// LLVMABISizeOfType because these functions will trip assertions |
src/codegen/llvm/FuncGen.zig+1097-830| ... | ... | @@ -169,7 +169,7 @@ fn resolveValue(self: *FuncGen, val: Value) Allocator.Error!Builder.Constant { |
| 169 | 169 | // We need a pointer to a global constant, i.e. a UAV. |
| 170 | 170 | return o.lowerUavRef( |
| 171 | 171 | val.toIntern(), |
| 172 | ty.abiAlignment(zcu), | |
| 172 | ty.abiAlignment(zcu).toLlvm(), | |
| 173 | 173 | target_util.defaultAddressSpace(zcu.getTarget(), .global_constant), |
| 174 | 174 | ); |
| 175 | 175 | } |
| ... | ... | @@ -190,10 +190,10 @@ pub fn genMainBody(fg: *FuncGen) TodoError!void { |
| 190 | 190 | const fn_info = zcu.typeToFunc(fn_ty).?; |
| 191 | 191 | const param_types = fn_info.param_types.get(ip); |
| 192 | 192 | |
| 193 | var it = iterateParamTypes(o, fn_info); | |
| 193 | var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types.get(ip)); | |
| 194 | 194 | |
| 195 | 195 | // Populate `fg.ret_ptr`... |
| 196 | fg.ret_ptr = switch (try fnReturnStrat(o, fn_info)) { | |
| 196 | fg.ret_ptr = switch (try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type))) { | |
| 197 | 197 | .sret => rp: { |
| 198 | 198 | defer it.llvm_index += 1; |
| 199 | 199 | break :rp fg.wip.arg(it.llvm_index); |
| ... | ... | @@ -721,29 +721,19 @@ fn genBodyDebugScope( |
| 721 | 721 | try self.genBody(body, coverage_point); |
| 722 | 722 | } |
| 723 | 723 | |
| 724 | const CallAttr = enum { | |
| 725 | Auto, | |
| 726 | NeverTail, | |
| 727 | NeverInline, | |
| 728 | AlwaysTail, | |
| 729 | AlwaysInline, | |
| 730 | }; | |
| 731 | ||
| 732 | fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) Allocator.Error!Builder.Value { | |
| 733 | const air_call = self.air.unwrapCall(inst); | |
| 734 | const args = air_call.args; | |
| 735 | const o = self.object; | |
| 736 | const pt = self.pt; | |
| 724 | fn airCall(fg: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) Allocator.Error!Builder.Value { | |
| 725 | const o = fg.object; | |
| 737 | 726 | const zcu = o.zcu; |
| 727 | const air_call = fg.air.unwrapCall(inst); | |
| 728 | const args = air_call.args; | |
| 738 | 729 | const ip = &zcu.intern_pool; |
| 739 | const callee_ty = self.typeOf(air_call.callee); | |
| 730 | const callee_ty = fg.typeOf(air_call.callee); | |
| 740 | 731 | const zig_fn_ty = switch (callee_ty.zigTypeTag(zcu)) { |
| 741 | 732 | .@"fn" => callee_ty, |
| 742 | 733 | .pointer => callee_ty.childType(zcu), |
| 743 | 734 | else => unreachable, |
| 744 | 735 | }; |
| 745 | 736 | const fn_info = zcu.typeToFunc(zig_fn_ty).?; |
| 746 | const return_type: Type = .fromInterned(fn_info.return_type); | |
| 747 | 737 | const llvm_fn = llvm_fn: { |
| 748 | 738 | // If the callee is a function *body*, we need to use a pointer to the global. |
| 749 | 739 | 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 |
| 752 | 742 | else => {}, |
| 753 | 743 | }; |
| 754 | 744 | // Otherwise, the operand is already a function pointer (possibly runtime-known). |
| 755 | break :llvm_fn try self.resolveInst(air_call.callee); | |
| 745 | break :llvm_fn try fg.resolveInst(air_call.callee); | |
| 756 | 746 | }; |
| 747 | ||
| 748 | const arg_types = try fg.gpa.alloc(InternPool.Index, args.len); | |
| 749 | defer fg.gpa.free(arg_types); | |
| 750 | const arg_values = try fg.gpa.alloc(Builder.Value, args.len); | |
| 751 | defer fg.gpa.free(arg_values); | |
| 752 | for (arg_types, arg_values, args) |*arg_type, *arg_value, arg| { | |
| 753 | const arg_ty = fg.typeOf(arg); | |
| 754 | arg_type.* = arg_ty.toIntern(); | |
| 755 | arg_value.* = if (arg_ty.hasRuntimeBits(zcu)) try fg.resolveInst(arg) else .none; | |
| 756 | } | |
| 757 | return fg.buildCall(.{ | |
| 758 | .is_unused = fg.liveness.isUnused(inst), | |
| 759 | .modifier = modifier, | |
| 760 | }, try o.lowerType(zig_fn_ty, .as_value), llvm_fn, .fromIntern(fn_info, ip), arg_types, arg_values); | |
| 761 | } | |
| 762 | ||
| 763 | fn buildCall( | |
| 764 | fg: *FuncGen, | |
| 765 | opts: struct { | |
| 766 | is_unused: bool = false, | |
| 767 | modifier: std.lang.CallModifier = .auto, | |
| 768 | }, | |
| 769 | llvm_fn_ty: Builder.Type, | |
| 770 | llvm_fn: Builder.Value, | |
| 771 | fn_info: Object.FuncInfo, | |
| 772 | arg_types: []const InternPool.Index, | |
| 773 | arg_values: []const Builder.Value, | |
| 774 | ) Allocator.Error!Builder.Value { | |
| 775 | const o = fg.object; | |
| 776 | const pt = fg.pt; | |
| 777 | const zcu = o.zcu; | |
| 778 | const return_type: Type = .fromInterned(fn_info.return_type); | |
| 757 | 779 | const target = zcu.getTarget(); |
| 758 | const ret_strat = try fnReturnStrat(o, fn_info); | |
| 780 | const ret_strat = try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type)); | |
| 759 | 781 | |
| 760 | var llvm_args = std.array_list.Managed(Builder.Value).init(self.gpa); | |
| 761 | defer llvm_args.deinit(); | |
| 782 | var llvm_args: std.ArrayList(Builder.Value) = .empty; | |
| 783 | defer llvm_args.deinit(fg.gpa); | |
| 762 | 784 | |
| 763 | 785 | var attributes: Builder.FunctionAttributes.Wip = .{}; |
| 764 | 786 | defer attributes.deinit(&o.builder); |
| 765 | 787 | |
| 766 | if (self.disable_intrinsics) { | |
| 788 | if (fg.disable_intrinsics) { | |
| 767 | 789 | try attributes.addFnAttr(.nobuiltin, &o.builder); |
| 768 | 790 | } |
| 769 | 791 | |
| 770 | switch (modifier) { | |
| 792 | switch (opts.modifier) { | |
| 771 | 793 | .auto, .always_tail => {}, |
| 772 | 794 | .never_tail, .never_inline => try attributes.addFnAttr(.@"noinline", &o.builder), |
| 773 | 795 | .no_suspend, .always_inline, .compile_time => unreachable, |
| ... | ... | @@ -775,10 +797,11 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 775 | 797 | |
| 776 | 798 | const sret_alloc: ?Builder.Value = switch (ret_strat) { |
| 777 | 799 | .sret => sret_alloc: { |
| 778 | try attributes.addParamAttr(0, .{ .sret = try o.lowerType(return_type, .in_memory) }, &o.builder); | |
| 800 | const alignment = return_type.abiAlignment(zcu).toLlvm(); | |
| 801 | try o.addSRetFnAttributes(&attributes, try o.lowerType(return_type, .in_memory), alignment, .callsite); | |
| 779 | 802 | |
| 780 | const ptr = try self.buildZigAlloca(return_type, .none); | |
| 781 | try llvm_args.append(ptr); | |
| 803 | const ptr = try fg.buildZigAlloca(return_type, .none); | |
| 804 | try llvm_args.append(fg.gpa, ptr); | |
| 782 | 805 | break :sret_alloc ptr; |
| 783 | 806 | }, |
| 784 | 807 | else => sret_alloc: { |
| ... | ... | @@ -792,132 +815,111 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 792 | 815 | |
| 793 | 816 | const err_return_tracing = fn_info.cc == .auto and zcu.comp.config.any_error_tracing; |
| 794 | 817 | if (err_return_tracing) { |
| 795 | assert(self.err_ret_trace != .none); | |
| 796 | try llvm_args.append(self.err_ret_trace); | |
| 797 | } | |
| 798 | ||
| 799 | var it = iterateParamTypes(o, fn_info); | |
| 800 | while (try it.nextCall(self, args)) |lowering| switch (lowering) { | |
| 801 | .no_bits => continue, | |
| 802 | .byval => { | |
| 803 | const arg = args[it.zig_index - 1]; | |
| 804 | const param_ty = self.typeOf(arg); | |
| 805 | const llvm_arg = try self.resolveInst(arg); | |
| 806 | if (isByRef(param_ty, zcu)) { | |
| 807 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); | |
| 808 | // We don't need to handle non-ABI-sized integer types in memory here since they are | |
| 809 | // never by-ref. | |
| 810 | const llvm_param_ty = try o.lowerType(param_ty, .in_memory); | |
| 811 | const loaded = try self.wip.load(.normal, llvm_param_ty, llvm_arg, alignment, ""); | |
| 812 | try llvm_args.append(loaded); | |
| 813 | } else { | |
| 814 | try llvm_args.append(llvm_arg); | |
| 815 | } | |
| 816 | }, | |
| 817 | .byref => { | |
| 818 | const arg = args[it.zig_index - 1]; | |
| 819 | const param_ty = self.typeOf(arg); | |
| 820 | const llvm_arg = try self.resolveInst(arg); | |
| 821 | if (isByRef(param_ty, zcu)) { | |
| 822 | try llvm_args.append(llvm_arg); | |
| 823 | } else { | |
| 824 | const arg_ptr = try self.buildZigAlloca(param_ty, .none); | |
| 825 | try self.store(arg_ptr, .none, llvm_arg, param_ty, .normal); | |
| 826 | try llvm_args.append(arg_ptr); | |
| 827 | } | |
| 828 | }, | |
| 829 | .byref_mut => { | |
| 830 | const arg = args[it.zig_index - 1]; | |
| 831 | const param_ty = self.typeOf(arg); | |
| 832 | const llvm_arg = try self.resolveInst(arg); | |
| 833 | ||
| 834 | const arg_ptr = try self.buildZigAlloca(param_ty, .none); | |
| 835 | try self.store(arg_ptr, .none, llvm_arg, param_ty, .normal); | |
| 836 | try llvm_args.append(arg_ptr); | |
| 837 | }, | |
| 838 | .abi_sized_int => { | |
| 839 | const arg = args[it.zig_index - 1]; | |
| 840 | const param_ty = self.typeOf(arg); | |
| 841 | const llvm_arg = try self.resolveInst(arg); | |
| 842 | const int_llvm_ty = try o.builder.intType(@intCast(param_ty.abiSize(zcu) * 8)); | |
| 843 | ||
| 844 | if (isByRef(param_ty, zcu)) { | |
| 845 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); | |
| 846 | const loaded = try self.wip.load(.normal, int_llvm_ty, llvm_arg, alignment, ""); | |
| 847 | try llvm_args.append(loaded); | |
| 848 | } else { | |
| 849 | // LLVM does not allow bitcasting structs so we must allocate | |
| 850 | // a local, store as one type, and then load as another type. | |
| 851 | const alignment = param_ty.abiAlignment(zcu).toLlvm(); | |
| 852 | const ptr = try self.buildAlloca(int_llvm_ty, alignment); | |
| 853 | try self.store(ptr, .none, llvm_arg, param_ty, .normal); | |
| 854 | const loaded = try self.wip.load(.normal, int_llvm_ty, ptr, alignment, ""); | |
| 855 | try llvm_args.append(loaded); | |
| 856 | } | |
| 857 | }, | |
| 858 | .slice => { | |
| 859 | const arg = args[it.zig_index - 1]; | |
| 860 | const llvm_arg = try self.resolveInst(arg); | |
| 861 | const ptr = try self.wip.extractValue(llvm_arg, &.{0}, ""); | |
| 862 | const len = try self.wip.extractValue(llvm_arg, &.{1}, ""); | |
| 863 | try llvm_args.appendSlice(&.{ ptr, len }); | |
| 864 | }, | |
| 865 | .multiple_llvm_types => { | |
| 866 | const arg = args[it.zig_index - 1]; | |
| 867 | const param_ty = self.typeOf(arg); | |
| 868 | const llvm_arg = try self.resolveInst(arg); | |
| 869 | const param_alignment = param_ty.abiAlignment(zcu); | |
| 870 | const llvm_ty = try o.builder.arrayType(it.offsets_buffer[it.types_len], .i8); | |
| 871 | const arg_ptr = try self.buildAlloca(llvm_ty, param_alignment.toLlvm()); | |
| 872 | try self.store(arg_ptr, .none, llvm_arg, param_ty, .normal); | |
| 873 | ||
| 874 | try llvm_args.ensureUnusedCapacity(it.types_len); | |
| 875 | for (it.types_buffer[0..it.types_len], it.offsets_buffer[0..it.types_len]) |field_ty, offset| { | |
| 876 | const field_ptr = try self.ptraddConst(arg_ptr, offset); | |
| 877 | const loaded = try self.wip.load(.normal, field_ty, field_ptr, param_alignment.offset(offset).toLlvm(), ""); | |
| 878 | llvm_args.appendAssumeCapacity(loaded); | |
| 879 | } | |
| 880 | }, | |
| 881 | .float_array => |count| { | |
| 882 | const arg = args[it.zig_index - 1]; | |
| 883 | const arg_ty = self.typeOf(arg); | |
| 884 | const arg_val = try self.resolveInst(arg); | |
| 885 | ||
| 886 | const arg_ptr: Builder.Value = if (!isByRef(arg_ty, zcu)) ptr: { | |
| 887 | const ptr = try self.buildZigAlloca(arg_ty, .none); | |
| 888 | try self.store(ptr, .none, arg_val, arg_ty, .normal); | |
| 889 | break :ptr ptr; | |
| 890 | } else arg_val; | |
| 891 | ||
| 892 | const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(arg_ty, zcu).?, .in_memory); | |
| 893 | const array_ty = try o.builder.arrayType(count, float_ty); | |
| 818 | assert(fg.err_ret_trace != .none); | |
| 819 | try llvm_args.append(fg.gpa, fg.err_ret_trace); | |
| 820 | } | |
| 894 | 821 | |
| 895 | const loaded = try self.wip.load(.normal, array_ty, arg_ptr, arg_ty.abiAlignment(zcu).toLlvm(), ""); | |
| 896 | try llvm_args.append(loaded); | |
| 897 | }, | |
| 898 | .i32_array, .i64_array => |arr_len| { | |
| 899 | const elem_size: u8 = if (lowering == .i32_array) 32 else 64; | |
| 900 | const arg = args[it.zig_index - 1]; | |
| 901 | const arg_ty = self.typeOf(arg); | |
| 902 | const arg_val = try self.resolveInst(arg); | |
| 903 | ||
| 904 | const arg_ptr: Builder.Value = if (!isByRef(arg_ty, zcu)) ptr: { | |
| 905 | const ptr = try self.buildZigAlloca(arg_ty, .none); | |
| 906 | try self.store(ptr, .none, arg_val, arg_ty, .normal); | |
| 907 | break :ptr ptr; | |
| 908 | } else arg_val; | |
| 822 | var it = iterateParamTypes(o, fn_info.cc, fn_info.param_types); | |
| 823 | while (try it.nextCall(arg_types)) |lowering| { | |
| 824 | const arg_ty: Type = .fromInterned(arg_types[it.zig_index - 1]); | |
| 825 | const arg_val = arg_values[it.zig_index - 1]; | |
| 826 | switch (lowering) { | |
| 827 | .no_bits => continue, | |
| 828 | .byval => { | |
| 829 | if (isByRef(arg_ty, zcu)) { | |
| 830 | const alignment = arg_ty.abiAlignment(zcu).toLlvm(); | |
| 831 | // We don't need to handle non-ABI-sized integer types in memory here since they are | |
| 832 | // never by-ref. | |
| 833 | const llvm_arg_ty = try o.lowerType(arg_ty, .in_memory); | |
| 834 | const loaded = try fg.wip.load(.normal, llvm_arg_ty, arg_val, alignment, ""); | |
| 835 | try llvm_args.append(fg.gpa, loaded); | |
| 836 | } else { | |
| 837 | try llvm_args.append(fg.gpa, arg_val); | |
| 838 | } | |
| 839 | }, | |
| 840 | .byref => { | |
| 841 | if (isByRef(arg_ty, zcu)) { | |
| 842 | try llvm_args.append(fg.gpa, arg_val); | |
| 843 | } else { | |
| 844 | const arg_ptr = try fg.buildZigAlloca(arg_ty, .none); | |
| 845 | try fg.store(arg_ptr, .none, arg_val, arg_ty, .normal); | |
| 846 | try llvm_args.append(fg.gpa, arg_ptr); | |
| 847 | } | |
| 848 | }, | |
| 849 | .byref_mut => { | |
| 850 | const arg_ptr = try fg.buildZigAlloca(arg_ty, .none); | |
| 851 | try fg.store(arg_ptr, .none, arg_val, arg_ty, .normal); | |
| 852 | try llvm_args.append(fg.gpa, arg_ptr); | |
| 853 | }, | |
| 854 | .abi_sized_int => { | |
| 855 | const int_llvm_ty = try o.builder.intType(@intCast(arg_ty.abiSize(zcu) * 8)); | |
| 909 | 856 | |
| 910 | const array_ty = try o.builder.arrayType(arr_len, try o.builder.intType(@intCast(elem_size))); | |
| 911 | const loaded = try self.wip.load(.normal, array_ty, arg_ptr, arg_ty.abiAlignment(zcu).toLlvm(), ""); | |
| 912 | try llvm_args.append(loaded); | |
| 913 | }, | |
| 914 | }; | |
| 857 | if (isByRef(arg_ty, zcu)) { | |
| 858 | const alignment = arg_ty.abiAlignment(zcu).toLlvm(); | |
| 859 | const loaded = try fg.wip.load(.normal, int_llvm_ty, arg_val, alignment, ""); | |
| 860 | try llvm_args.append(fg.gpa, loaded); | |
| 861 | } else { | |
| 862 | // LLVM does not allow bitcasting structs so we must allocate | |
| 863 | // a local, store as one type, and then load as another type. | |
| 864 | const alignment = arg_ty.abiAlignment(zcu).toLlvm(); | |
| 865 | const ptr = try fg.buildAlloca(int_llvm_ty, alignment); | |
| 866 | try fg.store(ptr, .none, arg_val, arg_ty, .normal); | |
| 867 | const loaded = try fg.wip.load(.normal, int_llvm_ty, ptr, alignment, ""); | |
| 868 | try llvm_args.append(fg.gpa, loaded); | |
| 869 | } | |
| 870 | }, | |
| 871 | .slice => { | |
| 872 | const ptr = try fg.wip.extractValue(arg_val, &.{0}, ""); | |
| 873 | const len = try fg.wip.extractValue(arg_val, &.{1}, ""); | |
| 874 | try llvm_args.appendSlice(fg.gpa, &.{ ptr, len }); | |
| 875 | }, | |
| 876 | .multiple_llvm_types => { | |
| 877 | const arg_alignment = arg_ty.abiAlignment(zcu); | |
| 878 | const llvm_ty = try o.builder.arrayType(it.offsets_buffer[it.types_len], .i8); | |
| 879 | const arg_ptr = try fg.buildAlloca(llvm_ty, arg_alignment.toLlvm()); | |
| 880 | try fg.store(arg_ptr, .none, arg_val, arg_ty, .normal); | |
| 881 | ||
| 882 | try llvm_args.ensureUnusedCapacity(fg.gpa, it.types_len); | |
| 883 | for (it.types_buffer[0..it.types_len], it.offsets_buffer[0..it.types_len]) |field_ty, offset| { | |
| 884 | const field_ptr = try fg.ptraddConst(arg_ptr, offset); | |
| 885 | const loaded = try fg.wip.load(.normal, field_ty, field_ptr, arg_alignment.offset(offset).toLlvm(), ""); | |
| 886 | llvm_args.appendAssumeCapacity(loaded); | |
| 887 | } | |
| 888 | }, | |
| 889 | .float_array => |count| { | |
| 890 | const arg_ptr: Builder.Value = if (!isByRef(arg_ty, zcu)) ptr: { | |
| 891 | const ptr = try fg.buildZigAlloca(arg_ty, .none); | |
| 892 | try fg.store(ptr, .none, arg_val, arg_ty, .normal); | |
| 893 | break :ptr ptr; | |
| 894 | } else arg_val; | |
| 895 | ||
| 896 | const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(arg_ty, zcu).?, .in_memory); | |
| 897 | const array_ty = try o.builder.arrayType(count, float_ty); | |
| 898 | ||
| 899 | const loaded = try fg.wip.load(.normal, array_ty, arg_ptr, arg_ty.abiAlignment(zcu).toLlvm(), ""); | |
| 900 | try llvm_args.append(fg.gpa, loaded); | |
| 901 | }, | |
| 902 | .i32_array, .i64_array => |arr_len| { | |
| 903 | const elem_size: u8 = if (lowering == .i32_array) 32 else 64; | |
| 904 | ||
| 905 | const arg_ptr: Builder.Value = if (!isByRef(arg_ty, zcu)) ptr: { | |
| 906 | const ptr = try fg.buildZigAlloca(arg_ty, .none); | |
| 907 | try fg.store(ptr, .none, arg_val, arg_ty, .normal); | |
| 908 | break :ptr ptr; | |
| 909 | } else arg_val; | |
| 910 | ||
| 911 | const array_ty = try o.builder.arrayType(arr_len, try o.builder.intType(@intCast(elem_size))); | |
| 912 | const loaded = try fg.wip.load(.normal, array_ty, arg_ptr, arg_ty.abiAlignment(zcu).toLlvm(), ""); | |
| 913 | try llvm_args.append(fg.gpa, loaded); | |
| 914 | }, | |
| 915 | } | |
| 916 | } | |
| 915 | 917 | |
| 916 | 918 | const cc_info = llvm.toLlvmCallConv(fn_info.cc, target).?; |
| 917 | 919 | |
| 918 | 920 | { |
| 919 | 921 | // Add argument attributes. |
| 920 | it = iterateParamTypes(o, fn_info); | |
| 922 | it = iterateParamTypes(o, fn_info.cc, fn_info.param_types); | |
| 921 | 923 | it.llvm_index += @intFromBool(ret_strat == .sret); |
| 922 | 924 | it.llvm_index += @intFromBool(err_return_tracing); |
| 923 | 925 | 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 |
| 925 | 927 | while (try it.next()) |lowering| switch (lowering) { |
| 926 | 928 | .byval => { |
| 927 | 929 | const param_index = it.zig_index - 1; |
| 928 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); | |
| 930 | const param_ty = Type.fromInterned(fn_info.param_types[param_index]); | |
| 929 | 931 | if (!isByRef(param_ty, zcu)) { |
| 930 | 932 | try o.addByValParamAttrs(pt, &attributes, param_ty, param_index, fn_info, it.llvm_index - 1); |
| 931 | 933 | } |
| ... | ... | @@ -947,7 +949,7 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 947 | 949 | }, |
| 948 | 950 | .byref => { |
| 949 | 951 | const param_index = it.zig_index - 1; |
| 950 | const param_ty: Type = .fromInterned(fn_info.param_types.get(ip)[param_index]); | |
| 952 | const param_ty: Type = .fromInterned(fn_info.param_types[param_index]); | |
| 951 | 953 | try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, it.byval_attr, param_ty); |
| 952 | 954 | }, |
| 953 | 955 | .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 |
| 962 | 964 | |
| 963 | 965 | .slice => { |
| 964 | 966 | assert(!it.byval_attr); |
| 965 | const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); | |
| 967 | const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]); | |
| 966 | 968 | const ptr_info = param_ty.ptrInfo(zcu); |
| 967 | 969 | const llvm_arg_i = it.llvm_index - 2; |
| 968 | 970 | |
| ... | ... | @@ -989,8 +991,8 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 989 | 991 | }; |
| 990 | 992 | } |
| 991 | 993 | |
| 992 | const call = try self.wip.call( | |
| 993 | switch (modifier) { | |
| 994 | const call = try fg.wip.call( | |
| 995 | switch (opts.modifier) { | |
| 994 | 996 | .auto, .never_inline => .normal, |
| 995 | 997 | .never_tail => .notail, |
| 996 | 998 | .always_tail => .musttail, |
| ... | ... | @@ -998,19 +1000,14 @@ fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier |
| 998 | 1000 | }, |
| 999 | 1001 | cc_info.llvm_cc, |
| 1000 | 1002 | try attributes.finish(&o.builder), |
| 1001 | try o.lowerType(zig_fn_ty, .as_value), | |
| 1003 | llvm_fn_ty, | |
| 1002 | 1004 | llvm_fn, |
| 1003 | 1005 | llvm_args.items, |
| 1004 | 1006 | "", |
| 1005 | 1007 | ); |
| 1006 | 1008 | |
| 1007 | if (fn_info.return_type == .noreturn_type and modifier != .always_tail) { | |
| 1008 | return .none; | |
| 1009 | } | |
| 1010 | ||
| 1011 | if (self.liveness.isUnused(inst)) { | |
| 1012 | return .none; | |
| 1013 | } | |
| 1009 | if (opts.is_unused) return .none; | |
| 1010 | if (fn_info.return_type == .noreturn_type and opts.modifier != .always_tail) return .none; | |
| 1014 | 1011 | |
| 1015 | 1012 | // We exit this `switch` if we have a pointer to the return value. |
| 1016 | 1013 | 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 |
| 1020 | 1017 | .sret => sret_alloc.?, |
| 1021 | 1018 | .mem_cast => |llvm_ret_ty| ret_val_ptr: { |
| 1022 | 1019 | const alignment = return_type.abiAlignment(zcu).toLlvm(); |
| 1023 | const ptr = try self.buildAlloca(llvm_ret_ty, alignment); | |
| 1024 | _ = try self.wip.store(.normal, call, ptr, alignment); | |
| 1020 | const ptr = try fg.buildAlloca(llvm_ret_ty, alignment); | |
| 1021 | _ = try fg.wip.store(.normal, call, ptr, alignment); | |
| 1025 | 1022 | break :ret_val_ptr ptr; |
| 1026 | 1023 | }, |
| 1027 | 1024 | }; |
| 1028 | 1025 | if (isByRef(return_type, zcu)) { |
| 1029 | 1026 | return ret_val_ptr; |
| 1030 | 1027 | } else { |
| 1031 | return self.load(ret_val_ptr, .none, return_type, .normal); | |
| 1028 | return fg.load(ret_val_ptr, .none, return_type, .normal); | |
| 1032 | 1029 | } |
| 1033 | 1030 | } |
| 1034 | 1031 | |
| ... | ... | @@ -1067,7 +1064,7 @@ fn airRet(self: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!vo |
| 1067 | 1064 | |
| 1068 | 1065 | const fn_info = zcu.typeToFunc(Type.fromInterned(ip.getNav(self.nav_index).resolved.?.type)).?; |
| 1069 | 1066 | |
| 1070 | const ret_strat = try fnReturnStrat(o, fn_info); | |
| 1067 | const ret_strat = try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type)); | |
| 1071 | 1068 | const val_is_undef = if (un_op.toInterned()) |i| Value.fromInterned(i).isUndef(zcu) else false; |
| 1072 | 1069 | const ret_ty_align = ret_ty.abiAlignment(zcu); |
| 1073 | 1070 | |
| ... | ... | @@ -1141,7 +1138,7 @@ fn airRetLoad(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!void { |
| 1141 | 1138 | const ret_ty = ptr_ty.childType(zcu); |
| 1142 | 1139 | const fn_info = zcu.typeToFunc(.fromInterned(ip.getNav(self.nav_index).resolved.?.type)).?; |
| 1143 | 1140 | const ptr = try self.resolveInst(un_op); |
| 1144 | switch (try fnReturnStrat(o, fn_info)) { | |
| 1141 | switch (try fnReturnStrat(o, fn_info.cc, .fromInterned(fn_info.return_type))) { | |
| 1145 | 1142 | .void => _ = try self.wip.retVoid(), |
| 1146 | 1143 | .sret => { |
| 1147 | 1144 | assert(self.ret_ptr != .none); |
| ... | ... | @@ -2028,135 +2025,95 @@ fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder |
| 2028 | 2025 | return self.wip.buildAggregate(slice_llvm_ty, &.{ operand, len }, ""); |
| 2029 | 2026 | } |
| 2030 | 2027 | |
| 2031 | fn airFloatFromInt(self: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value { | |
| 2032 | const o = self.object; | |
| 2028 | fn airFloatFromInt(fg: *FuncGen, inst: Air.Inst.Index) TodoError!Builder.Value { | |
| 2029 | const o = fg.object; | |
| 2033 | 2030 | const zcu = o.zcu; |
| 2034 | const ty_op = self.air.instructions.items(.data)[@backingInt(inst)].ty_op; | |
| 2031 | const ty_op = fg.air.instructions.items(.data)[@backingInt(inst)].ty_op; | |
| 2035 | 2032 | |
| 2036 | const operand = try self.resolveInst(ty_op.operand); | |
| 2037 | const operand_ty = self.typeOf(ty_op.operand); | |
| 2033 | const operand = try fg.resolveInst(ty_op.operand); | |
| 2034 | const operand_ty = fg.typeOf(ty_op.operand); | |
| 2038 | 2035 | const operand_scalar_ty = operand_ty.scalarType(zcu); |
| 2039 | const is_signed_int = operand_scalar_ty.isSignedInt(zcu); | |
| 2036 | const operand_scalar_info = operand_scalar_ty.intInfo(zcu); | |
| 2040 | 2037 | |
| 2041 | const dest_ty = self.typeOfIndex(inst); | |
| 2038 | const dest_ty = fg.typeOfIndex(inst); | |
| 2042 | 2039 | const dest_scalar_ty = dest_ty.scalarType(zcu); |
| 2043 | const dest_llvm_ty = try o.lowerType(dest_ty, .as_value); | |
| 2044 | 2040 | const target = zcu.getTarget(); |
| 2045 | 2041 | |
| 2046 | if (intrinsicsAllowed(dest_scalar_ty, target)) return self.wip.conv( | |
| 2047 | if (is_signed_int) .signed else .unsigned, | |
| 2048 | operand, | |
| 2049 | dest_llvm_ty, | |
| 2050 | "", | |
| 2051 | ); | |
| 2042 | if (intrinsicsAllowed(dest_scalar_ty, target)) | |
| 2043 | return fg.wip.conv(.fromStdLang(operand_scalar_info.signedness), operand, try o.lowerType(dest_ty, .as_value), ""); | |
| 2052 | 2044 | |
| 2053 | const rt_int_bits = compilerRtIntBits(@intCast(operand_scalar_ty.bitSize(zcu))) orelse { | |
| 2054 | return self.todo("float_from_int on {d} bit integer", .{operand_scalar_ty.bitSize(zcu)}); | |
| 2045 | const rt_int_ty = compilerRtPromoteInt(operand_scalar_info) orelse { | |
| 2046 | return fg.todo("float_from_int on {d} bit integer", .{operand_scalar_info.bits}); | |
| 2055 | 2047 | }; |
| 2056 | const rt_int_ty = try o.builder.intType(rt_int_bits); | |
| 2057 | var extended = try self.wip.conv( | |
| 2058 | if (is_signed_int) .signed else .unsigned, | |
| 2048 | const vector_len = if (operand_ty.isVector(zcu)) operand_ty.vectorLen(zcu) else null; | |
| 2049 | const rt_llvm_int_ty = try o.lowerType(rt_int_ty, .as_value); | |
| 2050 | const extended = try fg.wip.conv( | |
| 2051 | .fromStdLang(operand_scalar_info.signedness), | |
| 2059 | 2052 | operand, |
| 2060 | rt_int_ty, | |
| 2053 | if (vector_len) |len| | |
| 2054 | try o.builder.vectorType(.normal, len, rt_llvm_int_ty) | |
| 2055 | else | |
| 2056 | rt_llvm_int_ty, | |
| 2061 | 2057 | "", |
| 2062 | 2058 | ); |
| 2063 | const dest_bits = dest_scalar_ty.floatBits(target); | |
| 2064 | const compiler_rt_operand_abbrev = compilerRtIntAbbrev(rt_int_bits); | |
| 2065 | const compiler_rt_dest_abbrev = compilerRtFloatAbbrev(dest_bits); | |
| 2066 | const sign_prefix = if (is_signed_int) "" else "un"; | |
| 2067 | 2059 | const fn_name = try o.builder.strtabStringFmt("__float{s}{s}i{s}f", .{ |
| 2068 | sign_prefix, | |
| 2069 | compiler_rt_operand_abbrev, | |
| 2070 | compiler_rt_dest_abbrev, | |
| 2060 | switch (operand_scalar_info.signedness) { | |
| 2061 | .signed => "", | |
| 2062 | .unsigned => "un", | |
| 2063 | }, | |
| 2064 | compilerRtIntAbbrev(rt_int_ty.intInfo(zcu).bits), | |
| 2065 | compilerRtFloatAbbrev(target, dest_scalar_ty.floatBits(target)), | |
| 2071 | 2066 | }); |
| 2072 | ||
| 2073 | var param_type = rt_int_ty; | |
| 2074 | if (rt_int_bits == 128 and (target.os.tag == .windows and target.cpu.arch == .x86_64)) { | |
| 2075 | // On Windows x86-64, "ti" functions must use Vector(2, u64) instead of the standard | |
| 2076 | // i128 calling convention to adhere to the ABI that LLVM expects compiler-rt to have. | |
| 2077 | param_type = try o.builder.vectorType(.normal, 2, .i64); | |
| 2078 | extended = try self.wip.cast(.bitcast, extended, param_type, ""); | |
| 2079 | } | |
| 2080 | ||
| 2081 | const libc_fn = try o.getLibcFunction(fn_name, &.{param_type}, dest_llvm_ty); | |
| 2082 | return self.wip.call( | |
| 2083 | .normal, | |
| 2084 | .ccc, | |
| 2085 | .none, | |
| 2086 | libc_fn.typeOf(&o.builder), | |
| 2087 | libc_fn.toValue(&o.builder), | |
| 2088 | &.{extended}, | |
| 2089 | "", | |
| 2090 | ); | |
| 2067 | return fg.buildElementwiseCall(fn_name, .{ | |
| 2068 | .cc = target.cCallingConvention().?, | |
| 2069 | .param_types = &.{rt_int_ty.toIntern()}, | |
| 2070 | .return_type = dest_scalar_ty.toIntern(), | |
| 2071 | }, &.{extended}, vector_len); | |
| 2091 | 2072 | } |
| 2092 | 2073 | |
| 2093 | 2074 | fn airIntFromFloat( |
| 2094 | self: *FuncGen, | |
| 2075 | fg: *FuncGen, | |
| 2095 | 2076 | inst: Air.Inst.Index, |
| 2096 | 2077 | fast: Builder.FastMathKind, |
| 2097 | 2078 | ) TodoError!Builder.Value { |
| 2098 | 2079 | _ = fast; |
| 2099 | 2080 | |
| 2100 | const o = self.object; | |
| 2081 | const o = fg.object; | |
| 2101 | 2082 | const zcu = o.zcu; |
| 2102 | 2083 | const target = zcu.getTarget(); |
| 2103 | const ty_op = self.air.instructions.items(.data)[@backingInt(inst)].ty_op; | |
| 2084 | const ty_op = fg.air.instructions.items(.data)[@backingInt(inst)].ty_op; | |
| 2104 | 2085 | |
| 2105 | const operand = try self.resolveInst(ty_op.operand); | |
| 2106 | const operand_ty = self.typeOf(ty_op.operand); | |
| 2086 | const operand = try fg.resolveInst(ty_op.operand); | |
| 2087 | const operand_ty = fg.typeOf(ty_op.operand); | |
| 2107 | 2088 | const operand_scalar_ty = operand_ty.scalarType(zcu); |
| 2108 | 2089 | |
| 2109 | const dest_ty = self.typeOfIndex(inst); | |
| 2090 | const dest_ty = fg.typeOfIndex(inst); | |
| 2110 | 2091 | const dest_scalar_ty = dest_ty.scalarType(zcu); |
| 2111 | 2092 | const dest_llvm_ty = try o.lowerType(dest_ty, .as_value); |
| 2093 | const dest_scalar_info = dest_scalar_ty.intInfo(zcu); | |
| 2112 | 2094 | |
| 2113 | 2095 | if (intrinsicsAllowed(operand_scalar_ty, target)) { |
| 2114 | 2096 | // TODO set fast math flag |
| 2115 | return self.wip.conv( | |
| 2116 | if (dest_scalar_ty.isSignedInt(zcu)) .signed else .unsigned, | |
| 2117 | operand, | |
| 2118 | dest_llvm_ty, | |
| 2119 | "", | |
| 2120 | ); | |
| 2097 | return fg.wip.conv(.fromStdLang(dest_scalar_info.signedness), operand, dest_llvm_ty, ""); | |
| 2121 | 2098 | } |
| 2122 | 2099 | |
| 2123 | const rt_int_bits = compilerRtIntBits(@intCast(dest_scalar_ty.bitSize(zcu))) orelse { | |
| 2124 | return self.todo("int_from_float to {d} bit integer", .{dest_scalar_ty.bitSize(zcu)}); | |
| 2100 | const rt_int_ty = compilerRtPromoteInt(dest_scalar_info) orelse { | |
| 2101 | return fg.todo("int_from_float to {d} bit integer", .{dest_scalar_info.bits}); | |
| 2125 | 2102 | }; |
| 2126 | const ret_ty = try o.builder.intType(rt_int_bits); | |
| 2127 | const libc_ret_ty = if (rt_int_bits == 128 and (target.os.tag == .windows and target.cpu.arch == .x86_64)) b: { | |
| 2128 | // On Windows x86-64, "ti" functions must use Vector(2, u64) instead of the standard | |
| 2129 | // i128 calling convention to adhere to the ABI that LLVM expects compiler-rt to have. | |
| 2130 | break :b try o.builder.vectorType(.normal, 2, .i64); | |
| 2131 | } else ret_ty; | |
| 2132 | ||
| 2133 | const operand_bits = operand_scalar_ty.floatBits(target); | |
| 2134 | const compiler_rt_operand_abbrev = compilerRtFloatAbbrev(operand_bits); | |
| 2135 | ||
| 2136 | const compiler_rt_dest_abbrev = compilerRtIntAbbrev(rt_int_bits); | |
| 2137 | const sign_prefix = if (dest_scalar_ty.isSignedInt(zcu)) "" else "uns"; | |
| 2138 | ||
| 2139 | 2103 | const fn_name = try o.builder.strtabStringFmt("__fix{s}{s}f{s}i", .{ |
| 2140 | sign_prefix, | |
| 2141 | compiler_rt_operand_abbrev, | |
| 2142 | compiler_rt_dest_abbrev, | |
| 2104 | switch (dest_scalar_info.signedness) { | |
| 2105 | .signed => "", | |
| 2106 | .unsigned => "uns", | |
| 2107 | }, | |
| 2108 | compilerRtFloatAbbrev(target, operand_scalar_ty.floatBits(target)), | |
| 2109 | compilerRtIntAbbrev(rt_int_ty.intInfo(zcu).bits), | |
| 2143 | 2110 | }); |
| 2144 | ||
| 2145 | const operand_llvm_ty = try o.lowerType(operand_ty, .as_value); | |
| 2146 | const libc_fn = try o.getLibcFunction(fn_name, &.{operand_llvm_ty}, libc_ret_ty); | |
| 2147 | var result = try self.wip.call( | |
| 2148 | .normal, | |
| 2149 | .ccc, | |
| 2150 | .none, | |
| 2151 | libc_fn.typeOf(&o.builder), | |
| 2152 | libc_fn.toValue(&o.builder), | |
| 2153 | &.{operand}, | |
| 2154 | "", | |
| 2155 | ); | |
| 2156 | ||
| 2157 | if (libc_ret_ty != ret_ty) result = try self.wip.cast(.bitcast, result, ret_ty, ""); | |
| 2158 | if (ret_ty != dest_llvm_ty) result = try self.wip.cast(.trunc, result, dest_llvm_ty, ""); | |
| 2159 | return result; | |
| 2111 | const result = try fg.buildElementwiseCall(fn_name, .{ | |
| 2112 | .cc = target.cCallingConvention().?, | |
| 2113 | .param_types = &.{operand_scalar_ty.toIntern()}, | |
| 2114 | .return_type = rt_int_ty.toIntern(), | |
| 2115 | }, &.{operand}, if (operand_ty.isVector(zcu)) operand_ty.vectorLen(zcu) else null); | |
| 2116 | return fg.wip.cast(.trunc, result, try o.lowerType(dest_ty, .as_value), ""); | |
| 2160 | 2117 | } |
| 2161 | 2118 | |
| 2162 | 2119 | 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 |
| 3692 | 3649 | const lhs = try self.resolveInst(bin_op.lhs); |
| 3693 | 3650 | const rhs = try self.resolveInst(bin_op.rhs); |
| 3694 | 3651 | const inst_ty = self.typeOfIndex(inst); |
| 3695 | const inst_llvm_ty = try o.lowerType(inst_ty, .as_value); | |
| 3696 | 3652 | const scalar_ty = inst_ty.scalarType(zcu); |
| 3697 | 3653 | |
| 3698 | 3654 | if (scalar_ty.isRuntimeFloat()) { |
| 3699 | 3655 | const a = try self.buildFloatOp(.fmod, fast, inst_ty, 2, .{ lhs, rhs }); |
| 3700 | 3656 | const b = try self.buildFloatOp(.add, fast, inst_ty, 2, .{ a, rhs }); |
| 3701 | 3657 | const c = try self.buildFloatOp(.fmod, fast, inst_ty, 2, .{ b, rhs }); |
| 3702 | const zero = try o.builder.zeroInitValue(inst_llvm_ty); | |
| 3703 | const ltz = try self.buildFloatCmp(fast, .lt, inst_ty, .{ lhs, zero }); | |
| 3658 | const zero = if (isByRef(inst_ty, zcu)) zero: { | |
| 3659 | const zero = try o.builder.zeroInitConst(try o.lowerType(inst_ty, .in_memory)); | |
| 3660 | break :zero try o.lowerConstRef(zero, inst_ty.abiAlignment(zcu).toLlvm()); | |
| 3661 | } else try o.builder.zeroInitConst(try o.lowerType(inst_ty, .as_value)); | |
| 3662 | const ltz = try self.buildFloatCmp(fast, .lt, inst_ty, .{ lhs, zero.toValue() }); | |
| 3704 | 3663 | return self.wip.select(fast, ltz, c, a, ""); |
| 3705 | 3664 | } |
| 3706 | 3665 | if (scalar_ty.isSignedInt(zcu)) { |
| ... | ... | @@ -3709,6 +3668,7 @@ fn airMod(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allo |
| 3709 | 3668 | var bfa: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), self.gpa); |
| 3710 | 3669 | const allocator = bfa.allocator(); |
| 3711 | 3670 | |
| 3671 | const inst_llvm_ty = try o.lowerType(inst_ty, .as_value); | |
| 3712 | 3672 | const scalar_bits = scalar_ty.intInfo(zcu).bits; |
| 3713 | 3673 | var smin_big_int: std.math.big.int.Mutable = .{ |
| 3714 | 3674 | .limbs = try allocator.alloc( |
| ... | ... | @@ -3818,34 +3778,97 @@ fn airOverflow( |
| 3818 | 3778 | } |
| 3819 | 3779 | |
| 3820 | 3780 | fn buildElementwiseCall( |
| 3821 | self: *FuncGen, | |
| 3822 | llvm_fn: Builder.Function.Index, | |
| 3823 | args_vectors: []const Builder.Value, | |
| 3824 | result_vector: Builder.Value, | |
| 3825 | vector_len: usize, | |
| 3781 | fg: *FuncGen, | |
| 3782 | fn_name: Builder.StrtabString, | |
| 3783 | fn_info: Object.FuncInfo, | |
| 3784 | arg_values: []const Builder.Value, | |
| 3785 | vector_len: ?u32, | |
| 3826 | 3786 | ) Allocator.Error!Builder.Value { |
| 3827 | const o = self.object; | |
| 3828 | assert(args_vectors.len <= 3); | |
| 3787 | const o = fg.object; | |
| 3788 | const zcu = o.zcu; | |
| 3789 | const llvm_fn = try fg.object.getLibcFunction(fg.pt, fn_name, fn_info); | |
| 3829 | 3790 | |
| 3830 | var i: usize = 0; | |
| 3831 | var result = result_vector; | |
| 3832 | while (i < vector_len) : (i += 1) { | |
| 3833 | const index_i32 = try o.builder.intValue(.i32, i); | |
| 3791 | const iterations = vector_len orelse 1; | |
| 3792 | const ret_ty: Type = .fromInterned(fn_info.return_type); | |
| 3793 | const ret_is_by_ref = isByRef(ret_ty, zcu); | |
| 3794 | if (iterations > 1 and (fn_info.return_type == .void_type or ret_is_by_ref) and | |
| 3795 | for (fn_info.param_types) |param_type| { | |
| 3796 | if (!isByRef(.fromInterned(param_type), zcu)) break false; | |
| 3797 | } else true) | |
| 3798 | { | |
| 3799 | const entry_block = fg.wip.cursor.block; | |
| 3800 | const loop_block = try fg.wip.block(2, "elementwise.loop"); | |
| 3801 | const done_block = try fg.wip.block(1, "elementwise.done"); | |
| 3802 | ||
| 3803 | const result_ptr = if (fn_info.return_type == .void_type) .none else result_ptr: { | |
| 3804 | const ret_llvm_ty = try o.lowerType(ret_ty, .in_memory); | |
| 3805 | break :result_ptr try fg.buildAlloca( | |
| 3806 | if (vector_len) |len| try o.builder.arrayType(len, ret_llvm_ty) else ret_llvm_ty, | |
| 3807 | ret_ty.abiAlignment(zcu).toLlvm(), | |
| 3808 | ); | |
| 3809 | }; | |
| 3810 | _ = try fg.wip.br(loop_block); | |
| 3834 | 3811 | |
| 3835 | var args: [3]Builder.Value = undefined; | |
| 3836 | for (args[0..args_vectors.len], args_vectors) |*arg_elem, arg_vector| { | |
| 3837 | arg_elem.* = try self.wip.extractElement(arg_vector, index_i32, ""); | |
| 3812 | fg.wip.cursor = .{ .block = loop_block }; | |
| 3813 | const index = try fg.wip.phi(.i32, "elementwise.index"); | |
| 3814 | ||
| 3815 | var arg_elems_buf: [3]Builder.Value = undefined; | |
| 3816 | const arg_elems = arg_elems_buf[0..arg_values.len]; | |
| 3817 | for (arg_elems, fn_info.param_types, arg_values) |*arg_elem, param_type, arg_value| { | |
| 3818 | const arg_elem_ptr = try fg.ptraddScaled(arg_value, index.toValue(), Type.fromInterned(param_type).abiSize(zcu)); | |
| 3819 | arg_elem.* = try fg.load(arg_elem_ptr, .none, .fromInterned(param_type), .normal); | |
| 3838 | 3820 | } |
| 3839 | const result_elem = try self.wip.call( | |
| 3840 | .normal, | |
| 3841 | .ccc, | |
| 3842 | .none, | |
| 3843 | llvm_fn.typeOf(&o.builder), | |
| 3844 | llvm_fn.toValue(&o.builder), | |
| 3845 | args[0..args_vectors.len], | |
| 3846 | "", | |
| 3821 | const result_elem = try fg.buildCall(.{}, llvm_fn.typeOf(&o.builder), llvm_fn.toValue(&o.builder), fn_info, fn_info.param_types, arg_elems); | |
| 3822 | if (fn_info.return_type == .void_type) { | |
| 3823 | assert(result_elem == .none); | |
| 3824 | } else if (result_elem != .none) { | |
| 3825 | const result_elem_ptr = try fg.ptraddScaled(result_ptr, index.toValue(), ret_ty.abiSize(zcu)); | |
| 3826 | try fg.store(result_elem_ptr, .none, result_elem, ret_ty, .normal); | |
| 3827 | } | |
| 3828 | ||
| 3829 | const next_index = try fg.wip.bin(.@"add nuw", index.toValue(), try o.builder.intValue(.i32, 1), "elementwise.next_index"); | |
| 3830 | index.finish(&.{ try o.builder.intValue(.i32, 0), next_index }, &.{ entry_block, loop_block }, &fg.wip); | |
| 3831 | const is_done = try fg.wip.icmp(.eq, next_index, try o.builder.intValue(.i32, iterations), "elementwise.is_done"); | |
| 3832 | _ = try fg.wip.brCond(is_done, done_block, loop_block, .none); | |
| 3833 | ||
| 3834 | fg.wip.cursor = .{ .block = done_block }; | |
| 3835 | return result_ptr; | |
| 3836 | } | |
| 3837 | ||
| 3838 | var result = if (fn_info.return_type == .void_type) .none else if (ret_is_by_ref) result: { | |
| 3839 | const ret_llvm_ty = try o.lowerType(ret_ty, .in_memory); | |
| 3840 | break :result try fg.buildAlloca( | |
| 3841 | if (vector_len) |len| try o.builder.arrayType(len, ret_llvm_ty) else ret_llvm_ty, | |
| 3842 | ret_ty.abiAlignment(zcu).toLlvm(), | |
| 3847 | 3843 | ); |
| 3848 | result = try self.wip.insertElement(result, result_elem, index_i32, ""); | |
| 3844 | } else if (vector_len) |len| try o.builder.poisonValue( | |
| 3845 | try o.builder.vectorType(.normal, len, try o.lowerType(ret_ty, .as_value)), | |
| 3846 | ) else .none; | |
| 3847 | for (0..iterations) |index| { | |
| 3848 | const index_value = try o.builder.intValue(.i32, index); | |
| 3849 | var arg_elems_buf: [3]Builder.Value = undefined; | |
| 3850 | const arg_elems = arg_elems_buf[0..arg_values.len]; | |
| 3851 | for (arg_elems, fn_info.param_types, arg_values) |*arg_elem_value, param_type, arg_value| { | |
| 3852 | const arg_ty: Type = .fromInterned(param_type); | |
| 3853 | if (isByRef(arg_ty, zcu)) { | |
| 3854 | const arg_elem_ptr = try fg.ptraddConst(arg_value, index * arg_ty.abiSize(zcu)); | |
| 3855 | arg_elem_value.* = try fg.load(arg_elem_ptr, .none, .fromInterned(param_type), .normal); | |
| 3856 | } else if (vector_len) |_| { | |
| 3857 | arg_elem_value.* = try fg.wip.extractElement(arg_value, index_value, "elementwise.arg_elem"); | |
| 3858 | } else arg_elem_value.* = arg_value; | |
| 3859 | } | |
| 3860 | const result_elem = try fg.buildCall(.{}, llvm_fn.typeOf(&o.builder), llvm_fn.toValue(&o.builder), fn_info, fn_info.param_types, arg_elems); | |
| 3861 | if (fn_info.return_type == .void_type) { | |
| 3862 | assert(result_elem == .none); | |
| 3863 | } else if (ret_is_by_ref) { | |
| 3864 | const result_elem_ptr = try fg.ptraddConst(result, index * ret_ty.abiSize(zcu)); | |
| 3865 | try fg.store(result_elem_ptr, .none, result_elem, ret_ty, .normal); | |
| 3866 | } else if (vector_len) |_| { | |
| 3867 | result = try fg.wip.insertElement(result, result_elem, index_value, "elementwise.result"); | |
| 3868 | } else { | |
| 3869 | assert(result == .none); | |
| 3870 | result = result_elem; | |
| 3871 | } | |
| 3849 | 3872 | } |
| 3850 | 3873 | return result; |
| 3851 | 3874 | } |
| ... | ... | @@ -3853,17 +3876,16 @@ fn buildElementwiseCall( |
| 3853 | 3876 | /// Creates a floating point comparison by lowering to the appropriate |
| 3854 | 3877 | /// hardware instruction or softfloat routine for the target |
| 3855 | 3878 | fn buildFloatCmp( |
| 3856 | self: *FuncGen, | |
| 3879 | fg: *FuncGen, | |
| 3857 | 3880 | fast: Builder.FastMathKind, |
| 3858 | 3881 | pred: math.CompareOperator, |
| 3859 | 3882 | ty: Type, |
| 3860 | 3883 | params: [2]Builder.Value, |
| 3861 | 3884 | ) Allocator.Error!Builder.Value { |
| 3862 | const o = self.object; | |
| 3885 | const o = fg.object; | |
| 3863 | 3886 | const zcu = o.zcu; |
| 3864 | 3887 | const target = zcu.getTarget(); |
| 3865 | 3888 | const scalar_ty = ty.scalarType(zcu); |
| 3866 | const scalar_llvm_ty = try o.lowerType(scalar_ty, .as_value); | |
| 3867 | 3889 | |
| 3868 | 3890 | if (intrinsicsAllowed(scalar_ty, target)) { |
| 3869 | 3891 | const cond: Builder.FloatCondition = switch (pred) { |
| ... | ... | @@ -3874,53 +3896,33 @@ fn buildFloatCmp( |
| 3874 | 3896 | .gt => .ogt, |
| 3875 | 3897 | .gte => .oge, |
| 3876 | 3898 | }; |
| 3877 | return self.wip.fcmp(fast, cond, params[0], params[1], ""); | |
| 3899 | return fg.wip.fcmp(fast, cond, params[0], params[1], ""); | |
| 3878 | 3900 | } |
| 3879 | 3901 | |
| 3880 | const float_bits = scalar_ty.floatBits(target); | |
| 3881 | const compiler_rt_float_abbrev = compilerRtFloatAbbrev(float_bits); | |
| 3882 | const fn_base_name = switch (pred) { | |
| 3883 | .neq => "ne", | |
| 3884 | .eq => "eq", | |
| 3885 | .lt => "lt", | |
| 3886 | .lte => "le", | |
| 3887 | .gt => "gt", | |
| 3888 | .gte => "ge", | |
| 3889 | }; | |
| 3890 | const fn_name = try o.builder.strtabStringFmt("__{s}{s}f2", .{ fn_base_name, compiler_rt_float_abbrev }); | |
| 3891 | ||
| 3892 | const libc_fn = try o.getLibcFunction(fn_name, &.{ scalar_llvm_ty, scalar_llvm_ty }, .i32); | |
| 3893 | ||
| 3894 | const int_cond: Builder.IntegerCondition = switch (pred) { | |
| 3902 | const fn_name = try o.builder.strtabStringFmt("__{s}{s}f2", .{ | |
| 3903 | switch (pred) { | |
| 3904 | .neq => "ne", | |
| 3905 | .eq => "eq", | |
| 3906 | .lt => "lt", | |
| 3907 | .lte => "le", | |
| 3908 | .gt => "gt", | |
| 3909 | .gte => "ge", | |
| 3910 | }, | |
| 3911 | compilerRtFloatAbbrev(target, scalar_ty.floatBits(target)), | |
| 3912 | }); | |
| 3913 | const result = try fg.buildElementwiseCall(fn_name, .{ | |
| 3914 | .cc = target.cCallingConvention().?, | |
| 3915 | .param_types = &.{ scalar_ty.toIntern(), scalar_ty.toIntern() }, | |
| 3916 | .return_type = .i32_type, | |
| 3917 | }, &params, if (ty.isVector(zcu)) ty.vectorLen(zcu) else null); | |
| 3918 | return fg.wip.icmp(switch (pred) { | |
| 3895 | 3919 | .eq => .eq, |
| 3896 | 3920 | .neq => .ne, |
| 3897 | 3921 | .lt => .slt, |
| 3898 | 3922 | .lte => .sle, |
| 3899 | 3923 | .gt => .sgt, |
| 3900 | 3924 | .gte => .sge, |
| 3901 | }; | |
| 3902 | ||
| 3903 | if (ty.zigTypeTag(zcu) == .vector) { | |
| 3904 | const vec_len = ty.vectorLen(zcu); | |
| 3905 | const vector_result_ty = try o.builder.vectorType(.normal, vec_len, .i32); | |
| 3906 | ||
| 3907 | const init = try o.builder.poisonValue(vector_result_ty); | |
| 3908 | const result = try self.buildElementwiseCall(libc_fn, &params, init, vec_len); | |
| 3909 | ||
| 3910 | const zero_vector = try o.builder.splatValue(vector_result_ty, .@"0"); | |
| 3911 | return self.wip.icmp(int_cond, result, zero_vector, ""); | |
| 3912 | } | |
| 3913 | ||
| 3914 | const result = try self.wip.call( | |
| 3915 | .normal, | |
| 3916 | .ccc, | |
| 3917 | .none, | |
| 3918 | libc_fn.typeOf(&o.builder), | |
| 3919 | libc_fn.toValue(&o.builder), | |
| 3920 | &params, | |
| 3921 | "", | |
| 3922 | ); | |
| 3923 | return self.wip.icmp(int_cond, result, .@"0", ""); | |
| 3925 | }, result, try o.builder.splatValue(result.typeOfWip(&fg.wip), .@"0"), ""); | |
| 3924 | 3926 | } |
| 3925 | 3927 | |
| 3926 | 3928 | const FloatOp = enum { |
| ... | ... | @@ -3949,32 +3951,26 @@ const FloatOp = enum { |
| 3949 | 3951 | trunc, |
| 3950 | 3952 | }; |
| 3951 | 3953 | |
| 3952 | const FloatOpStrat = union(enum) { | |
| 3953 | intrinsic: []const u8, | |
| 3954 | libc: Builder.String, | |
| 3955 | }; | |
| 3956 | ||
| 3957 | 3954 | /// Creates a floating point operation (add, sub, fma, sqrt, exp, etc.) |
| 3958 | 3955 | /// by lowering to the appropriate hardware instruction or softfloat |
| 3959 | 3956 | /// routine for the target |
| 3960 | 3957 | fn buildFloatOp( |
| 3961 | self: *FuncGen, | |
| 3958 | fg: *FuncGen, | |
| 3962 | 3959 | comptime op: FloatOp, |
| 3963 | 3960 | fast: Builder.FastMathKind, |
| 3964 | 3961 | ty: Type, |
| 3965 | 3962 | comptime params_len: usize, |
| 3966 | 3963 | params: [params_len]Builder.Value, |
| 3967 | 3964 | ) Allocator.Error!Builder.Value { |
| 3968 | const o = self.object; | |
| 3965 | const o = fg.object; | |
| 3969 | 3966 | const zcu = o.zcu; |
| 3970 | 3967 | const target = zcu.getTarget(); |
| 3971 | 3968 | const scalar_ty = ty.scalarType(zcu); |
| 3972 | const llvm_ty = try o.lowerType(ty, .as_value); | |
| 3973 | 3969 | |
| 3974 | 3970 | if (op != .tan and intrinsicsAllowed(scalar_ty, target)) switch (op) { |
| 3975 | 3971 | // Some operations are dedicated LLVM instructions, not available as intrinsics |
| 3976 | .neg => return self.wip.un(.fneg, params[0], ""), | |
| 3977 | .add, .sub, .mul, .div, .fmod => return self.wip.bin(switch (fast) { | |
| 3972 | .neg => return fg.wip.un(.fneg, params[0], ""), | |
| 3973 | .add, .sub, .mul, .div, .fmod => return fg.wip.bin(switch (fast) { | |
| 3978 | 3974 | .normal => switch (op) { |
| 3979 | 3975 | .add => .fadd, |
| 3980 | 3976 | .sub => .fsub, |
| ... | ... | @@ -4008,7 +4004,7 @@ fn buildFloatOp( |
| 4008 | 4004 | .sqrt, |
| 4009 | 4005 | .trunc, |
| 4010 | 4006 | .fma, |
| 4011 | => return self.wip.callIntrinsic(fast, .none, switch (op) { | |
| 4007 | => return fg.wip.callIntrinsic(fast, .none, switch (op) { | |
| 4012 | 4008 | .fmax => .maxnum, |
| 4013 | 4009 | .fmin => .minnum, |
| 4014 | 4010 | .ceil => .ceil, |
| ... | ... | @@ -4026,36 +4022,152 @@ fn buildFloatOp( |
| 4026 | 4022 | .trunc => .trunc, |
| 4027 | 4023 | .fma => .fma, |
| 4028 | 4024 | else => unreachable, |
| 4029 | }, &.{llvm_ty}, &params, ""), | |
| 4025 | }, &.{try o.lowerType(ty, .as_value)}, &params, ""), | |
| 4030 | 4026 | .tan => unreachable, |
| 4031 | 4027 | }; |
| 4032 | 4028 | |
| 4033 | 4029 | const float_bits = scalar_ty.floatBits(target); |
| 4034 | 4030 | const fn_name = switch (op) { |
| 4035 | .neg => { | |
| 4036 | // In this case we can generate a softfloat negation by XORing the | |
| 4037 | // bits with a constant. | |
| 4031 | // In these cases we can generate a softfloat operation by modifying the sign bit using a bitwise operation. | |
| 4032 | .neg, .fabs => if (isByRef(scalar_ty, zcu)) { | |
| 4033 | const is_vector = ty.toIntern() != scalar_ty.toIntern(); | |
| 4034 | const result_ptr = try fg.buildZigAlloca(ty, .none); | |
| 4035 | const entry_block = fg.wip.cursor.block; | |
| 4036 | const loop_block, const done_block, const llvm_usize_ty, const offset, const elem, const result_elem = if (is_vector) loop: { | |
| 4037 | const loop_block = try fg.wip.block(2, "neg_fabs.loop"); | |
| 4038 | const done_block = try fg.wip.block(1, "neg_fabs.done"); | |
| 4039 | _ = try fg.wip.br(loop_block); | |
| 4040 | ||
| 4041 | fg.wip.cursor = .{ .block = loop_block }; | |
| 4042 | const llvm_usize_ty = try o.lowerType(.usize, .as_value); | |
| 4043 | const offset = try fg.wip.phi(llvm_usize_ty, "neg_fabs.offset"); | |
| 4044 | break :loop .{ | |
| 4045 | loop_block, | |
| 4046 | done_block, | |
| 4047 | llvm_usize_ty, | |
| 4048 | offset, | |
| 4049 | try fg.ptraddScaled(params[0], offset.toValue(), 1), | |
| 4050 | try fg.ptraddScaled(result_ptr, offset.toValue(), 1), | |
| 4051 | }; | |
| 4052 | } else .{ undefined, undefined, undefined, undefined, params[0], result_ptr }; | |
| 4053 | switch (scalar_ty.floatBits(target)) { | |
| 4054 | else => unreachable, | |
| 4055 | 80 => { | |
| 4056 | const f80_layout = o.softF80Layout(.{}) catch unreachable; | |
| 4057 | const mantissa = try fg.load( | |
| 4058 | try fg.ptraddConst(elem, f80_layout.mantissa_offset), | |
| 4059 | f80_layout.alignment.offset(f80_layout.mantissa_offset), | |
| 4060 | .u64, | |
| 4061 | .normal, | |
| 4062 | ); | |
| 4063 | const exponent = try fg.load( | |
| 4064 | try fg.ptraddConst(elem, f80_layout.exponent_offset), | |
| 4065 | f80_layout.alignment.offset(f80_layout.exponent_offset), | |
| 4066 | .u16, | |
| 4067 | .normal, | |
| 4068 | ); | |
| 4069 | const exponent_sign_bit: u16 = 1 << (16 - 1); | |
| 4070 | const updated_exponent = try fg.wip.bin(switch (op) { | |
| 4071 | else => unreachable, | |
| 4072 | .neg => .xor, | |
| 4073 | .fabs => .@"and", | |
| 4074 | }, exponent, try o.builder.intValue(.i16, switch (op) { | |
| 4075 | else => unreachable, | |
| 4076 | .neg => exponent_sign_bit, | |
| 4077 | .fabs => exponent_sign_bit - 1, | |
| 4078 | }), "neg_fabs.updated_exponent"); | |
| 4079 | try fg.store( | |
| 4080 | try fg.ptraddConst(result_elem, f80_layout.mantissa_offset), | |
| 4081 | f80_layout.alignment.offset(f80_layout.mantissa_offset), | |
| 4082 | mantissa, | |
| 4083 | .u64, | |
| 4084 | .normal, | |
| 4085 | ); | |
| 4086 | try fg.store( | |
| 4087 | try fg.ptraddConst(result_elem, f80_layout.exponent_offset), | |
| 4088 | f80_layout.alignment.offset(f80_layout.exponent_offset), | |
| 4089 | updated_exponent, | |
| 4090 | .u16, | |
| 4091 | .normal, | |
| 4092 | ); | |
| 4093 | }, | |
| 4094 | 128 => { | |
| 4095 | const f128_layout = o.softF128Layout(.{}) catch unreachable; | |
| 4096 | const lo = try fg.load( | |
| 4097 | try fg.ptraddConst(elem, f128_layout.lo_offset), | |
| 4098 | f128_layout.alignment.offset(f128_layout.lo_offset), | |
| 4099 | .u64, | |
| 4100 | .normal, | |
| 4101 | ); | |
| 4102 | const hi = try fg.load( | |
| 4103 | try fg.ptraddConst(elem, f128_layout.hi_offset), | |
| 4104 | f128_layout.alignment.offset(f128_layout.hi_offset), | |
| 4105 | .u64, | |
| 4106 | .normal, | |
| 4107 | ); | |
| 4108 | const hi_sign_bit: u64 = 1 << (64 - 1); | |
| 4109 | const updated_hi = try fg.wip.bin(switch (op) { | |
| 4110 | else => unreachable, | |
| 4111 | .neg => .xor, | |
| 4112 | .fabs => .@"and", | |
| 4113 | }, hi, try o.builder.intValue(.i64, switch (op) { | |
| 4114 | else => unreachable, | |
| 4115 | .neg => hi_sign_bit, | |
| 4116 | .fabs => hi_sign_bit - 1, | |
| 4117 | }), "neg_fabs.updated_hi"); | |
| 4118 | try fg.store( | |
| 4119 | try fg.ptraddConst(result_elem, f128_layout.lo_offset), | |
| 4120 | f128_layout.alignment.offset(f128_layout.lo_offset), | |
| 4121 | lo, | |
| 4122 | .u64, | |
| 4123 | .normal, | |
| 4124 | ); | |
| 4125 | try fg.store( | |
| 4126 | try fg.ptraddConst(result_elem, f128_layout.hi_offset), | |
| 4127 | f128_layout.alignment.offset(f128_layout.hi_offset), | |
| 4128 | updated_hi, | |
| 4129 | .u64, | |
| 4130 | .normal, | |
| 4131 | ); | |
| 4132 | }, | |
| 4133 | } | |
| 4134 | if (is_vector) { | |
| 4135 | 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"); | |
| 4136 | offset.finish(&.{ try o.builder.intValue(llvm_usize_ty, 0), next_offset }, &.{ entry_block, loop_block }, &fg.wip); | |
| 4137 | const is_done = try fg.wip.icmp(.eq, next_offset, try o.builder.intValue(llvm_usize_ty, ty.abiSize(zcu)), "neg_fabs.is_done"); | |
| 4138 | _ = try fg.wip.brCond(is_done, done_block, loop_block, .none); | |
| 4139 | ||
| 4140 | fg.wip.cursor = .{ .block = done_block }; | |
| 4141 | } | |
| 4142 | return result_ptr; | |
| 4143 | } else { | |
| 4038 | 4144 | const int_ty = try o.builder.intType(@intCast(float_bits)); |
| 4039 | 4145 | const cast_ty = switch (ty.zigTypeTag(zcu)) { |
| 4040 | 4146 | .vector => try o.builder.vectorType(.normal, ty.vectorLen(zcu), int_ty), |
| 4041 | 4147 | else => int_ty, |
| 4042 | 4148 | }; |
| 4043 | const sign_mask = try o.builder.splatValue( | |
| 4044 | cast_ty, | |
| 4045 | try o.builder.intConst(int_ty, @as(u128, 1) << @intCast(float_bits - 1)), | |
| 4046 | ); | |
| 4047 | const bitcasted_operand = try self.wip.cast(.bitcast, params[0], cast_ty, ""); | |
| 4048 | const result = try self.wip.bin(.xor, bitcasted_operand, sign_mask, ""); | |
| 4049 | return self.wip.cast(.bitcast, result, llvm_ty, ""); | |
| 4149 | const sign_bit = @as(u128, 1) << @intCast(float_bits - 1); | |
| 4150 | const bitwise_rhs = try o.builder.splatValue(cast_ty, try o.builder.intConst(int_ty, switch (op) { | |
| 4151 | else => unreachable, | |
| 4152 | .neg => sign_bit, | |
| 4153 | .fabs => sign_bit - 1, | |
| 4154 | })); | |
| 4155 | const bitcasted_operand = try fg.wip.cast(.bitcast, params[0], cast_ty, ""); | |
| 4156 | const result = try fg.wip.bin(switch (op) { | |
| 4157 | else => unreachable, | |
| 4158 | .neg => .xor, | |
| 4159 | .fabs => .@"and", | |
| 4160 | }, bitcasted_operand, bitwise_rhs, ""); | |
| 4161 | const llvm_ty = try o.lowerType(ty, .as_value); | |
| 4162 | return fg.wip.cast(.bitcast, result, llvm_ty, ""); | |
| 4050 | 4163 | }, |
| 4051 | 4164 | .add, .sub, .div, .mul => try o.builder.strtabStringFmt("__{s}{s}f3", .{ |
| 4052 | @tagName(op), compilerRtFloatAbbrev(float_bits), | |
| 4165 | @tagName(op), compilerRtFloatAbbrev(target, float_bits), | |
| 4053 | 4166 | }), |
| 4054 | 4167 | .ceil, |
| 4055 | 4168 | .cos, |
| 4056 | 4169 | .exp, |
| 4057 | 4170 | .exp2, |
| 4058 | .fabs, | |
| 4059 | 4171 | .floor, |
| 4060 | 4172 | .fma, |
| 4061 | 4173 | .fmax, |
| ... | ... | @@ -4073,27 +4185,27 @@ fn buildFloatOp( |
| 4073 | 4185 | libcFloatPrefix(float_bits), @tagName(op), libcFloatSuffix(float_bits), |
| 4074 | 4186 | }), |
| 4075 | 4187 | }; |
| 4188 | return fg.buildElementwiseCall(fn_name, .{ | |
| 4189 | .cc = target.cCallingConvention().?, | |
| 4190 | .param_types = &@as([params_len]InternPool.Index, @splat(scalar_ty.toIntern())), | |
| 4191 | .return_type = scalar_ty.toIntern(), | |
| 4192 | }, &params, if (ty.isVector(zcu)) ty.vectorLen(zcu) else null); | |
| 4193 | } | |
| 4076 | 4194 | |
| 4077 | const scalar_llvm_ty = try o.lowerType(scalar_ty, .as_value); | |
| 4078 | const libc_fn = try o.getLibcFunction( | |
| 4079 | fn_name, | |
| 4080 | @as([3]Builder.Type, @splat(scalar_llvm_ty))[0..params.len], | |
| 4081 | scalar_llvm_ty, | |
| 4082 | ); | |
| 4083 | if (ty.zigTypeTag(zcu) == .vector) { | |
| 4084 | const result = try o.builder.poisonValue(llvm_ty); | |
| 4085 | return self.buildElementwiseCall(libc_fn, &params, result, ty.vectorLen(zcu)); | |
| 4086 | } | |
| 4087 | ||
| 4088 | return self.wip.call( | |
| 4089 | fast.toCallKind(), | |
| 4090 | .ccc, | |
| 4091 | .none, | |
| 4092 | libc_fn.typeOf(&o.builder), | |
| 4093 | libc_fn.toValue(&o.builder), | |
| 4094 | &params, | |
| 4095 | "", | |
| 4096 | ); | |
| 4195 | /// Creates a floating point cast operation by lowering to the specified softfloat routine. | |
| 4196 | fn buildFloatCastCall( | |
| 4197 | fg: *FuncGen, | |
| 4198 | dest_ty: Type, | |
| 4199 | fn_name: Builder.StrtabString, | |
| 4200 | operand_ty: Type, | |
| 4201 | operand: Builder.Value, | |
| 4202 | ) Allocator.Error!Builder.Value { | |
| 4203 | const zcu = fg.object.zcu; | |
| 4204 | return fg.buildElementwiseCall(fn_name, .{ | |
| 4205 | .cc = zcu.getTarget().cCallingConvention().?, | |
| 4206 | .param_types = &.{operand_ty.scalarType(zcu).toIntern()}, | |
| 4207 | .return_type = dest_ty.scalarType(zcu).toIntern(), | |
| 4208 | }, &.{operand}, if (operand_ty.isVector(zcu)) operand_ty.vectorLen(zcu) else null); | |
| 4097 | 4209 | } |
| 4098 | 4210 | |
| 4099 | 4211 | 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 |
| 4471 | 4583 | const ty_op = self.air.instructions.items(.data)[@backingInt(inst)].ty_op; |
| 4472 | 4584 | const operand = try self.resolveInst(ty_op.operand); |
| 4473 | 4585 | const operand_ty = self.typeOf(ty_op.operand); |
| 4586 | const operand_scalar_ty = operand_ty.scalarType(zcu); | |
| 4474 | 4587 | const dest_ty = self.typeOfIndex(inst); |
| 4588 | const dest_scalar_ty = dest_ty.scalarType(zcu); | |
| 4475 | 4589 | const target = zcu.getTarget(); |
| 4476 | 4590 | |
| 4477 | if (intrinsicsAllowed(dest_ty, target) and intrinsicsAllowed(operand_ty, target)) { | |
| 4591 | if (intrinsicsAllowed(dest_scalar_ty, target) and intrinsicsAllowed(operand_scalar_ty, target)) | |
| 4478 | 4592 | return self.wip.cast(.fptrunc, operand, try o.lowerType(dest_ty, .as_value), ""); |
| 4479 | } else { | |
| 4480 | const operand_llvm_ty = try o.lowerType(operand_ty, .as_value); | |
| 4481 | const dest_llvm_ty = try o.lowerType(dest_ty, .as_value); | |
| 4482 | ||
| 4483 | const dest_bits = dest_ty.floatBits(target); | |
| 4484 | const src_bits = operand_ty.floatBits(target); | |
| 4485 | const fn_name = try o.builder.strtabStringFmt("__trunc{s}f{s}f2", .{ | |
| 4486 | compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits), | |
| 4487 | }); | |
| 4488 | ||
| 4489 | const libc_fn = try o.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); | |
| 4490 | return self.wip.call( | |
| 4491 | .normal, | |
| 4492 | .ccc, | |
| 4493 | .none, | |
| 4494 | libc_fn.typeOf(&o.builder), | |
| 4495 | libc_fn.toValue(&o.builder), | |
| 4496 | &.{operand}, | |
| 4497 | "", | |
| 4498 | ); | |
| 4499 | } | |
| 4593 | const dest_bits = dest_scalar_ty.floatBits(target); | |
| 4594 | const src_bits = operand_scalar_ty.floatBits(target); | |
| 4595 | const fn_name = try o.builder.strtabStringFmt("__trunc{s}f{s}f2", .{ | |
| 4596 | compilerRtFloatAbbrev(target, src_bits), compilerRtFloatAbbrev(target, dest_bits), | |
| 4597 | }); | |
| 4598 | return self.buildFloatCastCall(dest_ty, fn_name, operand_ty, operand); | |
| 4500 | 4599 | } |
| 4501 | 4600 | |
| 4502 | 4601 | 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 |
| 4505 | 4604 | const ty_op = self.air.instructions.items(.data)[@backingInt(inst)].ty_op; |
| 4506 | 4605 | const operand = try self.resolveInst(ty_op.operand); |
| 4507 | 4606 | const operand_ty = self.typeOf(ty_op.operand); |
| 4607 | const operand_scalar_ty = operand_ty.scalarType(zcu); | |
| 4508 | 4608 | const dest_ty = self.typeOfIndex(inst); |
| 4609 | const dest_scalar_ty = dest_ty.scalarType(zcu); | |
| 4509 | 4610 | const target = zcu.getTarget(); |
| 4510 | 4611 | |
| 4511 | if (intrinsicsAllowed(dest_ty, target) and intrinsicsAllowed(operand_ty, target)) { | |
| 4612 | if (intrinsicsAllowed(dest_scalar_ty, target) and intrinsicsAllowed(operand_scalar_ty, target)) | |
| 4512 | 4613 | return self.wip.cast(.fpext, operand, try o.lowerType(dest_ty, .as_value), ""); |
| 4513 | } else { | |
| 4514 | const operand_llvm_ty = try o.lowerType(operand_ty, .as_value); | |
| 4515 | const dest_llvm_ty = try o.lowerType(dest_ty, .as_value); | |
| 4516 | ||
| 4517 | const dest_bits = dest_ty.scalarType(zcu).floatBits(target); | |
| 4518 | const src_bits = operand_ty.scalarType(zcu).floatBits(target); | |
| 4519 | const fn_name = try o.builder.strtabStringFmt("__extend{s}f{s}f2", .{ | |
| 4520 | compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits), | |
| 4521 | }); | |
| 4522 | ||
| 4523 | const libc_fn = try o.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); | |
| 4524 | if (dest_ty.isVector(zcu)) return self.buildElementwiseCall( | |
| 4525 | libc_fn, | |
| 4526 | &.{operand}, | |
| 4527 | try o.builder.poisonValue(dest_llvm_ty), | |
| 4528 | dest_ty.vectorLen(zcu), | |
| 4529 | ); | |
| 4530 | return self.wip.call( | |
| 4531 | .normal, | |
| 4532 | .ccc, | |
| 4533 | .none, | |
| 4534 | libc_fn.typeOf(&o.builder), | |
| 4535 | libc_fn.toValue(&o.builder), | |
| 4536 | &.{operand}, | |
| 4537 | "", | |
| 4538 | ); | |
| 4539 | } | |
| 4614 | const dest_bits = dest_scalar_ty.floatBits(target); | |
| 4615 | const src_bits = operand_scalar_ty.floatBits(target); | |
| 4616 | const fn_name = try o.builder.strtabStringFmt("__extend{s}f{s}f2", .{ | |
| 4617 | compilerRtFloatAbbrev(target, src_bits), compilerRtFloatAbbrev(target, dest_bits), | |
| 4618 | }); | |
| 4619 | return self.buildFloatCastCall(dest_ty, fn_name, operand_ty, operand); | |
| 4540 | 4620 | } |
| 4541 | 4621 | |
| 4542 | 4622 | 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! |
| 4558 | 4638 | // * bool/int/float <-> bool/int/float |
| 4559 | 4639 | // * `@Vector(n, A)` <-> `@Vector(n, B)` |
| 4560 | 4640 | // |
| 4561 | // All of these cases can be handled by LLVM's `bitcast` instruction. | |
| 4641 | // Most of these cases can be handled by LLVM's `bitcast` instruction, except when | |
| 4642 | // a non-native type like `f80` is used. | |
| 4562 | 4643 | |
| 4563 | assert(!isByRef(operand_ty, zcu)); | |
| 4564 | assert(!isByRef(dest_ty, zcu)); | |
| 4644 | if (isByRef(operand_ty, zcu)) { | |
| 4645 | const operand_scalar_ty = operand_ty.scalarType(zcu); | |
| 4646 | const target = zcu.getTarget(); | |
| 4647 | const bits = operand_scalar_ty.floatBits(target); | |
| 4648 | const dest_scalar_ty = dest_ty.scalarType(zcu); | |
| 4649 | if (isByRef(dest_ty, zcu)) { | |
| 4650 | assert(dest_scalar_ty.floatBits(target) == bits); | |
| 4651 | return operand; | |
| 4652 | } | |
| 4653 | assert(dest_scalar_ty.intInfo(zcu).bits == bits); | |
| 4654 | ||
| 4655 | const len = if (operand_ty.toIntern() != operand_scalar_ty.toIntern()) | |
| 4656 | operand_ty.vectorLen(zcu) | |
| 4657 | else | |
| 4658 | null; | |
| 4659 | const operand_scalar_size = operand_scalar_ty.abiSize(zcu); | |
| 4660 | var result = if (len) |_| | |
| 4661 | try o.builder.poisonValue(try o.lowerType(dest_ty, .as_value)) | |
| 4662 | else | |
| 4663 | undefined; | |
| 4664 | for (0..len orelse 1) |index| { | |
| 4665 | const result_elem = result_elem: switch (bits) { | |
| 4666 | else => unreachable, | |
| 4667 | 80 => { | |
| 4668 | const f80_layout = o.softF80Layout(.{}) catch unreachable; | |
| 4669 | const mantissa = try fg.load( | |
| 4670 | try fg.ptraddConst(operand, operand_scalar_size * index + f80_layout.mantissa_offset), | |
| 4671 | f80_layout.alignment.offset(f80_layout.mantissa_offset), | |
| 4672 | .u64, | |
| 4673 | .normal, | |
| 4674 | ); | |
| 4675 | const exponent = try fg.load( | |
| 4676 | try fg.ptraddConst(operand, operand_scalar_size * index + f80_layout.exponent_offset), | |
| 4677 | f80_layout.alignment.offset(f80_layout.exponent_offset), | |
| 4678 | .u16, | |
| 4679 | .normal, | |
| 4680 | ); | |
| 4681 | const casted_mantissa = try fg.wip.cast(.zext, mantissa, .i80, "bitCast.casted_mantissa"); | |
| 4682 | const casted_exponent = try fg.wip.cast(.zext, exponent, .i80, "bitCast.casted_exponent"); | |
| 4683 | const shifted_exponent = try fg.wip.bin(.@"shl nuw", casted_exponent, try o.builder.intValue(.i80, 64), "bitCast.shifted_exponent"); | |
| 4684 | break :result_elem try fg.wip.bin(.@"or", casted_mantissa, shifted_exponent, "bitCast.result_elem"); | |
| 4685 | }, | |
| 4686 | 128 => { | |
| 4687 | const f128_layout = o.softF128Layout(.{}) catch unreachable; | |
| 4688 | const lo = try fg.load( | |
| 4689 | try fg.ptraddConst(operand, operand_scalar_size * index + f128_layout.lo_offset), | |
| 4690 | f128_layout.alignment.offset(f128_layout.lo_offset), | |
| 4691 | .u64, | |
| 4692 | .normal, | |
| 4693 | ); | |
| 4694 | const hi = try fg.load( | |
| 4695 | try fg.ptraddConst(operand, operand_scalar_size * index + f128_layout.hi_offset), | |
| 4696 | f128_layout.alignment.offset(f128_layout.hi_offset), | |
| 4697 | .u64, | |
| 4698 | .normal, | |
| 4699 | ); | |
| 4700 | const casted_lo = try fg.wip.cast(.zext, lo, .i128, "bitCast.casted_lo"); | |
| 4701 | const casted_hi = try fg.wip.cast(.zext, hi, .i128, "bitCast.casted_hi"); | |
| 4702 | const shifted_hi = try fg.wip.bin(.@"shl nuw", casted_hi, try o.builder.intValue(.i128, 64), "bitCast.shifted_hi"); | |
| 4703 | break :result_elem try fg.wip.bin(.@"or", casted_lo, shifted_hi, "bitCast.result_elem"); | |
| 4704 | }, | |
| 4705 | }; | |
| 4706 | result = if (len) |_| | |
| 4707 | try fg.wip.insertElement(result, result_elem, try o.builder.intValue(.i32, index), "elementwise.result") | |
| 4708 | else | |
| 4709 | result_elem; | |
| 4710 | } | |
| 4711 | return result; | |
| 4712 | } | |
| 4713 | ||
| 4714 | if (isByRef(dest_ty, zcu)) { | |
| 4715 | const dest_scalar_ty = dest_ty.scalarType(zcu); | |
| 4716 | const bits = dest_scalar_ty.floatBits(zcu.getTarget()); | |
| 4717 | assert(dest_scalar_ty.isRuntimeFloat()); | |
| 4718 | const operand_scalar_ty = operand_ty.scalarType(zcu); | |
| 4719 | assert(operand_scalar_ty.intInfo(zcu).bits == bits); | |
| 4720 | ||
| 4721 | const len = if (operand_ty.toIntern() != operand_scalar_ty.toIntern()) | |
| 4722 | operand_ty.vectorLen(zcu) | |
| 4723 | else | |
| 4724 | null; | |
| 4725 | const operand_scalar_size = operand_scalar_ty.abiSize(zcu); | |
| 4726 | const result_ptr = try fg.buildZigAlloca(dest_ty, .none); | |
| 4727 | for (0..len orelse 1) |index| { | |
| 4728 | const operand_elem = if (len) |_| | |
| 4729 | try fg.wip.extractElement(operand, try o.builder.intValue(.i32, index), "elementwise.operand_elem") | |
| 4730 | else | |
| 4731 | operand; | |
| 4732 | switch (bits) { | |
| 4733 | else => unreachable, | |
| 4734 | 80 => { | |
| 4735 | const f80_layout = o.softF80Layout(.{}) catch unreachable; | |
| 4736 | const mantissa = try fg.wip.cast(.trunc, operand_elem, .i64, "bitCast.mantissa"); | |
| 4737 | const shifted_exponent = try fg.wip.bin(.lshr, operand_elem, try o.builder.intValue(.i80, 64), "bitCast.shifted_exponent"); | |
| 4738 | const exponent = try fg.wip.cast(.@"trunc nuw", shifted_exponent, .i16, "bitCast.exponent"); | |
| 4739 | try fg.store( | |
| 4740 | try fg.ptraddConst(result_ptr, operand_scalar_size * index + f80_layout.mantissa_offset), | |
| 4741 | f80_layout.alignment.offset(f80_layout.mantissa_offset), | |
| 4742 | mantissa, | |
| 4743 | .u64, | |
| 4744 | .normal, | |
| 4745 | ); | |
| 4746 | try fg.store( | |
| 4747 | try fg.ptraddConst(result_ptr, operand_scalar_size * index + f80_layout.exponent_offset), | |
| 4748 | f80_layout.alignment.offset(f80_layout.exponent_offset), | |
| 4749 | exponent, | |
| 4750 | .u16, | |
| 4751 | .normal, | |
| 4752 | ); | |
| 4753 | }, | |
| 4754 | 128 => { | |
| 4755 | const f128_layout = o.softF128Layout(.{}) catch unreachable; | |
| 4756 | const lo = try fg.wip.cast(.trunc, operand_elem, .i64, "bitCast.lo"); | |
| 4757 | const shifted_hi = try fg.wip.bin(.lshr, operand_elem, try o.builder.intValue(.i128, 64), "bitCast.shifted_hi"); | |
| 4758 | const hi = try fg.wip.cast(.@"trunc nuw", shifted_hi, .i64, "bitCast.hi"); | |
| 4759 | try fg.store( | |
| 4760 | try fg.ptraddConst(result_ptr, operand_scalar_size * index + f128_layout.lo_offset), | |
| 4761 | f128_layout.alignment.offset(f128_layout.lo_offset), | |
| 4762 | lo, | |
| 4763 | .u64, | |
| 4764 | .normal, | |
| 4765 | ); | |
| 4766 | try fg.store( | |
| 4767 | try fg.ptraddConst(result_ptr, operand_scalar_size * index + f128_layout.hi_offset), | |
| 4768 | f128_layout.alignment.offset(f128_layout.hi_offset), | |
| 4769 | hi, | |
| 4770 | .u64, | |
| 4771 | .normal, | |
| 4772 | ); | |
| 4773 | }, | |
| 4774 | } | |
| 4775 | } | |
| 4776 | return result_ptr; | |
| 4777 | } | |
| 4565 | 4778 | |
| 4566 | 4779 | const llvm_dest_ty = try o.lowerType(dest_ty, .as_value); |
| 4567 | 4780 | 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 |
| 4730 | 4943 | const ptr_align = ptr_ty.ptrAlignment(zcu); |
| 4731 | 4944 | const elem_ty = ptr_ty.childType(zcu); |
| 4732 | 4945 | if (!elem_ty.hasRuntimeBits(zcu)) { |
| 4733 | return (try o.lowerPtrToVoid(ptr_align, ptr_ty.ptrAddressSpace(zcu))).toValue(); | |
| 4946 | return (try o.lowerPtrToVoid(ptr_align.toLlvm(), ptr_ty.ptrAddressSpace(zcu))).toValue(); | |
| 4734 | 4947 | } |
| 4735 | 4948 | return self.buildZigAlloca(elem_ty, ptr_align); |
| 4736 | 4949 | } |
| ... | ... | @@ -4743,7 +4956,7 @@ fn airRetPtr(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value |
| 4743 | 4956 | const ptr_align = ptr_ty.ptrAlignment(zcu); |
| 4744 | 4957 | const elem_ty = ptr_ty.childType(zcu); |
| 4745 | 4958 | if (!elem_ty.hasRuntimeBits(zcu)) { |
| 4746 | return (try o.lowerPtrToVoid(ptr_align, ptr_ty.ptrAddressSpace(zcu))).toValue(); | |
| 4959 | return (try o.lowerPtrToVoid(ptr_align.toLlvm(), ptr_ty.ptrAddressSpace(zcu))).toValue(); | |
| 4747 | 4960 | } |
| 4748 | 4961 | return self.buildZigAlloca(elem_ty, ptr_align); |
| 4749 | 4962 | } |
| ... | ... | @@ -4850,17 +5063,24 @@ fn airStore(fg: *FuncGen, inst: Air.Inst.Index, safety: bool) Allocator.Error!Bu |
| 4850 | 5063 | const elem = try fg.resolveInst(bin_op.rhs); |
| 4851 | 5064 | |
| 4852 | 5065 | if (ptr_info.flags.vector_index != .none) { |
| 4853 | // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. | |
| 4854 | const vec_ty = try fg.pt.vectorType(.{ | |
| 4855 | .len = ptr_info.packed_offset.host_size, | |
| 4856 | .child = elem_ty.toIntern(), | |
| 4857 | }); | |
| 5066 | if (isByRef(elem_ty, zcu)) { | |
| 5067 | const offset = @backingInt(ptr_info.flags.vector_index) * elem_ty.abiSize(zcu); | |
| 5068 | const elem_ptr = try fg.ptraddConst(ptr, offset); | |
| 5069 | try fg.store(elem_ptr, ptr_alignment.offset(offset), elem, elem_ty, access_kind); | |
| 5070 | } else { | |
| 5071 | // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. | |
| 5072 | const vec_ty = try fg.pt.vectorType(.{ | |
| 5073 | .len = ptr_info.packed_offset.host_size, | |
| 5074 | .child = elem_ty.toIntern(), | |
| 5075 | }); | |
| 4858 | 5076 | |
| 4859 | const loaded_vector = try fg.load(ptr, ptr_alignment, vec_ty, access_kind); | |
| 4860 | const index_val = try o.builder.intValue(.i32, ptr_info.flags.vector_index); | |
| 4861 | const modified_vector = try fg.wip.insertElement(loaded_vector, elem, index_val, ""); | |
| 5077 | const loaded_vector = try fg.load(ptr, ptr_alignment, vec_ty, access_kind); | |
| 5078 | const index_val = try o.builder.intValue(.i32, ptr_info.flags.vector_index); | |
| 5079 | const modified_vector = try fg.wip.insertElement(loaded_vector, elem, index_val, ""); | |
| 5080 | ||
| 5081 | try fg.store(ptr, ptr_alignment, modified_vector, vec_ty, access_kind); | |
| 5082 | } | |
| 4862 | 5083 | |
| 4863 | try fg.store(ptr, ptr_alignment, modified_vector, vec_ty, access_kind); | |
| 4864 | 5084 | return .none; |
| 4865 | 5085 | } |
| 4866 | 5086 | |
| ... | ... | @@ -4927,22 +5147,27 @@ fn airLoad(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { |
| 4927 | 5147 | if (ptr_info.flags.is_volatile) .@"volatile" else .normal; |
| 4928 | 5148 | |
| 4929 | 5149 | if (ptr_info.flags.vector_index != .none) { |
| 4930 | // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. | |
| 4931 | const vec_ty = try fg.pt.vectorType(.{ | |
| 4932 | .len = ptr_info.packed_offset.host_size, | |
| 4933 | .child = elem_ty.toIntern(), | |
| 4934 | }); | |
| 4935 | const vector_val = try fg.load(ptr, ptr_align, vec_ty, access_kind); | |
| 4936 | const index_val = try o.builder.intValue(.i32, ptr_info.flags.vector_index); | |
| 4937 | return fg.wip.extractElement(vector_val, index_val, ""); | |
| 5150 | if (isByRef(elem_ty, zcu)) { | |
| 5151 | const elem_size = elem_ty.abiSize(zcu); | |
| 5152 | const offset = @backingInt(ptr_info.flags.vector_index) * elem_size; | |
| 5153 | const elem_ptr = try fg.ptraddConst(ptr, offset); | |
| 5154 | return fg.load(elem_ptr, ptr_align.offset(offset), elem_ty, access_kind); | |
| 5155 | } else { | |
| 5156 | // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. | |
| 5157 | const vec_ty = try fg.pt.vectorType(.{ | |
| 5158 | .len = ptr_info.packed_offset.host_size, | |
| 5159 | .child = elem_ty.toIntern(), | |
| 5160 | }); | |
| 5161 | const vector_val = try fg.load(ptr, ptr_align, vec_ty, access_kind); | |
| 5162 | const index_val = try o.builder.intValue(.i32, ptr_info.flags.vector_index); | |
| 5163 | return fg.wip.extractElement(vector_val, index_val, ""); | |
| 5164 | } | |
| 4938 | 5165 | } |
| 4939 | 5166 | |
| 4940 | 5167 | if (ptr_info.packed_offset.host_size == 0) { |
| 4941 | 5168 | return fg.load(ptr, ptr_align, elem_ty, access_kind); |
| 4942 | 5169 | } |
| 4943 | 5170 | |
| 4944 | assert(!isByRef(elem_ty, zcu)); // all packable types are by-val | |
| 4945 | ||
| 4946 | 5171 | // Accepted proposal https://github.com/ziglang/zig/issues/24061 will eliminate this usage of `pt`. |
| 4947 | 5172 | const backing_int_ty = try fg.pt.intType(.unsigned, @intCast(ptr_info.packed_offset.host_size * 8)); |
| 4948 | 5173 | 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 { |
| 4952 | 5177 | const elem_bits = ptr_ty.childType(zcu).bitSize(zcu); |
| 4953 | 5178 | const shift_amt = try o.builder.intValue(llvm_backing_int_ty, ptr_info.packed_offset.bit_offset); |
| 4954 | 5179 | const shifted_value = try fg.wip.bin(.lshr, backing_int_val, shift_amt, ""); |
| 5180 | ||
| 5181 | if (isByRef(elem_ty, zcu)) { | |
| 5182 | const result_ptr = try fg.buildZigAlloca(elem_ty, .none); | |
| 5183 | switch (elem_ty.floatBits(zcu.getTarget())) { | |
| 5184 | else => unreachable, | |
| 5185 | 80 => { | |
| 5186 | const f80_layout = o.softF80Layout(.{}) catch unreachable; | |
| 5187 | const mantissa = try fg.wip.cast(.trunc, shifted_value, .i64, "load.mantissa"); | |
| 5188 | const shifted_exponent = try fg.wip.bin( | |
| 5189 | .lshr, | |
| 5190 | backing_int_val, | |
| 5191 | try o.builder.intValue(llvm_backing_int_ty, ptr_info.packed_offset.bit_offset + 64), | |
| 5192 | "load.shifted_exponent", | |
| 5193 | ); | |
| 5194 | const exponent = try fg.wip.cast(.trunc, shifted_exponent, .i16, "load.exponent"); | |
| 5195 | ||
| 5196 | try fg.store( | |
| 5197 | try fg.ptraddConst(result_ptr, f80_layout.mantissa_offset), | |
| 5198 | f80_layout.alignment.offset(f80_layout.mantissa_offset), | |
| 5199 | mantissa, | |
| 5200 | .u64, | |
| 5201 | .normal, | |
| 5202 | ); | |
| 5203 | try fg.store( | |
| 5204 | try fg.ptraddConst(result_ptr, f80_layout.exponent_offset), | |
| 5205 | f80_layout.alignment.offset(f80_layout.exponent_offset), | |
| 5206 | exponent, | |
| 5207 | .u16, | |
| 5208 | .normal, | |
| 5209 | ); | |
| 5210 | }, | |
| 5211 | 128 => { | |
| 5212 | const f128_layout = o.softF128Layout(.{}) catch unreachable; | |
| 5213 | const lo = try fg.wip.cast(.trunc, shifted_value, .i64, "load.lo"); | |
| 5214 | const shifted_hi = try fg.wip.bin( | |
| 5215 | .lshr, | |
| 5216 | backing_int_val, | |
| 5217 | try o.builder.intValue(llvm_backing_int_ty, ptr_info.packed_offset.bit_offset + 64), | |
| 5218 | "load.shifted_hi", | |
| 5219 | ); | |
| 5220 | const hi = try fg.wip.cast(.trunc, shifted_hi, .i64, "load.hi"); | |
| 5221 | ||
| 5222 | try fg.store( | |
| 5223 | try fg.ptraddConst(result_ptr, f128_layout.lo_offset), | |
| 5224 | f128_layout.alignment.offset(f128_layout.lo_offset), | |
| 5225 | lo, | |
| 5226 | .u64, | |
| 5227 | .normal, | |
| 5228 | ); | |
| 5229 | try fg.store( | |
| 5230 | try fg.ptraddConst(result_ptr, f128_layout.hi_offset), | |
| 5231 | f128_layout.alignment.offset(f128_layout.hi_offset), | |
| 5232 | hi, | |
| 5233 | .u64, | |
| 5234 | .normal, | |
| 5235 | ); | |
| 5236 | }, | |
| 5237 | } | |
| 5238 | return result_ptr; | |
| 5239 | } | |
| 5240 | ||
| 4955 | 5241 | const elem_llvm_ty = try o.lowerType(elem_ty, .as_value); |
| 4956 | 5242 | |
| 4957 | 5243 | 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 |
| 5848 | 6134 | ); |
| 5849 | 6135 | } |
| 5850 | 6136 | |
| 5851 | /// Reduce a vector by repeatedly applying `llvm_fn` to produce an accumulated result. | |
| 5852 | /// | |
| 5853 | /// Equivalent to: | |
| 5854 | /// ``` | |
| 5855 | /// var accum: T = init; | |
| 5856 | /// for (0..i) |i| { | |
| 5857 | /// accum = llvm_fn(accum, vec[i]); | |
| 5858 | /// } | |
| 5859 | /// // result is 'accum' | |
| 5860 | /// ``` | |
| 5861 | fn buildReducedCall( | |
| 5862 | self: *FuncGen, | |
| 5863 | llvm_fn: Builder.Function.Index, | |
| 5864 | operand_vector: Builder.Value, | |
| 5865 | vector_len: usize, | |
| 5866 | accum_init: Builder.Value, | |
| 5867 | ) Allocator.Error!Builder.Value { | |
| 5868 | const o = self.object; | |
| 5869 | const llvm_usize_ty = try o.lowerType(.usize, .as_value); | |
| 5870 | const llvm_vector_len = try o.builder.intValue(llvm_usize_ty, vector_len); | |
| 5871 | const llvm_result_ty = accum_init.typeOfWip(&self.wip); | |
| 5872 | ||
| 5873 | const entry_block = self.wip.cursor.block; | |
| 5874 | ||
| 5875 | const cond_block = try self.wip.block(2, "ReduceLoopCond"); | |
| 5876 | const body_block = try self.wip.block(1, "ReduceLoopBody"); | |
| 5877 | const exit_block = try self.wip.block(1, "ReduceLoopExit"); | |
| 5878 | ||
| 5879 | _ = try self.wip.br(cond_block); | |
| 5880 | ||
| 5881 | // ReduceLoopCond: | |
| 5882 | // %index = phi iN [0, %Entry], [%new_index, %ReduceLoopBody] | |
| 5883 | // %accum = phi T [%accum_init, %Entry], [%new_accum, %ReduceLoopBody] | |
| 5884 | // %cond = icmp ult iN %index, %vector_len | |
| 5885 | // br i1 %cond, label %ReduceLoopBody, label %ReduceLoopExit | |
| 5886 | self.wip.cursor = .{ .block = cond_block }; | |
| 5887 | const index = try self.wip.phi(llvm_usize_ty, ""); | |
| 5888 | const accum = try self.wip.phi(llvm_result_ty, ""); | |
| 5889 | const cond = try self.wip.icmp(.ult, index.toValue(), llvm_vector_len, ""); | |
| 5890 | _ = try self.wip.brCond(cond, body_block, exit_block, .none); | |
| 5891 | ||
| 5892 | // ReduceLoopBody: | |
| 5893 | // %elem = extractelement <n x T> %operand_vec, iN %index | |
| 5894 | // %new_accum = call T @llvm_fn(T %accum, T %elem) | |
| 5895 | // %new_index = add nuw iN %index, 1 | |
| 5896 | // br label %ReduceLoopCond | |
| 5897 | self.wip.cursor = .{ .block = body_block }; | |
| 5898 | const elem = try self.wip.extractElement(operand_vector, index.toValue(), ""); | |
| 5899 | const new_accum = try self.wip.call( | |
| 5900 | .normal, | |
| 5901 | .ccc, | |
| 5902 | .none, | |
| 5903 | llvm_fn.typeOf(&o.builder), | |
| 5904 | llvm_fn.toValue(&o.builder), | |
| 5905 | &.{ accum.toValue(), elem }, | |
| 5906 | "", | |
| 5907 | ); | |
| 5908 | const new_index = try self.wip.bin(.@"add nuw", index.toValue(), try o.builder.intValue(llvm_usize_ty, 1), ""); | |
| 5909 | _ = try self.wip.br(cond_block); | |
| 5910 | ||
| 5911 | const index_init = try o.builder.intValue(llvm_usize_ty, 0); | |
| 5912 | index.finish(&.{ index_init, new_index }, &.{ entry_block, body_block }, &self.wip); | |
| 5913 | accum.finish(&.{ accum_init, new_accum }, &.{ entry_block, body_block }, &self.wip); | |
| 5914 | ||
| 5915 | self.wip.cursor = .{ .block = exit_block }; | |
| 5916 | return accum.toValue(); | |
| 5917 | } | |
| 5918 | ||
| 5919 | fn airReduce(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value { | |
| 5920 | const o = self.object; | |
| 6137 | fn airReduce(fg: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) Allocator.Error!Builder.Value { | |
| 6138 | const o = fg.object; | |
| 5921 | 6139 | const zcu = o.zcu; |
| 5922 | 6140 | const target = zcu.getTarget(); |
| 5923 | 6141 | |
| 5924 | const reduce = self.air.instructions.items(.data)[@backingInt(inst)].reduce; | |
| 5925 | const operand = try self.resolveInst(reduce.operand); | |
| 5926 | const operand_ty = self.typeOf(reduce.operand); | |
| 5927 | const llvm_operand_ty = try o.lowerType(operand_ty, .as_value); | |
| 5928 | const scalar_ty = self.typeOfIndex(inst); | |
| 5929 | const llvm_scalar_ty = try o.lowerType(scalar_ty, .as_value); | |
| 6142 | const reduce = fg.air.instructions.items(.data)[@backingInt(inst)].reduce; | |
| 6143 | const operand = try fg.resolveInst(reduce.operand); | |
| 6144 | const operand_ty = fg.typeOf(reduce.operand); | |
| 6145 | const scalar_ty = fg.typeOfIndex(inst); | |
| 5930 | 6146 | |
| 5931 | 6147 | switch (reduce.operation) { |
| 5932 | .And, .Or, .Xor => return self.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { | |
| 6148 | .And, .Or, .Xor => return fg.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { | |
| 5933 | 6149 | .And => .@"vector.reduce.and", |
| 5934 | 6150 | .Or => .@"vector.reduce.or", |
| 5935 | 6151 | .Xor => .@"vector.reduce.xor", |
| 5936 | 6152 | else => unreachable, |
| 5937 | }, &.{llvm_operand_ty}, &.{operand}, ""), | |
| 6153 | }, &.{try o.lowerType(operand_ty, .as_value)}, &.{operand}, ""), | |
| 5938 | 6154 | .Min, .Max => switch (scalar_ty.zigTypeTag(zcu)) { |
| 5939 | .int => return self.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { | |
| 6155 | .int => return fg.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { | |
| 5940 | 6156 | .Min => if (scalar_ty.isSignedInt(zcu)) |
| 5941 | 6157 | .@"vector.reduce.smin" |
| 5942 | 6158 | else |
| ... | ... | @@ -5946,29 +6162,29 @@ fn airReduce(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) A |
| 5946 | 6162 | else |
| 5947 | 6163 | .@"vector.reduce.umax", |
| 5948 | 6164 | else => unreachable, |
| 5949 | }, &.{llvm_operand_ty}, &.{operand}, ""), | |
| 6165 | }, &.{try o.lowerType(operand_ty, .as_value)}, &.{operand}, ""), | |
| 5950 | 6166 | .float => if (intrinsicsAllowed(scalar_ty, target)) |
| 5951 | return self.wip.callIntrinsic(fast, .none, switch (reduce.operation) { | |
| 6167 | return fg.wip.callIntrinsic(fast, .none, switch (reduce.operation) { | |
| 5952 | 6168 | .Min => .@"vector.reduce.fmin", |
| 5953 | 6169 | .Max => .@"vector.reduce.fmax", |
| 5954 | 6170 | else => unreachable, |
| 5955 | }, &.{llvm_operand_ty}, &.{operand}, ""), | |
| 6171 | }, &.{try o.lowerType(operand_ty, .as_value)}, &.{operand}, ""), | |
| 5956 | 6172 | else => unreachable, |
| 5957 | 6173 | }, |
| 5958 | 6174 | .Add, .Mul => switch (scalar_ty.zigTypeTag(zcu)) { |
| 5959 | .int => return self.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { | |
| 6175 | .int => return fg.wip.callIntrinsic(.normal, .none, switch (reduce.operation) { | |
| 5960 | 6176 | .Add => .@"vector.reduce.add", |
| 5961 | 6177 | .Mul => .@"vector.reduce.mul", |
| 5962 | 6178 | else => unreachable, |
| 5963 | }, &.{llvm_operand_ty}, &.{operand}, ""), | |
| 6179 | }, &.{try o.lowerType(operand_ty, .as_value)}, &.{operand}, ""), | |
| 5964 | 6180 | .float => if (intrinsicsAllowed(scalar_ty, target)) |
| 5965 | return self.wip.callIntrinsic(fast, .none, switch (reduce.operation) { | |
| 6181 | return fg.wip.callIntrinsic(fast, .none, switch (reduce.operation) { | |
| 5966 | 6182 | .Add => .@"vector.reduce.fadd", |
| 5967 | 6183 | .Mul => .@"vector.reduce.fmul", |
| 5968 | 6184 | else => unreachable, |
| 5969 | }, &.{llvm_operand_ty}, &.{ switch (reduce.operation) { | |
| 5970 | .Add => try o.builder.fpValue(llvm_scalar_ty, -0.0), | |
| 5971 | .Mul => try o.builder.fpValue(llvm_scalar_ty, 1.0), | |
| 6185 | }, &.{try o.lowerType(operand_ty, .as_value)}, &.{ switch (reduce.operation) { | |
| 6186 | .Add => try o.builder.fpValue(try o.lowerType(scalar_ty, .as_value), -0.0), | |
| 6187 | .Mul => try o.builder.fpValue(try o.lowerType(scalar_ty, .as_value), 1.0), | |
| 5972 | 6188 | else => unreachable, |
| 5973 | 6189 | }, operand }, ""), |
| 5974 | 6190 | else => unreachable, |
| ... | ... | @@ -5986,62 +6202,119 @@ fn airReduce(self: *FuncGen, inst: Air.Inst.Index, fast: Builder.FastMathKind) A |
| 5986 | 6202 | libcFloatPrefix(float_bits), libcFloatSuffix(float_bits), |
| 5987 | 6203 | }), |
| 5988 | 6204 | .Add => try o.builder.strtabStringFmt("__add{s}f3", .{ |
| 5989 | compilerRtFloatAbbrev(float_bits), | |
| 6205 | compilerRtFloatAbbrev(target, float_bits), | |
| 5990 | 6206 | }), |
| 5991 | 6207 | .Mul => try o.builder.strtabStringFmt("__mul{s}f3", .{ |
| 5992 | compilerRtFloatAbbrev(float_bits), | |
| 6208 | compilerRtFloatAbbrev(target, float_bits), | |
| 5993 | 6209 | }), |
| 5994 | 6210 | else => unreachable, |
| 5995 | 6211 | }; |
| 5996 | ||
| 5997 | const libc_fn = try o.getLibcFunction(fn_name, &.{ llvm_scalar_ty, llvm_scalar_ty }, llvm_scalar_ty); | |
| 5998 | const init_val = switch (llvm_scalar_ty) { | |
| 5999 | .i16 => try o.builder.intValue(.i16, @as(i16, @bitCast( | |
| 6000 | @as(f16, switch (reduce.operation) { | |
| 6001 | .Min, .Max => std.math.nan(f16), | |
| 6002 | .Add => -0.0, | |
| 6003 | .Mul => 1.0, | |
| 6004 | else => unreachable, | |
| 6005 | }), | |
| 6006 | ))), | |
| 6007 | .i80 => try o.builder.intValue(.i80, @as(i80, @bitCast( | |
| 6008 | @as(f80, switch (reduce.operation) { | |
| 6009 | .Min, .Max => std.math.nan(f80), | |
| 6010 | .Add => -0.0, | |
| 6011 | .Mul => 1.0, | |
| 6012 | else => unreachable, | |
| 6013 | }), | |
| 6014 | ))), | |
| 6015 | .i128 => try o.builder.intValue(.i128, @as(i128, @bitCast( | |
| 6016 | @as(f128, switch (reduce.operation) { | |
| 6017 | .Min, .Max => std.math.nan(f128), | |
| 6018 | .Add => -0.0, | |
| 6019 | .Mul => 1.0, | |
| 6020 | else => unreachable, | |
| 6021 | }), | |
| 6022 | ))), | |
| 6212 | const fn_info: Object.FuncInfo = .{ | |
| 6213 | .cc = target.cCallingConvention().?, | |
| 6214 | .param_types = &.{ scalar_ty.toIntern(), scalar_ty.toIntern() }, | |
| 6215 | .return_type = scalar_ty.toIntern(), | |
| 6216 | }; | |
| 6217 | const llvm_fn = try fg.object.getLibcFunction(fg.pt, fn_name, fn_info); | |
| 6218 | const init = switch (float_bits) { | |
| 6023 | 6219 | else => unreachable, |
| 6220 | 16 => try o.f16Const(switch (reduce.operation) { | |
| 6221 | else => unreachable, | |
| 6222 | .Min, .Max => std.math.nan(f16), | |
| 6223 | .Add => -0.0, | |
| 6224 | .Mul => 1.0, | |
| 6225 | }), | |
| 6226 | 32 => try o.f32Const(switch (reduce.operation) { | |
| 6227 | else => unreachable, | |
| 6228 | .Min, .Max => std.math.nan(f32), | |
| 6229 | .Add => -0.0, | |
| 6230 | .Mul => 1.0, | |
| 6231 | }), | |
| 6232 | 64 => try o.f64Const(switch (reduce.operation) { | |
| 6233 | else => unreachable, | |
| 6234 | .Min, .Max => std.math.nan(f64), | |
| 6235 | .Add => -0.0, | |
| 6236 | .Mul => 1.0, | |
| 6237 | }), | |
| 6238 | 80 => try o.f80Const(switch (reduce.operation) { | |
| 6239 | else => unreachable, | |
| 6240 | .Min, .Max => std.math.nan(f80), | |
| 6241 | .Add => -0.0, | |
| 6242 | .Mul => 1.0, | |
| 6243 | }), | |
| 6244 | 128 => try o.f128Const(switch (reduce.operation) { | |
| 6245 | else => unreachable, | |
| 6246 | .Min, .Max => std.math.nan(f128), | |
| 6247 | .Add => -0.0, | |
| 6248 | .Mul => 1.0, | |
| 6249 | }), | |
| 6024 | 6250 | }; |
| 6025 | return self.buildReducedCall(libc_fn, operand, operand_ty.vectorLen(zcu), init_val); | |
| 6251 | const iterations = operand_ty.vectorLen(zcu); | |
| 6252 | const is_by_ref = isByRef(operand_ty, zcu); | |
| 6253 | if (iterations > 1 and is_by_ref) { | |
| 6254 | const init_ref = try o.lowerConstRef(init, scalar_ty.abiAlignment(zcu).toLlvm()); | |
| 6255 | ||
| 6256 | const entry_block = fg.wip.cursor.block; | |
| 6257 | const loop_block = try fg.wip.block(2, "reduce.loop"); | |
| 6258 | const done_block = try fg.wip.block(1, "reduce.loop"); | |
| 6259 | ||
| 6260 | _ = try fg.wip.br(loop_block); | |
| 6261 | ||
| 6262 | fg.wip.cursor = .{ .block = loop_block }; | |
| 6263 | const index = try fg.wip.phi(.i32, "reduce.index"); | |
| 6264 | const result = try fg.wip.phi(.ptr, "reduce.result"); | |
| 6265 | ||
| 6266 | const rhs_elem_ptr = try fg.ptraddScaled(operand, index.toValue(), scalar_ty.abiSize(zcu)); | |
| 6267 | const rhs_elem = try fg.load(rhs_elem_ptr, .none, scalar_ty, .normal); | |
| 6268 | 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 }); | |
| 6269 | ||
| 6270 | const next_index = try fg.wip.bin(.@"add nuw", index.toValue(), try o.builder.intValue(.i32, 1), "reduce.next_index"); | |
| 6271 | index.finish(&.{ try o.builder.intValue(.i32, 0), next_index }, &.{ entry_block, loop_block }, &fg.wip); | |
| 6272 | result.finish(&.{ init_ref.toValue(), next_result }, &.{ entry_block, loop_block }, &fg.wip); | |
| 6273 | const is_done = try fg.wip.icmp(.eq, next_index, try o.builder.intValue(.i32, iterations), "reduce.is_done"); | |
| 6274 | _ = try fg.wip.brCond(is_done, done_block, loop_block, .none); | |
| 6275 | ||
| 6276 | fg.wip.cursor = .{ .block = done_block }; | |
| 6277 | return next_result; | |
| 6278 | } | |
| 6279 | var result = init.toValue(); | |
| 6280 | for (0..iterations) |index| { | |
| 6281 | const index_value = try o.builder.intValue(.i32, index); | |
| 6282 | const rhs_elem = if (is_by_ref) rhs_elem: { | |
| 6283 | const rhs_elem_ptr = try fg.ptraddConst(operand, index * scalar_ty.abiSize(zcu)); | |
| 6284 | break :rhs_elem try fg.load(rhs_elem_ptr, .none, scalar_ty, .normal); | |
| 6285 | } else try fg.wip.extractElement(operand, index_value, "reduce.rhs_elem"); | |
| 6286 | result = try fg.buildCall(.{}, llvm_fn.typeOf(&o.builder), llvm_fn.toValue(&o.builder), fn_info, fn_info.param_types, &.{ result, rhs_elem }); | |
| 6287 | } | |
| 6288 | return result; | |
| 6026 | 6289 | } |
| 6027 | 6290 | |
| 6028 | fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { | |
| 6029 | const o = self.object; | |
| 6291 | fn airAggregateInit(fg: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { | |
| 6292 | const o = fg.object; | |
| 6030 | 6293 | const zcu = o.zcu; |
| 6031 | 6294 | const ip = &zcu.intern_pool; |
| 6032 | const ty_pl = self.air.instructions.items(.data)[@backingInt(inst)].ty_pl; | |
| 6033 | const result_ty = self.typeOfIndex(inst); | |
| 6295 | const ty_pl = fg.air.instructions.items(.data)[@backingInt(inst)].ty_pl; | |
| 6296 | const result_ty = fg.typeOfIndex(inst); | |
| 6034 | 6297 | const len: usize = @intCast(result_ty.arrayLen(zcu)); |
| 6035 | const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[ty_pl.payload..][0..len]); | |
| 6298 | const elements: []const Air.Inst.Ref = @ptrCast(fg.air.extra.items[ty_pl.payload..][0..len]); | |
| 6036 | 6299 | |
| 6037 | 6300 | switch (result_ty.zigTypeTag(zcu)) { |
| 6038 | .vector => { | |
| 6301 | .vector => if (isByRef(result_ty, zcu)) { | |
| 6302 | const elem_ty = result_ty.childType(zcu); | |
| 6303 | const elem_size = elem_ty.abiSize(zcu); | |
| 6304 | const result_ptr = try fg.buildZigAlloca(result_ty, .none); | |
| 6305 | for (elements, 0..) |elem, elem_index| { | |
| 6306 | const elem_ptr = try fg.ptraddConst(result_ptr, elem_index * elem_size); | |
| 6307 | const llvm_elem = try fg.resolveInst(elem); | |
| 6308 | try fg.store(elem_ptr, .none, llvm_elem, elem_ty, .normal); | |
| 6309 | } | |
| 6310 | return result_ptr; | |
| 6311 | } else { | |
| 6039 | 6312 | const llvm_result_ty = try o.lowerType(result_ty, .as_value); |
| 6040 | 6313 | var vector = try o.builder.poisonValue(llvm_result_ty); |
| 6041 | for (elements, 0..) |elem, i| { | |
| 6042 | const index_u32 = try o.builder.intValue(.i32, i); | |
| 6043 | const llvm_elem = try self.resolveInst(elem); | |
| 6044 | vector = try self.wip.insertElement(vector, llvm_elem, index_u32, ""); | |
| 6314 | for (elements, 0..) |elem, elem_index| { | |
| 6315 | const elem_index_val = try o.builder.intValue(.i32, elem_index); | |
| 6316 | const llvm_elem = try fg.resolveInst(elem); | |
| 6317 | vector = try fg.wip.insertElement(vector, llvm_elem, elem_index_val, ""); | |
| 6045 | 6318 | } |
| 6046 | 6319 | return vector; |
| 6047 | 6320 | }, |
| ... | ... | @@ -6057,18 +6330,18 @@ fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builde |
| 6057 | 6330 | for (elements, struct_type.field_types.get(ip)) |elem, field_ty| { |
| 6058 | 6331 | if (!Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; |
| 6059 | 6332 | |
| 6060 | const non_int_val = try self.resolveInst(elem); | |
| 6333 | const non_int_val = try fg.resolveInst(elem); | |
| 6061 | 6334 | const ty_bit_size: u16 = @intCast(Type.fromInterned(field_ty).bitSize(zcu)); |
| 6062 | 6335 | const small_int_ty = try o.builder.intType(ty_bit_size); |
| 6063 | 6336 | const small_int_val = if (Type.fromInterned(field_ty).isPtrAtRuntime(zcu)) |
| 6064 | try self.wip.cast(.ptrtoint, non_int_val, small_int_ty, "") | |
| 6337 | try fg.wip.cast(.ptrtoint, non_int_val, small_int_ty, "") | |
| 6065 | 6338 | else |
| 6066 | try self.wip.cast(.bitcast, non_int_val, small_int_ty, ""); | |
| 6339 | try fg.wip.cast(.bitcast, non_int_val, small_int_ty, ""); | |
| 6067 | 6340 | const shift_rhs = try o.builder.intValue(int_ty, running_bits); |
| 6068 | 6341 | const extended_int_val = |
| 6069 | try self.wip.conv(.unsigned, small_int_val, int_ty, ""); | |
| 6070 | const shifted = try self.wip.bin(.shl, extended_int_val, shift_rhs, ""); | |
| 6071 | running_int = try self.wip.bin(.@"or", running_int, shifted, ""); | |
| 6342 | try fg.wip.conv(.unsigned, small_int_val, int_ty, ""); | |
| 6343 | const shifted = try fg.wip.bin(.shl, extended_int_val, shift_rhs, ""); | |
| 6344 | running_int = try fg.wip.bin(.@"or", running_int, shifted, ""); | |
| 6072 | 6345 | running_bits += ty_bit_size; |
| 6073 | 6346 | } |
| 6074 | 6347 | return running_int; |
| ... | ... | @@ -6078,19 +6351,19 @@ fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builde |
| 6078 | 6351 | // TODO in debug builds init to undef so that the padding will be 0xaa |
| 6079 | 6352 | // even if we fully populate the fields. |
| 6080 | 6353 | const struct_align = result_ty.abiAlignment(zcu); |
| 6081 | const alloca_inst = try self.buildZigAlloca(result_ty, .none); | |
| 6354 | const alloca_inst = try fg.buildZigAlloca(result_ty, .none); | |
| 6082 | 6355 | |
| 6083 | 6356 | for (elements, 0..) |elem, field_index| { |
| 6084 | 6357 | if (result_ty.structFieldIsComptime(field_index, zcu)) continue; |
| 6085 | 6358 | const field_ty = result_ty.fieldType(field_index, zcu); |
| 6086 | 6359 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 6087 | 6360 | const offset = result_ty.structFieldOffset(field_index, zcu); |
| 6088 | const field_ptr = try self.ptraddConst(alloca_inst, offset); | |
| 6361 | const field_ptr = try fg.ptraddConst(alloca_inst, offset); | |
| 6089 | 6362 | const field_ptr_align = struct_align.offset(offset); |
| 6090 | 6363 | |
| 6091 | const llvm_field_val = try self.resolveInst(elem); | |
| 6364 | const llvm_field_val = try fg.resolveInst(elem); | |
| 6092 | 6365 | |
| 6093 | try self.store(field_ptr, field_ptr_align, llvm_field_val, field_ty, .normal); | |
| 6366 | try fg.store(field_ptr, field_ptr_align, llvm_field_val, field_ty, .normal); | |
| 6094 | 6367 | } |
| 6095 | 6368 | |
| 6096 | 6369 | return alloca_inst; |
| ... | ... | @@ -6099,21 +6372,21 @@ fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builde |
| 6099 | 6372 | .array => { |
| 6100 | 6373 | assert(isByRef(result_ty, zcu)); |
| 6101 | 6374 | |
| 6102 | const alloca_inst = try self.buildZigAlloca(result_ty, .none); | |
| 6375 | const alloca_inst = try fg.buildZigAlloca(result_ty, .none); | |
| 6103 | 6376 | |
| 6104 | 6377 | const array_info = result_ty.arrayInfo(zcu); |
| 6105 | 6378 | |
| 6106 | 6379 | const elem_size = array_info.elem_type.abiSize(zcu); |
| 6107 | 6380 | |
| 6108 | 6381 | for (elements, 0..) |elem, i| { |
| 6109 | const elem_ptr = try self.ptraddConst(alloca_inst, elem_size * i); | |
| 6110 | const llvm_elem = try self.resolveInst(elem); | |
| 6111 | try self.store(elem_ptr, .none, llvm_elem, array_info.elem_type, .normal); | |
| 6382 | const elem_ptr = try fg.ptraddConst(alloca_inst, elem_size * i); | |
| 6383 | const llvm_elem = try fg.resolveInst(elem); | |
| 6384 | try fg.store(elem_ptr, .none, llvm_elem, array_info.elem_type, .normal); | |
| 6112 | 6385 | } |
| 6113 | 6386 | if (array_info.sentinel) |sent_val| { |
| 6114 | const elem_ptr = try self.ptraddConst(alloca_inst, elem_size * array_info.len); | |
| 6115 | const llvm_elem = try self.resolveValue(sent_val); | |
| 6116 | try self.store(elem_ptr, .none, llvm_elem.toValue(), array_info.elem_type, .normal); | |
| 6387 | const elem_ptr = try fg.ptraddConst(alloca_inst, elem_size * array_info.len); | |
| 6388 | const llvm_elem = try fg.resolveValue(sent_val); | |
| 6389 | try fg.store(elem_ptr, .none, llvm_elem.toValue(), array_info.elem_type, .normal); | |
| 6117 | 6390 | } |
| 6118 | 6391 | |
| 6119 | 6392 | return alloca_inst; |
| ... | ... | @@ -6469,26 +6742,12 @@ fn store( |
| 6469 | 6742 | .unsigned => .zext, |
| 6470 | 6743 | .signed => .sext, |
| 6471 | 6744 | }, elem, llvm_memory_ty, ""); |
| 6472 | _ = try fg.wip.storeAtomic( | |
| 6473 | access_kind, | |
| 6474 | extended, | |
| 6475 | ptr, | |
| 6476 | fg.sync_scope, | |
| 6477 | .none, | |
| 6478 | llvm_ptr_align, | |
| 6479 | ); | |
| 6745 | _ = try fg.wip.store(access_kind, extended, ptr, llvm_ptr_align); | |
| 6480 | 6746 | return; |
| 6481 | 6747 | } |
| 6482 | 6748 | |
| 6483 | 6749 | // `elem_ty` is a simple by-val type which requires no special handling. |
| 6484 | _ = try fg.wip.storeAtomic( | |
| 6485 | access_kind, | |
| 6486 | elem, | |
| 6487 | ptr, | |
| 6488 | fg.sync_scope, | |
| 6489 | .none, | |
| 6490 | llvm_ptr_align, | |
| 6491 | ); | |
| 6750 | _ = try fg.wip.store(access_kind, elem, ptr, llvm_ptr_align); | |
| 6492 | 6751 | } |
| 6493 | 6752 | |
| 6494 | 6753 | 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 { |
| 6650 | 6909 | |
| 6651 | 6910 | const ParamTypeIterator = struct { |
| 6652 | 6911 | object: *Object, |
| 6653 | fn_info: InternPool.Key.FuncType, | |
| 6912 | cc: std.lang.CallingConvention, | |
| 6913 | param_types: []const InternPool.Index, | |
| 6654 | 6914 | zig_index: u32, |
| 6655 | 6915 | llvm_index: u32, |
| 6656 | 6916 | types_len: u32, |
| ... | ... | @@ -6672,63 +6932,66 @@ const ParamTypeIterator = struct { |
| 6672 | 6932 | }; |
| 6673 | 6933 | |
| 6674 | 6934 | pub fn next(it: *ParamTypeIterator) Allocator.Error!?Lowering { |
| 6675 | if (it.zig_index >= it.fn_info.param_types.len) return null; | |
| 6676 | const ip = &it.object.zcu.intern_pool; | |
| 6677 | const ty = it.fn_info.param_types.get(ip)[it.zig_index]; | |
| 6935 | if (it.zig_index >= it.param_types.len) return null; | |
| 6936 | const ty = it.param_types[it.zig_index]; | |
| 6678 | 6937 | it.byval_attr = false; |
| 6679 | 6938 | return nextInner(it, Type.fromInterned(ty)); |
| 6680 | 6939 | } |
| 6681 | 6940 | |
| 6682 | 6941 | /// `airCall` uses this instead of `next` so that it can take into account variadic functions. |
| 6683 | fn nextCall(it: *ParamTypeIterator, fg: *FuncGen, args: []const Air.Inst.Ref) Allocator.Error!?Lowering { | |
| 6684 | const ip = &it.object.zcu.intern_pool; | |
| 6685 | if (it.zig_index >= it.fn_info.param_types.len) { | |
| 6686 | if (it.zig_index >= args.len) { | |
| 6942 | fn nextCall(it: *ParamTypeIterator, arg_types: []const InternPool.Index) Allocator.Error!?Lowering { | |
| 6943 | if (it.zig_index >= it.param_types.len) { | |
| 6944 | if (it.zig_index >= arg_types.len) { | |
| 6687 | 6945 | return null; |
| 6688 | 6946 | } else { |
| 6689 | return nextInner(it, fg.typeOf(args[it.zig_index])); | |
| 6947 | return nextInner(it, .fromInterned(arg_types[it.zig_index])); | |
| 6690 | 6948 | } |
| 6691 | 6949 | } else { |
| 6692 | return nextInner(it, Type.fromInterned(it.fn_info.param_types.get(ip)[it.zig_index])); | |
| 6950 | return nextInner(it, .fromInterned(it.param_types[it.zig_index])); | |
| 6693 | 6951 | } |
| 6694 | 6952 | } |
| 6695 | 6953 | |
| 6696 | 6954 | fn nextInner(it: *ParamTypeIterator, ty: Type) Allocator.Error!?Lowering { |
| 6697 | 6955 | const zcu = it.object.zcu; |
| 6698 | const target = zcu.getTarget(); | |
| 6699 | ||
| 6956 | ty.assertHasLayout(zcu); | |
| 6700 | 6957 | if (!ty.hasRuntimeBits(zcu)) { |
| 6701 | 6958 | it.zig_index += 1; |
| 6702 | 6959 | return .no_bits; |
| 6703 | 6960 | } |
| 6704 | switch (it.fn_info.cc) { | |
| 6961 | switch (it.cc) { | |
| 6705 | 6962 | .@"inline" => unreachable, |
| 6706 | 6963 | .auto => { |
| 6707 | 6964 | it.zig_index += 1; |
| 6708 | 6965 | it.llvm_index += 1; |
| 6966 | ||
| 6967 | // Match the c calling convention in some cases to avoid llvm bugs. | |
| 6968 | const target = zcu.getTarget(); | |
| 6969 | if (target.cpu.arch == .x86_64 and ty.isVector(zcu) and ty.childType(zcu).toIntern() == .bool_type) return switch (ty.vectorLen(zcu)) { | |
| 6970 | 0 => .no_bits, | |
| 6971 | 1...32 => .abi_sized_int, | |
| 6972 | 33...64 => { | |
| 6973 | it.types_buffer[0..1].* = .{.double}; | |
| 6974 | it.offsets_buffer[0..2].* = .{ 0, 8 }; | |
| 6975 | it.types_len = 1; | |
| 6976 | return .multiple_llvm_types; | |
| 6977 | }, | |
| 6978 | else => .byval, | |
| 6979 | }; | |
| 6980 | ||
| 6709 | 6981 | if (ty.isSlice(zcu) or |
| 6710 | 6982 | (ty.zigTypeTag(zcu) == .optional and ty.optionalChild(zcu).isSlice(zcu) and !ty.ptrAllowsZero(zcu))) |
| 6711 | 6983 | { |
| 6712 | 6984 | it.llvm_index += 1; |
| 6713 | 6985 | return .slice; |
| 6714 | } else if (isByRef(ty, zcu)) { | |
| 6715 | return .byref; | |
| 6716 | } else if (target.cpu.arch.isX86() and | |
| 6717 | !target.cpu.has(.x86, .avx512f) and | |
| 6718 | ty.totalVectorBits(zcu) >= 512) | |
| 6719 | { | |
| 6720 | // As of LLVM 18, passing a vector byval with fastcc that is 512 bits or more returns | |
| 6721 | // "512-bit vector arguments require 'avx512f' for AVX512" | |
| 6722 | return .byref; | |
| 6723 | } else { | |
| 6724 | return .byval; | |
| 6725 | 6986 | } |
| 6987 | if (isByRef(ty, zcu)) return .byref; | |
| 6988 | return .byval; | |
| 6726 | 6989 | }, |
| 6727 | 6990 | .async => { |
| 6728 | 6991 | @panic("TODO implement async function lowering in the LLVM backend"); |
| 6729 | 6992 | }, |
| 6730 | .x86_64_sysv, .x86_64_x32 => return it.nextSystemV(ty), | |
| 6731 | .x86_64_win => return it.nextWin64(ty), | |
| 6993 | .x86_64_sysv, .x86_64_x32 => return try it.next_x86_64_sysv(ty), | |
| 6994 | .x86_64_win => return it.next_x86_64_win(ty), | |
| 6732 | 6995 | .x86_stdcall => { |
| 6733 | 6996 | it.zig_index += 1; |
| 6734 | 6997 | it.llvm_index += 1; |
| ... | ... | @@ -6748,9 +7011,9 @@ const ParamTypeIterator = struct { |
| 6748 | 7011 | .float_array => |len| return Lowering{ .float_array = len }, |
| 6749 | 7012 | .byval => return .byval, |
| 6750 | 7013 | .integer => { |
| 6751 | it.types_len = 1; | |
| 6752 | 7014 | it.types_buffer[0..1].* = .{.i64}; |
| 6753 | 7015 | it.offsets_buffer[0..2].* = .{ 0, 8 }; |
| 7016 | it.types_len = 1; | |
| 6754 | 7017 | return .multiple_llvm_types; |
| 6755 | 7018 | }, |
| 6756 | 7019 | .double_integer => return Lowering{ .i64_array = 2 }, |
| ... | ... | @@ -6857,7 +7120,7 @@ const ParamTypeIterator = struct { |
| 6857 | 7120 | } |
| 6858 | 7121 | } |
| 6859 | 7122 | |
| 6860 | fn nextWin64(it: *ParamTypeIterator, ty: Type) ?Lowering { | |
| 7123 | fn next_x86_64_win(it: *ParamTypeIterator, ty: Type) Lowering { | |
| 6861 | 7124 | const zcu = it.object.zcu; |
| 6862 | 7125 | switch (x86_64_abi.classifyWindows(ty, zcu, zcu.getTarget(), .arg)) { |
| 6863 | 7126 | .integer => { |
| ... | ... | @@ -6898,113 +7161,108 @@ const ParamTypeIterator = struct { |
| 6898 | 7161 | } |
| 6899 | 7162 | } |
| 6900 | 7163 | |
| 6901 | fn nextSystemV(it: *ParamTypeIterator, ty: Type) Allocator.Error!?Lowering { | |
| 6902 | const zcu = it.object.zcu; | |
| 6903 | const ip = &zcu.intern_pool; | |
| 6904 | ty.assertHasLayout(zcu); | |
| 6905 | const classes = x86_64_abi.classifySystemV(ty, zcu, zcu.getTarget(), .arg); | |
| 6906 | if (classes[0] == .memory) { | |
| 6907 | it.zig_index += 1; | |
| 6908 | it.llvm_index += 1; | |
| 6909 | it.byval_attr = true; | |
| 6910 | return .byref; | |
| 6911 | } | |
| 6912 | if (isScalar(zcu, ty)) { | |
| 6913 | it.zig_index += 1; | |
| 6914 | it.llvm_index += 1; | |
| 6915 | return .byval; | |
| 6916 | } | |
| 6917 | var types_index: u32 = 0; | |
| 6918 | var offset: u64 = 0; | |
| 6919 | for (classes) |class| { | |
| 6920 | switch (class) { | |
| 6921 | .integer => { | |
| 6922 | it.types_buffer[types_index] = .i64; | |
| 6923 | it.offsets_buffer[types_index] = offset; | |
| 6924 | types_index += 1; | |
| 6925 | }, | |
| 6926 | .sse => { | |
| 6927 | it.types_buffer[types_index] = .double; | |
| 6928 | it.offsets_buffer[types_index] = offset; | |
| 6929 | types_index += 1; | |
| 6930 | }, | |
| 6931 | .sseup => { | |
| 6932 | if (it.types_buffer[types_index - 1] == .double) { | |
| 6933 | it.types_buffer[types_index - 1] = .fp128; | |
| 6934 | } else { | |
| 6935 | it.types_buffer[types_index] = .double; | |
| 6936 | it.offsets_buffer[types_index] = offset; | |
| 6937 | types_index += 1; | |
| 7164 | fn next_x86_64_sysv(it: *ParamTypeIterator, ty: Type) Allocator.Error!Lowering { | |
| 7165 | const o = it.object; | |
| 7166 | const zcu = o.zcu; | |
| 7167 | const target = zcu.getTarget(); | |
| 7168 | const classes = x86_64_abi.classifySystemV(ty, zcu, target, .arg); | |
| 7169 | var types_len: u32 = 0; | |
| 7170 | const classes_len = for (classes, 0..) |class, class_index| switch (class) { | |
| 7171 | .integer => { | |
| 7172 | it.types_buffer[types_len] = try o.builder.intType(@min(8 * ty.abiSize(zcu) - 64 * class_index, 64)); | |
| 7173 | it.offsets_buffer[types_len] = 8 * class_index; | |
| 7174 | types_len += 1; | |
| 7175 | }, | |
| 7176 | .sse => { | |
| 7177 | it.types_buffer[types_len] = .double; | |
| 7178 | it.offsets_buffer[types_len] = 8 * class_index; | |
| 7179 | types_len += 1; | |
| 7180 | }, | |
| 7181 | .sseup => { | |
| 7182 | if (it.types_buffer[types_len - 1] == .double) { | |
| 7183 | if (ty.isVector(zcu)) { | |
| 7184 | it.zig_index += 1; | |
| 7185 | it.llvm_index += 1; | |
| 7186 | return .byval; | |
| 6938 | 7187 | } |
| 6939 | }, | |
| 6940 | .float => { | |
| 6941 | it.types_buffer[types_index] = .float; | |
| 6942 | it.offsets_buffer[types_index] = offset; | |
| 6943 | types_index += 1; | |
| 6944 | }, | |
| 6945 | .float_combine => { | |
| 6946 | it.types_buffer[types_index] = try it.object.builder.vectorType(.normal, 2, .float); | |
| 6947 | it.offsets_buffer[types_index] = offset; | |
| 6948 | types_index += 1; | |
| 6949 | }, | |
| 6950 | .x87 => { | |
| 6951 | it.zig_index += 1; | |
| 6952 | it.llvm_index += 1; | |
| 6953 | it.byval_attr = true; | |
| 6954 | return .byref; | |
| 6955 | }, | |
| 6956 | .x87up => unreachable, | |
| 6957 | .none => break, | |
| 6958 | .memory => unreachable, // handled above | |
| 6959 | .win_i128 => unreachable, // windows only | |
| 6960 | .bool_vector_mask, | |
| 6961 | .integer_per_element, | |
| 6962 | .sse_per_element, | |
| 6963 | .sse_sse_x87_per_qword, | |
| 6964 | .sse_per_xword, | |
| 6965 | .sse_per_yword, | |
| 6966 | .sse_per_zword, | |
| 6967 | => unreachable, // vectors already handled by `isScalar` above | |
| 6968 | } | |
| 6969 | offset += 8; | |
| 6970 | } | |
| 6971 | const first_non_integer = std.mem.indexOfNone(x86_64_abi.Class, &classes, &.{.integer}); | |
| 6972 | if (first_non_integer == null or classes[first_non_integer.?] == .none) { | |
| 6973 | assert(first_non_integer orelse classes.len == types_index); | |
| 6974 | if (types_index == 1) { | |
| 7188 | it.types_buffer[types_len - 1] = .fp128; | |
| 7189 | } else { | |
| 7190 | it.types_buffer[types_len] = .double; | |
| 7191 | it.offsets_buffer[types_len] = 8 * class_index; | |
| 7192 | types_len += 1; | |
| 7193 | } | |
| 7194 | }, | |
| 7195 | .float => { | |
| 7196 | it.types_buffer[types_len] = .float; | |
| 7197 | it.offsets_buffer[types_len] = 8 * class_index; | |
| 7198 | types_len += 1; | |
| 7199 | }, | |
| 7200 | .float_combine => { | |
| 7201 | it.types_buffer[types_len] = try it.object.builder.vectorType(.normal, 2, .float); | |
| 7202 | it.offsets_buffer[types_len] = 8 * class_index; | |
| 7203 | types_len += 1; | |
| 7204 | }, | |
| 7205 | .x87 => { | |
| 6975 | 7206 | it.zig_index += 1; |
| 6976 | 7207 | it.llvm_index += 1; |
| 6977 | return .abi_sized_int; | |
| 6978 | } | |
| 6979 | if (it.llvm_index + types_index > 6) { | |
| 7208 | it.byval_attr = true; | |
| 7209 | return .byref; | |
| 7210 | }, | |
| 7211 | .x87up => unreachable, | |
| 7212 | .none => break class_index, | |
| 7213 | .memory => { | |
| 7214 | it.zig_index += 1; | |
| 7215 | it.llvm_index += 1; | |
| 7216 | it.byval_attr = true; | |
| 7217 | return .byref; | |
| 7218 | }, | |
| 7219 | .win_i128 => unreachable, // windows only | |
| 7220 | .bool_vector_mask, | |
| 7221 | .integer_per_element, | |
| 7222 | .sse_per_element, | |
| 7223 | .sse_sse_x87_per_qword, | |
| 7224 | .sse_per_xword, | |
| 7225 | .sse_per_yword, | |
| 7226 | .sse_per_zword, | |
| 7227 | => { | |
| 7228 | it.zig_index += 1; | |
| 7229 | it.llvm_index += 1; | |
| 7230 | return .byval; | |
| 7231 | }, | |
| 7232 | } else classes.len; | |
| 7233 | if (types_len > 1) { | |
| 7234 | if (it.llvm_index + classes_len > 6) { | |
| 6980 | 7235 | it.zig_index += 1; |
| 6981 | 7236 | it.llvm_index += 1; |
| 6982 | 7237 | it.byval_attr = true; |
| 6983 | 7238 | return .byref; |
| 6984 | 7239 | } |
| 6985 | switch (ip.indexToKey(ty.toIntern())) { | |
| 6986 | .struct_type => { | |
| 6987 | const size = ty.abiSize(zcu); | |
| 6988 | assert(@divCeil(size, 8) == types_index); | |
| 6989 | if (size % 8 > 0) { | |
| 6990 | it.types_buffer[types_index - 1] = | |
| 6991 | try it.object.builder.intType(@intCast(size % 8 * 8)); | |
| 6992 | } | |
| 6993 | }, | |
| 6994 | else => {}, | |
| 7240 | } else if (!isByRef(ty, zcu)) { | |
| 7241 | const llvm_ty = try o.lowerType(ty, .as_value); | |
| 7242 | if (it.types_buffer[0] == llvm_ty or | |
| 7243 | (it.types_buffer[0] == .i64 and llvm_ty.isPointer(&o.builder))) | |
| 7244 | { | |
| 7245 | it.zig_index += 1; | |
| 7246 | it.llvm_index += 1; | |
| 7247 | return .byval; | |
| 6995 | 7248 | } |
| 6996 | 7249 | } |
| 6997 | it.offsets_buffer[types_index] = offset; | |
| 6998 | it.types_len = types_index; | |
| 6999 | it.llvm_index += types_index; | |
| 7250 | it.offsets_buffer[types_len] = 8 * classes_len; | |
| 7251 | it.types_len = types_len; | |
| 7252 | it.llvm_index += types_len; | |
| 7000 | 7253 | it.zig_index += 1; |
| 7001 | 7254 | return .multiple_llvm_types; |
| 7002 | 7255 | } |
| 7003 | 7256 | }; |
| 7004 | pub fn iterateParamTypes(object: *Object, fn_info: InternPool.Key.FuncType) ParamTypeIterator { | |
| 7257 | pub fn iterateParamTypes( | |
| 7258 | object: *Object, | |
| 7259 | cc: std.lang.CallingConvention, | |
| 7260 | param_types: []const InternPool.Index, | |
| 7261 | ) ParamTypeIterator { | |
| 7005 | 7262 | return .{ |
| 7006 | 7263 | .object = object, |
| 7007 | .fn_info = fn_info, | |
| 7264 | .cc = cc, | |
| 7265 | .param_types = param_types, | |
| 7008 | 7266 | .zig_index = 0, |
| 7009 | 7267 | .llvm_index = 0, |
| 7010 | 7268 | .types_len = undefined, |
| ... | ... | @@ -7035,35 +7293,34 @@ pub const FnReturnStrat = union(enum) { |
| 7035 | 7293 | /// In order to support the C calling convention, some return types need to be lowered |
| 7036 | 7294 | /// completely differently in the function prototype to honor the C ABI, and then |
| 7037 | 7295 | /// be effectively bitcasted to the actual return type. |
| 7038 | pub fn fnReturnStrat(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!FnReturnStrat { | |
| 7296 | pub fn fnReturnStrat(o: *Object, cc: std.lang.CallingConvention, ret_ty: Type) Allocator.Error!FnReturnStrat { | |
| 7039 | 7297 | const zcu = o.zcu; |
| 7040 | const ret_ty: Type = .fromInterned(fn_info.return_type); | |
| 7041 | 7298 | ret_ty.assertHasLayout(zcu); |
| 7042 | 7299 | if (!ret_ty.hasRuntimeBits(zcu)) return .void; |
| 7043 | switch (fn_info.cc) { | |
| 7300 | switch (cc) { | |
| 7044 | 7301 | .@"inline" => unreachable, |
| 7045 | 7302 | .auto => { |
| 7046 | if (isByRef(ret_ty, zcu)) return .sret; | |
| 7047 | ||
| 7303 | // Match the c calling convention in some cases to avoid llvm bugs. | |
| 7048 | 7304 | const target = zcu.getTarget(); |
| 7049 | if (target.cpu.arch.isX86() and | |
| 7050 | !target.cpu.has(.x86, .avx512f) and | |
| 7051 | ret_ty.totalVectorBits(zcu) >= 512) | |
| 7052 | { | |
| 7053 | // As of LLVM 18, passing a vector byval with fastcc that is 512 bits or more returns | |
| 7054 | // "512-bit vector arguments require 'avx512f' for AVX512" | |
| 7055 | return .sret; | |
| 7056 | } | |
| 7305 | 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)) { | |
| 7306 | 0 => .void, | |
| 7307 | 1...8 => .{ .mem_cast = .i8 }, | |
| 7308 | 9...16 => .{ .mem_cast = .i16 }, | |
| 7309 | 17...32 => .{ .mem_cast = .i32 }, | |
| 7310 | 33...64 => .{ .mem_cast = .double }, | |
| 7311 | else => .by_val, | |
| 7312 | }; | |
| 7057 | 7313 | |
| 7314 | if (isByRef(ret_ty, zcu)) return .sret; | |
| 7058 | 7315 | return .by_val; |
| 7059 | 7316 | }, |
| 7060 | .x86_64_sysv, .x86_64_x32 => return lowerSystemVFnRetTy(o, fn_info), | |
| 7061 | .x86_64_win => return lowerWin64FnRetTy(o, fn_info), | |
| 7317 | .x86_64_sysv, .x86_64_x32 => return fnReturnStrat_x86_64_sysv(o, ret_ty), | |
| 7318 | .x86_64_win => return fnReturnStrat_x86_64_win(o, ret_ty), | |
| 7062 | 7319 | .x86_stdcall => if (isScalar(zcu, ret_ty)) { |
| 7063 | 7320 | assert(!isByRef(ret_ty, zcu)); |
| 7064 | 7321 | return .by_val; |
| 7065 | 7322 | } else return .sret, |
| 7066 | .x86_fastcall => return lowerX86FastcallFnRetTy(o, zcu, ret_ty), | |
| 7323 | .x86_fastcall => return fnReturnStrat_x86_fastcall(o, zcu, ret_ty), | |
| 7067 | 7324 | .x86_sysv, .x86_win => return if (isByRef(ret_ty, zcu)) .sret else .by_val, |
| 7068 | 7325 | .aarch64_aapcs, .aarch64_aapcs_darwin, .aarch64_aapcs_win => switch (aarch64_c_abi.classifyType(ret_ty, zcu)) { |
| 7069 | 7326 | .memory => return .sret, |
| ... | ... | @@ -7124,7 +7381,7 @@ pub fn fnReturnStrat(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Err |
| 7124 | 7381 | } |
| 7125 | 7382 | } |
| 7126 | 7383 | |
| 7127 | fn lowerX86FastcallFnRetTy(o: *Object, zcu: *Zcu, ty: Type) Allocator.Error!FnReturnStrat { | |
| 7384 | fn fnReturnStrat_x86_fastcall(o: *Object, zcu: *Zcu, ty: Type) Allocator.Error!FnReturnStrat { | |
| 7128 | 7385 | if (isScalar(zcu, ty)) { |
| 7129 | 7386 | assert(!isByRef(ty, zcu)); |
| 7130 | 7387 | return .by_val; |
| ... | ... | @@ -7139,9 +7396,8 @@ fn lowerX86FastcallFnRetTy(o: *Object, zcu: *Zcu, ty: Type) Allocator.Error!FnRe |
| 7139 | 7396 | return .sret; |
| 7140 | 7397 | } |
| 7141 | 7398 | |
| 7142 | fn lowerWin64FnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!FnReturnStrat { | |
| 7399 | fn fnReturnStrat_x86_64_win(o: *Object, ret_ty: Type) Allocator.Error!FnReturnStrat { | |
| 7143 | 7400 | const zcu = o.zcu; |
| 7144 | const ret_ty = Type.fromInterned(fn_info.return_type); | |
| 7145 | 7401 | switch (x86_64_abi.classifyWindows(ret_ty, zcu, zcu.getTarget(), .ret)) { |
| 7146 | 7402 | .integer => if (isScalar(zcu, ret_ty)) { |
| 7147 | 7403 | assert(!isByRef(ret_ty, zcu)); |
| ... | ... | @@ -7174,78 +7430,65 @@ fn lowerWin64FnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Err |
| 7174 | 7430 | } |
| 7175 | 7431 | } |
| 7176 | 7432 | |
| 7177 | fn lowerSystemVFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.Error!FnReturnStrat { | |
| 7433 | fn fnReturnStrat_x86_64_sysv(o: *Object, ret_ty: Type) Allocator.Error!FnReturnStrat { | |
| 7178 | 7434 | const zcu = o.zcu; |
| 7179 | const ip = &zcu.intern_pool; | |
| 7180 | const ret_ty = Type.fromInterned(fn_info.return_type); | |
| 7181 | if (isScalar(zcu, ret_ty)) { | |
| 7182 | assert(!isByRef(ret_ty, zcu)); | |
| 7183 | return .by_val; | |
| 7184 | } | |
| 7185 | 7435 | const classes = x86_64_abi.classifySystemV(ret_ty, zcu, zcu.getTarget(), .ret); |
| 7186 | var types_index: u32 = 0; | |
| 7187 | 7436 | var types_buffer: [8]Builder.Type = undefined; |
| 7188 | for (classes) |class| { | |
| 7189 | switch (class) { | |
| 7190 | .integer => { | |
| 7191 | types_buffer[types_index] = .i64; | |
| 7192 | types_index += 1; | |
| 7193 | }, | |
| 7194 | .sse => { | |
| 7195 | types_buffer[types_index] = .double; | |
| 7196 | types_index += 1; | |
| 7197 | }, | |
| 7198 | .sseup => { | |
| 7199 | if (types_buffer[types_index - 1] == .double) { | |
| 7200 | types_buffer[types_index - 1] = .fp128; | |
| 7201 | } else { | |
| 7202 | types_buffer[types_index] = .double; | |
| 7203 | types_index += 1; | |
| 7204 | } | |
| 7205 | }, | |
| 7206 | .float => { | |
| 7207 | types_buffer[types_index] = .float; | |
| 7208 | types_index += 1; | |
| 7209 | }, | |
| 7210 | .float_combine => { | |
| 7211 | types_buffer[types_index] = try o.builder.vectorType(.normal, 2, .float); | |
| 7212 | types_index += 1; | |
| 7213 | }, | |
| 7214 | .x87 => { | |
| 7215 | if (types_index != 0 or classes[2] != .none) return .sret; | |
| 7216 | types_buffer[types_index] = .x86_fp80; | |
| 7217 | types_index += 1; | |
| 7218 | }, | |
| 7219 | .x87up => continue, | |
| 7220 | .none => break, | |
| 7221 | .memory => return .sret, | |
| 7222 | .win_i128 => unreachable, // windows only | |
| 7223 | .bool_vector_mask, | |
| 7224 | .integer_per_element, | |
| 7225 | .sse_per_element, | |
| 7226 | .sse_sse_x87_per_qword, | |
| 7227 | .sse_per_xword, | |
| 7228 | .sse_per_yword, | |
| 7229 | .sse_per_zword, | |
| 7230 | => unreachable, // vectors already handled by `isScalar` above | |
| 7231 | } | |
| 7232 | } | |
| 7233 | const first_non_integer = std.mem.indexOfNone(x86_64_abi.Class, &classes, &.{.integer}); | |
| 7234 | if (first_non_integer == null or classes[first_non_integer.?] == .none) { | |
| 7235 | assert(first_non_integer orelse classes.len == types_index); | |
| 7236 | switch (ip.indexToKey(ret_ty.toIntern())) { | |
| 7237 | .struct_type => { | |
| 7238 | const size = ret_ty.abiSize(zcu); | |
| 7239 | assert(@divCeil(size, 8) == types_index); | |
| 7240 | if (size % 8 > 0) { | |
| 7241 | types_buffer[types_index - 1] = try o.builder.intType(@intCast(size % 8 * 8)); | |
| 7242 | } | |
| 7243 | }, | |
| 7244 | else => {}, | |
| 7245 | } | |
| 7246 | if (types_index == 1) return .{ .mem_cast = types_buffer[0] }; | |
| 7437 | var types_len: u32 = 0; | |
| 7438 | for (classes, 0..) |class, class_index| switch (class) { | |
| 7439 | .integer => { | |
| 7440 | types_buffer[types_len] = try o.builder.intType(@min(8 * ret_ty.abiSize(zcu) - 64 * class_index, 64)); | |
| 7441 | types_len += 1; | |
| 7442 | }, | |
| 7443 | .sse => { | |
| 7444 | types_buffer[types_len] = .double; | |
| 7445 | types_len += 1; | |
| 7446 | }, | |
| 7447 | .sseup => { | |
| 7448 | if (types_buffer[types_len - 1] == .double) { | |
| 7449 | if (ret_ty.isVector(zcu)) return .by_val; | |
| 7450 | types_buffer[types_len - 1] = .fp128; | |
| 7451 | } else { | |
| 7452 | types_buffer[types_len] = .double; | |
| 7453 | types_len += 1; | |
| 7454 | } | |
| 7455 | }, | |
| 7456 | .float => { | |
| 7457 | types_buffer[types_len] = .float; | |
| 7458 | types_len += 1; | |
| 7459 | }, | |
| 7460 | .float_combine => { | |
| 7461 | types_buffer[types_len] = try o.builder.vectorType(.normal, 2, .float); | |
| 7462 | types_len += 1; | |
| 7463 | }, | |
| 7464 | .x87 => { | |
| 7465 | if (types_len > 0 or classes[2] != .none) return .sret; | |
| 7466 | types_buffer[types_len] = .x86_fp80; | |
| 7467 | types_len += 1; | |
| 7468 | }, | |
| 7469 | .x87up => continue, | |
| 7470 | .none => break, | |
| 7471 | .memory => return if (ret_ty.isVector(zcu)) .by_val else .sret, | |
| 7472 | .win_i128 => unreachable, // windows only | |
| 7473 | .bool_vector_mask, | |
| 7474 | .integer_per_element, | |
| 7475 | .sse_per_element, | |
| 7476 | .sse_sse_x87_per_qword, | |
| 7477 | .sse_per_xword, | |
| 7478 | .sse_per_yword, | |
| 7479 | .sse_per_zword, | |
| 7480 | => return .by_val, | |
| 7481 | }; | |
| 7482 | if (types_len > 1) return .{ .mem_cast = try o.builder.structType(.normal, types_buffer[0..types_len]) }; | |
| 7483 | if (!isByRef(ret_ty, zcu)) { | |
| 7484 | const llvm_ty = try o.lowerType(ret_ty, .as_value); | |
| 7485 | if (types_buffer[0] == llvm_ty) return .by_val; | |
| 7486 | if (types_buffer[0] == .i64 and llvm_ty.isPointer(&o.builder)) return .by_val; | |
| 7487 | if (types_buffer[0] == .double and llvm_ty.isVector(&o.builder) and | |
| 7488 | llvm_ty.vectorLen(&o.builder) == 1 and | |
| 7489 | llvm_ty.scalarType(&o.builder) == .double) return .by_val; | |
| 7247 | 7490 | } |
| 7248 | return .{ .mem_cast = try o.builder.structType(.normal, types_buffer[0..types_index]) }; | |
| 7491 | return .{ .mem_cast = types_buffer[0] }; | |
| 7249 | 7492 | } |
| 7250 | 7493 | |
| 7251 | 7494 | /// 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 |
| 7258 | 7501 | else => {}, |
| 7259 | 7502 | } |
| 7260 | 7503 | |
| 7261 | const ty_tag = ty.zigTypeTag(zcu); | |
| 7262 | const int_info = switch (ty_tag) { | |
| 7263 | .bool => Type.u1.intInfo(zcu), | |
| 7264 | else => if (ty.isAbiInt(zcu)) ty.intInfo(zcu) else return null, | |
| 7265 | }; | |
| 7504 | const target = zcu.getTarget(); | |
| 7505 | const int_info: std.lang.Type.Int = if (ty.toIntern() == .bool_type) | |
| 7506 | .{ .signedness = .unsigned, .bits = 1 } | |
| 7507 | else if (ty.isAbiInt(zcu)) | |
| 7508 | ty.intInfo(zcu) | |
| 7509 | else if (ty.isRuntimeFloat()) switch (ty.floatBits(target)) { | |
| 7510 | else => unreachable, | |
| 7511 | 16, 32, 64 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { | |
| 7512 | .hard => return null, | |
| 7513 | .soft => .{ .signedness = .unsigned, .bits = bits }, | |
| 7514 | }, | |
| 7515 | 80, 128 => return null, | |
| 7516 | } else return null; | |
| 7266 | 7517 | |
| 7267 | assert(int_info.bits == 0 or (int_info.bits == 1 and ty_tag == .bool) or std.math.isPowerOfTwo(int_info.bits)); | |
| 7518 | assert(int_info.bits == 0 or (int_info.bits == 1 and ty.toIntern() == .bool_type) or std.math.isPowerOfTwo(int_info.bits)); | |
| 7268 | 7519 | |
| 7269 | const target = zcu.getTarget(); | |
| 7270 | 7520 | return switch (target.cpu.arch) { |
| 7271 | 7521 | .aarch64, |
| 7272 | 7522 | .aarch64_be, |
| ... | ... | @@ -7362,15 +7612,26 @@ pub fn isByRef(ty: Type, zcu: *const Zcu) bool { |
| 7362 | 7612 | .void, |
| 7363 | 7613 | .bool, |
| 7364 | 7614 | .int, |
| 7365 | .float, | |
| 7366 | 7615 | .pointer, |
| 7367 | 7616 | .error_set, |
| 7368 | 7617 | .@"fn", |
| 7369 | 7618 | .@"enum", |
| 7370 | .vector, | |
| 7371 | 7619 | .@"anyframe", |
| 7372 | 7620 | => false, |
| 7373 | 7621 | |
| 7622 | .float, .vector => { | |
| 7623 | const target = zcu.getTarget(); | |
| 7624 | const scalar_ty = ty.scalarType(zcu); | |
| 7625 | return if (scalar_ty.isRuntimeFloat()) switch (scalar_ty.floatBits(target)) { | |
| 7626 | else => unreachable, | |
| 7627 | 16, 32, 64 => false, | |
| 7628 | 80, 128 => |bits| switch (std.zig.target.compilerRtFloatAbi(target, bits)) { | |
| 7629 | .hard => false, | |
| 7630 | .soft => true, | |
| 7631 | }, | |
| 7632 | } else false; | |
| 7633 | }, | |
| 7634 | ||
| 7374 | 7635 | .array, |
| 7375 | 7636 | .frame, |
| 7376 | 7637 | => ty.hasRuntimeBits(zcu), |
| ... | ... | @@ -7431,12 +7692,19 @@ fn ptraddScaled(fg: *FuncGen, ptr: Builder.Value, index: Builder.Value, scale: u |
| 7431 | 7692 | return fg.wip.gep(.inbounds, llvm_scale_ty, ptr, &.{index}, ""); |
| 7432 | 7693 | } |
| 7433 | 7694 | |
| 7434 | fn compilerRtIntBits(bits: u16) ?u16 { | |
| 7435 | inline for (.{ 32, 64, 128 }) |b| { | |
| 7436 | if (bits <= b) { | |
| 7437 | return b; | |
| 7438 | } | |
| 7439 | } | |
| 7695 | fn compilerRtPromoteInt(int_info: InternPool.Key.IntType) ?Type { | |
| 7696 | if (int_info.bits <= 32) return switch (int_info.signedness) { | |
| 7697 | .signed => .i32, | |
| 7698 | .unsigned => .u32, | |
| 7699 | }; | |
| 7700 | if (int_info.bits <= 64) return switch (int_info.signedness) { | |
| 7701 | .signed => .i64, | |
| 7702 | .unsigned => .u64, | |
| 7703 | }; | |
| 7704 | if (int_info.bits <= 128) return switch (int_info.signedness) { | |
| 7705 | .signed => .i128, | |
| 7706 | .unsigned => .u128, | |
| 7707 | }; | |
| 7440 | 7708 | return null; |
| 7441 | 7709 | } |
| 7442 | 7710 | |
| ... | ... | @@ -7495,13 +7763,12 @@ fn appendConstraints( |
| 7495 | 7763 | } |
| 7496 | 7764 | |
| 7497 | 7765 | /// LLVM does not support all relevant intrinsics for all targets, so we |
| 7498 | /// may need to manually generate a compiler-rt call. | |
| 7766 | /// may need to manually generate a compiler-rt call using a soft type. | |
| 7499 | 7767 | fn intrinsicsAllowed(scalar_ty: Type, target: *const std.Target) bool { |
| 7500 | return switch (scalar_ty.toIntern()) { | |
| 7501 | .f16_type => llvm.backendSupportsF16(target), | |
| 7502 | .f80_type => (target.cTypeBitSize(.longdouble) == 80) and llvm.backendSupportsF80(target), | |
| 7503 | .f128_type => (target.cTypeBitSize(.longdouble) == 128) and llvm.backendSupportsF128(target), | |
| 7504 | else => true, | |
| 7768 | if (!scalar_ty.isRuntimeFloat()) return true; | |
| 7769 | return switch (std.zig.target.compilerRtFloatAbi(target, scalar_ty.floatBits(target))) { | |
| 7770 | .hard => true, | |
| 7771 | .soft => false, | |
| 7505 | 7772 | }; |
| 7506 | 7773 | } |
| 7507 | 7774 |
src/codegen/mips/abi.zig+8-1| ... | ... | @@ -38,7 +38,14 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { |
| 38 | 38 | return .byval; |
| 39 | 39 | }, |
| 40 | 40 | .bool => return .byval, |
| 41 | .float => return .byval, | |
| 41 | .float => return switch (ty.floatBits(target)) { | |
| 42 | else => unreachable, | |
| 43 | 16, 32, 64 => .byval, | |
| 44 | 80, 128 => switch (max_direct_size) { | |
| 45 | else => unreachable, | |
| 46 | 64 => .memory, | |
| 47 | }, | |
| 48 | }, | |
| 42 | 49 | .int, .@"enum", .error_set => { |
| 43 | 50 | return .byval; |
| 44 | 51 | }, |
src/codegen/riscv64/CodeGen.zig+2-2| ... | ... | @@ -5036,7 +5036,7 @@ fn airRet(func: *Func, inst: Air.Inst.Index, safety: bool) !void { |
| 5036 | 5036 | .register_pair, |
| 5037 | 5037 | => { |
| 5038 | 5038 | if (ret_ty.isVector(zcu)) { |
| 5039 | const bit_size = ret_ty.totalVectorBits(zcu); | |
| 5039 | const bit_size = ret_ty.bitSize(zcu); | |
| 5040 | 5040 | |
| 5041 | 5041 | // set the vtype to hold the entire vector's contents in a single element |
| 5042 | 5042 | try func.setVl(.zero, 0, .{ |
| ... | ... | @@ -6871,7 +6871,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 6871 | 6871 | // size to the total size of the vector, and vmv.x.s will work then |
| 6872 | 6872 | if (src_reg.class() == .vector) { |
| 6873 | 6873 | try func.setVl(.zero, 0, .{ |
| 6874 | .vsew = switch (ty.totalVectorBits(zcu)) { | |
| 6874 | .vsew = switch (ty.bitSize(zcu)) { | |
| 6875 | 6875 | 8 => .@"8", |
| 6876 | 6876 | 16 => .@"16", |
| 6877 | 6877 | 32 => .@"32", |
src/codegen/riscv64/abi.zig+10-2| ... | ... | @@ -56,12 +56,20 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class { |
| 56 | 56 | return .integer; |
| 57 | 57 | }, |
| 58 | 58 | .bool => return .integer, |
| 59 | .float => return .byval, | |
| 60 | 59 | .int, .@"enum", .error_set => { |
| 61 | 60 | const bit_size = ty.bitSize(zcu); |
| 62 | 61 | if (bit_size > max_byval_size) return .memory; |
| 63 | 62 | return .byval; |
| 64 | 63 | }, |
| 64 | .float => return switch (ty.floatBits(target)) { | |
| 65 | else => unreachable, | |
| 66 | 16, 32, 64, 128 => .byval, | |
| 67 | 80 => switch (max_byval_size) { | |
| 68 | else => unreachable, | |
| 69 | 64 => .memory, | |
| 70 | 128 => .double_integer, | |
| 71 | }, | |
| 72 | }, | |
| 65 | 73 | .vector => { |
| 66 | 74 | const bit_size = ty.bitSize(zcu); |
| 67 | 75 | if (bit_size > max_byval_size) return .memory; |
| ... | ... | @@ -190,7 +198,7 @@ pub fn classifySystem(ty: Type, zcu: *Zcu) [8]SystemClass { |
| 190 | 198 | }, |
| 191 | 199 | .vector => { |
| 192 | 200 | // we pass vectors through integer registers if they are small enough to fit. |
| 193 | const vec_bits = ty.totalVectorBits(zcu); | |
| 201 | const vec_bits = ty.bitSize(zcu); | |
| 194 | 202 | if (vec_bits <= 64) { |
| 195 | 203 | result[0] = .integer; |
| 196 | 204 | return result; |
src/codegen/s390x/abi.zig+5-3| ... | ... | @@ -38,9 +38,11 @@ pub fn classifyType(ty: Type, context: Context, zcu: *Zcu) Class { |
| 38 | 38 | 1...64 => .simple, |
| 39 | 39 | else => .pointer, |
| 40 | 40 | }, |
| 41 | .float => return switch (ty.floatBits(zcu.getTarget())) { | |
| 42 | 16, 32, 64 => .double_or_float, | |
| 43 | else => .pointer, | |
| 41 | .float => switch (ty.floatBits(zcu.getTarget())) { | |
| 42 | else => unreachable, | |
| 43 | 16, 32, 64 => return .double_or_float, | |
| 44 | 80 => {}, | |
| 45 | 128 => return .pointer, | |
| 44 | 46 | }, |
| 45 | 47 | .pointer, .optional => return .simple, |
| 46 | 48 | .array => switch (ty.arrayLen(zcu)) { |
src/codegen/wasm/CodeGen.zig+9-15| ... | ... | @@ -24,12 +24,6 @@ const Alignment = InternPool.Alignment; |
| 24 | 24 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 25 | 25 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 26 | 26 | |
| 27 | const target_util = @import("../../target.zig"); | |
| 28 | const libcFloatPrefix = target_util.libcFloatPrefix; | |
| 29 | const libcFloatSuffix = target_util.libcFloatSuffix; | |
| 30 | const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev; | |
| 31 | const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; | |
| 32 | ||
| 33 | 27 | pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { |
| 34 | 28 | return comptime &.initMany(&.{ |
| 35 | 29 | .expand_bit_cast_safe, |
| ... | ... | @@ -2515,15 +2509,15 @@ const IntType = struct { |
| 2515 | 2509 | .anyerror, .adhoc_inferred_error_set => .{ .is_signed = false, .bits = zcu.errorSetBits() }, |
| 2516 | 2510 | .isize => .{ .is_signed = true, .bits = cg.target.ptrBitWidth() }, |
| 2517 | 2511 | .usize => .{ .is_signed = false, .bits = cg.target.ptrBitWidth() }, |
| 2518 | .c_char => .{ .is_signed = cg.target.cCharSignedness() == .signed, .bits = cg.target.cTypeBitSize(.char) }, | |
| 2519 | .c_short => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.short) }, | |
| 2520 | .c_ushort => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.short) }, | |
| 2521 | .c_int => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.int) }, | |
| 2522 | .c_uint => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.int) }, | |
| 2523 | .c_long => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.long) }, | |
| 2524 | .c_ulong => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.long) }, | |
| 2525 | .c_longlong => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.longlong) }, | |
| 2526 | .c_ulonglong => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.longlong) }, | |
| 2512 | .c_char => .{ .is_signed = cg.target.cCharSignedness().? == .signed, .bits = cg.target.cTypeBitSize(.char).? }, | |
| 2513 | .c_short => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.short).? }, | |
| 2514 | .c_ushort => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.short).? }, | |
| 2515 | .c_int => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.int).? }, | |
| 2516 | .c_uint => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.int).? }, | |
| 2517 | .c_long => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.long).? }, | |
| 2518 | .c_ulong => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.long).? }, | |
| 2519 | .c_longlong => .{ .is_signed = true, .bits = cg.target.cTypeBitSize(.longlong).? }, | |
| 2520 | .c_ulonglong => .{ .is_signed = false, .bits = cg.target.cTypeBitSize(.longlong).? }, | |
| 2527 | 2521 | .f16, .f32, .f64, .f80, .f128, .c_longdouble => unreachable, |
| 2528 | 2522 | .anyopaque, .void, .type, .comptime_int, .comptime_float, .noreturn, .null, .undefined, .enum_literal, .generic_poison => unreachable, |
| 2529 | 2523 | }, |
src/codegen/wasm/abi.zig+5-1| ... | ... | @@ -25,7 +25,11 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class { |
| 25 | 25 | assert(ty.hasRuntimeBits(zcu)); |
| 26 | 26 | switch (ty.zigTypeTag(zcu)) { |
| 27 | 27 | .int, .@"enum", .error_set => return .{ .direct = ty }, |
| 28 | .float => return .{ .direct = ty }, | |
| 28 | .float => return switch (ty.floatBits(zcu.getTarget())) { | |
| 29 | else => unreachable, | |
| 30 | 16, 32, 64, 128 => .{ .direct = ty }, | |
| 31 | 80 => .indirect, | |
| 32 | }, | |
| 29 | 33 | .bool => return .{ .direct = ty }, |
| 30 | 34 | .vector => return .{ .direct = ty }, |
| 31 | 35 | .array => return .indirect, |
src/codegen/x86_64/CodeGen.zig+9-9| ... | ... | @@ -182636,15 +182636,15 @@ fn intInfo(cg: *CodeGen, ty: Type) ?std.lang.Type.Int { |
| 182636 | 182636 | .anyerror => .{ .signedness = .unsigned, .bits = zcu.errorSetBits() }, |
| 182637 | 182637 | .isize => .{ .signedness = .signed, .bits = cg.target.ptrBitWidth() }, |
| 182638 | 182638 | .usize => .{ .signedness = .unsigned, .bits = cg.target.ptrBitWidth() }, |
| 182639 | .c_char => .{ .signedness = cg.target.cCharSignedness(), .bits = cg.target.cTypeBitSize(.char) }, | |
| 182640 | .c_short => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.short) }, | |
| 182641 | .c_ushort => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.short) }, | |
| 182642 | .c_int => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.int) }, | |
| 182643 | .c_uint => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.int) }, | |
| 182644 | .c_long => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.long) }, | |
| 182645 | .c_ulong => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.long) }, | |
| 182646 | .c_longlong => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.longlong) }, | |
| 182647 | .c_ulonglong => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.longlong) }, | |
| 182639 | .c_char => .{ .signedness = cg.target.cCharSignedness().?, .bits = cg.target.cTypeBitSize(.char).? }, | |
| 182640 | .c_short => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.short).? }, | |
| 182641 | .c_ushort => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.short).? }, | |
| 182642 | .c_int => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.int).? }, | |
| 182643 | .c_uint => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.int).? }, | |
| 182644 | .c_long => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.long).? }, | |
| 182645 | .c_ulong => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.long).? }, | |
| 182646 | .c_longlong => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.longlong).? }, | |
| 182647 | .c_ulonglong => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.longlong).? }, | |
| 182648 | 182648 | .f16, .f32, .f64, .f80, .f128, .c_longdouble => null, |
| 182649 | 182649 | .anyopaque, |
| 182650 | 182650 | .void, |
src/codegen/x86_64/abi.zig+7-5| ... | ... | @@ -133,7 +133,7 @@ pub fn classifyWindows(init_ty: Type, zcu: *Zcu, target: *const std.Target, ctx: |
| 133 | 133 | .float => switch (ty.floatBits(target)) { |
| 134 | 134 | 16, 32, 64 => .sse, |
| 135 | 135 | 80 => .memory, |
| 136 | 128 => if (ctx == .arg) .memory else .sse, | |
| 136 | 128 => .win_i128, | |
| 137 | 137 | else => unreachable, |
| 138 | 138 | }, |
| 139 | 139 | .vector => { |
| ... | ... | @@ -238,16 +238,18 @@ pub fn classifySystemV(ty: Type, zcu: *Zcu, target: *const std.Target, ctx: Cont |
| 238 | 238 | }; |
| 239 | 239 | const unaligned_size = elem_ty.abiSize(zcu) * len; |
| 240 | 240 | if (unaligned_size <= 4) return Class.one_integer; |
| 241 | if (ctx == .arg and unaligned_size == 8 * 1 * 1 and len == 1 and | |
| 242 | elem_ty.isRuntimeFloat()) return Class.stack; // what | |
| 241 | if (unaligned_size == 8 * 1 * 1 and len == 1) { | |
| 242 | if (ctx == .arg and elem_ty.isRuntimeFloat()) return Class.stack; // what? | |
| 243 | if (ctx != .other and !elem_ty.isRuntimeFloat() and target.os.tag == .freebsd) return Class.one_integer; // who? | |
| 244 | } | |
| 243 | 245 | if (unaligned_size <= 8 * 1) return .{ .sse, .none, .none, .none, .none, .none, .none, .none }; |
| 244 | 246 | if (unaligned_size <= 8 * 2) return .{ .sse, .sseup, .none, .none, .none, .none, .none, .none }; |
| 245 | 247 | if (!target.cpu.has(.x86, .avx)) { |
| 246 | 248 | if (ctx == .ret) switch (unaligned_size) { |
| 247 | 249 | else => {}, |
| 248 | 250 | 8 * 3 => if (len == 3) return if (elem_ty.isRuntimeFloat()) .{ |
| 249 | .sse_sse_x87_per_qword, .none, .none, .none, .none, .none, .none, .none, // how | |
| 250 | } else Class.len_integers, // why | |
| 251 | .sse_sse_x87_per_qword, .none, .none, .none, .none, .none, .none, .none, // how? | |
| 252 | } else Class.len_integers, // why? | |
| 251 | 253 | 8 * 2 * 2, 8 * 2 * 4 => return .{ .sse_per_xword, .none, .none, .none, .none, .none, .none, .none }, |
| 252 | 254 | }; |
| 253 | 255 | return Class.stack; |
src/libs/mingw/Preprocessor.zig+2-2| ... | ... | @@ -91,9 +91,9 @@ fn addTokenAssumeCapacity(pp: *Preprocessor, tok: Token) void { |
| 91 | 91 | |
| 92 | 92 | fn defineBuiltins(pp: *Preprocessor) !void { |
| 93 | 93 | var buf: [5]u8 = undefined; |
| 94 | var val = std.fmt.bufPrint(&buf, "{d}", .{pp.target.cTypeBitSize(.longdouble)}) catch unreachable; | |
| 94 | var val = std.fmt.bufPrint(&buf, "{d}", .{pp.target.cTypeByteSize(.longdouble).?}) catch unreachable; | |
| 95 | 95 | try pp.defineBuiltinValue("__SIZEOF_LONG_DOUBLE__", val, .pp_num); |
| 96 | val = std.fmt.bufPrint(&buf, "{d}", .{pp.target.cTypeBitSize(.double)}) catch unreachable; | |
| 96 | val = std.fmt.bufPrint(&buf, "{d}", .{pp.target.cTypeByteSize(.double).?}) catch unreachable; | |
| 97 | 97 | try pp.defineBuiltinValue("__SIZEOF_DOUBLE__", val, .pp_num); |
| 98 | 98 | |
| 99 | 99 | if (pp.target.abi.isGnu()) { |
src/target.zig+2-2| ... | ... | @@ -881,13 +881,13 @@ pub fn libcFloatSuffix(float_bits: u16) []const u8 { |
| 881 | 881 | }; |
| 882 | 882 | } |
| 883 | 883 | |
| 884 | pub fn compilerRtFloatAbbrev(float_bits: u16) []const u8 { | |
| 884 | pub fn compilerRtFloatAbbrev(target: *const std.Target, float_bits: u16) []const u8 { | |
| 885 | 885 | return switch (float_bits) { |
| 886 | 886 | 16 => "h", |
| 887 | 887 | 32 => "s", |
| 888 | 888 | 64 => "d", |
| 889 | 889 | 80 => "x", |
| 890 | 128 => "t", | |
| 890 | 128 => if (target.cpu.arch.isPowerPC()) "k" else "t", | |
| 891 | 891 | else => unreachable, |
| 892 | 892 | }; |
| 893 | 893 | } |
test/behavior/align.zig+32-10| ... | ... | @@ -129,8 +129,7 @@ test "alignment and size of structs with 128-bit fields" { |
| 129 | 129 | y: u8, |
| 130 | 130 | }; |
| 131 | 131 | const expected = switch (builtin.cpu.arch) { |
| 132 | .s390x, | |
| 133 | => .{ | |
| 132 | .s390x => .{ | |
| 134 | 133 | .a_align = 8, |
| 135 | 134 | .a_size = 16, |
| 136 | 135 | |
| ... | ... | @@ -142,7 +141,32 @@ test "alignment and size of structs with 128-bit fields" { |
| 142 | 141 | .u129_align = 8, |
| 143 | 142 | .u129_size = 24, |
| 144 | 143 | }, |
| 145 | ||
| 144 | .x86 => switch (builtin.os.tag) { | |
| 145 | else => .{ | |
| 146 | .a_align = 4, | |
| 147 | .a_size = 16, | |
| 148 | ||
| 149 | .b_align = 4, | |
| 150 | .b_size = 20, | |
| 151 | ||
| 152 | .u128_align = 4, | |
| 153 | .u128_size = 16, | |
| 154 | .u129_align = 4, | |
| 155 | .u129_size = 20, | |
| 156 | }, | |
| 157 | .uefi, .windows => .{ | |
| 158 | .a_align = 8, | |
| 159 | .a_size = 16, | |
| 160 | ||
| 161 | .b_align = 8, | |
| 162 | .b_size = 24, | |
| 163 | ||
| 164 | .u128_align = 8, | |
| 165 | .u128_size = 16, | |
| 166 | .u129_align = 8, | |
| 167 | .u129_size = 24, | |
| 168 | }, | |
| 169 | }, | |
| 146 | 170 | .amdgcn, |
| 147 | 171 | .arm, |
| 148 | 172 | .armeb, |
| ... | ... | @@ -155,12 +179,13 @@ test "alignment and size of structs with 128-bit fields" { |
| 155 | 179 | .powerpc, |
| 156 | 180 | .powerpcle, |
| 157 | 181 | .riscv32, |
| 182 | .sparc, | |
| 158 | 183 | => .{ |
| 159 | 184 | .a_align = 8, |
| 160 | 185 | .a_size = 16, |
| 161 | 186 | |
| 162 | .b_align = 16, | |
| 163 | .b_size = 32, | |
| 187 | .b_align = 8, | |
| 188 | .b_size = 24, | |
| 164 | 189 | |
| 165 | 190 | .u128_align = 8, |
| 166 | 191 | .u128_size = 16, |
| ... | ... | @@ -178,12 +203,10 @@ test "alignment and size of structs with 128-bit fields" { |
| 178 | 203 | .nvptx64, |
| 179 | 204 | .powerpc64, |
| 180 | 205 | .powerpc64le, |
| 181 | .sparc, | |
| 182 | 206 | .sparc64, |
| 183 | 207 | .riscv64, |
| 184 | 208 | .wasm32, |
| 185 | 209 | .wasm64, |
| 186 | .x86, | |
| 187 | 210 | .x86_64, |
| 188 | 211 | => .{ |
| 189 | 212 | .a_align = 16, |
| ... | ... | @@ -200,12 +223,11 @@ test "alignment and size of structs with 128-bit fields" { |
| 200 | 223 | |
| 201 | 224 | else => return error.SkipZigTest, |
| 202 | 225 | }; |
| 203 | const min_struct_align = if (builtin.zig_backend == .stage2_c) if (builtin.cpu.arch == .s390x) 8 else 16 else 0; | |
| 204 | 226 | comptime { |
| 205 | assert(@alignOf(A) == @max(expected.a_align, min_struct_align)); | |
| 227 | assert(@alignOf(A) == expected.a_align); | |
| 206 | 228 | assert(@sizeOf(A) == expected.a_size); |
| 207 | 229 | |
| 208 | assert(@alignOf(B) == @max(expected.b_align, min_struct_align)); | |
| 230 | assert(@alignOf(B) == expected.b_align); | |
| 209 | 231 | assert(@sizeOf(B) == expected.b_size); |
| 210 | 232 | |
| 211 | 233 | assert(@alignOf(u128) == expected.u128_align); |
test/behavior/cast.zig+2-1| ... | ... | @@ -181,6 +181,7 @@ test "@floatFromInt(f80)" { |
| 181 | 181 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 182 | 182 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 183 | 183 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 184 | if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; | |
| 184 | 185 | |
| 185 | 186 | const S = struct { |
| 186 | 187 | fn doTheTest(comptime Int: type) !void { |
| ... | ... | @@ -204,7 +205,7 @@ test "@floatFromInt(f80)" { |
| 204 | 205 | try S.doTheTest(i64); |
| 205 | 206 | try S.doTheTest(i80); |
| 206 | 207 | try S.doTheTest(i128); |
| 207 | // try S.doTheTest(i256); // TODO missing compiler_rt symbols | |
| 208 | try S.doTheTest(i256); | |
| 208 | 209 | try comptime S.doTheTest(i31); |
| 209 | 210 | try comptime S.doTheTest(i32); |
| 210 | 211 | try comptime S.doTheTest(i45); |
test/behavior/floatop.zig-22| ... | ... | @@ -118,7 +118,6 @@ fn testMul(comptime T: type) !void { |
| 118 | 118 | test "cmp f16" { |
| 119 | 119 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 120 | 120 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 121 | if (builtin.cpu.arch.isArm() and builtin.target.abi.float() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 | |
| 122 | 121 | |
| 123 | 122 | try testCmp(f16); |
| 124 | 123 | try comptime testCmp(f16); |
| ... | ... | @@ -127,7 +126,6 @@ test "cmp f16" { |
| 127 | 126 | test "cmp f32" { |
| 128 | 127 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 129 | 128 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 130 | if (builtin.cpu.arch.isArm() and builtin.target.abi.float() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 | |
| 131 | 129 | |
| 132 | 130 | try testCmp(f32); |
| 133 | 131 | try comptime testCmp(f32); |
| ... | ... | @@ -1173,11 +1171,6 @@ test "@floor f80/f128/c_longdouble" { |
| 1173 | 1171 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 1174 | 1172 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 1175 | 1173 | |
| 1176 | if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { | |
| 1177 | // https://github.com/ziglang/zig/issues/12602 | |
| 1178 | return error.SkipZigTest; | |
| 1179 | } | |
| 1180 | ||
| 1181 | 1174 | try testFloor(f80); |
| 1182 | 1175 | try comptime testFloor(f80); |
| 1183 | 1176 | try testFloor(f128); |
| ... | ... | @@ -1261,11 +1254,6 @@ test "@ceil f80/f128/c_longdouble" { |
| 1261 | 1254 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 1262 | 1255 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 1263 | 1256 | |
| 1264 | if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { | |
| 1265 | // https://github.com/ziglang/zig/issues/12602 | |
| 1266 | return error.SkipZigTest; | |
| 1267 | } | |
| 1268 | ||
| 1269 | 1257 | try testCeil(f80); |
| 1270 | 1258 | try comptime testCeil(f80); |
| 1271 | 1259 | try testCeil(f128); |
| ... | ... | @@ -1280,11 +1268,6 @@ test "@ceil f80 maxInt(u64)" { |
| 1280 | 1268 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 1281 | 1269 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 1282 | 1270 | |
| 1283 | if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { | |
| 1284 | // https://github.com/ziglang/zig/issues/12602 | |
| 1285 | return error.SkipZigTest; | |
| 1286 | } | |
| 1287 | ||
| 1288 | 1271 | var x: u64 = std.math.maxInt(u64); |
| 1289 | 1272 | x = x; |
| 1290 | 1273 | const float: f80 = @floatFromInt(x); |
| ... | ... | @@ -1366,11 +1349,6 @@ test "@trunc f80/f128/c_longdouble" { |
| 1366 | 1349 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 1367 | 1350 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 1368 | 1351 | |
| 1369 | if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { | |
| 1370 | // https://github.com/ziglang/zig/issues/12602 | |
| 1371 | return error.SkipZigTest; | |
| 1372 | } | |
| 1373 | ||
| 1374 | 1352 | try testTrunc(f80); |
| 1375 | 1353 | try comptime testTrunc(f80); |
| 1376 | 1354 | try testTrunc(f128); |
test/behavior/math.zig-6| ... | ... | @@ -2142,11 +2142,6 @@ test "remainder division" { |
| 2142 | 2142 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 2143 | 2143 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 2144 | 2144 | |
| 2145 | if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { | |
| 2146 | // https://github.com/ziglang/zig/issues/12602 | |
| 2147 | return error.SkipZigTest; | |
| 2148 | } | |
| 2149 | ||
| 2150 | 2145 | if (builtin.zig_backend == .stage2_x86_64 and builtin.object_format == .coff and builtin.abi != .gnu) return error.SkipZigTest; |
| 2151 | 2146 | |
| 2152 | 2147 | try comptime remdiv(f16); |
| ... | ... | @@ -2337,7 +2332,6 @@ test "NaN comparison" { |
| 2337 | 2332 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 2338 | 2333 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 2339 | 2334 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 2340 | if (builtin.cpu.arch.isArm() and builtin.target.abi.float() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 | |
| 2341 | 2335 | |
| 2342 | 2336 | try testNanEqNan(f16); |
| 2343 | 2337 | try testNanEqNan(f32); |
test/behavior/vector.zig+19-5| ... | ... | @@ -774,6 +774,8 @@ test "vector reduce operation" { |
| 774 | 774 | try testReduce(.Add, [4]f16{ -1.9, 5.1, -60.3, 100.0 }, @as(f16, 42.9)); |
| 775 | 775 | try testReduce(.Add, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 42.9)); |
| 776 | 776 | try testReduce(.Add, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 42.9)); |
| 777 | try testReduce(.Add, [4]f80{ -1.9, 5.1, -60.3, 100.0 }, @as(f80, 42.9)); | |
| 778 | try testReduce(.Add, [4]f128{ -1.9, 5.1, -60.3, 100.0 }, @as(f128, 42.9)); | |
| 777 | 779 | |
| 778 | 780 | try testReduce(.And, [4]bool{ true, false, true, true }, @as(bool, false)); |
| 779 | 781 | try testReduce(.And, [4]u1{ 1, 0, 1, 1 }, @as(u1, 0)); |
| ... | ... | @@ -792,6 +794,8 @@ test "vector reduce operation" { |
| 792 | 794 | try testReduce(.Min, [4]f16{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f16, -100.0)); |
| 793 | 795 | try testReduce(.Min, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, -100.0)); |
| 794 | 796 | try testReduce(.Min, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, -100.0)); |
| 797 | try testReduce(.Min, [4]f80{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f80, -100.0)); | |
| 798 | try testReduce(.Min, [4]f128{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f128, -100.0)); | |
| 795 | 799 | |
| 796 | 800 | try testReduce(.Max, [4]i16{ -1, 2, 3, 4 }, @as(i16, 4)); |
| 797 | 801 | try testReduce(.Max, [4]u16{ 1, 2, 3, 4 }, @as(u16, 4)); |
| ... | ... | @@ -804,6 +808,8 @@ test "vector reduce operation" { |
| 804 | 808 | try testReduce(.Max, [4]f16{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f16, 10.0e9)); |
| 805 | 809 | try testReduce(.Max, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, 10.0e9)); |
| 806 | 810 | try testReduce(.Max, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, 10.0e9)); |
| 811 | try testReduce(.Max, [4]f80{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f80, 10.0e9)); | |
| 812 | try testReduce(.Max, [4]f128{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f128, 10.0e9)); | |
| 807 | 813 | |
| 808 | 814 | try testReduce(.Mul, [4]i16{ -1, 2, 3, 4 }, @as(i16, -24)); |
| 809 | 815 | try testReduce(.Mul, [4]u16{ 1, 2, 3, 4 }, @as(u16, 24)); |
| ... | ... | @@ -816,6 +822,8 @@ test "vector reduce operation" { |
| 816 | 822 | try testReduce(.Mul, [4]f16{ -1.9, 5.1, -60.3, 100.0 }, @as(f16, 58430.7)); |
| 817 | 823 | try testReduce(.Mul, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 58430.7)); |
| 818 | 824 | try testReduce(.Mul, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 58430.7)); |
| 825 | try testReduce(.Mul, [4]f80{ -1.9, 5.1, -60.3, 100.0 }, @as(f80, 58430.7)); | |
| 826 | try testReduce(.Mul, [4]f128{ -1.9, 5.1, -60.3, 100.0 }, @as(f128, 58430.7)); | |
| 819 | 827 | |
| 820 | 828 | try testReduce(.Or, [4]bool{ false, true, false, false }, @as(bool, true)); |
| 821 | 829 | try testReduce(.Or, [4]u1{ 0, 1, 0, 0 }, @as(u1, 1)); |
| ... | ... | @@ -823,6 +831,7 @@ test "vector reduce operation" { |
| 823 | 831 | try testReduce(.Or, [4]u32{ 0xffff0000, 0xff00, 0xf0, 0xf }, ~@as(u32, 0)); |
| 824 | 832 | try testReduce(.Or, [4]u64{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u64, 0xffffffff)); |
| 825 | 833 | try testReduce(.Or, [4]u128{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u128, 0xffffffff)); |
| 834 | try testReduce(.Or, [4]u80{ 0xffff0000, 0xff00, 0xf0, 0xf }, @as(u80, 0xffffffff)); | |
| 826 | 835 | |
| 827 | 836 | try testReduce(.Xor, [4]bool{ true, true, true, false }, @as(bool, true)); |
| 828 | 837 | try testReduce(.Xor, [4]u1{ 1, 1, 1, 0 }, @as(u1, 1)); |
| ... | ... | @@ -835,22 +844,32 @@ test "vector reduce operation" { |
| 835 | 844 | const f16_nan = math.nan(f16); |
| 836 | 845 | const f32_nan = math.nan(f32); |
| 837 | 846 | const f64_nan = math.nan(f64); |
| 847 | const f80_nan = math.nan(f80); | |
| 848 | const f128_nan = math.nan(f128); | |
| 838 | 849 | |
| 839 | 850 | try testReduce(.Add, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); |
| 840 | 851 | try testReduce(.Add, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); |
| 841 | 852 | try testReduce(.Add, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); |
| 853 | try testReduce(.Add, [4]f80{ -1.9, 5.1, f80_nan, 100.0 }, f80_nan); | |
| 854 | try testReduce(.Add, [4]f128{ -1.9, 5.1, f128_nan, 100.0 }, f128_nan); | |
| 842 | 855 | |
| 843 | 856 | try testReduce(.Min, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, @as(f16, -1.9)); |
| 844 | 857 | try testReduce(.Min, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, @as(f32, -1.9)); |
| 845 | 858 | try testReduce(.Min, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, @as(f64, -1.9)); |
| 859 | try testReduce(.Min, [4]f80{ -1.9, 5.1, f80_nan, 100.0 }, @as(f80, -1.9)); | |
| 860 | try testReduce(.Min, [4]f128{ -1.9, 5.1, f128_nan, 100.0 }, @as(f128, -1.9)); | |
| 846 | 861 | |
| 847 | 862 | try testReduce(.Max, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, @as(f16, 100.0)); |
| 848 | 863 | try testReduce(.Max, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, @as(f32, 100.0)); |
| 849 | 864 | try testReduce(.Max, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, @as(f64, 100.0)); |
| 865 | try testReduce(.Max, [4]f80{ -1.9, 5.1, f80_nan, 100.0 }, @as(f80, 100.0)); | |
| 866 | try testReduce(.Max, [4]f128{ -1.9, 5.1, f128_nan, 100.0 }, @as(f128, 100.0)); | |
| 850 | 867 | |
| 851 | 868 | try testReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); |
| 852 | 869 | try testReduce(.Mul, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); |
| 853 | 870 | try testReduce(.Mul, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); |
| 871 | try testReduce(.Mul, [4]f80{ -1.9, 5.1, f80_nan, 100.0 }, f80_nan); | |
| 872 | try testReduce(.Mul, [4]f128{ -1.9, 5.1, f128_nan, 100.0 }, f128_nan); | |
| 854 | 873 | } |
| 855 | 874 | }; |
| 856 | 875 | |
| ... | ... | @@ -1319,11 +1338,6 @@ test "byte vector initialized in inline function" { |
| 1319 | 1338 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 1320 | 1339 | if (builtin.cpu.arch == .hexagon and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; |
| 1321 | 1340 | |
| 1322 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and comptime builtin.cpu.has(.x86, .avx512f)) { | |
| 1323 | // TODO https://github.com/ziglang/zig/issues/13279 | |
| 1324 | return error.SkipZigTest; | |
| 1325 | } | |
| 1326 | ||
| 1327 | 1341 | const S = struct { |
| 1328 | 1342 | fn boolx4(e0: bool, e1: bool, e2: bool, e3: bool) @Vector(4, bool) { |
| 1329 | 1343 | return .{ e0, e1, e2, e3 }; |
test/c_abi/cfuncs.c+9-1| ... | ... | @@ -408,7 +408,11 @@ void c_test_longdouble(void) { |
| 408 | 408 | zig_8_longdouble(0, 1, 2, 3, 4, 5, 6, 7, 10, 9); |
| 409 | 409 | } |
| 410 | 410 | |
| 411 | #if defined(ZIG_BACKEND_STAGE2_X86_64) || defined(ZIG_PPC32) || defined(__wasm__) | |
| 411 | #ifndef __hexagon__ | |
| 412 | #ifndef __loongarch__ | |
| 413 | #ifndef __mips__ | |
| 414 | #ifndef ZIG_PPC64 | |
| 415 | #if !(defined(__i386__) && defined(_WIN32)) | |
| 412 | 416 | |
| 413 | 417 | typedef bool Vector_2_bool __attribute__((ext_vector_type(2))); |
| 414 | 418 | |
| ... | ... | @@ -4657,6 +4661,10 @@ void c_test_vector_512_bool(void) { |
| 4657 | 4661 | }); |
| 4658 | 4662 | } |
| 4659 | 4663 | |
| 4664 | #endif | |
| 4665 | #endif | |
| 4666 | #endif | |
| 4667 | #endif | |
| 4660 | 4668 | #endif |
| 4661 | 4669 | |
| 4662 | 4670 | typedef uint8_t Vector_1_u8 __attribute__((vector_size(1 * sizeof(uint8_t)))); |
test/c_abi/main.zig+103-38| ... | ... | @@ -451,8 +451,11 @@ test "long double" { |
| 451 | 451 | |
| 452 | 452 | comptime { |
| 453 | 453 | skip: { |
| 454 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; | |
| 455 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 454 | if (builtin.cpu.arch == .hexagon) break :skip; | |
| 455 | if (builtin.cpu.arch == .loongarch64) break :skip; | |
| 456 | if (builtin.cpu.arch.isMIPS()) break :skip; | |
| 457 | if (builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 458 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; | |
| 456 | 459 | |
| 457 | 460 | _ = struct { |
| 458 | 461 | export fn zig_ret_vector_2_bool() @Vector(2, bool) { |
| ... | ... | @@ -474,7 +477,13 @@ extern fn c_vector_2_bool(@Vector(2, bool)) void; |
| 474 | 477 | extern fn c_test_vector_2_bool() void; |
| 475 | 478 | |
| 476 | 479 | test "@Vector(2, bool)" { |
| 477 | if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; | |
| 480 | if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; | |
| 481 | if (builtin.cpu.arch.isArm()) return error.SkipZigTest; | |
| 482 | if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; | |
| 483 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; | |
| 484 | if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; | |
| 485 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; | |
| 486 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; | |
| 478 | 487 | |
| 479 | 488 | const vec = c_ret_vector_2_bool(); |
| 480 | 489 | try expect(vec[0] == true); |
| ... | ... | @@ -488,8 +497,11 @@ test "@Vector(2, bool)" { |
| 488 | 497 | |
| 489 | 498 | comptime { |
| 490 | 499 | skip: { |
| 491 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; | |
| 492 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 500 | if (builtin.cpu.arch == .hexagon) break :skip; | |
| 501 | if (builtin.cpu.arch == .loongarch64) break :skip; | |
| 502 | if (builtin.cpu.arch.isMIPS()) break :skip; | |
| 503 | if (builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 504 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; | |
| 493 | 505 | |
| 494 | 506 | _ = struct { |
| 495 | 507 | export fn zig_ret_vector_4_bool() @Vector(4, bool) { |
| ... | ... | @@ -515,7 +527,13 @@ extern fn c_vector_4_bool(@Vector(4, bool)) void; |
| 515 | 527 | extern fn c_test_vector_4_bool() void; |
| 516 | 528 | |
| 517 | 529 | test "@Vector(4, bool)" { |
| 518 | if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; | |
| 530 | if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; | |
| 531 | if (builtin.cpu.arch.isArm()) return error.SkipZigTest; | |
| 532 | if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; | |
| 533 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; | |
| 534 | if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; | |
| 535 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; | |
| 536 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; | |
| 519 | 537 | |
| 520 | 538 | const vec = c_ret_vector_4_bool(); |
| 521 | 539 | try expect(vec[0] == true); |
| ... | ... | @@ -533,8 +551,11 @@ test "@Vector(4, bool)" { |
| 533 | 551 | |
| 534 | 552 | comptime { |
| 535 | 553 | skip: { |
| 536 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; | |
| 537 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 554 | if (builtin.cpu.arch == .hexagon) break :skip; | |
| 555 | if (builtin.cpu.arch == .loongarch64) break :skip; | |
| 556 | if (builtin.cpu.arch.isMIPS()) break :skip; | |
| 557 | if (builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 558 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; | |
| 538 | 559 | |
| 539 | 560 | _ = struct { |
| 540 | 561 | export fn zig_ret_vector_8_bool() @Vector(8, bool) { |
| ... | ... | @@ -568,7 +589,13 @@ extern fn c_vector_8_bool(@Vector(8, bool)) void; |
| 568 | 589 | extern fn c_test_vector_8_bool() void; |
| 569 | 590 | |
| 570 | 591 | test "@Vector(8, bool)" { |
| 571 | if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; | |
| 592 | if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; | |
| 593 | if (builtin.cpu.arch.isArm()) return error.SkipZigTest; | |
| 594 | if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; | |
| 595 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; | |
| 596 | if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; | |
| 597 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; | |
| 598 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; | |
| 572 | 599 | |
| 573 | 600 | const vec = c_ret_vector_8_bool(); |
| 574 | 601 | try expect(vec[0] == false); |
| ... | ... | @@ -594,8 +621,11 @@ test "@Vector(8, bool)" { |
| 594 | 621 | |
| 595 | 622 | comptime { |
| 596 | 623 | skip: { |
| 597 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; | |
| 598 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 624 | if (builtin.cpu.arch == .hexagon) break :skip; | |
| 625 | if (builtin.cpu.arch == .loongarch64) break :skip; | |
| 626 | if (builtin.cpu.arch.isMIPS()) break :skip; | |
| 627 | if (builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 628 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; | |
| 599 | 629 | |
| 600 | 630 | _ = struct { |
| 601 | 631 | export fn zig_ret_vector_16_bool() @Vector(16, bool) { |
| ... | ... | @@ -645,7 +675,13 @@ extern fn c_vector_16_bool(@Vector(16, bool)) void; |
| 645 | 675 | extern fn c_test_vector_16_bool() void; |
| 646 | 676 | |
| 647 | 677 | test "@Vector(16, bool)" { |
| 648 | if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; | |
| 678 | if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; | |
| 679 | if (builtin.cpu.arch.isArm()) return error.SkipZigTest; | |
| 680 | if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; | |
| 681 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; | |
| 682 | if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; | |
| 683 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; | |
| 684 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; | |
| 649 | 685 | |
| 650 | 686 | const vec = c_ret_vector_16_bool(); |
| 651 | 687 | try expect(vec[0] == true); |
| ... | ... | @@ -687,8 +723,11 @@ test "@Vector(16, bool)" { |
| 687 | 723 | |
| 688 | 724 | comptime { |
| 689 | 725 | skip: { |
| 690 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; | |
| 691 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 726 | if (builtin.cpu.arch == .hexagon) break :skip; | |
| 727 | if (builtin.cpu.arch == .loongarch64) break :skip; | |
| 728 | if (builtin.cpu.arch.isMIPS()) break :skip; | |
| 729 | if (builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 730 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; | |
| 692 | 731 | |
| 693 | 732 | _ = struct { |
| 694 | 733 | export fn zig_ret_vector_32_bool() @Vector(32, bool) { |
| ... | ... | @@ -770,7 +809,13 @@ extern fn c_vector_32_bool(@Vector(32, bool)) void; |
| 770 | 809 | extern fn c_test_vector_32_bool() void; |
| 771 | 810 | |
| 772 | 811 | test "@Vector(32, bool)" { |
| 773 | if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; | |
| 812 | if (builtin.cpu.arch.isAARCH64()) return error.SkipZigTest; | |
| 813 | if (builtin.cpu.arch.isArm()) return error.SkipZigTest; | |
| 814 | if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; | |
| 815 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; | |
| 816 | if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; | |
| 817 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; | |
| 818 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; | |
| 774 | 819 | |
| 775 | 820 | const vec = c_ret_vector_32_bool(); |
| 776 | 821 | try expect(vec[0] == true); |
| ... | ... | @@ -844,8 +889,11 @@ test "@Vector(32, bool)" { |
| 844 | 889 | |
| 845 | 890 | comptime { |
| 846 | 891 | skip: { |
| 847 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; | |
| 848 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 892 | if (builtin.cpu.arch == .hexagon) break :skip; | |
| 893 | if (builtin.cpu.arch == .loongarch64) break :skip; | |
| 894 | if (builtin.cpu.arch.isMIPS()) break :skip; | |
| 895 | if (builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 896 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; | |
| 849 | 897 | |
| 850 | 898 | _ = struct { |
| 851 | 899 | export fn zig_ret_vector_64_bool() @Vector(64, bool) { |
| ... | ... | @@ -991,7 +1039,11 @@ extern fn c_vector_64_bool(@Vector(64, bool)) void; |
| 991 | 1039 | extern fn c_test_vector_64_bool() void; |
| 992 | 1040 | |
| 993 | 1041 | test "@Vector(64, bool)" { |
| 994 | if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; | |
| 1042 | if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; | |
| 1043 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; | |
| 1044 | if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; | |
| 1045 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; | |
| 1046 | if (builtin.cpu.arch == .x86) return error.SkipZigTest; | |
| 995 | 1047 | |
| 996 | 1048 | const vec = c_ret_vector_64_bool(); |
| 997 | 1049 | try expect(vec[0] == false); |
| ... | ... | @@ -1129,8 +1181,11 @@ test "@Vector(64, bool)" { |
| 1129 | 1181 | |
| 1130 | 1182 | comptime { |
| 1131 | 1183 | skip: { |
| 1132 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; | |
| 1133 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 1184 | if (builtin.cpu.arch == .hexagon) break :skip; | |
| 1185 | if (builtin.cpu.arch == .loongarch64) break :skip; | |
| 1186 | if (builtin.cpu.arch.isMIPS()) break :skip; | |
| 1187 | if (builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 1188 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; | |
| 1134 | 1189 | |
| 1135 | 1190 | _ = struct { |
| 1136 | 1191 | export fn zig_ret_vector_128_bool() @Vector(128, bool) { |
| ... | ... | @@ -1404,7 +1459,11 @@ extern fn c_vector_128_bool(@Vector(128, bool)) void; |
| 1404 | 1459 | extern fn c_test_vector_128_bool() void; |
| 1405 | 1460 | |
| 1406 | 1461 | test "@Vector(128, bool)" { |
| 1407 | if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; | |
| 1462 | if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; | |
| 1463 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; | |
| 1464 | if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; | |
| 1465 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; | |
| 1466 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; | |
| 1408 | 1467 | |
| 1409 | 1468 | const vec = c_ret_vector_128_bool(); |
| 1410 | 1469 | try expect(vec[0] == false); |
| ... | ... | @@ -1670,8 +1729,11 @@ test "@Vector(128, bool)" { |
| 1670 | 1729 | |
| 1671 | 1730 | comptime { |
| 1672 | 1731 | skip: { |
| 1673 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; | |
| 1674 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 1732 | if (builtin.cpu.arch == .hexagon) break :skip; | |
| 1733 | if (builtin.cpu.arch == .loongarch64) break :skip; | |
| 1734 | if (builtin.cpu.arch.isMIPS()) break :skip; | |
| 1735 | if (builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 1736 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; | |
| 1675 | 1737 | |
| 1676 | 1738 | _ = struct { |
| 1677 | 1739 | export fn zig_ret_vector_256_bool() @Vector(256, bool) { |
| ... | ... | @@ -2201,7 +2263,11 @@ extern fn c_vector_256_bool(@Vector(256, bool)) void; |
| 2201 | 2263 | extern fn c_test_vector_256_bool() void; |
| 2202 | 2264 | |
| 2203 | 2265 | test "@Vector(256, bool)" { |
| 2204 | if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; | |
| 2266 | if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; | |
| 2267 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; | |
| 2268 | if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; | |
| 2269 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; | |
| 2270 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; | |
| 2205 | 2271 | |
| 2206 | 2272 | const vec = c_ret_vector_256_bool(); |
| 2207 | 2273 | try expect(vec[0] == true); |
| ... | ... | @@ -2723,8 +2789,11 @@ test "@Vector(256, bool)" { |
| 2723 | 2789 | |
| 2724 | 2790 | comptime { |
| 2725 | 2791 | skip: { |
| 2726 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) break :skip; | |
| 2727 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 2792 | if (builtin.cpu.arch == .hexagon) break :skip; | |
| 2793 | if (builtin.cpu.arch == .loongarch64) break :skip; | |
| 2794 | if (builtin.cpu.arch.isMIPS()) break :skip; | |
| 2795 | if (builtin.cpu.arch.isPowerPC64()) break :skip; | |
| 2796 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) break :skip; | |
| 2728 | 2797 | |
| 2729 | 2798 | _ = struct { |
| 2730 | 2799 | export fn zig_ret_vector_512_bool() @Vector(512, bool) { |
| ... | ... | @@ -3766,7 +3835,11 @@ extern fn c_vector_512_bool(@Vector(512, bool)) void; |
| 3766 | 3835 | extern fn c_test_vector_512_bool() void; |
| 3767 | 3836 | |
| 3768 | 3837 | test "@Vector(512, bool)" { |
| 3769 | if (builtin.zig_backend == .stage2_llvm and (builtin.cpu.arch != .powerpc and builtin.cpu.arch != .wasm32)) return error.SkipZigTest; | |
| 3838 | if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; | |
| 3839 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; | |
| 3840 | if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; | |
| 3841 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; | |
| 3842 | if (builtin.cpu.arch == .x86 and builtin.os.tag == .windows) return error.SkipZigTest; | |
| 3770 | 3843 | |
| 3771 | 3844 | const vec = c_ret_vector_512_bool(); |
| 3772 | 3845 | try expect(vec[0] == false); |
| ... | ... | @@ -4840,7 +4913,7 @@ test "@Vector(2, u8)" { |
| 4840 | 4913 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; |
| 4841 | 4914 | if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; |
| 4842 | 4915 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; |
| 4843 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) return error.SkipZigTest; | |
| 4916 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; | |
| 4844 | 4917 | |
| 4845 | 4918 | const v = c_ret_vector_2_u8(); |
| 4846 | 4919 | try expect(v[0] == 9); |
| ... | ... | @@ -4869,7 +4942,6 @@ test "@Vector(3, u8)" { |
| 4869 | 4942 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; |
| 4870 | 4943 | if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; |
| 4871 | 4944 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; |
| 4872 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag != .windows) return error.SkipZigTest; | |
| 4873 | 4945 | |
| 4874 | 4946 | const v = c_ret_vector_3_u8(); |
| 4875 | 4947 | try expect(v[0] == 19); |
| ... | ... | @@ -4912,7 +4984,7 @@ test "@Vector(4, u8)" { |
| 4912 | 4984 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; |
| 4913 | 4985 | if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; |
| 4914 | 4986 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; |
| 4915 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) return error.SkipZigTest; | |
| 4987 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; | |
| 4916 | 4988 | |
| 4917 | 4989 | const v = c_ret_vector_4_u8(); |
| 4918 | 4990 | try expect(v[0] == 41); |
| ... | ... | @@ -4946,7 +5018,6 @@ test "@Vector(6, u8)" { |
| 4946 | 5018 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; |
| 4947 | 5019 | if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; |
| 4948 | 5020 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; |
| 4949 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag != .windows) return error.SkipZigTest; | |
| 4950 | 5021 | |
| 4951 | 5022 | const v = c_ret_vector_6_u8(); |
| 4952 | 5023 | try expect(v[0] == 53); |
| ... | ... | @@ -9063,7 +9134,7 @@ test "@Vector(2, u16)" { |
| 9063 | 9134 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; |
| 9064 | 9135 | if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; |
| 9065 | 9136 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; |
| 9066 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64) return error.SkipZigTest; | |
| 9137 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; | |
| 9067 | 9138 | |
| 9068 | 9139 | const v = c_ret_vector_2_u16(); |
| 9069 | 9140 | try expect(v[0] == 9); |
| ... | ... | @@ -9091,7 +9162,6 @@ test "@Vector(3, u16)" { |
| 9091 | 9162 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; |
| 9092 | 9163 | if (builtin.cpu.arch.isMIPS()) return error.SkipZigTest; |
| 9093 | 9164 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; |
| 9094 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag != .windows) return error.SkipZigTest; | |
| 9095 | 9165 | |
| 9096 | 9166 | const v = c_ret_vector_3_u16(); |
| 9097 | 9167 | try expect(v[0] == 19); |
| ... | ... | @@ -12564,8 +12634,6 @@ extern fn c_vector_1_u64(@Vector(1, u64), usize) void; |
| 12564 | 12634 | extern fn c_test_vector_1_u64() void; |
| 12565 | 12635 | |
| 12566 | 12636 | test "@Vector(1, u64)" { |
| 12567 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag != .windows) return error.SkipZigTest; | |
| 12568 | ||
| 12569 | 12637 | const v = c_ret_vector_1_u64(); |
| 12570 | 12638 | try expect(v[0] == 3); |
| 12571 | 12639 | c_vector_1_u64(.{4}, 1); |
| ... | ... | @@ -13291,7 +13359,6 @@ test "@Vector(1, f32)" { |
| 13291 | 13359 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; |
| 13292 | 13360 | if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; |
| 13293 | 13361 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; |
| 13294 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and builtin.os.tag != .windows) return error.SkipZigTest; | |
| 13295 | 13362 | |
| 13296 | 13363 | const v = c_ret_vector_1_f32(); |
| 13297 | 13364 | try expect(v[0] == 3); |
| ... | ... | @@ -14633,7 +14700,6 @@ test "@Vector(4, f64)" { |
| 14633 | 14700 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; |
| 14634 | 14701 | if (builtin.cpu.arch.isMIPS32()) return error.SkipZigTest; |
| 14635 | 14702 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; |
| 14636 | if (builtin.cpu.arch.isArm()) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35899 | |
| 14637 | 14703 | |
| 14638 | 14704 | const v = c_ret_vector_4_f64(); |
| 14639 | 14705 | try expect(v[0] == 33); |
| ... | ... | @@ -14701,7 +14767,6 @@ test "@Vector(8, f64)" { |
| 14701 | 14767 | if (builtin.cpu.arch == .loongarch64) return error.SkipZigTest; |
| 14702 | 14768 | if (builtin.cpu.arch.isMIPS32()) return error.SkipZigTest; |
| 14703 | 14769 | if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest; |
| 14704 | if (builtin.cpu.arch.isArm()) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35899 | |
| 14705 | 14770 | |
| 14706 | 14771 | const v = c_ret_vector_8_f64(); |
| 14707 | 14772 | try expect(v[0] == 81); |
test/tests.zig-6| ... | ... | @@ -2015,7 +2015,6 @@ const c_abi_targets = blk: { |
| 2015 | 2015 | .abi = .musl, |
| 2016 | 2016 | }, |
| 2017 | 2017 | .use_llvm = false, |
| 2018 | .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, | |
| 2019 | 2018 | }, |
| 2020 | 2019 | .{ |
| 2021 | 2020 | .target = .{ |
| ... | ... | @@ -2026,7 +2025,6 @@ const c_abi_targets = blk: { |
| 2026 | 2025 | }, |
| 2027 | 2026 | .use_llvm = false, |
| 2028 | 2027 | .strip = true, |
| 2029 | .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, | |
| 2030 | 2028 | }, |
| 2031 | 2029 | .{ |
| 2032 | 2030 | .target = .{ |
| ... | ... | @@ -2037,7 +2035,6 @@ const c_abi_targets = blk: { |
| 2037 | 2035 | }, |
| 2038 | 2036 | .use_llvm = false, |
| 2039 | 2037 | .pic = true, |
| 2040 | .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, | |
| 2041 | 2038 | }, |
| 2042 | 2039 | .{ |
| 2043 | 2040 | .target = .{ |
| ... | ... | @@ -2082,7 +2079,6 @@ const c_abi_targets = blk: { |
| 2082 | 2079 | .abi = .gnu, |
| 2083 | 2080 | }, |
| 2084 | 2081 | .use_llvm = false, |
| 2085 | .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, | |
| 2086 | 2082 | }, |
| 2087 | 2083 | .{ |
| 2088 | 2084 | .target = .{ |
| ... | ... | @@ -2092,7 +2088,6 @@ const c_abi_targets = blk: { |
| 2092 | 2088 | .abi = .gnu, |
| 2093 | 2089 | }, |
| 2094 | 2090 | .use_llvm = false, |
| 2095 | .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, | |
| 2096 | 2091 | }, |
| 2097 | 2092 | .{ |
| 2098 | 2093 | .target = .{ |
| ... | ... | @@ -2102,7 +2097,6 @@ const c_abi_targets = blk: { |
| 2102 | 2097 | .abi = .gnu, |
| 2103 | 2098 | }, |
| 2104 | 2099 | .use_llvm = false, |
| 2105 | .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, | |
| 2106 | 2100 | }, |
| 2107 | 2101 | .{ |
| 2108 | 2102 | .target = .{ |