| 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,6 +1773,88 @@ pub const Target = struct { |
| 1773 | else => false, | 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 | test { | 1860 | test { |
src/AstGen.zig+10-50| ... | @@ -7423,42 +7423,22 @@ fn builtinCall( | ... | @@ -7423,42 +7423,22 @@ fn builtinCall( |
| 7423 | }, | 7423 | }, |
| 7424 | 7424 | ||
| 7425 | .atomic_load => { | 7425 | .atomic_load => { |
| 7426 | const int_type = try typeExpr(gz, scope, params[0]); | 7426 | const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{ |
| 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{ | ||
| 7438 | // zig fmt: off | 7427 | // zig fmt: off |
| 7439 | .lhs = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), | 7428 | .elem_type = try typeExpr(gz, scope, params[0]), |
| 7440 | .rhs = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[2]), | 7429 | .ptr = try expr (gz, scope, .none, params[1]), |
| 7430 | .ordering = try expr (gz, scope, .{ .coerced_ty = .atomic_order_type }, params[2]), | ||
| 7441 | // zig fmt: on | 7431 | // zig fmt: on |
| 7442 | }); | 7432 | }); |
| 7443 | return rvalue(gz, rl, result, node); | 7433 | return rvalue(gz, rl, result, node); |
| 7444 | }, | 7434 | }, |
| 7445 | .atomic_rmw => { | 7435 | .atomic_rmw => { |
| 7446 | const int_type = try typeExpr(gz, scope, params[0]); | 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 | const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{ | 7437 | const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{ |
| 7458 | // zig fmt: off | 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 | .operation = try expr(gz, scope, .{ .coerced_ty = .atomic_rmw_op_type }, params[2]), | 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 | .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]), | 7442 | .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]), |
| 7463 | // zig fmt: on | 7443 | // zig fmt: on |
| 7464 | }); | 7444 | }); |
| ... | @@ -7466,20 +7446,10 @@ fn builtinCall( | ... | @@ -7466,20 +7446,10 @@ fn builtinCall( |
| 7466 | }, | 7446 | }, |
| 7467 | .atomic_store => { | 7447 | .atomic_store => { |
| 7468 | const int_type = try typeExpr(gz, scope, params[0]); | 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 | const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{ | 7449 | const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{ |
| 7480 | // zig fmt: off | 7450 | // zig fmt: off |
| 7481 | .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), | 7451 | .ptr = try expr(gz, scope, .none, params[1]), |
| 7482 | .operand = try expr(gz, scope, .{ .coerced_ty = int_type }, params[2]), | 7452 | .operand = try expr(gz, scope, .{ .ty = int_type }, params[2]), |
| 7483 | .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[3]), | 7453 | .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[3]), |
| 7484 | // zig fmt: on | 7454 | // zig fmt: on |
| 7485 | }); | 7455 | }); |
| ... | @@ -7684,20 +7654,10 @@ fn cmpxchg( | ... | @@ -7684,20 +7654,10 @@ fn cmpxchg( |
| 7684 | tag: Zir.Inst.Tag, | 7654 | tag: Zir.Inst.Tag, |
| 7685 | ) InnerError!Zir.Inst.Ref { | 7655 | ) InnerError!Zir.Inst.Ref { |
| 7686 | const int_type = try typeExpr(gz, scope, params[0]); | 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 | const result = try gz.addPlNode(tag, node, Zir.Inst.Cmpxchg{ | 7657 | const result = try gz.addPlNode(tag, node, Zir.Inst.Cmpxchg{ |
| 7698 | // zig fmt: off | 7658 | // zig fmt: off |
| 7699 | .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), | 7659 | .ptr = try expr(gz, scope, .none, params[1]), |
| 7700 | .expected_value = try expr(gz, scope, .{ .coerced_ty = int_type }, params[2]), | 7660 | .expected_value = try expr(gz, scope, .{ .ty = int_type }, params[2]), |
| 7701 | .new_value = try expr(gz, scope, .{ .coerced_ty = int_type }, params[3]), | 7661 | .new_value = try expr(gz, scope, .{ .coerced_ty = int_type }, params[3]), |
| 7702 | .success_order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]), | 7662 | .success_order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]), |
| 7703 | .failure_order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[5]), | 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,6 +151,8 @@ allocated_decls: std.SegmentedList(Decl, 0) = .{}, |
| 151 | /// When a Decl object is freed from `allocated_decls`, it is pushed into this stack. | 151 | /// When a Decl object is freed from `allocated_decls`, it is pushed into this stack. |
| 152 | decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{}, | 152 | decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{}, |
| 153 | 153 | ||
| 154 | global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{}, | ||
| 155 | |||
| 154 | const MonomorphedFuncsSet = std.HashMapUnmanaged( | 156 | const MonomorphedFuncsSet = std.HashMapUnmanaged( |
| 155 | *Fn, | 157 | *Fn, |
| 156 | void, | 158 | void, |
| ... | @@ -2831,6 +2833,7 @@ pub fn deinit(mod: *Module) void { | ... | @@ -2831,6 +2833,7 @@ pub fn deinit(mod: *Module) void { |
| 2831 | 2833 | ||
| 2832 | mod.decls_free_list.deinit(gpa); | 2834 | mod.decls_free_list.deinit(gpa); |
| 2833 | mod.allocated_decls.deinit(gpa); | 2835 | mod.allocated_decls.deinit(gpa); |
| 2836 | mod.global_assembly.deinit(gpa); | ||
| 2834 | } | 2837 | } |
| 2835 | 2838 | ||
| 2836 | pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void { | 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,6 +2845,9 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void { |
| 2842 | if (decl.deletion_flag) { | 2845 | if (decl.deletion_flag) { |
| 2843 | assert(mod.deletion_set.swapRemove(decl_index)); | 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 | if (decl.has_tv) { | 2851 | if (decl.has_tv) { |
| 2846 | if (decl.getInnerNamespace()) |namespace| { | 2852 | if (decl.getInnerNamespace()) |namespace| { |
| 2847 | namespace.destroyDecls(mod); | 2853 | namespace.destroyDecls(mod); |
| ... | @@ -5714,3 +5720,12 @@ pub fn markDeclAlive(mod: *Module, decl: *Decl) void { | ... | @@ -5714,3 +5720,12 @@ pub fn markDeclAlive(mod: *Module, decl: *Decl) void { |
| 5714 | fn markDeclIndexAlive(mod: *Module, decl_index: Decl.Index) void { | 5720 | fn markDeclIndexAlive(mod: *Module, decl_index: Decl.Index) void { |
| 5715 | return mod.markDeclAlive(mod.declPtr(decl_index)); | 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,16 +10517,35 @@ fn zirAsm( |
| 10517 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | 10517 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; |
| 10518 | const is_global_assembly = sema.func == null; | 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 | if (extra.data.asm_source == 0) { | 10520 | if (extra.data.asm_source == 0) { |
| 10525 | // This can move to become an AstGen error after inline assembly improvements land | 10521 | // This can move to become an AstGen error after inline assembly improvements land |
| 10526 | // and stage1 code matches stage2 code. | 10522 | // and stage1 code matches stage2 code. |
| 10527 | return sema.fail(block, src, "assembly code must use string literal syntax", .{}); | 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 | if (outputs_len > 1) { | 10549 | if (outputs_len > 1) { |
| 10531 | return sema.fail(block, src, "TODO implement Sema for asm with more than 1 output", .{}); | 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,7 +10610,6 @@ fn zirAsm( |
| 10591 | needed_capacity += name.*.len / 4 + 1; | 10610 | needed_capacity += name.*.len / 4 + 1; |
| 10592 | } | 10611 | } |
| 10593 | 10612 | ||
| 10594 | const asm_source = sema.code.nullTerminatedString(extra.data.asm_source); | ||
| 10595 | needed_capacity += (asm_source.len + 3) / 4; | 10613 | needed_capacity += (asm_source.len + 3) / 4; |
| 10596 | 10614 | ||
| 10597 | const gpa = sema.gpa; | 10615 | const gpa = sema.gpa; |
| ... | @@ -14715,51 +14733,64 @@ fn checkNumericType( | ... | @@ -14715,51 +14733,64 @@ fn checkNumericType( |
| 14715 | } | 14733 | } |
| 14716 | } | 14734 | } |
| 14717 | 14735 | ||
| 14718 | fn checkAtomicOperandType( | 14736 | /// Returns the casted pointer. |
| 14737 | fn checkAtomicPtrOperand( | ||
| 14719 | sema: *Sema, | 14738 | sema: *Sema, |
| 14720 | block: *Block, | 14739 | block: *Block, |
| 14721 | ty_src: LazySrcLoc, | 14740 | elem_ty: Type, |
| 14722 | ty: Type, | 14741 | elem_ty_src: LazySrcLoc, |
| 14723 | ) CompileError!void { | 14742 | ptr: Air.Inst.Ref, |
| 14724 | var buffer: Type.Payload.Bits = undefined; | 14743 | ptr_src: LazySrcLoc, |
| 14744 | ptr_const: bool, | ||
| 14745 | ) CompileError!Air.Inst.Ref { | ||
| 14725 | const target = sema.mod.getTarget(); | 14746 | const target = sema.mod.getTarget(); |
| 14726 | const max_atomic_bits = target_util.largestAtomicBits(target); | 14747 | var diag: target_util.AtomicPtrAlignmentDiagnostics = .{}; |
| 14727 | const int_ty = switch (ty.zigTypeTag()) { | 14748 | const alignment = target_util.atomicPtrAlignment(target, elem_ty, &diag) catch |err| switch (err) { |
| 14728 | .Int => ty, | 14749 | error.FloatTooBig => return sema.fail( |
| 14729 | .Enum => ty.intTagType(&buffer), | 14750 | block, |
| 14730 | .Float => { | 14751 | elem_ty_src, |
| 14731 | const bit_count = ty.floatBits(target); | 14752 | "expected {d}-bit float type or smaller; found {d}-bit float type", |
| 14732 | if (bit_count > max_atomic_bits) { | 14753 | .{ diag.max_bits, diag.bits }, |
| 14733 | return sema.fail( | 14754 | ), |
| 14734 | block, | 14755 | error.IntTooBig => return sema.fail( |
| 14735 | ty_src, | 14756 | block, |
| 14736 | "expected {d}-bit float type or smaller; found {d}-bit float type", | 14757 | elem_ty_src, |
| 14737 | .{ max_atomic_bits, bit_count }, | 14758 | "expected {d}-bit integer type or smaller; found {d}-bit integer type", |
| 14738 | ); | 14759 | .{ diag.max_bits, diag.bits }, |
| 14739 | } | 14760 | ), |
| 14740 | return; | 14761 | error.BadType => return sema.fail( |
| 14741 | }, | 14762 | block, |
| 14742 | .Bool => return, // Will be treated as `u8`. | 14763 | elem_ty_src, |
| 14743 | else => { | 14764 | "expected bool, integer, float, enum, or pointer type; found {}", |
| 14744 | if (ty.isPtrAtRuntime()) return; | 14765 | .{elem_ty.fmt(sema.mod)}, |
| 14766 | ), | ||
| 14767 | }; | ||
| 14745 | 14768 | ||
| 14746 | return sema.fail( | 14769 | var wanted_ptr_data: Type.Payload.Pointer.Data = .{ |
| 14747 | block, | 14770 | .pointee_type = elem_ty, |
| 14748 | ty_src, | 14771 | .@"align" = alignment, |
| 14749 | "expected bool, integer, float, enum, or pointer type; found {}", | 14772 | .@"addrspace" = .generic, |
| 14750 | .{ty.fmt(sema.mod)}, | 14773 | .mutable = !ptr_const, |
| 14751 | ); | 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; | 14785 | |
| 14755 | if (bit_count > max_atomic_bits) { | 14786 | wanted_ptr_data.@"addrspace" = ptr_data.@"addrspace"; |
| 14756 | return sema.fail( | 14787 | wanted_ptr_data.@"allowzero" = ptr_data.@"allowzero"; |
| 14757 | block, | 14788 | wanted_ptr_data.@"volatile" = ptr_data.@"volatile"; |
| 14758 | ty_src, | 14789 | |
| 14759 | "expected {d}-bit integer type or smaller; found {d}-bit integer type", | 14790 | const wanted_ptr_ty = try Type.ptr(sema.arena, sema.mod, wanted_ptr_data); |
| 14760 | .{ max_atomic_bits, bit_count }, | 14791 | const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); |
| 14761 | ); | 14792 | |
| 14762 | } | 14793 | return casted_ptr; |
| 14763 | } | 14794 | } |
| 14764 | 14795 | ||
| 14765 | fn checkPtrIsNotComptimeMutable( | 14796 | fn checkPtrIsNotComptimeMutable( |
| ... | @@ -15036,10 +15067,8 @@ fn zirCmpxchg( | ... | @@ -15036,10 +15067,8 @@ fn zirCmpxchg( |
| 15036 | const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; | 15067 | const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; |
| 15037 | const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = inst_data.src_node }; | 15068 | const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = inst_data.src_node }; |
| 15038 | // zig fmt: on | 15069 | // zig fmt: on |
| 15039 | const ptr = sema.resolveInst(extra.ptr); | 15070 | const expected_value = sema.resolveInst(extra.expected_value); |
| 15040 | const ptr_ty = sema.typeOf(ptr); | 15071 | const elem_ty = sema.typeOf(expected_value); |
| 15041 | const elem_ty = ptr_ty.elemType(); | ||
| 15042 | try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty); | ||
| 15043 | if (elem_ty.zigTypeTag() == .Float) { | 15072 | if (elem_ty.zigTypeTag() == .Float) { |
| 15044 | return sema.fail( | 15073 | return sema.fail( |
| 15045 | block, | 15074 | block, |
| ... | @@ -15048,7 +15077,8 @@ fn zirCmpxchg( | ... | @@ -15048,7 +15077,8 @@ fn zirCmpxchg( |
| 15048 | .{elem_ty.fmt(sema.mod)}, | 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 | const new_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.new_value), new_value_src); | 15082 | const new_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.new_value), new_value_src); |
| 15053 | const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order); | 15083 | const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order); |
| 15054 | const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order); | 15084 | const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order); |
| ... | @@ -15081,6 +15111,7 @@ fn zirCmpxchg( | ... | @@ -15081,6 +15111,7 @@ fn zirCmpxchg( |
| 15081 | // to become undef as well | 15111 | // to become undef as well |
| 15082 | return sema.addConstUndef(result_ty); | 15112 | return sema.addConstUndef(result_ty); |
| 15083 | } | 15113 | } |
| 15114 | const ptr_ty = sema.typeOf(ptr); | ||
| 15084 | const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; | 15115 | const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; |
| 15085 | const result_val = if (stored_val.eql(expected_val, elem_ty, sema.mod)) blk: { | 15116 | const result_val = if (stored_val.eql(expected_val, elem_ty, sema.mod)) blk: { |
| 15086 | try sema.storePtr(block, src, ptr, new_value); | 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,17 +15518,16 @@ fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 15487 | 15518 | ||
| 15488 | fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 15519 | fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 15489 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 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 | // zig fmt: off | 15522 | // zig fmt: off |
| 15492 | const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | 15523 | const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 15493 | const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | 15524 | const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 15494 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | 15525 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 15495 | // zig fmt: on | 15526 | // zig fmt: on |
| 15496 | const ptr = sema.resolveInst(extra.lhs); | 15527 | const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); |
| 15497 | const ptr_ty = sema.typeOf(ptr); | 15528 | const uncasted_ptr = sema.resolveInst(extra.ptr); |
| 15498 | const elem_ty = ptr_ty.elemType(); | 15529 | const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); |
| 15499 | try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty); | 15530 | const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); |
| 15500 | const order = try sema.resolveAtomicOrder(block, order_src, extra.rhs); | ||
| 15501 | 15531 | ||
| 15502 | switch (order) { | 15532 | switch (order) { |
| 15503 | .Release, .AcqRel => { | 15533 | .Release, .AcqRel => { |
| ... | @@ -15516,7 +15546,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! | ... | @@ -15516,7 +15546,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! |
| 15516 | } | 15546 | } |
| 15517 | 15547 | ||
| 15518 | if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { | 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 | return sema.addConstant(elem_ty, elem_val); | 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,19 +15566,19 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 15536 | const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; | 15566 | const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; |
| 15537 | const src = inst_data.src(); | 15567 | const src = inst_data.src(); |
| 15538 | // zig fmt: off | 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 | const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | 15570 | const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 15541 | const op_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | 15571 | const op_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 15542 | const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; | 15572 | const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; |
| 15543 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; | 15573 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; |
| 15544 | // zig fmt: on | 15574 | // zig fmt: on |
| 15545 | const ptr = sema.resolveInst(extra.ptr); | 15575 | const operand = sema.resolveInst(extra.operand); |
| 15546 | const ptr_ty = sema.typeOf(ptr); | 15576 | const elem_ty = sema.typeOf(operand); |
| 15547 | const operand_ty = ptr_ty.elemType(); | 15577 | const uncasted_ptr = sema.resolveInst(extra.ptr); |
| 15548 | try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty); | 15578 | const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); |
| 15549 | const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); | 15579 | const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); |
| 15550 | 15580 | ||
| 15551 | switch (operand_ty.zigTypeTag()) { | 15581 | switch (elem_ty.zigTypeTag()) { |
| 15552 | .Enum => if (op != .Xchg) { | 15582 | .Enum => if (op != .Xchg) { |
| 15553 | return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{}); | 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,7 +15591,6 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 15561 | }, | 15591 | }, |
| 15562 | else => {}, | 15592 | else => {}, |
| 15563 | } | 15593 | } |
| 15564 | const operand = try sema.coerce(block, operand_ty, sema.resolveInst(extra.operand), operand_src); | ||
| 15565 | const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); | 15594 | const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); |
| 15566 | 15595 | ||
| 15567 | if (order == .Unordered) { | 15596 | if (order == .Unordered) { |
| ... | @@ -15569,8 +15598,8 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A | ... | @@ -15569,8 +15598,8 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 15569 | } | 15598 | } |
| 15570 | 15599 | ||
| 15571 | // special case zero bit types | 15600 | // special case zero bit types |
| 15572 | if (try sema.typeHasOnePossibleValue(block, operand_ty_src, operand_ty)) |val| { | 15601 | if (try sema.typeHasOnePossibleValue(block, elem_ty_src, elem_ty)) |val| { |
| 15573 | return sema.addConstant(operand_ty, val); | 15602 | return sema.addConstant(elem_ty, val); |
| 15574 | } | 15603 | } |
| 15575 | 15604 | ||
| 15576 | const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { | 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,22 +15610,23 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 15581 | }; | 15610 | }; |
| 15582 | if (ptr_val.isComptimeMutablePtr()) { | 15611 | if (ptr_val.isComptimeMutablePtr()) { |
| 15583 | const target = sema.mod.getTarget(); | 15612 | const target = sema.mod.getTarget(); |
| 15613 | const ptr_ty = sema.typeOf(ptr); | ||
| 15584 | const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; | 15614 | const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; |
| 15585 | const new_val = switch (op) { | 15615 | const new_val = switch (op) { |
| 15586 | // zig fmt: off | 15616 | // zig fmt: off |
| 15587 | .Xchg => operand_val, | 15617 | .Xchg => operand_val, |
| 15588 | .Add => try stored_val.numberAddWrap(operand_val, operand_ty, sema.arena, target), | 15618 | .Add => try stored_val.numberAddWrap(operand_val, elem_ty, sema.arena, target), |
| 15589 | .Sub => try stored_val.numberSubWrap(operand_val, operand_ty, sema.arena, target), | 15619 | .Sub => try stored_val.numberSubWrap(operand_val, elem_ty, sema.arena, target), |
| 15590 | .And => try stored_val.bitwiseAnd (operand_val, operand_ty, sema.arena, target), | 15620 | .And => try stored_val.bitwiseAnd (operand_val, elem_ty, sema.arena, target), |
| 15591 | .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena, target), | 15621 | .Nand => try stored_val.bitwiseNand (operand_val, elem_ty, sema.arena, target), |
| 15592 | .Or => try stored_val.bitwiseOr (operand_val, operand_ty, sema.arena, target), | 15622 | .Or => try stored_val.bitwiseOr (operand_val, elem_ty, sema.arena, target), |
| 15593 | .Xor => try stored_val.bitwiseXor (operand_val, operand_ty, sema.arena, target), | 15623 | .Xor => try stored_val.bitwiseXor (operand_val, elem_ty, sema.arena, target), |
| 15594 | .Max => stored_val.numberMax (operand_val, target), | 15624 | .Max => stored_val.numberMax (operand_val, target), |
| 15595 | .Min => stored_val.numberMin (operand_val, target), | 15625 | .Min => stored_val.numberMin (operand_val, target), |
| 15596 | // zig fmt: on | 15626 | // zig fmt: on |
| 15597 | }; | 15627 | }; |
| 15598 | try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty); | 15628 | try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty); |
| 15599 | return sema.addConstant(operand_ty, stored_val); | 15629 | return sema.addConstant(elem_ty, stored_val); |
| 15600 | } else break :rs ptr_src; | 15630 | } else break :rs ptr_src; |
| 15601 | } else ptr_src; | 15631 | } else ptr_src; |
| 15602 | 15632 | ||
| ... | @@ -15620,15 +15650,15 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError | ... | @@ -15620,15 +15650,15 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 15620 | const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; | 15650 | const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; |
| 15621 | const src = inst_data.src(); | 15651 | const src = inst_data.src(); |
| 15622 | // zig fmt: off | 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 | const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | 15654 | const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 15625 | const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | 15655 | const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 15626 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; | 15656 | const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; |
| 15627 | // zig fmt: on | 15657 | // zig fmt: on |
| 15628 | const ptr = sema.resolveInst(extra.ptr); | 15658 | const operand = sema.resolveInst(extra.operand); |
| 15629 | const operand_ty = sema.typeOf(ptr).elemType(); | 15659 | const elem_ty = sema.typeOf(operand); |
| 15630 | try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty); | 15660 | const uncasted_ptr = sema.resolveInst(extra.ptr); |
| 15631 | const operand = try sema.coerce(block, operand_ty, sema.resolveInst(extra.operand), operand_src); | 15661 | const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); |
| 15632 | const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); | 15662 | const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); |
| 15633 | 15663 | ||
| 15634 | const air_tag: Air.Inst.Tag = switch (order) { | 15664 | const air_tag: Air.Inst.Tag = switch (order) { |
src/Zir.zig+7-1| ... | @@ -903,7 +903,7 @@ pub const Inst = struct { | ... | @@ -903,7 +903,7 @@ pub const Inst = struct { |
| 903 | /// Uses the `pl_node` union field with payload `Select`. | 903 | /// Uses the `pl_node` union field with payload `Select`. |
| 904 | select, | 904 | select, |
| 905 | /// Implements the `@atomicLoad` builtin. | 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 | atomic_load, | 907 | atomic_load, |
| 908 | /// Implements the `@atomicRmw` builtin. | 908 | /// Implements the `@atomicRmw` builtin. |
| 909 | /// Uses the `pl_node` union field with payload `AtomicRmw`. | 909 | /// Uses the `pl_node` union field with payload `AtomicRmw`. |
| ... | @@ -3293,6 +3293,12 @@ pub const Inst = struct { | ... | @@ -3293,6 +3293,12 @@ pub const Inst = struct { |
| 3293 | ordering: Ref, | 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 | pub const MulAdd = struct { | 3302 | pub const MulAdd = struct { |
| 3297 | mulend1: Ref, | 3303 | mulend1: Ref, |
| 3298 | mulend2: Ref, | 3304 | mulend2: Ref, |
src/arch/x86_64/abi.zig+9-8| ... | @@ -12,13 +12,10 @@ pub fn classifyWindows(ty: Type, target: Target) Class { | ... | @@ -12,13 +12,10 @@ pub fn classifyWindows(ty: Type, target: Target) Class { |
| 12 | // and the registers used for those arguments. Any argument that doesn't fit in 8 | 12 | // and the registers used for those arguments. Any argument that doesn't fit in 8 |
| 13 | // bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference. A single argument | 13 | // bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference. A single argument |
| 14 | // is never spread across multiple registers." | 14 | // is never spread across multiple registers." |
| 15 | // "All floating point operations are done using the 16 XMM registers." | ||
| 15 | // "Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed | 16 | // "Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed |
| 16 | // as if they were integers of the same size." | 17 | // as if they were integers of the same size." |
| 17 | switch (ty.abiSize(target)) { | 18 | switch (ty.zigTypeTag()) { |
| 18 | 1, 2, 4, 8 => {}, | ||
| 19 | else => return .memory, | ||
| 20 | } | ||
| 21 | return switch (ty.zigTypeTag()) { | ||
| 22 | .Pointer, | 19 | .Pointer, |
| 23 | .Int, | 20 | .Int, |
| 24 | .Bool, | 21 | .Bool, |
| ... | @@ -33,9 +30,13 @@ pub fn classifyWindows(ty: Type, target: Target) Class { | ... | @@ -33,9 +30,13 @@ pub fn classifyWindows(ty: Type, target: Target) Class { |
| 33 | .ErrorUnion, | 30 | .ErrorUnion, |
| 34 | .AnyFrame, | 31 | .AnyFrame, |
| 35 | .Frame, | 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 | .Type, | 41 | .Type, |
| 41 | .ComptimeFloat, | 42 | .ComptimeFloat, |
| ... | @@ -47,7 +48,7 @@ pub fn classifyWindows(ty: Type, target: Target) Class { | ... | @@ -47,7 +48,7 @@ pub fn classifyWindows(ty: Type, target: Target) Class { |
| 47 | .Opaque, | 48 | .Opaque, |
| 48 | .EnumLiteral, | 49 | .EnumLiteral, |
| 49 | => unreachable, | 50 | => unreachable, |
| 50 | }; | 51 | } |
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | /// There are a maximum of 8 possible return slots. Returned values are in | 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,6 +476,19 @@ pub const Object = struct { |
| 476 | _ = builder.buildRet(is_lt); | 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 | pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void { | 492 | pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 480 | var sub_prog_node = prog_node.start("LLVM Emit Object", 0); | 493 | var sub_prog_node = prog_node.start("LLVM Emit Object", 0); |
| 481 | sub_prog_node.activate(); | 494 | sub_prog_node.activate(); |
| ... | @@ -484,6 +497,7 @@ pub const Object = struct { | ... | @@ -484,6 +497,7 @@ pub const Object = struct { |
| 484 | 497 | ||
| 485 | try self.genErrorNameTable(comp); | 498 | try self.genErrorNameTable(comp); |
| 486 | try self.genCmpLtErrorsLenFunction(comp); | 499 | try self.genCmpLtErrorsLenFunction(comp); |
| 500 | try self.genModuleLevelAssembly(comp); | ||
| 487 | 501 | ||
| 488 | if (self.di_builder) |dib| { | 502 | if (self.di_builder) |dib| { |
| 489 | // When lowering debug info for pointers, we emitted the element types as | 503 | // When lowering debug info for pointers, we emitted the element types as |
| ... | @@ -630,7 +644,17 @@ pub const Object = struct { | ... | @@ -630,7 +644,17 @@ pub const Object = struct { |
| 630 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; | 644 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 631 | 645 | ||
| 632 | const llvm_arg_i = @intCast(c_uint, args.items.len) + param_offset; | 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 | var di_file: ?*llvm.DIFile = null; | 660 | var di_file: ?*llvm.DIFile = null; |
| ... | @@ -3729,6 +3753,19 @@ pub const FuncGen = struct { | ... | @@ -3729,6 +3753,19 @@ pub const FuncGen = struct { |
| 3729 | arg_ptr.setAlignment(alignment); | 3753 | arg_ptr.setAlignment(alignment); |
| 3730 | const store_inst = self.builder.buildStore(llvm_arg, arg_ptr); | 3754 | const store_inst = self.builder.buildStore(llvm_arg, arg_ptr); |
| 3731 | store_inst.setAlignment(alignment); | 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 | break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, ""); | 3769 | break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, ""); |
| 3733 | }; | 3770 | }; |
| 3734 | 3771 | ||
| ... | @@ -7583,7 +7620,7 @@ pub const FuncGen = struct { | ... | @@ -7583,7 +7620,7 @@ pub const FuncGen = struct { |
| 7583 | const size_bytes = elem_ty.abiSize(target); | 7620 | const size_bytes = elem_ty.abiSize(target); |
| 7584 | _ = self.builder.buildMemCpy( | 7621 | _ = self.builder.buildMemCpy( |
| 7585 | self.builder.buildBitCast(ptr, llvm_ptr_u8, ""), | 7622 | self.builder.buildBitCast(ptr, llvm_ptr_u8, ""), |
| 7586 | ptr_ty.ptrAlignment(target), | 7623 | ptr_alignment, |
| 7587 | self.builder.buildBitCast(elem, llvm_ptr_u8, ""), | 7624 | self.builder.buildBitCast(elem, llvm_ptr_u8, ""), |
| 7588 | elem_ty.abiAlignment(target), | 7625 | elem_ty.abiAlignment(target), |
| 7589 | self.context.intType(Type.usize.intInfo(target).bits).constInt(size_bytes, .False), | 7626 | self.context.intType(Type.usize.intInfo(target).bits).constInt(size_bytes, .False), |
| ... | @@ -7917,6 +7954,8 @@ fn llvmFieldIndex( | ... | @@ -7917,6 +7954,8 @@ fn llvmFieldIndex( |
| 7917 | } | 7954 | } |
| 7918 | 7955 | ||
| 7919 | fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { | 7956 | fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { |
| 7957 | if (!fn_info.return_type.hasRuntimeBitsIgnoreComptime()) return false; | ||
| 7958 | |||
| 7920 | switch (fn_info.cc) { | 7959 | switch (fn_info.cc) { |
| 7921 | .Unspecified, .Inline => return isByRef(fn_info.return_type), | 7960 | .Unspecified, .Inline => return isByRef(fn_info.return_type), |
| 7922 | .C => switch (target.cpu.arch) { | 7961 | .C => switch (target.cpu.arch) { |
| ... | @@ -8016,7 +8055,8 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. | ... | @@ -8016,7 +8055,8 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. |
| 8016 | } | 8055 | } |
| 8017 | } | 8056 | } |
| 8018 | if (classes[0] == .integer and classes[1] == .none) { | 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 | return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); | 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,7 +8150,8 @@ fn lowerFnParamTy(dg: *DeclGen, cc: std.builtin.CallingConvention, ty: Type) !*c |
| 8110 | } | 8150 | } |
| 8111 | } | 8151 | } |
| 8112 | if (classes[0] == .integer and classes[1] == .none) { | 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 | return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); | 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,6 +381,9 @@ pub const Module = opaque { |
| 381 | 381 | ||
| 382 | pub const createDIBuilder = ZigLLVMCreateDIBuilder; | 382 | pub const createDIBuilder = ZigLLVMCreateDIBuilder; |
| 383 | extern fn ZigLLVMCreateDIBuilder(module: *const Module, allow_unresolved: bool) *DIBuilder; | 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 | pub const lookupIntrinsicID = LLVMLookupIntrinsicID; | 389 | pub const lookupIntrinsicID = LLVMLookupIntrinsicID; |
src/print_zir.zig+14-1| ... | @@ -283,6 +283,7 @@ const Writer = struct { | ... | @@ -283,6 +283,7 @@ const Writer = struct { |
| 283 | => try self.writeStructInit(stream, inst), | 283 | => try self.writeStructInit(stream, inst), |
| 284 | 284 | ||
| 285 | .cmpxchg_strong, .cmpxchg_weak => try self.writeCmpxchg(stream, inst), | 285 | .cmpxchg_strong, .cmpxchg_weak => try self.writeCmpxchg(stream, inst), |
| 286 | .atomic_load => try self.writeAtomicLoad(stream, inst), | ||
| 286 | .atomic_store => try self.writeAtomicStore(stream, inst), | 287 | .atomic_store => try self.writeAtomicStore(stream, inst), |
| 287 | .atomic_rmw => try self.writeAtomicRmw(stream, inst), | 288 | .atomic_rmw => try self.writeAtomicRmw(stream, inst), |
| 288 | .memcpy => try self.writeMemcpy(stream, inst), | 289 | .memcpy => try self.writeMemcpy(stream, inst), |
| ... | @@ -351,7 +352,6 @@ const Writer = struct { | ... | @@ -351,7 +352,6 @@ const Writer = struct { |
| 351 | .offset_of, | 352 | .offset_of, |
| 352 | .splat, | 353 | .splat, |
| 353 | .reduce, | 354 | .reduce, |
| 354 | .atomic_load, | ||
| 355 | .bitcast, | 355 | .bitcast, |
| 356 | .vector_type, | 356 | .vector_type, |
| 357 | .maximum, | 357 | .maximum, |
| ... | @@ -929,6 +929,19 @@ const Writer = struct { | ... | @@ -929,6 +929,19 @@ const Writer = struct { |
| 929 | try self.writeSrc(stream, inst_data.src()); | 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 | fn writeAtomicStore(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | 945 | fn writeAtomicStore(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
| 933 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | 946 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 934 | const extra = self.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; | 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,6 +7686,7 @@ ZigType *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { |
| 7686 | // However for some targets, LLVM incorrectly reports this as 8. | 7686 | // However for some targets, LLVM incorrectly reports this as 8. |
| 7687 | // See: https://github.com/ziglang/zig/issues/2987 | 7687 | // See: https://github.com/ziglang/zig/issues/2987 |
| 7688 | entry->abi_align = 16; | 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,5 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const llvm = @import("codegen/llvm/bindings.zig"); | 2 | const llvm = @import("codegen/llvm/bindings.zig"); |
| 3 | const Type = @import("type.zig").Type; | ||
| 3 | 4 | ||
| 4 | pub const ArchOsAbi = struct { | 5 | pub const ArchOsAbi = struct { |
| 5 | arch: std.Target.Cpu.Arch, | 6 | arch: std.Target.Cpu.Arch, |
| ... | @@ -543,10 +544,28 @@ pub fn needUnwindTables(target: std.Target) bool { | ... | @@ -543,10 +544,28 @@ pub fn needUnwindTables(target: std.Target) bool { |
| 543 | return target.os.tag == .windows; | 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 | pub const AtomicPtrAlignmentError = error{ |
| 547 | /// which can affect this value. Audit this! | 548 | FloatTooBig, |
| 548 | pub fn largestAtomicBits(target: std.Target) u32 { | 549 | IntTooBig, |
| 549 | return switch (target.cpu.arch) { | 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 | .avr, | 569 | .avr, |
| 551 | .msp430, | 570 | .msp430, |
| 552 | .spu_2, | 571 | .spu_2, |
| ... | @@ -611,6 +630,40 @@ pub fn largestAtomicBits(target: std.Target) u32 { | ... | @@ -611,6 +630,40 @@ pub fn largestAtomicBits(target: std.Target) u32 { |
| 611 | 630 | ||
| 612 | .x86_64 => 128, | 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 | pub fn defaultAddressSpace( | 669 | pub fn defaultAddressSpace( |
src/type.zig+24-16| ... | @@ -2788,11 +2788,6 @@ pub const Type = extern union { | ... | @@ -2788,11 +2788,6 @@ pub const Type = extern union { |
| 2788 | return AbiAlignmentAdvanced{ .scalar = target_util.defaultFunctionAlignment(target) }; | 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 | .isize, | 2791 | .isize, |
| 2797 | .usize, | 2792 | .usize, |
| 2798 | .single_const_pointer_to_comptime_int, | 2793 | .single_const_pointer_to_comptime_int, |
| ... | @@ -2865,14 +2860,15 @@ pub const Type = extern union { | ... | @@ -2865,14 +2860,15 @@ pub const Type = extern union { |
| 2865 | // ABI alignment of vectors? | 2860 | // ABI alignment of vectors? |
| 2866 | .vector => return AbiAlignmentAdvanced{ .scalar = 16 }, | 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 | .int_signed, .int_unsigned => { | 2868 | .int_signed, .int_unsigned => { |
| 2869 | const bits: u16 = ty.cast(Payload.Bits).?.data; | 2869 | const bits: u16 = ty.cast(Payload.Bits).?.data; |
| 2870 | if (bits == 0) return AbiAlignmentAdvanced{ .scalar = 0 }; | 2870 | if (bits == 0) return AbiAlignmentAdvanced{ .scalar = 0 }; |
| 2871 | if (bits <= 8) return AbiAlignmentAdvanced{ .scalar = 1 }; | 2871 | return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(bits, target) }; |
| 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 }; | ||
| 2876 | }, | 2872 | }, |
| 2877 | 2873 | ||
| 2878 | .optional => { | 2874 | .optional => { |
| ... | @@ -3113,10 +3109,6 @@ pub const Type = extern union { | ... | @@ -3113,10 +3109,6 @@ pub const Type = extern union { |
| 3113 | assert(elem_size >= payload.elem_type.abiAlignment(target)); | 3109 | assert(elem_size >= payload.elem_type.abiAlignment(target)); |
| 3114 | return (payload.len + 1) * elem_size; | 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 | .isize, | 3113 | .isize, |
| 3122 | .usize, | 3114 | .usize, |
| ... | @@ -3189,10 +3181,14 @@ pub const Type = extern union { | ... | @@ -3189,10 +3181,14 @@ pub const Type = extern union { |
| 3189 | .error_set_merged, | 3181 | .error_set_merged, |
| 3190 | => return 2, // TODO revisit this when we have the concept of the error tag type | 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 | .int_signed, .int_unsigned => { | 3188 | .int_signed, .int_unsigned => { |
| 3193 | const bits: u16 = self.cast(Payload.Bits).?.data; | 3189 | const bits: u16 = self.cast(Payload.Bits).?.data; |
| 3194 | if (bits == 0) return 0; | 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 | .optional => { | 3194 | .optional => { |
| ... | @@ -3234,6 +3230,18 @@ pub const Type = extern union { | ... | @@ -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 | /// Asserts the type has the bit size already resolved. | 3245 | /// Asserts the type has the bit size already resolved. |
| 3238 | pub fn bitSize(ty: Type, target: Target) u64 { | 3246 | pub fn bitSize(ty: Type, target: Target) u64 { |
| 3239 | return switch (ty.tag()) { | 3247 | return switch (ty.tag()) { |
| ... | @@ -5169,7 +5177,7 @@ pub const Type = extern union { | ... | @@ -5169,7 +5177,7 @@ pub const Type = extern union { |
| 5169 | 5177 | ||
| 5170 | const field = it.struct_obj.fields.values()[it.field]; | 5178 | const field = it.struct_obj.fields.values()[it.field]; |
| 5171 | defer it.field += 1; | 5179 | defer it.field += 1; |
| 5172 | if (!field.ty.hasRuntimeBits()) | 5180 | if (!field.ty.hasRuntimeBits() or field.is_comptime) |
| 5173 | return FieldOffset{ .field = it.field, .offset = it.offset }; | 5181 | return FieldOffset{ .field = it.field, .offset = it.offset }; |
| 5174 | 5182 | ||
| 5175 | const field_align = field.normalAlignment(it.target); | 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,41 +47,128 @@ fn expects4(x: *align(4) u32) void { |
| 47 | x.* += 1; | 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 | try expect(@alignOf(struct { | 51 | try expect(@alignOf(struct { |
| 52 | a: i32, | 52 | a: i32, |
| 53 | b: *i32, | 53 | b: *i32, |
| 54 | }) == @alignOf(usize)); | 54 | }) == @alignOf(usize)); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | test "alignment of >= 128-bit integer type" { | 57 | test "alignment and size of structs with 128-bit fields" { |
| 58 | try expect(@alignOf(u128) == 16); | 58 | if (builtin.zig_backend == .stage1) { |
| 59 | try expect(@alignOf(u129) == 16); | 59 | // stage1 gets the wrong answer for a lot of targets |
| 60 | } | 60 | return error.SkipZigTest; |
| 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); | ||
| 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" { | 65 | const A = struct { |
| 75 | try expect(@sizeOf(extern struct { | 66 | x: u128, |
| 67 | }; | ||
| 68 | const B = extern struct { | ||
| 76 | x: u128, | 69 | x: u128, |
| 77 | y: u8, | 70 | y: u8, |
| 78 | }) == 32); | 71 | }; |
| 79 | 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 | comptime { | 160 | comptime { |
| 81 | try expect(@sizeOf(extern struct { | 161 | std.debug.assert(@alignOf(A) == expected.a_align); |
| 82 | x: u128, | 162 | std.debug.assert(@sizeOf(A) == expected.a_size); |
| 83 | y: u8, | 163 | |
| 84 | }) == 32); | 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,7 +415,6 @@ test "read 128-bit field from default aligned struct in stack memory" { |
| 328 | .nevermind = 1, | 415 | .nevermind = 1, |
| 329 | .badguy = 12, | 416 | .badguy = 12, |
| 330 | }; | 417 | }; |
| 331 | try expect((@ptrToInt(&default_aligned.badguy) % 16) == 0); | ||
| 332 | try expect(12 == default_aligned.badguy); | 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,7 +431,6 @@ test "read 128-bit field from default aligned struct in global memory" { |
| 345 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 431 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 346 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 432 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 347 | 433 | ||
| 348 | try expect((@ptrToInt(&default_aligned_global.badguy) % 16) == 0); | ||
| 349 | try expect(12 == default_aligned_global.badguy); | 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,7 +23,6 @@ test "module level assembly" { |
| 23 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 23 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 24 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 24 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 25 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 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 | if (is_x86_64_linux) { | 27 | if (is_x86_64_linux) { |
| 29 | try expect(this_is_my_alias() == 1234); | 28 | try expect(this_is_my_alias() == 1234); |
test/behavior/atomics.zig+1-1| ... | @@ -127,7 +127,7 @@ test "128-bit cmpxchg" { | ... | @@ -127,7 +127,7 @@ test "128-bit cmpxchg" { |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | fn test_u128_cmpxchg() !void { | 129 | fn test_u128_cmpxchg() !void { |
| 130 | var x: u128 = 1234; | 130 | var x: u128 align(16) = 1234; |
| 131 | if (@cmpxchgWeak(u128, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { | 131 | if (@cmpxchgWeak(u128, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { |
| 132 | try expect(x1 == 1234); | 132 | try expect(x1 == 1234); |
| 133 | } else { | 133 | } else { |
test/behavior/bitcast.zig+7-12| ... | @@ -120,6 +120,10 @@ test "bitcast generates a temporary value" { | ... | @@ -120,6 +120,10 @@ test "bitcast generates a temporary value" { |
| 120 | } | 120 | } |
| 121 | 121 | ||
| 122 | test "@bitCast packed structs at runtime and comptime" { | 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 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | 127 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 124 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | 128 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 125 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 129 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| ... | @@ -138,18 +142,9 @@ test "@bitCast packed structs at runtime and comptime" { | ... | @@ -138,18 +142,9 @@ test "@bitCast packed structs at runtime and comptime" { |
| 138 | fn doTheTest() !void { | 142 | fn doTheTest() !void { |
| 139 | var full = Full{ .number = 0x1234 }; | 143 | var full = Full{ .number = 0x1234 }; |
| 140 | var two_halves = @bitCast(Divided, full); | 144 | var two_halves = @bitCast(Divided, full); |
| 141 | switch (native_endian) { | 145 | try expect(two_halves.half1 == 0x34); |
| 142 | .Big => { | 146 | try expect(two_halves.quarter3 == 0x2); |
| 143 | try expect(two_halves.half1 == 0x12); | 147 | try expect(two_halves.quarter4 == 0x1); |
| 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 | } | ||
| 153 | } | 148 | } |
| 154 | }; | 149 | }; |
| 155 | try S.doTheTest(); | 150 | try S.doTheTest(); |
test/behavior/struct.zig+9-6| ... | @@ -499,17 +499,18 @@ const Bitfields = packed struct { | ... | @@ -499,17 +499,18 @@ const Bitfields = packed struct { |
| 499 | f7: u8, | 499 | f7: u8, |
| 500 | }; | 500 | }; |
| 501 | 501 | ||
| 502 | test "native bit field understands endianness" { | 502 | test "packed struct fields are ordered from LSB to MSB" { |
| 503 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 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 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 508 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 505 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 509 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 506 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 510 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 507 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 511 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 508 | 512 | ||
| 509 | var all: u64 = if (native_endian != .Little) | 513 | var all: u64 = 0x7765443322221111; |
| 510 | 0x1111222233445677 | ||
| 511 | else | ||
| 512 | 0x7765443322221111; | ||
| 513 | var bytes: [8]u8 = undefined; | 514 | var bytes: [8]u8 = undefined; |
| 514 | @memcpy(&bytes, @ptrCast([*]u8, &all), 8); | 515 | @memcpy(&bytes, @ptrCast([*]u8, &all), 8); |
| 515 | var bitfields = @ptrCast(*Bitfields, &bytes).*; | 516 | var bitfields = @ptrCast(*Bitfields, &bytes).*; |
| ... | @@ -974,6 +975,8 @@ test "comptime struct field" { | ... | @@ -974,6 +975,8 @@ test "comptime struct field" { |
| 974 | comptime b: i32 = 1234, | 975 | comptime b: i32 = 1234, |
| 975 | }; | 976 | }; |
| 976 | 977 | ||
| 978 | comptime std.debug.assert(@sizeOf(T) == 4); | ||
| 979 | |||
| 977 | var foo: T = undefined; | 980 | var foo: T = undefined; |
| 978 | comptime try expect(foo.b == 1234); | 981 | comptime try expect(foo.b == 1234); |
| 979 | } | 982 | } |