| author | |
| committer | |
| log | dbe9a5114e2d56f847b674539ffa0d28fc57ea78 |
| tree | 078fe7aadd48de9c78aea90d6a383ba134622be5 |
| parent | dc9d76b630e0fe9a465cec67dc4dc66e8cce7c58 |
* test runner is improved to respect `error.SkipZigTest`
* start code is improved to `@setAlignStack(16)` before calling main()
* the newly passing behavior test has a workaround for the fact that
stage2 cannot yet call `std.Target.x86.featureSetHas()` at comptime.
This is blocking on comptime closures. The workaround is that there
is a new decl `@import("builtin").stage2_x86_cx16` which is a `bool`.
* Implement `@setAlignStack`. This language feature should be re-evaluated
at some point - I'll file an issue for it.
* LLVM backend: apply/remove the cold attribute and noinline attribute
where appropriate.
* LLVM backend: loads and stores are properly annotated with alignment
and volatile attributes.
* LLVM backend: allocas are properly annotated with alignment.
* Type: fix integers reporting wrong alignment for 256-bit integers and
beyond. Once you get to 16 byte aligned, there is no further
alignment for larger integers.12 files changed, 195 insertions(+), 49 deletions(-)
lib/std/special/test_runner.zig+9-1| ... | ... | @@ -123,8 +123,16 @@ pub fn log( |
| 123 | 123 | } |
| 124 | 124 | |
| 125 | 125 | pub fn main2() anyerror!void { |
| 126 | var bad = false; | |
| 126 | 127 | // Simpler main(), exercising fewer language features, so that stage2 can handle it. |
| 127 | 128 | for (builtin.test_functions) |test_fn| { |
| 128 | try test_fn.func(); | |
| 129 | test_fn.func() catch |err| { | |
| 130 | if (err != error.SkipZigTest) { | |
| 131 | bad = true; | |
| 132 | } | |
| 133 | }; | |
| 134 | } | |
| 135 | if (bad) { | |
| 136 | return error.TestsFailed; | |
| 129 | 137 | } |
| 130 | 138 | } |
lib/std/start.zig+5| ... | ... | @@ -87,6 +87,11 @@ fn main2() callconv(.C) c_int { |
| 87 | 87 | } |
| 88 | 88 | |
| 89 | 89 | fn _start2() callconv(.Naked) noreturn { |
| 90 | callMain2(); | |
| 91 | } | |
| 92 | ||
| 93 | fn callMain2() noreturn { | |
| 94 | @setAlignStack(16); | |
| 90 | 95 | root.main(); |
| 91 | 96 | exit2(0); |
| 92 | 97 | } |
src/Compilation.zig+5| ... | ... | @@ -3714,6 +3714,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc |
| 3714 | 3714 | const target = comp.getTarget(); |
| 3715 | 3715 | const generic_arch_name = target.cpu.arch.genericName(); |
| 3716 | 3716 | const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; |
| 3717 | const stage2_x86_cx16 = target.cpu.arch == .x86_64 and | |
| 3718 | std.Target.x86.featureSetHas(target.cpu.features, .cx16); | |
| 3717 | 3719 | |
| 3718 | 3720 | @setEvalBranchQuota(4000); |
| 3719 | 3721 | try buffer.writer().print( |
| ... | ... | @@ -3725,6 +3727,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc |
| 3725 | 3727 | \\pub const zig_is_stage2 = {}; |
| 3726 | 3728 | \\/// Temporary until self-hosted supports the `cpu.arch` value. |
| 3727 | 3729 | \\pub const stage2_arch: std.Target.Cpu.Arch = .{}; |
| 3730 | \\/// Temporary until self-hosted can call `std.Target.x86.featureSetHas` at comptime. | |
| 3731 | \\pub const stage2_x86_cx16 = {}; | |
| 3728 | 3732 | \\ |
| 3729 | 3733 | \\pub const output_mode = std.builtin.OutputMode.{}; |
| 3730 | 3734 | \\pub const link_mode = std.builtin.LinkMode.{}; |
| ... | ... | @@ -3740,6 +3744,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc |
| 3740 | 3744 | build_options.version, |
| 3741 | 3745 | !use_stage1, |
| 3742 | 3746 | std.zig.fmtId(@tagName(target.cpu.arch)), |
| 3747 | stage2_x86_cx16, | |
| 3743 | 3748 | std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)), |
| 3744 | 3749 | std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)), |
| 3745 | 3750 | comp.bin_file.options.is_test, |
src/Module.zig+21-1| ... | ... | @@ -64,11 +64,16 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{}, |
| 64 | 64 | /// The set of all the generic function instantiations. This is used so that when a generic |
| 65 | 65 | /// function is called twice with the same comptime parameter arguments, both calls dispatch |
| 66 | 66 | /// to the same function. |
| 67 | /// TODO: remove functions from this set when they are destroyed. | |
| 67 | 68 | monomorphed_funcs: MonomorphedFuncsSet = .{}, |
| 68 | ||
| 69 | 69 | /// The set of all comptime function calls that have been cached so that future calls |
| 70 | 70 | /// with the same parameters will get the same return value. |
| 71 | 71 | memoized_calls: MemoizedCallSet = .{}, |
| 72 | /// Contains the values from `@setAlignStack`. A sparse table is used here | |
| 73 | /// instead of a field of `Fn` because usage of `@setAlignStack` is rare, while | |
| 74 | /// functions are many. | |
| 75 | /// TODO: remove functions from this set when they are destroyed. | |
| 76 | align_stack_fns: std.AutoHashMapUnmanaged(*const Fn, SetAlignStack) = .{}, | |
| 72 | 77 | |
| 73 | 78 | /// We optimize memory usage for a compilation with no compile errors by storing the |
| 74 | 79 | /// error messages and mapping outside of `Decl`. |
| ... | ... | @@ -215,6 +220,13 @@ pub const MemoizedCall = struct { |
| 215 | 220 | } |
| 216 | 221 | }; |
| 217 | 222 | |
| 223 | pub const SetAlignStack = struct { | |
| 224 | alignment: u32, | |
| 225 | /// TODO: This needs to store a non-lazy source location for the case of an inline function | |
| 226 | /// which does `@setAlignStack` (applying it to the caller). | |
| 227 | src: LazySrcLoc, | |
| 228 | }; | |
| 229 | ||
| 218 | 230 | /// A `Module` has zero or one of these depending on whether `-femit-h` is enabled. |
| 219 | 231 | pub const GlobalEmitH = struct { |
| 220 | 232 | /// Where to put the output. |
| ... | ... | @@ -881,6 +893,7 @@ pub const Fn = struct { |
| 881 | 893 | |
| 882 | 894 | state: Analysis, |
| 883 | 895 | is_cold: bool = false, |
| 896 | is_noinline: bool = false, | |
| 884 | 897 | |
| 885 | 898 | pub const Analysis = enum { |
| 886 | 899 | queued, |
| ... | ... | @@ -2347,6 +2360,7 @@ pub fn deinit(mod: *Module) void { |
| 2347 | 2360 | |
| 2348 | 2361 | mod.error_name_list.deinit(gpa); |
| 2349 | 2362 | mod.test_functions.deinit(gpa); |
| 2363 | mod.align_stack_fns.deinit(gpa); | |
| 2350 | 2364 | mod.monomorphed_funcs.deinit(gpa); |
| 2351 | 2365 | |
| 2352 | 2366 | { |
| ... | ... | @@ -3977,6 +3991,12 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { |
| 3977 | 3991 | if (mod.failed_decls.fetchSwapRemove(decl)) |kv| { |
| 3978 | 3992 | kv.value.destroy(mod.gpa); |
| 3979 | 3993 | } |
| 3994 | if (decl.has_tv and decl.owns_tv) { | |
| 3995 | if (decl.val.castTag(.function)) |payload| { | |
| 3996 | const func = payload.data; | |
| 3997 | _ = mod.align_stack_fns.remove(func); | |
| 3998 | } | |
| 3999 | } | |
| 3980 | 4000 | if (mod.emit_h) |emit_h| { |
| 3981 | 4001 | if (emit_h.failed_decls.fetchSwapRemove(decl)) |kv| { |
| 3982 | 4002 | kv.value.destroy(mod.gpa); |
src/Sema.zig+46-1| ... | ... | @@ -833,6 +833,7 @@ fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) Compile |
| 833 | 833 | /// Appropriate to call when the coercion has already been done by result |
| 834 | 834 | /// location semantics. Asserts the value fits in the provided `Int` type. |
| 835 | 835 | /// Only supports `Int` types 64 bits or less. |
| 836 | /// TODO don't ever call this since we're migrating towards ResultLoc.coerced_ty. | |
| 836 | 837 | fn resolveAlreadyCoercedInt( |
| 837 | 838 | sema: *Sema, |
| 838 | 839 | block: *Scope.Block, |
| ... | ... | @@ -849,6 +850,23 @@ fn resolveAlreadyCoercedInt( |
| 849 | 850 | } |
| 850 | 851 | } |
| 851 | 852 | |
| 853 | fn resolveAlign( | |
| 854 | sema: *Sema, | |
| 855 | block: *Scope.Block, | |
| 856 | src: LazySrcLoc, | |
| 857 | zir_ref: Zir.Inst.Ref, | |
| 858 | ) !u16 { | |
| 859 | const alignment_big = try sema.resolveInt(block, src, zir_ref, Type.initTag(.u16)); | |
| 860 | const alignment = @intCast(u16, alignment_big); // We coerce to u16 in the prev line. | |
| 861 | if (alignment == 0) return sema.mod.fail(&block.base, src, "alignment must be >= 1", .{}); | |
| 862 | if (!std.math.isPowerOfTwo(alignment)) { | |
| 863 | return sema.mod.fail(&block.base, src, "alignment value {d} is not a power of two", .{ | |
| 864 | alignment, | |
| 865 | }); | |
| 866 | } | |
| 867 | return alignment; | |
| 868 | } | |
| 869 | ||
| 852 | 870 | fn resolveInt( |
| 853 | 871 | sema: *Sema, |
| 854 | 872 | block: *Scope.Block, |
| ... | ... | @@ -2285,9 +2303,36 @@ fn zirExport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro |
| 2285 | 2303 | } |
| 2286 | 2304 | |
| 2287 | 2305 | fn zirSetAlignStack(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { |
| 2306 | const mod = sema.mod; | |
| 2288 | 2307 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 2308 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 2289 | 2309 | const src: LazySrcLoc = inst_data.src(); |
| 2290 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirSetAlignStack", .{}); | |
| 2310 | const alignment = try sema.resolveAlign(block, operand_src, inst_data.operand); | |
| 2311 | if (alignment > 256) { | |
| 2312 | return mod.fail(&block.base, src, "attempt to @setAlignStack({d}); maximum is 256", .{ | |
| 2313 | alignment, | |
| 2314 | }); | |
| 2315 | } | |
| 2316 | const func = sema.owner_func orelse | |
| 2317 | return mod.fail(&block.base, src, "@setAlignStack outside function body", .{}); | |
| 2318 | ||
| 2319 | switch (func.owner_decl.ty.fnCallingConvention()) { | |
| 2320 | .Naked => return mod.fail(&block.base, src, "@setAlignStack in naked function", .{}), | |
| 2321 | .Inline => return mod.fail(&block.base, src, "@setAlignStack in inline function", .{}), | |
| 2322 | else => {}, | |
| 2323 | } | |
| 2324 | ||
| 2325 | const gop = try mod.align_stack_fns.getOrPut(mod.gpa, func); | |
| 2326 | if (gop.found_existing) { | |
| 2327 | const msg = msg: { | |
| 2328 | const msg = try mod.errMsg(&block.base, src, "multiple @setAlignStack in the same function body", .{}); | |
| 2329 | errdefer msg.destroy(mod.gpa); | |
| 2330 | try mod.errNote(&block.base, src, msg, "other instance here", .{}); | |
| 2331 | break :msg msg; | |
| 2332 | }; | |
| 2333 | return mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 2334 | } | |
| 2335 | gop.value_ptr.* = .{ .alignment = alignment, .src = src }; | |
| 2291 | 2336 | } |
| 2292 | 2337 | |
| 2293 | 2338 | fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void { |
src/codegen/llvm.zig+62-17| ... | ... | @@ -355,6 +355,20 @@ pub const Object = struct { |
| 355 | 355 | |
| 356 | 356 | const llvm_func = try dg.resolveLlvmFunction(decl); |
| 357 | 357 | |
| 358 | if (module.align_stack_fns.get(func)) |align_info| { | |
| 359 | dg.addFnAttrInt(llvm_func, "alignstack", align_info.alignment); | |
| 360 | dg.addFnAttr(llvm_func, "noinline"); | |
| 361 | } else { | |
| 362 | DeclGen.removeFnAttr(llvm_func, "alignstack"); | |
| 363 | if (!func.is_noinline) DeclGen.removeFnAttr(llvm_func, "noinline"); | |
| 364 | } | |
| 365 | ||
| 366 | if (func.is_cold) { | |
| 367 | dg.addFnAttr(llvm_func, "cold"); | |
| 368 | } else { | |
| 369 | DeclGen.removeFnAttr(llvm_func, "cold"); | |
| 370 | } | |
| 371 | ||
| 358 | 372 | // This gets the LLVM values from the function and stores them in `dg.args`. |
| 359 | 373 | const fn_param_len = decl.ty.fnParamLen(); |
| 360 | 374 | var args = try dg.gpa.alloc(*const llvm.Value, fn_param_len); |
| ... | ... | @@ -512,7 +526,9 @@ pub const DeclGen = struct { |
| 512 | 526 | } |
| 513 | 527 | } |
| 514 | 528 | |
| 515 | /// If the llvm function does not exist, create it | |
| 529 | /// If the llvm function does not exist, create it. | |
| 530 | /// Note that this can be called before the function's semantic analysis has | |
| 531 | /// completed, so if any attributes rely on that, they must be done in updateFunc, not here. | |
| 516 | 532 | fn resolveLlvmFunction(self: *DeclGen, decl: *Module.Decl) !*const llvm.Value { |
| 517 | 533 | if (self.llvmModule().getNamedFunction(decl.name)) |llvm_fn| return llvm_fn; |
| 518 | 534 | |
| ... | ... | @@ -895,17 +911,39 @@ pub const DeclGen = struct { |
| 895 | 911 | } |
| 896 | 912 | } |
| 897 | 913 | |
| 898 | // Helper functions | |
| 899 | fn addAttr(self: *DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { | |
| 914 | fn addAttr(dg: *DeclGen, val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { | |
| 915 | return dg.addAttrInt(val, index, name, 0); | |
| 916 | } | |
| 917 | ||
| 918 | fn removeAttr(val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { | |
| 900 | 919 | const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len); |
| 901 | 920 | assert(kind_id != 0); |
| 902 | const llvm_attr = self.context.createEnumAttribute(kind_id, 0); | |
| 921 | val.removeEnumAttributeAtIndex(index, kind_id); | |
| 922 | } | |
| 923 | ||
| 924 | fn addAttrInt( | |
| 925 | dg: *DeclGen, | |
| 926 | val: *const llvm.Value, | |
| 927 | index: llvm.AttributeIndex, | |
| 928 | name: []const u8, | |
| 929 | int: u64, | |
| 930 | ) void { | |
| 931 | const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len); | |
| 932 | assert(kind_id != 0); | |
| 933 | const llvm_attr = dg.context.createEnumAttribute(kind_id, int); | |
| 903 | 934 | val.addAttributeAtIndex(index, llvm_attr); |
| 904 | 935 | } |
| 905 | 936 | |
| 906 | fn addFnAttr(self: *DeclGen, val: *const llvm.Value, attr_name: []const u8) void { | |
| 907 | // TODO: improve this API, `addAttr(-1, attr_name)` | |
| 908 | self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name); | |
| 937 | fn addFnAttr(dg: *DeclGen, val: *const llvm.Value, name: []const u8) void { | |
| 938 | dg.addAttr(val, std.math.maxInt(llvm.AttributeIndex), name); | |
| 939 | } | |
| 940 | ||
| 941 | fn removeFnAttr(fn_val: *const llvm.Value, name: []const u8) void { | |
| 942 | removeAttr(fn_val, std.math.maxInt(llvm.AttributeIndex), name); | |
| 943 | } | |
| 944 | ||
| 945 | fn addFnAttrInt(dg: *DeclGen, fn_val: *const llvm.Value, name: []const u8, int: u64) void { | |
| 946 | return dg.addAttrInt(fn_val, std.math.maxInt(llvm.AttributeIndex), name, int); | |
| 909 | 947 | } |
| 910 | 948 | |
| 911 | 949 | /// If the operand type of an atomic operation is not byte sized we need to |
| ... | ... | @@ -1975,12 +2013,13 @@ pub const FuncGen = struct { |
| 1975 | 2013 | return null; |
| 1976 | 2014 | // buildAlloca expects the pointee type, not the pointer type, so assert that |
| 1977 | 2015 | // a Payload.PointerSimple is passed to the alloc instruction. |
| 1978 | const inst_ty = self.air.typeOfIndex(inst); | |
| 1979 | const pointee_type = inst_ty.castPointer().?.data; | |
| 1980 | ||
| 1981 | // TODO: figure out a way to get the name of the var decl. | |
| 1982 | // TODO: set alignment and volatile | |
| 1983 | return self.buildAlloca(try self.dg.llvmType(pointee_type)); | |
| 2016 | const ptr_ty = self.air.typeOfIndex(inst); | |
| 2017 | const pointee_type = ptr_ty.elemType(); | |
| 2018 | const pointee_llvm_ty = try self.dg.llvmType(pointee_type); | |
| 2019 | const target = self.dg.module.getTarget(); | |
| 2020 | const alloca_inst = self.buildAlloca(pointee_llvm_ty); | |
| 2021 | alloca_inst.setAlignment(ptr_ty.ptrAlignment(target)); | |
| 2022 | return alloca_inst; | |
| 1984 | 2023 | } |
| 1985 | 2024 | |
| 1986 | 2025 | /// Use this instead of builder.buildAlloca, because this function makes sure to |
| ... | ... | @@ -2200,8 +2239,11 @@ pub const FuncGen = struct { |
| 2200 | 2239 | } |
| 2201 | 2240 | |
| 2202 | 2241 | fn load(self: *FuncGen, ptr: *const llvm.Value, ptr_ty: Type) *const llvm.Value { |
| 2203 | _ = ptr_ty; // TODO set volatile and alignment on this load properly | |
| 2204 | return self.builder.buildLoad(ptr, ""); | |
| 2242 | const llvm_inst = self.builder.buildLoad(ptr, ""); | |
| 2243 | const target = self.dg.module.getTarget(); | |
| 2244 | llvm_inst.setAlignment(ptr_ty.ptrAlignment(target)); | |
| 2245 | llvm_inst.setVolatile(llvm.Bool.fromBool(ptr_ty.isVolatilePtr())); | |
| 2246 | return llvm_inst; | |
| 2205 | 2247 | } |
| 2206 | 2248 | |
| 2207 | 2249 | fn store( |
| ... | ... | @@ -2210,8 +2252,11 @@ pub const FuncGen = struct { |
| 2210 | 2252 | ptr_ty: Type, |
| 2211 | 2253 | elem: *const llvm.Value, |
| 2212 | 2254 | ) *const llvm.Value { |
| 2213 | _ = ptr_ty; // TODO set volatile and alignment on this store properly | |
| 2214 | return self.builder.buildStore(elem, ptr); | |
| 2255 | const llvm_inst = self.builder.buildStore(elem, ptr); | |
| 2256 | const target = self.dg.module.getTarget(); | |
| 2257 | llvm_inst.setAlignment(ptr_ty.ptrAlignment(target)); | |
| 2258 | llvm_inst.setVolatile(llvm.Bool.fromBool(ptr_ty.isVolatilePtr())); | |
| 2259 | return llvm_inst; | |
| 2215 | 2260 | } |
| 2216 | 2261 | }; |
| 2217 | 2262 |
src/codegen/llvm/bindings.zig+9| ... | ... | @@ -85,6 +85,9 @@ pub const Value = opaque { |
| 85 | 85 | pub const addAttributeAtIndex = LLVMAddAttributeAtIndex; |
| 86 | 86 | extern fn LLVMAddAttributeAtIndex(*const Value, Idx: AttributeIndex, A: *const Attribute) void; |
| 87 | 87 | |
| 88 | pub const removeEnumAttributeAtIndex = LLVMRemoveEnumAttributeAtIndex; | |
| 89 | extern fn LLVMRemoveEnumAttributeAtIndex(F: *const Value, Idx: AttributeIndex, KindID: c_uint) void; | |
| 90 | ||
| 88 | 91 | pub const getFirstBasicBlock = LLVMGetFirstBasicBlock; |
| 89 | 92 | extern fn LLVMGetFirstBasicBlock(Fn: *const Value) ?*const BasicBlock; |
| 90 | 93 | |
| ... | ... | @@ -136,6 +139,12 @@ pub const Value = opaque { |
| 136 | 139 | |
| 137 | 140 | pub const setOrdering = LLVMSetOrdering; |
| 138 | 141 | extern fn LLVMSetOrdering(MemoryAccessInst: *const Value, Ordering: AtomicOrdering) void; |
| 142 | ||
| 143 | pub const setVolatile = LLVMSetVolatile; | |
| 144 | extern fn LLVMSetVolatile(MemoryAccessInst: *const Value, IsVolatile: Bool) void; | |
| 145 | ||
| 146 | pub const setAlignment = LLVMSetAlignment; | |
| 147 | extern fn LLVMSetAlignment(V: *const Value, Bytes: c_uint) void; | |
| 139 | 148 | }; |
| 140 | 149 | |
| 141 | 150 | pub const Type = opaque { |
src/type.zig+5-1| ... | ... | @@ -1583,7 +1583,11 @@ pub const Type = extern union { |
| 1583 | 1583 | |
| 1584 | 1584 | .int_signed, .int_unsigned => { |
| 1585 | 1585 | const bits: u16 = self.cast(Payload.Bits).?.data; |
| 1586 | return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8); | |
| 1586 | if (bits <= 8) return 1; | |
| 1587 | if (bits <= 16) return 2; | |
| 1588 | if (bits <= 32) return 4; | |
| 1589 | if (bits <= 64) return 8; | |
| 1590 | return 16; | |
| 1587 | 1591 | }, |
| 1588 | 1592 | |
| 1589 | 1593 | .optional => { |
test/behavior/atomics.zig+30| ... | ... | @@ -81,3 +81,33 @@ test "cmpxchg with ignored result" { |
| 81 | 81 | |
| 82 | 82 | try expect(5678 == x); |
| 83 | 83 | } |
| 84 | ||
| 85 | test "128-bit cmpxchg" { | |
| 86 | try test_u128_cmpxchg(); | |
| 87 | comptime try test_u128_cmpxchg(); | |
| 88 | } | |
| 89 | ||
| 90 | fn test_u128_cmpxchg() !void { | |
| 91 | if (builtin.zig_is_stage2) { | |
| 92 | if (builtin.stage2_arch != .x86_64) return error.SkipZigTest; | |
| 93 | if (!builtin.stage2_x86_cx16) return error.SkipZigTest; | |
| 94 | } else { | |
| 95 | if (builtin.cpu.arch != .x86_64) return error.SkipZigTest; | |
| 96 | if (comptime !std.Target.x86.featureSetHas(builtin.cpu.features, .cx16)) return error.SkipZigTest; | |
| 97 | } | |
| 98 | ||
| 99 | var x: u128 = 1234; | |
| 100 | if (@cmpxchgWeak(u128, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { | |
| 101 | try expect(x1 == 1234); | |
| 102 | } else { | |
| 103 | @panic("cmpxchg should have failed"); | |
| 104 | } | |
| 105 | ||
| 106 | while (@cmpxchgWeak(u128, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| { | |
| 107 | try expect(x1 == 1234); | |
| 108 | } | |
| 109 | try expect(x == 5678); | |
| 110 | ||
| 111 | try expect(@cmpxchgStrong(u128, &x, 5678, 42, .SeqCst, .SeqCst) == null); | |
| 112 | try expect(x == 42); | |
| 113 | } |
test/behavior/atomics_stage1.zig-25| ... | ... | @@ -3,31 +3,6 @@ const expect = std.testing.expect; |
| 3 | 3 | const expectEqual = std.testing.expectEqual; |
| 4 | 4 | const builtin = @import("builtin"); |
| 5 | 5 | |
| 6 | test "128-bit cmpxchg" { | |
| 7 | try test_u128_cmpxchg(); | |
| 8 | comptime try test_u128_cmpxchg(); | |
| 9 | } | |
| 10 | ||
| 11 | fn test_u128_cmpxchg() !void { | |
| 12 | if (std.Target.current.cpu.arch != .x86_64) return error.SkipZigTest; | |
| 13 | if (comptime !std.Target.x86.featureSetHas(std.Target.current.cpu.features, .cx16)) return error.SkipZigTest; | |
| 14 | ||
| 15 | var x: u128 = 1234; | |
| 16 | if (@cmpxchgWeak(u128, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { | |
| 17 | try expect(x1 == 1234); | |
| 18 | } else { | |
| 19 | @panic("cmpxchg should have failed"); | |
| 20 | } | |
| 21 | ||
| 22 | while (@cmpxchgWeak(u128, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| { | |
| 23 | try expect(x1 == 1234); | |
| 24 | } | |
| 25 | try expect(x == 5678); | |
| 26 | ||
| 27 | try expect(@cmpxchgStrong(u128, &x, 5678, 42, .SeqCst, .SeqCst) == null); | |
| 28 | try expect(x == 42); | |
| 29 | } | |
| 30 | ||
| 31 | 6 | var a_global_variable = @as(u32, 1234); |
| 32 | 7 | |
| 33 | 8 | test "cmpxchg on a global variable" { |
test/cases.zig+1-1| ... | ... | @@ -26,7 +26,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 26 | 26 | var case = ctx.exe("hello world with updates", linux_x64); |
| 27 | 27 | |
| 28 | 28 | case.addError("", &[_][]const u8{ |
| 29 | ":90:9: error: struct 'tmp.tmp' has no member named 'main'", | |
| 29 | ":95:9: error: struct 'tmp.tmp' has no member named 'main'", | |
| 30 | 30 | }); |
| 31 | 31 | |
| 32 | 32 | // Incorrect return type |
test/stage2/darwin.zig+2-2| ... | ... | @@ -12,9 +12,9 @@ pub fn addCases(ctx: *TestContext) !void { |
| 12 | 12 | .os_tag = .macos, |
| 13 | 13 | }; |
| 14 | 14 | { |
| 15 | var case = ctx.exe("hello world with updates", target); | |
| 15 | var case = ctx.exe("darwin hello world with updates", target); | |
| 16 | 16 | case.addError("", &[_][]const u8{ |
| 17 | ":90:9: error: struct 'tmp.tmp' has no member named 'main'", | |
| 17 | ":95:9: error: struct 'tmp.tmp' has no member named 'main'", | |
| 18 | 18 | }); |
| 19 | 19 | |
| 20 | 20 | // Incorrect return type |