| author | |
| committer | |
| log | 59905a62f9da9946a797cc35a2523c9929663600 |
| tree | 93ca52f59d1803fca320819fc5a7058fbb58e61b |
| parent | 49a7ceb5bcd87d8aeb7b25f8b2972ad4ec0b9e24 |
| parent | 44252f4d352d53afd86d678c0b0a40b3f681c7eb |
| signature |
stage2 behavior tests for all targets passing with the LLVM backend17 files changed, 505 insertions(+), 210 deletions(-)
lib/std/target.zig+82| ... | ... | @@ -1773,6 +1773,88 @@ pub const Target = struct { |
| 1773 | 1773 | else => false, |
| 1774 | 1774 | }; |
| 1775 | 1775 | } |
| 1776 | ||
| 1777 | pub inline fn maxIntAlignment(target: Target) u16 { | |
| 1778 | return switch (target.cpu.arch) { | |
| 1779 | .avr => 1, | |
| 1780 | .msp430 => 2, | |
| 1781 | .xcore => 4, | |
| 1782 | ||
| 1783 | .arm, | |
| 1784 | .armeb, | |
| 1785 | .thumb, | |
| 1786 | .thumbeb, | |
| 1787 | .hexagon, | |
| 1788 | .mips, | |
| 1789 | .mipsel, | |
| 1790 | .powerpc, | |
| 1791 | .powerpcle, | |
| 1792 | .r600, | |
| 1793 | .amdgcn, | |
| 1794 | .riscv32, | |
| 1795 | .sparc, | |
| 1796 | .sparcel, | |
| 1797 | .s390x, | |
| 1798 | .lanai, | |
| 1799 | .wasm32, | |
| 1800 | .wasm64, | |
| 1801 | => 8, | |
| 1802 | ||
| 1803 | .i386 => return switch (target.os.tag) { | |
| 1804 | .windows => 8, | |
| 1805 | else => 4, | |
| 1806 | }, | |
| 1807 | ||
| 1808 | // For x86_64, LLVMABIAlignmentOfType(i128) reports 8. However I think 16 | |
| 1809 | // is a better number for two reasons: | |
| 1810 | // 1. Better machine code when loading into SIMD register. | |
| 1811 | // 2. The C ABI wants 16 for extern structs. | |
| 1812 | // 3. 16-byte cmpxchg needs 16-byte alignment. | |
| 1813 | // Same logic for riscv64, powerpc64, mips64, sparcv9. | |
| 1814 | .x86_64, | |
| 1815 | .riscv64, | |
| 1816 | .powerpc64, | |
| 1817 | .powerpc64le, | |
| 1818 | .mips64, | |
| 1819 | .mips64el, | |
| 1820 | .sparcv9, | |
| 1821 | ||
| 1822 | // Even LLVMABIAlignmentOfType(i128) agrees on these targets. | |
| 1823 | .aarch64, | |
| 1824 | .aarch64_be, | |
| 1825 | .aarch64_32, | |
| 1826 | .bpfel, | |
| 1827 | .bpfeb, | |
| 1828 | .nvptx, | |
| 1829 | .nvptx64, | |
| 1830 | => 16, | |
| 1831 | ||
| 1832 | // Below this comment are unverified but based on the fact that C requires | |
| 1833 | // int128_t to be 16 bytes aligned, it's a safe default. | |
| 1834 | .spu_2, | |
| 1835 | .csky, | |
| 1836 | .arc, | |
| 1837 | .m68k, | |
| 1838 | .tce, | |
| 1839 | .tcele, | |
| 1840 | .le32, | |
| 1841 | .amdil, | |
| 1842 | .hsail, | |
| 1843 | .spir, | |
| 1844 | .kalimba, | |
| 1845 | .renderscript32, | |
| 1846 | .spirv32, | |
| 1847 | .shave, | |
| 1848 | .le64, | |
| 1849 | .amdil64, | |
| 1850 | .hsail64, | |
| 1851 | .spir64, | |
| 1852 | .renderscript64, | |
| 1853 | .ve, | |
| 1854 | .spirv64, | |
| 1855 | => 16, | |
| 1856 | }; | |
| 1857 | } | |
| 1776 | 1858 | }; |
| 1777 | 1859 | |
| 1778 | 1860 | test { |
src/AstGen.zig+10-50| ... | ... | @@ -7423,42 +7423,22 @@ fn builtinCall( |
| 7423 | 7423 | }, |
| 7424 | 7424 | |
| 7425 | 7425 | .atomic_load => { |
| 7426 | const int_type = try typeExpr(gz, scope, params[0]); | |
| 7427 | // TODO allow this pointer type to be volatile | |
| 7428 | const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ | |
| 7429 | .ptr_type_simple = .{ | |
| 7430 | .is_allowzero = false, | |
| 7431 | .is_mutable = false, | |
| 7432 | .is_volatile = false, | |
| 7433 | .size = .One, | |
| 7434 | .elem_type = int_type, | |
| 7435 | }, | |
| 7436 | } }); | |
| 7437 | const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.Bin{ | |
| 7426 | const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{ | |
| 7438 | 7427 | // zig fmt: off |
| 7439 | .lhs = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), | |
| 7440 | .rhs = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[2]), | |
| 7428 | .elem_type = try typeExpr(gz, scope, params[0]), | |
| 7429 | .ptr = try expr (gz, scope, .none, params[1]), | |
| 7430 | .ordering = try expr (gz, scope, .{ .coerced_ty = .atomic_order_type }, params[2]), | |
| 7441 | 7431 | // zig fmt: on |
| 7442 | 7432 | }); |
| 7443 | 7433 | return rvalue(gz, rl, result, node); |
| 7444 | 7434 | }, |
| 7445 | 7435 | .atomic_rmw => { |
| 7446 | 7436 | const int_type = try typeExpr(gz, scope, params[0]); |
| 7447 | // TODO allow this pointer type to be volatile | |
| 7448 | const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ | |
| 7449 | .ptr_type_simple = .{ | |
| 7450 | .is_allowzero = false, | |
| 7451 | .is_mutable = true, | |
| 7452 | .is_volatile = false, | |
| 7453 | .size = .One, | |
| 7454 | .elem_type = int_type, | |
| 7455 | }, | |
| 7456 | } }); | |
| 7457 | 7437 | const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{ |
| 7458 | 7438 | // zig fmt: off |
| 7459 | .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), | |
| 7439 | .ptr = try expr(gz, scope, .none, params[1]), | |
| 7460 | 7440 | .operation = try expr(gz, scope, .{ .coerced_ty = .atomic_rmw_op_type }, params[2]), |
| 7461 | .operand = try expr(gz, scope, .{ .coerced_ty = int_type }, params[3]), | |
| 7441 | .operand = try expr(gz, scope, .{ .ty = int_type }, params[3]), | |
| 7462 | 7442 | .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]), |
| 7463 | 7443 | // zig fmt: on |
| 7464 | 7444 | }); |
| ... | ... | @@ -7466,20 +7446,10 @@ fn builtinCall( |
| 7466 | 7446 | }, |
| 7467 | 7447 | .atomic_store => { |
| 7468 | 7448 | const int_type = try typeExpr(gz, scope, params[0]); |
| 7469 | // TODO allow this pointer type to be volatile | |
| 7470 | const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ | |
| 7471 | .ptr_type_simple = .{ | |
| 7472 | .is_allowzero = false, | |
| 7473 | .is_mutable = true, | |
| 7474 | .is_volatile = false, | |
| 7475 | .size = .One, | |
| 7476 | .elem_type = int_type, | |
| 7477 | }, | |
| 7478 | } }); | |
| 7479 | 7449 | const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{ |
| 7480 | 7450 | // zig fmt: off |
| 7481 | .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), | |
| 7482 | .operand = try expr(gz, scope, .{ .coerced_ty = int_type }, params[2]), | |
| 7451 | .ptr = try expr(gz, scope, .none, params[1]), | |
| 7452 | .operand = try expr(gz, scope, .{ .ty = int_type }, params[2]), | |
| 7483 | 7453 | .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[3]), |
| 7484 | 7454 | // zig fmt: on |
| 7485 | 7455 | }); |
| ... | ... | @@ -7684,20 +7654,10 @@ fn cmpxchg( |
| 7684 | 7654 | tag: Zir.Inst.Tag, |
| 7685 | 7655 | ) InnerError!Zir.Inst.Ref { |
| 7686 | 7656 | const int_type = try typeExpr(gz, scope, params[0]); |
| 7687 | // TODO: allow this to be volatile | |
| 7688 | const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ | |
| 7689 | .ptr_type_simple = .{ | |
| 7690 | .is_allowzero = false, | |
| 7691 | .is_mutable = true, | |
| 7692 | .is_volatile = false, | |
| 7693 | .size = .One, | |
| 7694 | .elem_type = int_type, | |
| 7695 | }, | |
| 7696 | } }); | |
| 7697 | 7657 | const result = try gz.addPlNode(tag, node, Zir.Inst.Cmpxchg{ |
| 7698 | 7658 | // zig fmt: off |
| 7699 | .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), | |
| 7700 | .expected_value = try expr(gz, scope, .{ .coerced_ty = int_type }, params[2]), | |
| 7659 | .ptr = try expr(gz, scope, .none, params[1]), | |
| 7660 | .expected_value = try expr(gz, scope, .{ .ty = int_type }, params[2]), | |
| 7701 | 7661 | .new_value = try expr(gz, scope, .{ .coerced_ty = int_type }, params[3]), |
| 7702 | 7662 | .success_order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]), |
| 7703 | 7663 | .failure_order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[5]), |
src/Module.zig+15| ... | ... | @@ -151,6 +151,8 @@ allocated_decls: std.SegmentedList(Decl, 0) = .{}, |
| 151 | 151 | /// When a Decl object is freed from `allocated_decls`, it is pushed into this stack. |
| 152 | 152 | decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{}, |
| 153 | 153 | |
| 154 | global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{}, | |
| 155 | ||
| 154 | 156 | const MonomorphedFuncsSet = std.HashMapUnmanaged( |
| 155 | 157 | *Fn, |
| 156 | 158 | void, |
| ... | ... | @@ -2831,6 +2833,7 @@ pub fn deinit(mod: *Module) void { |
| 2831 | 2833 | |
| 2832 | 2834 | mod.decls_free_list.deinit(gpa); |
| 2833 | 2835 | mod.allocated_decls.deinit(gpa); |
| 2836 | mod.global_assembly.deinit(gpa); | |
| 2834 | 2837 | } |
| 2835 | 2838 | |
| 2836 | 2839 | pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void { |
| ... | ... | @@ -2842,6 +2845,9 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void { |
| 2842 | 2845 | if (decl.deletion_flag) { |
| 2843 | 2846 | assert(mod.deletion_set.swapRemove(decl_index)); |
| 2844 | 2847 | } |
| 2848 | if (mod.global_assembly.fetchRemove(decl_index)) |kv| { | |
| 2849 | gpa.free(kv.value); | |
| 2850 | } | |
| 2845 | 2851 | if (decl.has_tv) { |
| 2846 | 2852 | if (decl.getInnerNamespace()) |namespace| { |
| 2847 | 2853 | namespace.destroyDecls(mod); |
| ... | ... | @@ -5714,3 +5720,12 @@ pub fn markDeclAlive(mod: *Module, decl: *Decl) void { |
| 5714 | 5720 | fn markDeclIndexAlive(mod: *Module, decl_index: Decl.Index) void { |
| 5715 | 5721 | return mod.markDeclAlive(mod.declPtr(decl_index)); |
| 5716 | 5722 | } |
| 5723 | ||
| 5724 | pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u8) !void { | |
| 5725 | try mod.global_assembly.ensureUnusedCapacity(mod.gpa, 1); | |
| 5726 | ||
| 5727 | const duped_source = try mod.gpa.dupe(u8, source); | |
| 5728 | errdefer mod.gpa.free(duped_source); | |
| 5729 | ||
| 5730 | mod.global_assembly.putAssumeCapacityNoClobber(decl_index, duped_source); | |
| 5731 | } |
src/Sema.zig+110-80| ... | ... | @@ -10517,16 +10517,35 @@ fn zirAsm( |
| 10517 | 10517 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; |
| 10518 | 10518 | const is_global_assembly = sema.func == null; |
| 10519 | 10519 | |
| 10520 | if (block.is_comptime and !is_global_assembly) { | |
| 10521 | try sema.requireRuntimeBlock(block, src); | |
| 10522 | } | |
| 10523 | ||
| 10524 | 10520 | if (extra.data.asm_source == 0) { |
| 10525 | 10521 | // This can move to become an AstGen error after inline assembly improvements land |
| 10526 | 10522 | // and stage1 code matches stage2 code. |
| 10527 | 10523 | return sema.fail(block, src, "assembly code must use string literal syntax", .{}); |
| 10528 | 10524 | } |
| 10529 | 10525 | |
| 10526 | const asm_source = sema.code.nullTerminatedString(extra.data.asm_source); | |
| 10527 | ||
| 10528 | if (is_global_assembly) { | |
| 10529 | if (outputs_len != 0) { | |
| 10530 | return sema.fail(block, src, "module-level assembly does not support outputs", .{}); | |
| 10531 | } | |
| 10532 | if (inputs_len != 0) { | |
| 10533 | return sema.fail(block, src, "module-level assembly does not support inputs", .{}); | |
| 10534 | } | |
| 10535 | if (clobbers_len != 0) { | |
| 10536 | return sema.fail(block, src, "module-level assembly does not support clobbers", .{}); | |
| 10537 | } | |
| 10538 | if (is_volatile) { | |
| 10539 | return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{}); | |
| 10540 | } | |
| 10541 | try sema.mod.addGlobalAssembly(sema.owner_decl_index, asm_source); | |
| 10542 | return Air.Inst.Ref.void_value; | |
| 10543 | } | |
| 10544 | ||
| 10545 | if (block.is_comptime) { | |
| 10546 | try sema.requireRuntimeBlock(block, src); | |
| 10547 | } | |
| 10548 | ||
| 10530 | 10549 | if (outputs_len > 1) { |
| 10531 | 10550 | return sema.fail(block, src, "TODO implement Sema for asm with more than 1 output", .{}); |
| 10532 | 10551 | } |
| ... | ... | @@ -10591,7 +10610,6 @@ fn zirAsm( |
| 10591 | 10610 | needed_capacity += name.*.len / 4 + 1; |
| 10592 | 10611 | } |
| 10593 | 10612 | |
| 10594 | const asm_source = sema.code.nullTerminatedString(extra.data.asm_source); | |
| 10595 | 10613 | needed_capacity += (asm_source.len + 3) / 4; |
| 10596 | 10614 | |
| 10597 | 10615 | const gpa = sema.gpa; |
| ... | ... | @@ -14715,51 +14733,64 @@ fn checkNumericType( |
| 14715 | 14733 | } |
| 14716 | 14734 | } |
| 14717 | 14735 | |
| 14718 | fn checkAtomicOperandType( | |
| 14736 | /// Returns the casted pointer. | |
| 14737 | fn checkAtomicPtrOperand( | |
| 14719 | 14738 | sema: *Sema, |
| 14720 | 14739 | block: *Block, |
| 14721 | ty_src: LazySrcLoc, | |
| 14722 | ty: Type, | |
| 14723 | ) CompileError!void { | |
| 14724 | var buffer: Type.Payload.Bits = undefined; | |
| 14740 | elem_ty: Type, | |
| 14741 | elem_ty_src: LazySrcLoc, | |
| 14742 | ptr: Air.Inst.Ref, | |
| 14743 | ptr_src: LazySrcLoc, | |
| 14744 | ptr_const: bool, | |
| 14745 | ) CompileError!Air.Inst.Ref { | |
| 14725 | 14746 | const target = sema.mod.getTarget(); |
| 14726 | const max_atomic_bits = target_util.largestAtomicBits(target); | |
| 14727 | const int_ty = switch (ty.zigTypeTag()) { | |
| 14728 | .Int => ty, | |
| 14729 | .Enum => ty.intTagType(&buffer), | |
| 14730 | .Float => { | |
| 14731 | const bit_count = ty.floatBits(target); | |
| 14732 | if (bit_count > max_atomic_bits) { | |
| 14733 | return sema.fail( | |
| 14734 | block, | |
| 14735 | ty_src, | |
| 14736 | "expected {d}-bit float type or smaller; found {d}-bit float type", | |
| 14737 | .{ max_atomic_bits, bit_count }, | |
| 14738 | ); | |
| 14739 | } | |
| 14740 | return; | |
| 14741 | }, | |
| 14742 | .Bool => return, // Will be treated as `u8`. | |
| 14743 | else => { | |
| 14744 | if (ty.isPtrAtRuntime()) return; | |
| 14747 | var diag: target_util.AtomicPtrAlignmentDiagnostics = .{}; | |
| 14748 | const alignment = target_util.atomicPtrAlignment(target, elem_ty, &diag) catch |err| switch (err) { | |
| 14749 | error.FloatTooBig => return sema.fail( | |
| 14750 | block, | |
| 14751 | elem_ty_src, | |
| 14752 | "expected {d}-bit float type or smaller; found {d}-bit float type", | |
| 14753 | .{ diag.max_bits, diag.bits }, | |
| 14754 | ), | |
| 14755 | error.IntTooBig => return sema.fail( | |
| 14756 | block, | |
| 14757 | elem_ty_src, | |
| 14758 | "expected {d}-bit integer type or smaller; found {d}-bit integer type", | |
| 14759 | .{ diag.max_bits, diag.bits }, | |
| 14760 | ), | |
| 14761 | error.BadType => return sema.fail( | |
| 14762 | block, | |
| 14763 | elem_ty_src, | |
| 14764 | "expected bool, integer, float, enum, or pointer type; found {}", | |
| 14765 | .{elem_ty.fmt(sema.mod)}, | |
| 14766 | ), | |
| 14767 | }; | |
| 14745 | 14768 | |
| 14746 | return sema.fail( | |
| 14747 | block, | |
| 14748 | ty_src, | |
| 14749 | "expected bool, integer, float, enum, or pointer type; found {}", | |
| 14750 | .{ty.fmt(sema.mod)}, | |
| 14751 | ); | |
| 14769 | var wanted_ptr_data: Type.Payload.Pointer.Data = .{ | |
| 14770 | .pointee_type = elem_ty, | |
| 14771 | .@"align" = alignment, | |
| 14772 | .@"addrspace" = .generic, | |
| 14773 | .mutable = !ptr_const, | |
| 14774 | }; | |
| 14775 | ||
| 14776 | const ptr_ty = sema.typeOf(ptr); | |
| 14777 | const ptr_data = switch (try ptr_ty.zigTypeTagOrPoison()) { | |
| 14778 | .Pointer => ptr_ty.ptrInfo().data, | |
| 14779 | else => { | |
| 14780 | const wanted_ptr_ty = try Type.ptr(sema.arena, sema.mod, wanted_ptr_data); | |
| 14781 | _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); | |
| 14782 | unreachable; | |
| 14752 | 14783 | }, |
| 14753 | 14784 | }; |
| 14754 | const bit_count = int_ty.intInfo(target).bits; | |
| 14755 | if (bit_count > max_atomic_bits) { | |
| 14756 | return sema.fail( | |
| 14757 | block, | |
| 14758 | ty_src, | |
| 14759 | "expected {d}-bit integer type or smaller; found {d}-bit integer type", | |
| 14760 | .{ max_atomic_bits, bit_count }, | |
| 14761 | ); | |
| 14762 | } | |
| 14785 | ||
| 14786 | wanted_ptr_data.@"addrspace" = ptr_data.@"addrspace"; | |
| 14787 | wanted_ptr_data.@"allowzero" = ptr_data.@"allowzero"; | |
| 14788 | wanted_ptr_data.@"volatile" = ptr_data.@"volatile"; | |
| 14789 | ||
| 14790 | const wanted_ptr_ty = try Type.ptr(sema.arena, sema.mod, wanted_ptr_data); | |
| 14791 | const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); | |
| 14792 | ||
| 14793 | return casted_ptr; | |
| 14763 | 14794 | } |
| 14764 | 14795 | |
| 14765 | 14796 | fn checkPtrIsNotComptimeMutable( |
| ... | ... | @@ -15036,10 +15067,8 @@ fn zirCmpxchg( |
| 15036 | 15067 | const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; |
| 15037 | 15068 | const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = inst_data.src_node }; |
| 15038 | 15069 | // zig fmt: on |
| 15039 | const ptr = sema.resolveInst(extra.ptr); | |
| 15040 | const ptr_ty = sema.typeOf(ptr); | |
| 15041 | const elem_ty = ptr_ty.elemType(); | |
| 15042 | try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty); | |
| 15070 | const expected_value = sema.resolveInst(extra.expected_value); | |
| 15071 | const elem_ty = sema.typeOf(expected_value); | |
| 15043 | 15072 | if (elem_ty.zigTypeTag() == .Float) { |
| 15044 | 15073 | return sema.fail( |
| 15045 | 15074 | block, |
| ... | ... | @@ -15048,7 +15077,8 @@ fn zirCmpxchg( |
| 15048 | 15077 | .{elem_ty.fmt(sema.mod)}, |
| 15049 | 15078 | ); |
| 15050 | 15079 | } |
| 15051 | const expected_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.expected_value), expected_src); | |
| 15080 | const uncasted_ptr = sema.resolveInst(extra.ptr); | |
| 15081 | const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); | |
| 15052 | 15082 | const new_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.new_value), new_value_src); |
| 15053 | 15083 | const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order); |
| 15054 | 15084 | const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order); |
| ... | ... | @@ -15081,6 +15111,7 @@ fn zirCmpxchg( |
| 15081 | 15111 | // to become undef as well |
| 15082 | 15112 | return sema.addConstUndef(result_ty); |
| 15083 | 15113 | } |
| 15114 | const ptr_ty = sema.typeOf(ptr); | |
| 15084 | 15115 | const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; |
| 15085 | 15116 | const result_val = if (stored_val.eql(expected_val, elem_ty, sema.mod)) blk: { |
| 15086 | 15117 | try sema.storePtr(block, src, ptr, new_value); |
| ... | ... | @@ -15487,17 +15518,16 @@ fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 15487 | 15518 | |
| 15488 | 15519 | fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 15489 | 15520 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 15490 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | |
| 15521 | const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data; | |
| 15491 | 15522 | // zig fmt: off |
| 15492 | 15523 | const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 15493 | 15524 | const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 15494 | 15525 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 15495 | 15526 | // zig fmt: on |
| 15496 | const ptr = sema.resolveInst(extra.lhs); | |
| 15497 | const ptr_ty = sema.typeOf(ptr); | |
| 15498 | const elem_ty = ptr_ty.elemType(); | |
| 15499 | try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty); | |
| 15500 | const order = try sema.resolveAtomicOrder(block, order_src, extra.rhs); | |
| 15527 | const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); | |
| 15528 | const uncasted_ptr = sema.resolveInst(extra.ptr); | |
| 15529 | const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); | |
| 15530 | const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); | |
| 15501 | 15531 | |
| 15502 | 15532 | switch (order) { |
| 15503 | 15533 | .Release, .AcqRel => { |
| ... | ... | @@ -15516,7 +15546,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! |
| 15516 | 15546 | } |
| 15517 | 15547 | |
| 15518 | 15548 | if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { |
| 15519 | if (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) |elem_val| { | |
| 15549 | if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| { | |
| 15520 | 15550 | return sema.addConstant(elem_ty, elem_val); |
| 15521 | 15551 | } |
| 15522 | 15552 | } |
| ... | ... | @@ -15536,19 +15566,19 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 15536 | 15566 | const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; |
| 15537 | 15567 | const src = inst_data.src(); |
| 15538 | 15568 | // zig fmt: off |
| 15539 | const operand_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 15569 | const elem_ty_src : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 15540 | 15570 | const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 15541 | 15571 | const op_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 15542 | 15572 | const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; |
| 15543 | 15573 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; |
| 15544 | 15574 | // zig fmt: on |
| 15545 | const ptr = sema.resolveInst(extra.ptr); | |
| 15546 | const ptr_ty = sema.typeOf(ptr); | |
| 15547 | const operand_ty = ptr_ty.elemType(); | |
| 15548 | try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty); | |
| 15575 | const operand = sema.resolveInst(extra.operand); | |
| 15576 | const elem_ty = sema.typeOf(operand); | |
| 15577 | const uncasted_ptr = sema.resolveInst(extra.ptr); | |
| 15578 | const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); | |
| 15549 | 15579 | const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); |
| 15550 | 15580 | |
| 15551 | switch (operand_ty.zigTypeTag()) { | |
| 15581 | switch (elem_ty.zigTypeTag()) { | |
| 15552 | 15582 | .Enum => if (op != .Xchg) { |
| 15553 | 15583 | return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{}); |
| 15554 | 15584 | }, |
| ... | ... | @@ -15561,7 +15591,6 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 15561 | 15591 | }, |
| 15562 | 15592 | else => {}, |
| 15563 | 15593 | } |
| 15564 | const operand = try sema.coerce(block, operand_ty, sema.resolveInst(extra.operand), operand_src); | |
| 15565 | 15594 | const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); |
| 15566 | 15595 | |
| 15567 | 15596 | if (order == .Unordered) { |
| ... | ... | @@ -15569,8 +15598,8 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 15569 | 15598 | } |
| 15570 | 15599 | |
| 15571 | 15600 | // special case zero bit types |
| 15572 | if (try sema.typeHasOnePossibleValue(block, operand_ty_src, operand_ty)) |val| { | |
| 15573 | return sema.addConstant(operand_ty, val); | |
| 15601 | if (try sema.typeHasOnePossibleValue(block, elem_ty_src, elem_ty)) |val| { | |
| 15602 | return sema.addConstant(elem_ty, val); | |
| 15574 | 15603 | } |
| 15575 | 15604 | |
| 15576 | 15605 | const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { |
| ... | ... | @@ -15581,22 +15610,23 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 15581 | 15610 | }; |
| 15582 | 15611 | if (ptr_val.isComptimeMutablePtr()) { |
| 15583 | 15612 | const target = sema.mod.getTarget(); |
| 15613 | const ptr_ty = sema.typeOf(ptr); | |
| 15584 | 15614 | const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; |
| 15585 | 15615 | const new_val = switch (op) { |
| 15586 | 15616 | // zig fmt: off |
| 15587 | 15617 | .Xchg => operand_val, |
| 15588 | .Add => try stored_val.numberAddWrap(operand_val, operand_ty, sema.arena, target), | |
| 15589 | .Sub => try stored_val.numberSubWrap(operand_val, operand_ty, sema.arena, target), | |
| 15590 | .And => try stored_val.bitwiseAnd (operand_val, operand_ty, sema.arena, target), | |
| 15591 | .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena, target), | |
| 15592 | .Or => try stored_val.bitwiseOr (operand_val, operand_ty, sema.arena, target), | |
| 15593 | .Xor => try stored_val.bitwiseXor (operand_val, operand_ty, sema.arena, target), | |
| 15594 | .Max => stored_val.numberMax (operand_val, target), | |
| 15595 | .Min => stored_val.numberMin (operand_val, target), | |
| 15618 | .Add => try stored_val.numberAddWrap(operand_val, elem_ty, sema.arena, target), | |
| 15619 | .Sub => try stored_val.numberSubWrap(operand_val, elem_ty, sema.arena, target), | |
| 15620 | .And => try stored_val.bitwiseAnd (operand_val, elem_ty, sema.arena, target), | |
| 15621 | .Nand => try stored_val.bitwiseNand (operand_val, elem_ty, sema.arena, target), | |
| 15622 | .Or => try stored_val.bitwiseOr (operand_val, elem_ty, sema.arena, target), | |
| 15623 | .Xor => try stored_val.bitwiseXor (operand_val, elem_ty, sema.arena, target), | |
| 15624 | .Max => stored_val.numberMax (operand_val, target), | |
| 15625 | .Min => stored_val.numberMin (operand_val, target), | |
| 15596 | 15626 | // zig fmt: on |
| 15597 | 15627 | }; |
| 15598 | try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty); | |
| 15599 | return sema.addConstant(operand_ty, stored_val); | |
| 15628 | try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty); | |
| 15629 | return sema.addConstant(elem_ty, stored_val); | |
| 15600 | 15630 | } else break :rs ptr_src; |
| 15601 | 15631 | } else ptr_src; |
| 15602 | 15632 | |
| ... | ... | @@ -15620,15 +15650,15 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 15620 | 15650 | const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; |
| 15621 | 15651 | const src = inst_data.src(); |
| 15622 | 15652 | // zig fmt: off |
| 15623 | const operand_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 15653 | const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 15624 | 15654 | const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 15625 | 15655 | const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 15626 | 15656 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; |
| 15627 | 15657 | // zig fmt: on |
| 15628 | const ptr = sema.resolveInst(extra.ptr); | |
| 15629 | const operand_ty = sema.typeOf(ptr).elemType(); | |
| 15630 | try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty); | |
| 15631 | const operand = try sema.coerce(block, operand_ty, sema.resolveInst(extra.operand), operand_src); | |
| 15658 | const operand = sema.resolveInst(extra.operand); | |
| 15659 | const elem_ty = sema.typeOf(operand); | |
| 15660 | const uncasted_ptr = sema.resolveInst(extra.ptr); | |
| 15661 | const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); | |
| 15632 | 15662 | const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); |
| 15633 | 15663 | |
| 15634 | 15664 | const air_tag: Air.Inst.Tag = switch (order) { |
src/Zir.zig+7-1| ... | ... | @@ -903,7 +903,7 @@ pub const Inst = struct { |
| 903 | 903 | /// Uses the `pl_node` union field with payload `Select`. |
| 904 | 904 | select, |
| 905 | 905 | /// Implements the `@atomicLoad` builtin. |
| 906 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 906 | /// Uses the `pl_node` union field with payload `AtomicLoad`. | |
| 907 | 907 | atomic_load, |
| 908 | 908 | /// Implements the `@atomicRmw` builtin. |
| 909 | 909 | /// Uses the `pl_node` union field with payload `AtomicRmw`. |
| ... | ... | @@ -3293,6 +3293,12 @@ pub const Inst = struct { |
| 3293 | 3293 | ordering: Ref, |
| 3294 | 3294 | }; |
| 3295 | 3295 | |
| 3296 | pub const AtomicLoad = struct { | |
| 3297 | elem_type: Ref, | |
| 3298 | ptr: Ref, | |
| 3299 | ordering: Ref, | |
| 3300 | }; | |
| 3301 | ||
| 3296 | 3302 | pub const MulAdd = struct { |
| 3297 | 3303 | mulend1: Ref, |
| 3298 | 3304 | mulend2: Ref, |
src/arch/x86_64/abi.zig+9-8| ... | ... | @@ -12,13 +12,10 @@ pub fn classifyWindows(ty: Type, target: Target) Class { |
| 12 | 12 | // and the registers used for those arguments. Any argument that doesn't fit in 8 |
| 13 | 13 | // bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference. A single argument |
| 14 | 14 | // is never spread across multiple registers." |
| 15 | // "All floating point operations are done using the 16 XMM registers." | |
| 15 | 16 | // "Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed |
| 16 | 17 | // as if they were integers of the same size." |
| 17 | switch (ty.abiSize(target)) { | |
| 18 | 1, 2, 4, 8 => {}, | |
| 19 | else => return .memory, | |
| 20 | } | |
| 21 | return switch (ty.zigTypeTag()) { | |
| 18 | switch (ty.zigTypeTag()) { | |
| 22 | 19 | .Pointer, |
| 23 | 20 | .Int, |
| 24 | 21 | .Bool, |
| ... | ... | @@ -33,9 +30,13 @@ pub fn classifyWindows(ty: Type, target: Target) Class { |
| 33 | 30 | .ErrorUnion, |
| 34 | 31 | .AnyFrame, |
| 35 | 32 | .Frame, |
| 36 | => .integer, | |
| 33 | => switch (ty.abiSize(target)) { | |
| 34 | 0 => unreachable, | |
| 35 | 1, 2, 4, 8 => return .integer, | |
| 36 | else => return .memory, | |
| 37 | }, | |
| 37 | 38 | |
| 38 | .Float, .Vector => .sse, | |
| 39 | .Float, .Vector => return .sse, | |
| 39 | 40 | |
| 40 | 41 | .Type, |
| 41 | 42 | .ComptimeFloat, |
| ... | ... | @@ -47,7 +48,7 @@ pub fn classifyWindows(ty: Type, target: Target) Class { |
| 47 | 48 | .Opaque, |
| 48 | 49 | .EnumLiteral, |
| 49 | 50 | => unreachable, |
| 50 | }; | |
| 51 | } | |
| 51 | 52 | } |
| 52 | 53 | |
| 53 | 54 | /// There are a maximum of 8 possible return slots. Returned values are in |
src/codegen/llvm.zig+45-4| ... | ... | @@ -476,6 +476,19 @@ pub const Object = struct { |
| 476 | 476 | _ = builder.buildRet(is_lt); |
| 477 | 477 | } |
| 478 | 478 | |
| 479 | fn genModuleLevelAssembly(object: *Object, comp: *Compilation) !void { | |
| 480 | const mod = comp.bin_file.options.module.?; | |
| 481 | if (mod.global_assembly.count() == 0) return; | |
| 482 | var buffer = std.ArrayList(u8).init(comp.gpa); | |
| 483 | defer buffer.deinit(); | |
| 484 | var it = mod.global_assembly.iterator(); | |
| 485 | while (it.next()) |kv| { | |
| 486 | try buffer.appendSlice(kv.value_ptr.*); | |
| 487 | try buffer.append('\n'); | |
| 488 | } | |
| 489 | object.llvm_module.setModuleInlineAsm2(buffer.items.ptr, buffer.items.len - 1); | |
| 490 | } | |
| 491 | ||
| 479 | 492 | pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 480 | 493 | var sub_prog_node = prog_node.start("LLVM Emit Object", 0); |
| 481 | 494 | sub_prog_node.activate(); |
| ... | ... | @@ -484,6 +497,7 @@ pub const Object = struct { |
| 484 | 497 | |
| 485 | 498 | try self.genErrorNameTable(comp); |
| 486 | 499 | try self.genCmpLtErrorsLenFunction(comp); |
| 500 | try self.genModuleLevelAssembly(comp); | |
| 487 | 501 | |
| 488 | 502 | if (self.di_builder) |dib| { |
| 489 | 503 | // When lowering debug info for pointers, we emitted the element types as |
| ... | ... | @@ -630,7 +644,17 @@ pub const Object = struct { |
| 630 | 644 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 631 | 645 | |
| 632 | 646 | const llvm_arg_i = @intCast(c_uint, args.items.len) + param_offset; |
| 633 | try args.append(llvm_func.getParam(llvm_arg_i)); | |
| 647 | const param = llvm_func.getParam(llvm_arg_i); | |
| 648 | // It is possible for the calling convention to make the argument's by-reference nature | |
| 649 | // disagree with our canonical value for it, in which case we must dereference here. | |
| 650 | const need_deref = !param_ty.isPtrAtRuntime() and !isByRef(param_ty) and | |
| 651 | (param.typeOf().getTypeKind() == .Pointer); | |
| 652 | const loaded_param = if (!need_deref) param else l: { | |
| 653 | const load_inst = builder.buildLoad(param, ""); | |
| 654 | load_inst.setAlignment(param_ty.abiAlignment(target)); | |
| 655 | break :l load_inst; | |
| 656 | }; | |
| 657 | try args.append(loaded_param); | |
| 634 | 658 | } |
| 635 | 659 | |
| 636 | 660 | var di_file: ?*llvm.DIFile = null; |
| ... | ... | @@ -3729,6 +3753,19 @@ pub const FuncGen = struct { |
| 3729 | 3753 | arg_ptr.setAlignment(alignment); |
| 3730 | 3754 | const store_inst = self.builder.buildStore(llvm_arg, arg_ptr); |
| 3731 | 3755 | store_inst.setAlignment(alignment); |
| 3756 | ||
| 3757 | if (abi_llvm_ty.getTypeKind() == .Pointer) { | |
| 3758 | // In this case, the calling convention wants a pointer, but | |
| 3759 | // we have a value. | |
| 3760 | if (arg_ptr.typeOf() == abi_llvm_ty) { | |
| 3761 | try llvm_args.append(arg_ptr); | |
| 3762 | continue; | |
| 3763 | } | |
| 3764 | const casted_ptr = self.builder.buildBitCast(arg_ptr, abi_llvm_ty, ""); | |
| 3765 | try llvm_args.append(casted_ptr); | |
| 3766 | continue; | |
| 3767 | } | |
| 3768 | ||
| 3732 | 3769 | break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, ""); |
| 3733 | 3770 | }; |
| 3734 | 3771 | |
| ... | ... | @@ -7583,7 +7620,7 @@ pub const FuncGen = struct { |
| 7583 | 7620 | const size_bytes = elem_ty.abiSize(target); |
| 7584 | 7621 | _ = self.builder.buildMemCpy( |
| 7585 | 7622 | self.builder.buildBitCast(ptr, llvm_ptr_u8, ""), |
| 7586 | ptr_ty.ptrAlignment(target), | |
| 7623 | ptr_alignment, | |
| 7587 | 7624 | self.builder.buildBitCast(elem, llvm_ptr_u8, ""), |
| 7588 | 7625 | elem_ty.abiAlignment(target), |
| 7589 | 7626 | self.context.intType(Type.usize.intInfo(target).bits).constInt(size_bytes, .False), |
| ... | ... | @@ -7917,6 +7954,8 @@ fn llvmFieldIndex( |
| 7917 | 7954 | } |
| 7918 | 7955 | |
| 7919 | 7956 | fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { |
| 7957 | if (!fn_info.return_type.hasRuntimeBitsIgnoreComptime()) return false; | |
| 7958 | ||
| 7920 | 7959 | switch (fn_info.cc) { |
| 7921 | 7960 | .Unspecified, .Inline => return isByRef(fn_info.return_type), |
| 7922 | 7961 | .C => switch (target.cpu.arch) { |
| ... | ... | @@ -8016,7 +8055,8 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. |
| 8016 | 8055 | } |
| 8017 | 8056 | } |
| 8018 | 8057 | if (classes[0] == .integer and classes[1] == .none) { |
| 8019 | return llvm_types_buffer[0]; | |
| 8058 | const abi_size = fn_info.return_type.abiSize(target); | |
| 8059 | return dg.context.intType(@intCast(c_uint, abi_size * 8)); | |
| 8020 | 8060 | } |
| 8021 | 8061 | return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); |
| 8022 | 8062 | }, |
| ... | ... | @@ -8110,7 +8150,8 @@ fn lowerFnParamTy(dg: *DeclGen, cc: std.builtin.CallingConvention, ty: Type) !*c |
| 8110 | 8150 | } |
| 8111 | 8151 | } |
| 8112 | 8152 | if (classes[0] == .integer and classes[1] == .none) { |
| 8113 | return llvm_types_buffer[0]; | |
| 8153 | const abi_size = ty.abiSize(target); | |
| 8154 | return dg.context.intType(@intCast(c_uint, abi_size * 8)); | |
| 8114 | 8155 | } |
| 8115 | 8156 | return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); |
| 8116 | 8157 | }, |
src/codegen/llvm/bindings.zig+3| ... | ... | @@ -381,6 +381,9 @@ pub const Module = opaque { |
| 381 | 381 | |
| 382 | 382 | pub const createDIBuilder = ZigLLVMCreateDIBuilder; |
| 383 | 383 | extern fn ZigLLVMCreateDIBuilder(module: *const Module, allow_unresolved: bool) *DIBuilder; |
| 384 | ||
| 385 | pub const setModuleInlineAsm2 = LLVMSetModuleInlineAsm2; | |
| 386 | extern fn LLVMSetModuleInlineAsm2(M: *const Module, Asm: [*]const u8, Len: usize) void; | |
| 384 | 387 | }; |
| 385 | 388 | |
| 386 | 389 | pub const lookupIntrinsicID = LLVMLookupIntrinsicID; |
src/print_zir.zig+14-1| ... | ... | @@ -283,6 +283,7 @@ const Writer = struct { |
| 283 | 283 | => try self.writeStructInit(stream, inst), |
| 284 | 284 | |
| 285 | 285 | .cmpxchg_strong, .cmpxchg_weak => try self.writeCmpxchg(stream, inst), |
| 286 | .atomic_load => try self.writeAtomicLoad(stream, inst), | |
| 286 | 287 | .atomic_store => try self.writeAtomicStore(stream, inst), |
| 287 | 288 | .atomic_rmw => try self.writeAtomicRmw(stream, inst), |
| 288 | 289 | .memcpy => try self.writeMemcpy(stream, inst), |
| ... | ... | @@ -351,7 +352,6 @@ const Writer = struct { |
| 351 | 352 | .offset_of, |
| 352 | 353 | .splat, |
| 353 | 354 | .reduce, |
| 354 | .atomic_load, | |
| 355 | 355 | .bitcast, |
| 356 | 356 | .vector_type, |
| 357 | 357 | .maximum, |
| ... | ... | @@ -929,6 +929,19 @@ const Writer = struct { |
| 929 | 929 | try self.writeSrc(stream, inst_data.src()); |
| 930 | 930 | } |
| 931 | 931 | |
| 932 | fn writeAtomicLoad(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 933 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 934 | const extra = self.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data; | |
| 935 | ||
| 936 | try self.writeInstRef(stream, extra.elem_type); | |
| 937 | try stream.writeAll(", "); | |
| 938 | try self.writeInstRef(stream, extra.ptr); | |
| 939 | try stream.writeAll(", "); | |
| 940 | try self.writeInstRef(stream, extra.ordering); | |
| 941 | try stream.writeAll(") "); | |
| 942 | try self.writeSrc(stream, inst_data.src()); | |
| 943 | } | |
| 944 | ||
| 932 | 945 | fn writeAtomicStore(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
| 933 | 946 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 934 | 947 | const extra = self.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; |
src/stage1/analyze.cpp+1| ... | ... | @@ -7686,6 +7686,7 @@ ZigType *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { |
| 7686 | 7686 | // However for some targets, LLVM incorrectly reports this as 8. |
| 7687 | 7687 | // See: https://github.com/ziglang/zig/issues/2987 |
| 7688 | 7688 | entry->abi_align = 16; |
| 7689 | entry->abi_size = align_forward(entry->abi_size, entry->abi_align); | |
| 7689 | 7690 | } |
| 7690 | 7691 | } |
| 7691 | 7692 |
src/target.zig+57-4| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const llvm = @import("codegen/llvm/bindings.zig"); |
| 3 | const Type = @import("type.zig").Type; | |
| 3 | 4 | |
| 4 | 5 | pub const ArchOsAbi = struct { |
| 5 | 6 | arch: std.Target.Cpu.Arch, |
| ... | ... | @@ -543,10 +544,28 @@ pub fn needUnwindTables(target: std.Target) bool { |
| 543 | 544 | return target.os.tag == .windows; |
| 544 | 545 | } |
| 545 | 546 | |
| 546 | /// TODO this was ported from stage1 but it does not take into account CPU features, | |
| 547 | /// which can affect this value. Audit this! | |
| 548 | pub fn largestAtomicBits(target: std.Target) u32 { | |
| 549 | return switch (target.cpu.arch) { | |
| 547 | pub const AtomicPtrAlignmentError = error{ | |
| 548 | FloatTooBig, | |
| 549 | IntTooBig, | |
| 550 | BadType, | |
| 551 | }; | |
| 552 | ||
| 553 | pub const AtomicPtrAlignmentDiagnostics = struct { | |
| 554 | bits: u16 = undefined, | |
| 555 | max_bits: u16 = undefined, | |
| 556 | }; | |
| 557 | ||
| 558 | /// If ABI alignment of `ty` is OK for atomic operations, returns 0. | |
| 559 | /// Otherwise returns the alignment required on a pointer for the target | |
| 560 | /// to perform atomic operations. | |
| 561 | pub fn atomicPtrAlignment( | |
| 562 | target: std.Target, | |
| 563 | ty: Type, | |
| 564 | diags: *AtomicPtrAlignmentDiagnostics, | |
| 565 | ) AtomicPtrAlignmentError!u32 { | |
| 566 | // TODO this was ported from stage1 but it does not take into account CPU features, | |
| 567 | // which can affect this value. Audit this! | |
| 568 | const max_atomic_bits: u16 = switch (target.cpu.arch) { | |
| 550 | 569 | .avr, |
| 551 | 570 | .msp430, |
| 552 | 571 | .spu_2, |
| ... | ... | @@ -611,6 +630,40 @@ pub fn largestAtomicBits(target: std.Target) u32 { |
| 611 | 630 | |
| 612 | 631 | .x86_64 => 128, |
| 613 | 632 | }; |
| 633 | ||
| 634 | var buffer: Type.Payload.Bits = undefined; | |
| 635 | ||
| 636 | const int_ty = switch (ty.zigTypeTag()) { | |
| 637 | .Int => ty, | |
| 638 | .Enum => ty.intTagType(&buffer), | |
| 639 | .Float => { | |
| 640 | const bit_count = ty.floatBits(target); | |
| 641 | if (bit_count > max_atomic_bits) { | |
| 642 | diags.* = .{ | |
| 643 | .bits = bit_count, | |
| 644 | .max_bits = max_atomic_bits, | |
| 645 | }; | |
| 646 | return error.FloatTooBig; | |
| 647 | } | |
| 648 | return 0; | |
| 649 | }, | |
| 650 | .Bool => return 0, | |
| 651 | else => { | |
| 652 | if (ty.isPtrAtRuntime()) return 0; | |
| 653 | return error.BadType; | |
| 654 | }, | |
| 655 | }; | |
| 656 | ||
| 657 | const bit_count = int_ty.intInfo(target).bits; | |
| 658 | if (bit_count > max_atomic_bits) { | |
| 659 | diags.* = .{ | |
| 660 | .bits = bit_count, | |
| 661 | .max_bits = max_atomic_bits, | |
| 662 | }; | |
| 663 | return error.IntTooBig; | |
| 664 | } | |
| 665 | ||
| 666 | return 0; | |
| 614 | 667 | } |
| 615 | 668 | |
| 616 | 669 | pub fn defaultAddressSpace( |
src/type.zig+24-16| ... | ... | @@ -2788,11 +2788,6 @@ pub const Type = extern union { |
| 2788 | 2788 | return AbiAlignmentAdvanced{ .scalar = target_util.defaultFunctionAlignment(target) }; |
| 2789 | 2789 | }, |
| 2790 | 2790 | |
| 2791 | .i16, .u16 => return AbiAlignmentAdvanced{ .scalar = 2 }, | |
| 2792 | .i32, .u32 => return AbiAlignmentAdvanced{ .scalar = 4 }, | |
| 2793 | .i64, .u64 => return AbiAlignmentAdvanced{ .scalar = 8 }, | |
| 2794 | .u128, .i128 => return AbiAlignmentAdvanced{ .scalar = 16 }, | |
| 2795 | ||
| 2796 | 2791 | .isize, |
| 2797 | 2792 | .usize, |
| 2798 | 2793 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -2865,14 +2860,15 @@ pub const Type = extern union { |
| 2865 | 2860 | // ABI alignment of vectors? |
| 2866 | 2861 | .vector => return AbiAlignmentAdvanced{ .scalar = 16 }, |
| 2867 | 2862 | |
| 2863 | .i16, .u16 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(16, target) }, | |
| 2864 | .i32, .u32 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(32, target) }, | |
| 2865 | .i64, .u64 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(64, target) }, | |
| 2866 | .u128, .i128 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(128, target) }, | |
| 2867 | ||
| 2868 | 2868 | .int_signed, .int_unsigned => { |
| 2869 | 2869 | const bits: u16 = ty.cast(Payload.Bits).?.data; |
| 2870 | 2870 | if (bits == 0) return AbiAlignmentAdvanced{ .scalar = 0 }; |
| 2871 | if (bits <= 8) return AbiAlignmentAdvanced{ .scalar = 1 }; | |
| 2872 | if (bits <= 16) return AbiAlignmentAdvanced{ .scalar = 2 }; | |
| 2873 | if (bits <= 32) return AbiAlignmentAdvanced{ .scalar = 4 }; | |
| 2874 | if (bits <= 64) return AbiAlignmentAdvanced{ .scalar = 8 }; | |
| 2875 | return AbiAlignmentAdvanced{ .scalar = 16 }; | |
| 2871 | return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(bits, target) }; | |
| 2876 | 2872 | }, |
| 2877 | 2873 | |
| 2878 | 2874 | .optional => { |
| ... | ... | @@ -3113,10 +3109,6 @@ pub const Type = extern union { |
| 3113 | 3109 | assert(elem_size >= payload.elem_type.abiAlignment(target)); |
| 3114 | 3110 | return (payload.len + 1) * elem_size; |
| 3115 | 3111 | }, |
| 3116 | .i16, .u16 => return 2, | |
| 3117 | .i32, .u32 => return 4, | |
| 3118 | .i64, .u64 => return 8, | |
| 3119 | .u128, .i128 => return 16, | |
| 3120 | 3112 | |
| 3121 | 3113 | .isize, |
| 3122 | 3114 | .usize, |
| ... | ... | @@ -3189,10 +3181,14 @@ pub const Type = extern union { |
| 3189 | 3181 | .error_set_merged, |
| 3190 | 3182 | => return 2, // TODO revisit this when we have the concept of the error tag type |
| 3191 | 3183 | |
| 3184 | .i16, .u16 => return intAbiSize(16, target), | |
| 3185 | .i32, .u32 => return intAbiSize(32, target), | |
| 3186 | .i64, .u64 => return intAbiSize(64, target), | |
| 3187 | .u128, .i128 => return intAbiSize(128, target), | |
| 3192 | 3188 | .int_signed, .int_unsigned => { |
| 3193 | 3189 | const bits: u16 = self.cast(Payload.Bits).?.data; |
| 3194 | 3190 | if (bits == 0) return 0; |
| 3195 | return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8); | |
| 3191 | return intAbiSize(bits, target); | |
| 3196 | 3192 | }, |
| 3197 | 3193 | |
| 3198 | 3194 | .optional => { |
| ... | ... | @@ -3234,6 +3230,18 @@ pub const Type = extern union { |
| 3234 | 3230 | }; |
| 3235 | 3231 | } |
| 3236 | 3232 | |
| 3233 | fn intAbiSize(bits: u16, target: Target) u64 { | |
| 3234 | const alignment = intAbiAlignment(bits, target); | |
| 3235 | return std.mem.alignForwardGeneric(u64, (bits + 7) / 8, alignment); | |
| 3236 | } | |
| 3237 | ||
| 3238 | fn intAbiAlignment(bits: u16, target: Target) u32 { | |
| 3239 | return @minimum( | |
| 3240 | std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8), | |
| 3241 | target.maxIntAlignment(), | |
| 3242 | ); | |
| 3243 | } | |
| 3244 | ||
| 3237 | 3245 | /// Asserts the type has the bit size already resolved. |
| 3238 | 3246 | pub fn bitSize(ty: Type, target: Target) u64 { |
| 3239 | 3247 | return switch (ty.tag()) { |
| ... | ... | @@ -5169,7 +5177,7 @@ pub const Type = extern union { |
| 5169 | 5177 | |
| 5170 | 5178 | const field = it.struct_obj.fields.values()[it.field]; |
| 5171 | 5179 | defer it.field += 1; |
| 5172 | if (!field.ty.hasRuntimeBits()) | |
| 5180 | if (!field.ty.hasRuntimeBits() or field.is_comptime) | |
| 5173 | 5181 | return FieldOffset{ .field = it.field, .offset = it.offset }; |
| 5174 | 5182 | |
| 5175 | 5183 | const field_align = field.normalAlignment(it.target); |
test/behavior/align.zig+111-26| ... | ... | @@ -47,41 +47,128 @@ fn expects4(x: *align(4) u32) void { |
| 47 | 47 | x.* += 1; |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | test "alignment of structs" { | |
| 50 | test "alignment of struct with pointer has same alignment as usize" { | |
| 51 | 51 | try expect(@alignOf(struct { |
| 52 | 52 | a: i32, |
| 53 | 53 | b: *i32, |
| 54 | 54 | }) == @alignOf(usize)); |
| 55 | 55 | } |
| 56 | 56 | |
| 57 | test "alignment of >= 128-bit integer type" { | |
| 58 | try expect(@alignOf(u128) == 16); | |
| 59 | try expect(@alignOf(u129) == 16); | |
| 60 | } | |
| 61 | ||
| 62 | test "alignment of struct with 128-bit field" { | |
| 63 | try expect(@alignOf(struct { | |
| 64 | x: u128, | |
| 65 | }) == 16); | |
| 66 | ||
| 67 | comptime { | |
| 68 | try expect(@alignOf(struct { | |
| 69 | x: u128, | |
| 70 | }) == 16); | |
| 57 | test "alignment and size of structs with 128-bit fields" { | |
| 58 | if (builtin.zig_backend == .stage1) { | |
| 59 | // stage1 gets the wrong answer for a lot of targets | |
| 60 | return error.SkipZigTest; | |
| 71 | 61 | } |
| 72 | } | |
| 62 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 63 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 73 | 64 | |
| 74 | test "size of extern struct with 128-bit field" { | |
| 75 | try expect(@sizeOf(extern struct { | |
| 65 | const A = struct { | |
| 66 | x: u128, | |
| 67 | }; | |
| 68 | const B = extern struct { | |
| 76 | 69 | x: u128, |
| 77 | 70 | y: u8, |
| 78 | }) == 32); | |
| 79 | ||
| 71 | }; | |
| 72 | const expected = switch (builtin.cpu.arch) { | |
| 73 | .arm, | |
| 74 | .armeb, | |
| 75 | .thumb, | |
| 76 | .thumbeb, | |
| 77 | .hexagon, | |
| 78 | .mips, | |
| 79 | .mipsel, | |
| 80 | .powerpc, | |
| 81 | .powerpcle, | |
| 82 | .r600, | |
| 83 | .amdgcn, | |
| 84 | .riscv32, | |
| 85 | .sparc, | |
| 86 | .sparcel, | |
| 87 | .s390x, | |
| 88 | .lanai, | |
| 89 | .wasm32, | |
| 90 | .wasm64, | |
| 91 | => .{ | |
| 92 | .a_align = 8, | |
| 93 | .a_size = 16, | |
| 94 | ||
| 95 | .b_align = 8, | |
| 96 | .b_size = 24, | |
| 97 | ||
| 98 | .u128_align = 8, | |
| 99 | .u128_size = 16, | |
| 100 | .u129_align = 8, | |
| 101 | .u129_size = 24, | |
| 102 | }, | |
| 103 | ||
| 104 | .i386 => switch (builtin.os.tag) { | |
| 105 | .windows => .{ | |
| 106 | .a_align = 8, | |
| 107 | .a_size = 16, | |
| 108 | ||
| 109 | .b_align = 8, | |
| 110 | .b_size = 24, | |
| 111 | ||
| 112 | .u128_align = 8, | |
| 113 | .u128_size = 16, | |
| 114 | .u129_align = 8, | |
| 115 | .u129_size = 24, | |
| 116 | }, | |
| 117 | else => .{ | |
| 118 | .a_align = 4, | |
| 119 | .a_size = 16, | |
| 120 | ||
| 121 | .b_align = 4, | |
| 122 | .b_size = 20, | |
| 123 | ||
| 124 | .u128_align = 4, | |
| 125 | .u128_size = 16, | |
| 126 | .u129_align = 4, | |
| 127 | .u129_size = 20, | |
| 128 | }, | |
| 129 | }, | |
| 130 | ||
| 131 | .mips64, | |
| 132 | .mips64el, | |
| 133 | .powerpc64, | |
| 134 | .powerpc64le, | |
| 135 | .riscv64, | |
| 136 | .sparcv9, | |
| 137 | .x86_64, | |
| 138 | .aarch64, | |
| 139 | .aarch64_be, | |
| 140 | .aarch64_32, | |
| 141 | .bpfel, | |
| 142 | .bpfeb, | |
| 143 | .nvptx, | |
| 144 | .nvptx64, | |
| 145 | => .{ | |
| 146 | .a_align = 16, | |
| 147 | .a_size = 16, | |
| 148 | ||
| 149 | .b_align = 16, | |
| 150 | .b_size = 32, | |
| 151 | ||
| 152 | .u128_align = 16, | |
| 153 | .u128_size = 16, | |
| 154 | .u129_align = 16, | |
| 155 | .u129_size = 32, | |
| 156 | }, | |
| 157 | ||
| 158 | else => return error.SkipZigTest, | |
| 159 | }; | |
| 80 | 160 | comptime { |
| 81 | try expect(@sizeOf(extern struct { | |
| 82 | x: u128, | |
| 83 | y: u8, | |
| 84 | }) == 32); | |
| 161 | std.debug.assert(@alignOf(A) == expected.a_align); | |
| 162 | std.debug.assert(@sizeOf(A) == expected.a_size); | |
| 163 | ||
| 164 | std.debug.assert(@alignOf(B) == expected.b_align); | |
| 165 | std.debug.assert(@sizeOf(B) == expected.b_size); | |
| 166 | ||
| 167 | std.debug.assert(@alignOf(u128) == expected.u128_align); | |
| 168 | std.debug.assert(@sizeOf(u128) == expected.u128_size); | |
| 169 | ||
| 170 | std.debug.assert(@alignOf(u129) == expected.u129_align); | |
| 171 | std.debug.assert(@sizeOf(u129) == expected.u129_size); | |
| 85 | 172 | } |
| 86 | 173 | } |
| 87 | 174 | |
| ... | ... | @@ -328,7 +415,6 @@ test "read 128-bit field from default aligned struct in stack memory" { |
| 328 | 415 | .nevermind = 1, |
| 329 | 416 | .badguy = 12, |
| 330 | 417 | }; |
| 331 | try expect((@ptrToInt(&default_aligned.badguy) % 16) == 0); | |
| 332 | 418 | try expect(12 == default_aligned.badguy); |
| 333 | 419 | } |
| 334 | 420 | |
| ... | ... | @@ -345,7 +431,6 @@ test "read 128-bit field from default aligned struct in global memory" { |
| 345 | 431 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 346 | 432 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 347 | 433 | |
| 348 | try expect((@ptrToInt(&default_aligned_global.badguy) % 16) == 0); | |
| 349 | 434 | try expect(12 == default_aligned_global.badguy); |
| 350 | 435 | } |
| 351 | 436 |
test/behavior/asm.zig-1| ... | ... | @@ -23,7 +23,6 @@ test "module level assembly" { |
| 23 | 23 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 24 | 24 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 25 | 25 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 26 | if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO | |
| 27 | 26 | |
| 28 | 27 | if (is_x86_64_linux) { |
| 29 | 28 | try expect(this_is_my_alias() == 1234); |
test/behavior/atomics.zig+1-1| ... | ... | @@ -127,7 +127,7 @@ test "128-bit cmpxchg" { |
| 127 | 127 | } |
| 128 | 128 | |
| 129 | 129 | fn test_u128_cmpxchg() !void { |
| 130 | var x: u128 = 1234; | |
| 130 | var x: u128 align(16) = 1234; | |
| 131 | 131 | if (@cmpxchgWeak(u128, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { |
| 132 | 132 | try expect(x1 == 1234); |
| 133 | 133 | } else { |
test/behavior/bitcast.zig+7-12| ... | ... | @@ -120,6 +120,10 @@ test "bitcast generates a temporary value" { |
| 120 | 120 | } |
| 121 | 121 | |
| 122 | 122 | test "@bitCast packed structs at runtime and comptime" { |
| 123 | if (builtin.zig_backend == .stage1) { | |
| 124 | // stage1 gets the wrong answer for a lot of targets | |
| 125 | return error.SkipZigTest; | |
| 126 | } | |
| 123 | 127 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 124 | 128 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 125 | 129 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| ... | ... | @@ -138,18 +142,9 @@ test "@bitCast packed structs at runtime and comptime" { |
| 138 | 142 | fn doTheTest() !void { |
| 139 | 143 | var full = Full{ .number = 0x1234 }; |
| 140 | 144 | var two_halves = @bitCast(Divided, full); |
| 141 | switch (native_endian) { | |
| 142 | .Big => { | |
| 143 | try expect(two_halves.half1 == 0x12); | |
| 144 | try expect(two_halves.quarter3 == 0x3); | |
| 145 | try expect(two_halves.quarter4 == 0x4); | |
| 146 | }, | |
| 147 | .Little => { | |
| 148 | try expect(two_halves.half1 == 0x34); | |
| 149 | try expect(two_halves.quarter3 == 0x2); | |
| 150 | try expect(two_halves.quarter4 == 0x1); | |
| 151 | }, | |
| 152 | } | |
| 145 | try expect(two_halves.half1 == 0x34); | |
| 146 | try expect(two_halves.quarter3 == 0x2); | |
| 147 | try expect(two_halves.quarter4 == 0x1); | |
| 153 | 148 | } |
| 154 | 149 | }; |
| 155 | 150 | try S.doTheTest(); |
test/behavior/struct.zig+9-6| ... | ... | @@ -499,17 +499,18 @@ const Bitfields = packed struct { |
| 499 | 499 | f7: u8, |
| 500 | 500 | }; |
| 501 | 501 | |
| 502 | test "native bit field understands endianness" { | |
| 503 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 502 | test "packed struct fields are ordered from LSB to MSB" { | |
| 503 | if (builtin.zig_backend == .stage1) { | |
| 504 | // stage1 gets the wrong answer for a lot of targets | |
| 505 | return error.SkipZigTest; | |
| 506 | } | |
| 507 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 504 | 508 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 505 | 509 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 506 | 510 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 507 | 511 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 508 | 512 | |
| 509 | var all: u64 = if (native_endian != .Little) | |
| 510 | 0x1111222233445677 | |
| 511 | else | |
| 512 | 0x7765443322221111; | |
| 513 | var all: u64 = 0x7765443322221111; | |
| 513 | 514 | var bytes: [8]u8 = undefined; |
| 514 | 515 | @memcpy(&bytes, @ptrCast([*]u8, &all), 8); |
| 515 | 516 | var bitfields = @ptrCast(*Bitfields, &bytes).*; |
| ... | ... | @@ -974,6 +975,8 @@ test "comptime struct field" { |
| 974 | 975 | comptime b: i32 = 1234, |
| 975 | 976 | }; |
| 976 | 977 | |
| 978 | comptime std.debug.assert(@sizeOf(T) == 4); | |
| 979 | ||
| 977 | 980 | var foo: T = undefined; |
| 978 | 981 | comptime try expect(foo.b == 1234); |
| 979 | 982 | } |