From e878a6633f2447666217a5f9247af7c34507dca0 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Mon, 30 Aug 2021 00:31:40 -0700 Subject: stage2: implement runtime `%` and `@rem` --- src/codegen/c.zig | 1 + src/codegen/llvm.zig | 14 ++++++++++++++ src/codegen/llvm/bindings.zig | 9 +++++++++ 3 files changed, 24 insertions(+) (limited to 'src/codegen') diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 2084b1e1ce..fd964f2829 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -858,6 +858,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM // TODO use a different strategy for div that communicates to the optimizer // that wrapping is UB. .div => try airBinOp( o, inst, " / "), + .rem => try airBinOp( o, inst, " % "), .cmp_eq => try airBinOp(o, inst, " == "), .cmp_gt => try airBinOp(o, inst, " > "), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index d7aa2d45b3..5d20e380b4 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -985,6 +985,7 @@ pub const FuncGen = struct { .mul => try self.airMul(inst, false), .mulwrap => try self.airMul(inst, true), .div => try self.airDiv(inst), + .rem => try self.airRem(inst), .ptr_add => try self.airPtrAdd(inst), .ptr_sub => try self.airPtrSub(inst), @@ -1727,6 +1728,19 @@ pub const FuncGen = struct { return self.builder.buildUDiv(lhs, rhs, ""); } + fn airRem(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const inst_ty = self.air.typeOfIndex(inst); + + if (inst_ty.isFloat()) return self.builder.buildFRem(lhs, rhs, ""); + if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, ""); + return self.builder.buildURem(lhs, rhs, ""); + } + fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index d33ca29d4f..3c59fbe056 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -386,6 +386,15 @@ pub const Builder = opaque { pub const buildFDiv = LLVMBuildFDiv; extern fn LLVMBuildFDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildURem = LLVMBuildURem; + extern fn LLVMBuildURem(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildSRem = LLVMBuildSRem; + extern fn LLVMBuildSRem(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildFRem = LLVMBuildFRem; + extern fn LLVMBuildFRem(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildAnd = LLVMBuildAnd; extern fn LLVMBuildAnd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; -- cgit v1.2.3 From 908f72210741cc10b6bf494ace06f8e74a08f215 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Sun, 22 Aug 2021 14:44:33 -0400 Subject: stage2 llvm backend: if an array has a senteniel, add it --- src/codegen/llvm.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/codegen') diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index d7aa2d45b3..e487dec538 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -796,11 +796,13 @@ pub const DeclGen = struct { const gpa = self.gpa; const elem_ty = tv.ty.elemType(); const elem_vals = payload.data; - const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len); + const sento = tv.ty.sentinel(); + const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len + @boolToInt(sento != null)); defer gpa.free(llvm_elems); for (elem_vals) |elem_val, i| { llvm_elems[i] = try self.genTypedValue(.{ .ty = elem_ty, .val = elem_val }); } + if (sento) |sent| llvm_elems[elem_vals.len] = try self.genTypedValue(.{ .ty = elem_ty, .val = sent }); const llvm_elem_ty = try self.llvmType(elem_ty); return llvm_elem_ty.constArray( llvm_elems.ptr, -- cgit v1.2.3 From 332eafeb7f3d866556ab767b960a04661bc43bc7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Aug 2021 15:35:59 -0700 Subject: stage2: first pass at implementing usingnamespace Ran into a design flaw here which will need to get solved by having AstGen annotate ZIR with which instructions are closed over. --- src/AstGen.zig | 5 +- src/Module.zig | 62 ++++++++++--- src/Sema.zig | 115 ++++++++++++++++++++---- src/codegen.zig | 26 +++--- src/codegen/aarch64.zig | 2 +- src/codegen/arm.zig | 2 +- src/codegen/riscv64.zig | 4 +- src/codegen/x86.zig | 16 ++-- src/codegen/x86_64.zig | 34 +++---- src/link.zig | 2 +- src/link/Elf.zig | 152 ++++++++++++++++---------------- src/link/MachO/DebugSymbols.zig | 140 ++++++++++++++--------------- src/link/MachO/Object.zig | 4 +- test/behavior.zig | 3 +- test/behavior/eval.zig | 6 -- test/behavior/usingnamespace.zig | 23 ++--- test/behavior/usingnamespace_stage1.zig | 22 +++++ test/compile_errors.zig | 10 +++ 18 files changed, 385 insertions(+), 243 deletions(-) create mode 100644 test/behavior/usingnamespace_stage1.zig (limited to 'src/codegen') diff --git a/src/AstGen.zig b/src/AstGen.zig index 245e40c3a1..c96a97ddeb 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -6416,6 +6416,9 @@ fn identifier( }, .top => break, }; + if (found_already == null) { + return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name}); + } // Decl references happen by name rather than ZIR index so that when unrelated // decls are modified, ZIR code containing references to them can be unmodified. @@ -10052,7 +10055,7 @@ fn isPrimitive(name: []const u8) bool { } } -/// Local variables shadowing detection, including function parameters and primitives. +/// Local variables shadowing detection, including function parameters. fn detectLocalShadowing( astgen: *AstGen, scope: *Scope, diff --git a/src/Module.zig b/src/Module.zig index 77f880b492..c17780cdc3 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -365,6 +365,8 @@ pub const Decl = struct { /// Decl is marked alive, then it sends the Decl to the linker. Otherwise it /// deletes the Decl on the spot. alive: bool, + /// Whether the Decl is a `usingnamespace` declaration. + is_usingnamespace: bool, /// Represents the position of the code in the output file. /// This is populated regardless of semantic analysis and code generation. @@ -1008,6 +1010,11 @@ pub const Scope = struct { anon_decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, + /// Key is usingnamespace Decl itself. To find the namespace being included, + /// the Decl Value has to be resolved as a Type which has a Namespace. + /// Value is whether the usingnamespace decl is marked `pub`. + usingnamespace_set: std.AutoHashMapUnmanaged(*Decl, bool) = .{}, + pub fn deinit(ns: *Namespace, mod: *Module) void { ns.destroyDecls(mod); ns.* = undefined; @@ -3174,6 +3181,31 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { errdefer decl_arena.deinit(); const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); + if (decl.is_usingnamespace) { + const ty_ty = Type.initTag(.type); + if (!decl_tv.ty.eql(ty_ty)) { + return mod.fail(&block_scope.base, src, "expected type, found {}", .{decl_tv.ty}); + } + var buffer: Value.ToTypeBuffer = undefined; + const ty = decl_tv.val.toType(&buffer); + if (ty.getNamespace() == null) { + return mod.fail(&block_scope.base, src, "type {} has no namespace", .{ty}); + } + + decl.ty = ty_ty; + decl.val = try Value.Tag.ty.create(&decl_arena.allocator, ty); + decl.align_val = Value.initTag(.null_value); + decl.linksection_val = Value.initTag(.null_value); + decl.has_tv = true; + decl.owns_tv = false; + decl_arena_state.* = decl_arena.state; + decl.value_arena = decl_arena_state; + decl.analysis = .complete; + decl.generation = mod.generation; + + return true; + } + if (decl_tv.val.castTag(.function)) |fn_payload| { const func = fn_payload.data; const owns_tv = func.owner_decl == decl; @@ -3269,16 +3301,13 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { if (type_changed and mod.emit_h != null) { try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); } - } else if (decl_tv.ty.zigTypeTag() == .Type) { - // In case this Decl is a struct or union, we need to resolve the fields - // while we still have the `Sema` in scope, so that the field type expressions - // can use the resolved AIR instructions that they possibly reference. - // We do this after the decl is populated and set to `complete` so that a `Decl` - // may reference itself. - var buffer: Value.ToTypeBuffer = undefined; - const ty = decl.val.toType(&buffer); - try sema.resolveDeclFields(&block_scope, src, ty); } + // In case this Decl is a struct or union, we need to resolve the fields + // while we still have the `Sema` in scope, so that the field type expressions + // can use the resolved AIR instructions that they possibly reference. + // We do this after the decl is populated and set to `complete` so that a `Decl` + // may reference itself. + try sema.resolvePendingTypes(&block_scope); if (decl.is_exported) { const export_src = src; // TODO point to the export token @@ -3494,7 +3523,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi // zig fmt: off const is_pub = (flags & 0b0001) != 0; - const is_exported = (flags & 0b0010) != 0; + const export_bit = (flags & 0b0010) != 0; const has_align = (flags & 0b0100) != 0; const has_linksection = (flags & 0b1000) != 0; // zig fmt: on @@ -3509,7 +3538,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi var is_named_test = false; const decl_name: [:0]const u8 = switch (decl_name_index) { 0 => name: { - if (is_exported) { + if (export_bit) { const i = iter.usingnamespace_index; iter.usingnamespace_index += 1; break :name try std.fmt.allocPrintZ(gpa, "usingnamespace_{d}", .{i}); @@ -3535,11 +3564,17 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi } }, }; + const is_exported = export_bit and decl_name_index != 0; + const is_usingnamespace = export_bit and decl_name_index == 0; + if (is_usingnamespace) try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1); // We create a Decl for it regardless of analysis status. const gop = try namespace.decls.getOrPut(gpa, decl_name); if (!gop.found_existing) { const new_decl = try mod.allocateNewDecl(namespace, decl_node); + if (is_usingnamespace) { + namespace.usingnamespace_set.putAssumeCapacity(new_decl, is_pub); + } log.debug("scan new {*} ({s}) into {*}", .{ new_decl, decl_name, namespace }); new_decl.src_line = line; new_decl.name = decl_name; @@ -3548,7 +3583,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi // test decls if in test mode, get analyzed. const decl_pkg = namespace.file_scope.pkg; const want_analysis = is_exported or switch (decl_name_index) { - 0 => true, // comptime decl + 0 => true, // comptime or usingnamespace decl 1 => blk: { // test decl with no name. Skip the part where we check against // the test name filter. @@ -3571,6 +3606,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi } new_decl.is_pub = is_pub; new_decl.is_exported = is_exported; + new_decl.is_usingnamespace = is_usingnamespace; new_decl.has_align = has_align; new_decl.has_linksection = has_linksection; new_decl.zir_decl_index = @intCast(u32, decl_sub_index); @@ -3587,6 +3623,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi decl.is_pub = is_pub; decl.is_exported = is_exported; + decl.is_usingnamespace = is_usingnamespace; decl.has_align = has_align; decl.has_linksection = has_linksection; decl.zir_decl_index = @intCast(u32, decl_sub_index); @@ -3979,6 +4016,7 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast. .has_linksection = false, .has_align = false, .alive = false, + .is_usingnamespace = false, }; return new_decl; } diff --git a/src/Sema.zig b/src/Sema.zig index 108470ec79..b3eb3b1b85 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -58,6 +58,9 @@ comptime_args_fn_inst: Zir.Inst.Index = 0, /// extra hash table lookup in the `monomorphed_funcs` set. /// Sema will set this to null when it takes ownership. preallocated_new_func: ?*Module.Fn = null, +/// Collects struct, union, enum, and opaque decls which need to have their +/// fields resolved before this Sema is deinitialized. +types_pending_resolution: std.ArrayListUnmanaged(Type) = .{}, const std = @import("std"); const mem = std.mem; @@ -90,6 +93,7 @@ pub fn deinit(sema: *Sema) void { sema.air_values.deinit(gpa); sema.inst_map.deinit(gpa); sema.decl_val_table.deinit(gpa); + sema.types_pending_resolution.deinit(gpa); sema.* = undefined; } @@ -908,7 +912,9 @@ fn zirStructDecl( &struct_obj.namespace, new_decl, new_decl.name, }); try sema.analyzeStructDecl(new_decl, inst, struct_obj); + try sema.types_pending_resolution.ensureUnusedCapacity(sema.gpa, 1); try new_decl.finalizeNewArena(&new_decl_arena); + sema.types_pending_resolution.appendAssumeCapacity(struct_ty); return sema.analyzeDeclVal(block, src, new_decl); } @@ -1198,7 +1204,9 @@ fn zirUnionDecl( _ = try sema.mod.scanNamespace(&union_obj.namespace, extra_index, decls_len, new_decl); + try sema.types_pending_resolution.ensureUnusedCapacity(sema.gpa, 1); try new_decl.finalizeNewArena(&new_decl_arena); + sema.types_pending_resolution.appendAssumeCapacity(union_ty); return sema.analyzeDeclVal(block, src, new_decl); } @@ -2324,42 +2332,105 @@ fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr } fn lookupIdentifier(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, name: []const u8) !*Decl { - // TODO emit a compile error if more than one decl would be matched. var namespace = sema.namespace; while (true) { - if (try sema.lookupInNamespace(namespace, name)) |decl| { + if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl| { return decl; } namespace = namespace.parent orelse break; } - return sema.mod.fail(&block.base, src, "use of undeclared identifier '{s}'", .{name}); + unreachable; // AstGen detects use of undeclared identifier errors. } /// This looks up a member of a specific namespace. It is affected by `usingnamespace` but /// only for ones in the specified namespace. fn lookupInNamespace( sema: *Sema, + block: *Scope.Block, + src: LazySrcLoc, namespace: *Scope.Namespace, ident_name: []const u8, + observe_usingnamespace: bool, ) CompileError!?*Decl { + const mod = sema.mod; + const namespace_decl = namespace.getDecl(); if (namespace_decl.analysis == .file_failure) { - try sema.mod.declareDeclDependency(sema.owner_decl, namespace_decl); + try mod.declareDeclDependency(sema.owner_decl, namespace_decl); return error.AnalysisFail; } - // TODO implement usingnamespace - if (namespace.decls.get(ident_name)) |decl| { - try sema.mod.declareDeclDependency(sema.owner_decl, decl); + if (observe_usingnamespace and namespace.usingnamespace_set.count() != 0) { + const src_file = block.src_decl.namespace.file_scope; + + const gpa = sema.gpa; + var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Scope.Namespace, void) = .{}; + defer checked_namespaces.deinit(gpa); + + // Keep track of name conflicts for error notes. + var candidates: std.ArrayListUnmanaged(*Decl) = .{}; + defer candidates.deinit(gpa); + + try checked_namespaces.put(gpa, namespace, {}); + var check_i: usize = 0; + + while (check_i < checked_namespaces.count()) : (check_i += 1) { + const check_ns = checked_namespaces.keys()[check_i]; + if (check_ns.decls.get(ident_name)) |decl| { + // Skip decls which are not marked pub, which are in a different + // file than the `a.b`/`@hasDecl` syntax. + if (decl.is_pub or src_file == decl.namespace.file_scope) { + try candidates.append(gpa, decl); + } + } + var it = check_ns.usingnamespace_set.iterator(); + while (it.next()) |entry| { + const sub_usingnamespace_decl = entry.key_ptr.*; + const sub_is_pub = entry.value_ptr.*; + if (!sub_is_pub and src_file != sub_usingnamespace_decl.namespace.file_scope) { + // Skip usingnamespace decls which are not marked pub, which are in + // a different file than the `a.b`/`@hasDecl` syntax. + continue; + } + try sema.ensureDeclAnalyzed(sub_usingnamespace_decl); + const ns_ty = sub_usingnamespace_decl.val.castTag(.ty).?.data; + const sub_ns = ns_ty.getNamespace().?; + try checked_namespaces.put(gpa, sub_ns, {}); + } + } + + switch (candidates.items.len) { + 0 => {}, + 1 => { + const decl = candidates.items[0]; + try mod.declareDeclDependency(sema.owner_decl, decl); + return decl; + }, + else => { + const msg = msg: { + const msg = try mod.errMsg(&block.base, src, "ambiguous reference", .{}); + errdefer msg.destroy(gpa); + for (candidates.items) |candidate| { + const src_loc = candidate.srcLoc(); + try mod.errNoteNonLazy(src_loc, msg, "declared here", .{}); + } + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(&block.base, msg); + }, + } + } else if (namespace.decls.get(ident_name)) |decl| { + try mod.declareDeclDependency(sema.owner_decl, decl); return decl; } + log.debug("{*} ({s}) depends on non-existence of '{s}' in {*} ({s})", .{ sema.owner_decl, sema.owner_decl.name, ident_name, namespace_decl, namespace_decl.name, }); // TODO This dependency is too strong. Really, it should only be a dependency // on the non-existence of `ident_name` in the namespace. We can lessen the number of // outdated declarations by making this dependency more sophisticated. - try sema.mod.declareDeclDependency(sema.owner_decl, namespace_decl); + try mod.declareDeclDependency(sema.owner_decl, namespace_decl); return null; } @@ -2727,10 +2798,7 @@ fn analyzeCall( // we need to resolve the field type expressions right here, right now, while // the child `Sema` is still available, with the AIR instruction map intact, // because the field type expressions may reference into it. - if (sema.typeOf(result).zigTypeTag() == .Type) { - const ty = try sema.analyzeAsType(&child_block, call_src, result); - try sema.resolveDeclFields(&child_block, call_src, ty); - } + try sema.resolvePendingTypes(&child_block); } break :res2 result; @@ -5332,6 +5400,7 @@ fn zirHasField(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr fn zirHasDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; + const src = inst_data.src(); const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const container_type = try sema.resolveType(block, lhs_src, extra.lhs); @@ -5344,7 +5413,7 @@ fn zirHasDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr "expected struct, enum, union, or opaque, found '{}'", .{container_type}, ); - if (try sema.lookupInNamespace(namespace, decl_name)) |decl| { + if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl| { if (decl.is_pub or decl.namespace.file_scope == block.base.namespace().file_scope) { return Air.Inst.Ref.bool_true; } @@ -8203,7 +8272,7 @@ fn namespaceLookup( ) CompileError!?*Decl { const mod = sema.mod; const gpa = sema.gpa; - if (try sema.lookupInNamespace(namespace, decl_name)) |decl| { + if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl| { if (!decl.is_pub and decl.namespace.file_scope != block.getFileScope()) { const msg = msg: { const msg = try mod.errMsg(&block.base, src, "'{s}' is not marked 'pub'", .{ @@ -8919,8 +8988,7 @@ fn analyzeDeclVal( return result; } -fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref { - try sema.mod.declareDeclDependency(sema.owner_decl, decl); +fn ensureDeclAnalyzed(sema: *Sema, decl: *Decl) CompileError!void { sema.mod.ensureDeclAnalyzed(decl) catch |err| { if (sema.owner_func) |owner_func| { owner_func.state = .dependency_failure; @@ -8929,6 +8997,11 @@ fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref { } return err; }; +} + +fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref { + try sema.mod.declareDeclDependency(sema.owner_decl, decl); + try sema.ensureDeclAnalyzed(decl); const decl_tv = try decl.typedValue(); if (decl_tv.val.castTag(.variable)) |payload| { @@ -9560,6 +9633,16 @@ pub fn resolveTypeLayout( } } +pub fn resolvePendingTypes(sema: *Sema, block: *Scope.Block) !void { + for (sema.types_pending_resolution.items) |ty| { + // If an error happens resolving the fields of a struct, it will be marked + // invalid and a proper compile error set up. But we should still look at the + // other types pending resolution. + const src: LazySrcLoc = .{ .node_offset = 0 }; + sema.resolveDeclFields(block, src, ty) catch continue; + } +} + /// `sema` and `block` are expected to be the same ones used for the `Decl`. pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void { switch (ty.tag()) { diff --git a/src/codegen.zig b/src/codegen.zig index cf51f30fe3..0a8797d887 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -899,7 +899,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { fn dbgSetPrologueEnd(self: *Self) InnerError!void { switch (self.debug_output) { .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS_set_prologue_end); + try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .none => {}, @@ -909,7 +909,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { fn dbgSetEpilogueBegin(self: *Self) InnerError!void { switch (self.debug_output) { .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS_set_epilogue_begin); + try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .none => {}, @@ -925,13 +925,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // It lets you emit single-byte opcodes that add different numbers to // both the PC and the line number at the same time. try dbg_out.dbg_line.ensureUnusedCapacity(11); - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_advance_pc); + dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; if (delta_line != 0) { - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_advance_line); + dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; } - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_copy); + dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); }, .none => {}, } @@ -1010,7 +1010,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .dwarf => |dbg_out| { assert(ty.hasCodeGenBits()); const index = dbg_out.dbg_info.items.len; - try dbg_out.dbg_info.resize(index + 4); // DW.AT_type, DW.FORM_ref4 + try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.gpa, ty); if (!gop.found_existing) { @@ -2438,13 +2438,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .dwarf => |dbg_out| { try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 3); dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter); - dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT_location, DW.FORM_exprloc + dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), }); try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len); - try self.addDbgInfoTypeReloc(ty); // DW.AT_type, DW.FORM_ref4 - dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, .none => {}, } @@ -2467,15 +2467,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { var counting_writer = std.io.countingWriter(std.io.null_writer); leb128.writeILEB128(counting_writer.writer(), adjusted_stack_offset) catch unreachable; - // DW.AT_location, DW.FORM_exprloc + // DW.AT.location, DW.FORM.exprloc // ULEB128 dwarf expression length try leb128.writeULEB128(dbg_out.dbg_info.writer(), counting_writer.bytes_written + 1); - try dbg_out.dbg_info.append(DW.OP_breg11); + try dbg_out.dbg_info.append(DW.OP.breg11); try leb128.writeILEB128(dbg_out.dbg_info.writer(), adjusted_stack_offset); try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len); - try self.addDbgInfoTypeReloc(ty); // DW.AT_type, DW.FORM_ref4 - dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, else => {}, } diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig index 1c176df017..dfda04da85 100644 --- a/src/codegen/aarch64.zig +++ b/src/codegen/aarch64.zig @@ -52,7 +52,7 @@ pub const Register = enum(u6) { } pub fn dwarfLocOp(self: Register) u8 { - return @as(u8, self.id()) + DW.OP_reg0; + return @as(u8, self.id()) + DW.OP.reg0; } }; diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig index 42e3e52fac..ec9152f96b 100644 --- a/src/codegen/arm.zig +++ b/src/codegen/arm.zig @@ -170,7 +170,7 @@ pub const Register = enum(u5) { } pub fn dwarfLocOp(self: Register) u8 { - return @as(u8, self.id()) + DW.OP_reg0; + return @as(u8, self.id()) + DW.OP.reg0; } }; diff --git a/src/codegen/riscv64.zig b/src/codegen/riscv64.zig index 831f74b1b7..b297737816 100644 --- a/src/codegen/riscv64.zig +++ b/src/codegen/riscv64.zig @@ -390,7 +390,7 @@ pub const RawRegister = enum(u5) { x24, x25, x26, x27, x28, x29, x30, x31, pub fn dwarfLocOp(reg: RawRegister) u8 { - return @enumToInt(reg) + DW.OP_reg0; + return @enumToInt(reg) + DW.OP.reg0; } }; @@ -424,7 +424,7 @@ pub const Register = enum(u5) { } pub fn dwarfLocOp(reg: Register) u8 { - return @as(u8, @enumToInt(reg)) + DW.OP_reg0; + return @as(u8, @enumToInt(reg)) + DW.OP.reg0; } }; diff --git a/src/codegen/x86.zig b/src/codegen/x86.zig index fdad4e56db..5b981b9ef4 100644 --- a/src/codegen/x86.zig +++ b/src/codegen/x86.zig @@ -59,14 +59,14 @@ pub const Register = enum(u8) { pub fn dwarfLocOp(reg: Register) u8 { return switch (reg.to32()) { - .eax => DW.OP_reg0, - .ecx => DW.OP_reg1, - .edx => DW.OP_reg2, - .ebx => DW.OP_reg3, - .esp => DW.OP_reg4, - .ebp => DW.OP_reg5, - .esi => DW.OP_reg6, - .edi => DW.OP_reg7, + .eax => DW.OP.reg0, + .ecx => DW.OP.reg1, + .edx => DW.OP.reg2, + .ebx => DW.OP.reg3, + .esp => DW.OP.reg4, + .ebp => DW.OP.reg5, + .esi => DW.OP.reg6, + .edi => DW.OP.reg7, else => unreachable, }; } diff --git a/src/codegen/x86_64.zig b/src/codegen/x86_64.zig index 2964d7245e..72a7468041 100644 --- a/src/codegen/x86_64.zig +++ b/src/codegen/x86_64.zig @@ -115,23 +115,23 @@ pub const Register = enum(u8) { pub fn dwarfLocOp(self: Register) u8 { return switch (self.to64()) { - .rax => DW.OP_reg0, - .rdx => DW.OP_reg1, - .rcx => DW.OP_reg2, - .rbx => DW.OP_reg3, - .rsi => DW.OP_reg4, - .rdi => DW.OP_reg5, - .rbp => DW.OP_reg6, - .rsp => DW.OP_reg7, - - .r8 => DW.OP_reg8, - .r9 => DW.OP_reg9, - .r10 => DW.OP_reg10, - .r11 => DW.OP_reg11, - .r12 => DW.OP_reg12, - .r13 => DW.OP_reg13, - .r14 => DW.OP_reg14, - .r15 => DW.OP_reg15, + .rax => DW.OP.reg0, + .rdx => DW.OP.reg1, + .rcx => DW.OP.reg2, + .rbx => DW.OP.reg3, + .rsi => DW.OP.reg4, + .rdi => DW.OP.reg5, + .rbp => DW.OP.reg6, + .rsp => DW.OP.reg7, + + .r8 => DW.OP.reg8, + .r9 => DW.OP.reg9, + .r10 => DW.OP.reg10, + .r11 => DW.OP.reg11, + .r12 => DW.OP.reg12, + .r13 => DW.OP.reg13, + .r14 => DW.OP.reg14, + .r15 => DW.OP.reg15, else => unreachable, }; diff --git a/src/link.zig b/src/link.zig index 1293fab4d2..dbca4fc956 100644 --- a/src/link.zig +++ b/src/link.zig @@ -179,7 +179,7 @@ pub const File = struct { /// This is where the .debug_info tag for the type is. off: u32, /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). - /// List of DW.AT_type / DW.FORM_ref4 that points to the type. + /// List of DW.AT.type / DW.FORM.ref4 that points to the type. relocs: std.ArrayListUnmanaged(u32), }; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9ddebd3453..938fede8ab 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -772,48 +772,48 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { // These are LEB encoded but since the values are all less than 127 // we can simply append these bytes. const abbrev_buf = [_]u8{ - abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header - DW.AT_stmt_list, DW.FORM_sec_offset, DW.AT_low_pc, - DW.FORM_addr, DW.AT_high_pc, DW.FORM_addr, - DW.AT_name, DW.FORM_strp, DW.AT_comp_dir, - DW.FORM_strp, DW.AT_producer, DW.FORM_strp, - DW.AT_language, DW.FORM_data2, 0, + abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header + DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, + DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, + DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, + DW.FORM.strp, DW.AT.producer, DW.FORM.strp, + DW.AT.language, DW.FORM.data2, 0, 0, // table sentinel abbrev_subprogram, - DW.TAG_subprogram, - DW.CHILDREN_yes, // header - DW.AT_low_pc, - DW.FORM_addr, - DW.AT_high_pc, - DW.FORM_data4, - DW.AT_type, - DW.FORM_ref4, - DW.AT_name, - DW.FORM_string, + DW.TAG.subprogram, + DW.CHILDREN.yes, // header + DW.AT.low_pc, + DW.FORM.addr, + DW.AT.high_pc, + DW.FORM.data4, + DW.AT.type, + DW.FORM.ref4, + DW.AT.name, + DW.FORM.string, 0, 0, // table sentinel abbrev_subprogram_retvoid, - DW.TAG_subprogram, DW.CHILDREN_yes, // header - DW.AT_low_pc, DW.FORM_addr, - DW.AT_high_pc, DW.FORM_data4, - DW.AT_name, DW.FORM_string, + DW.TAG.subprogram, DW.CHILDREN.yes, // header + DW.AT.low_pc, DW.FORM.addr, + DW.AT.high_pc, DW.FORM.data4, + DW.AT.name, DW.FORM.string, 0, 0, // table sentinel abbrev_base_type, - DW.TAG_base_type, - DW.CHILDREN_no, // header - DW.AT_encoding, - DW.FORM_data1, - DW.AT_byte_size, - DW.FORM_data1, - DW.AT_name, - DW.FORM_string, 0, 0, // table sentinel - abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header + DW.TAG.base_type, + DW.CHILDREN.no, // header + DW.AT.encoding, + DW.FORM.data1, + DW.AT.byte_size, + DW.FORM.data1, + DW.AT.name, + DW.FORM.string, 0, 0, // table sentinel + abbrev_pad1, DW.TAG.unspecified_type, DW.CHILDREN.no, // header 0, 0, // table sentinel abbrev_parameter, - DW.TAG_formal_parameter, DW.CHILDREN_no, // header - DW.AT_location, DW.FORM_exprloc, - DW.AT_type, DW.FORM_ref4, - DW.AT_name, DW.FORM_string, + DW.TAG.formal_parameter, DW.CHILDREN.no, // header + DW.AT.location, DW.FORM.exprloc, + DW.AT.type, DW.FORM.ref4, + DW.AT.name, DW.FORM.string, 0, 0, // table sentinel 0, @@ -897,7 +897,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; di_buf.appendAssumeCapacity(abbrev_compile_unit); - self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset + self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT.stmt_list, DW.FORM.sec_offset self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc); self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc); self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp); @@ -906,7 +906,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number: // http://dwarfstd.org/ShowIssue.php?issue=171115.1 // Until then we say it is C99. - mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian); + mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99, target_endian); if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) { // Move the first N decls to the end to make more padding for the header. @@ -1030,7 +1030,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { di_buf.items.len += ptr_width_bytes; // We will come back and write this. const after_header_len = di_buf.items.len; - const opcode_base = DW.LNS_set_isa + 1; + const opcode_base = DW.LNS.set_isa + 1; di_buf.appendSliceAssumeCapacity(&[_]u8{ 1, // minimum_instruction_length 1, // maximum_operations_per_instruction @@ -1041,18 +1041,18 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { // Standard opcode lengths. The number of items here is based on `opcode_base`. // The value is the number of LEB128 operands the instruction takes. - 0, // `DW.LNS_copy` - 1, // `DW.LNS_advance_pc` - 1, // `DW.LNS_advance_line` - 1, // `DW.LNS_set_file` - 1, // `DW.LNS_set_column` - 0, // `DW.LNS_negate_stmt` - 0, // `DW.LNS_set_basic_block` - 0, // `DW.LNS_const_add_pc` - 1, // `DW.LNS_fixed_advance_pc` - 0, // `DW.LNS_set_prologue_end` - 0, // `DW.LNS_set_epilogue_begin` - 1, // `DW.LNS_set_isa` + 0, // `DW.LNS.copy` + 1, // `DW.LNS.advance_pc` + 1, // `DW.LNS.advance_line` + 1, // `DW.LNS.set_file` + 1, // `DW.LNS.set_column` + 0, // `DW.LNS.negate_stmt` + 0, // `DW.LNS.set_basic_block` + 0, // `DW.LNS.const_add_pc` + 1, // `DW.LNS.fixed_advance_pc` + 0, // `DW.LNS.set_prologue_end` + 0, // `DW.LNS.set_epilogue_begin` + 1, // `DW.LNS.set_isa` 0, // include_directories (none except the compilation unit cwd) }); // file_names[0] @@ -2053,7 +2053,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address // range of the compilation unit. When we expand the text section, this range changes, - // so the DW_TAG_compile_unit tag of the .debug_info section becomes dirty. + // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. self.debug_info_header_dirty = true; // This becomes dirty for the same reason. We could potentially make this more // fine-grained with the addition of support for more compilation units. It is planned to @@ -2303,22 +2303,22 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven const ptr_width_bytes = self.ptrWidthBytes(); dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{ - DW.LNS_extended_op, + DW.LNS.extended_op, ptr_width_bytes + 1, - DW.LNE_set_address, + DW.LNE.set_address, }); // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`. assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len); dbg_line_buffer.items.len += ptr_width_bytes; - dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line); + dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line); // This is the "relocatable" relative line offset from the previous function's end curly // to this function's begin curly. assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len); // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later. leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off); - dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file); + dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file); assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len); // Once we support more than one source file, this will have the ability to be more // than one possible value. @@ -2327,7 +2327,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven // Emit a line for the begin curly with prologue_end=false. The codegen will // do the work of setting prologue_end=true and epilogue_begin=true. - dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy); + dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy); // .debug_info subprogram const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1]; @@ -2344,9 +2344,9 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven // "relocations" and have to be in this fixed place so that functions can be // moved in virtual address space. assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len); - dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT_low_pc, DW.FORM_addr + dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT.low_pc, DW.FORM.addr assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len); - dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4 + dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4 if (fn_ret_has_bits) { const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type); if (!gop.found_existing) { @@ -2356,9 +2356,9 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven }; } try gop.value_ptr.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len)); - dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4 + dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 } - dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string + dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ .dwarf = .{ @@ -2409,7 +2409,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian); } - try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence }); + try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence }); // Now we have the full contents and may allocate a region to store it. @@ -2493,7 +2493,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven const file_pos = debug_line_sect.sh_offset + src_fn.off; try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos); - // .debug_info - End the TAG_subprogram children. + // .debug_info - End the TAG.subprogram children. try dbg_info_buffer.append(0); return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer); @@ -2566,34 +2566,34 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo .Bool => { try dbg_info_buffer.appendSlice(&[_]u8{ abbrev_base_type, - DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1 - 1, // DW.AT_byte_size, DW.FORM_data1 - 'b', 'o', 'o', 'l', 0, // DW.AT_name, DW.FORM_string + DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 + 1, // DW.AT.byte_size, DW.FORM.data1 + 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string }); }, .Int => { const info = ty.intInfo(self.base.options.target); try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12); dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); - // DW.AT_encoding, DW.FORM_data1 + // DW.AT.encoding, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { - .signed => DW.ATE_signed, - .unsigned => DW.ATE_unsigned, + .signed => DW.ATE.signed, + .unsigned => DW.ATE.unsigned, }); - // DW.AT_byte_size, DW.FORM_data1 + // DW.AT.byte_size, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target))); - // DW.AT_name, DW.FORM_string + // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{}\x00", .{ty}); }, .Optional => { if (ty.isPtrLikeOptional()) { try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12); dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); - // DW.AT_encoding, DW.FORM_data1 - dbg_info_buffer.appendAssumeCapacity(DW.ATE_address); - // DW.AT_byte_size, DW.FORM_data1 + // DW.AT.encoding, DW.FORM.data1 + dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); + // DW.AT.byte_size, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target))); - // DW.AT_name, DW.FORM_string + // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{}\x00", .{ty}); } else { log.err("TODO implement .debug_info for type '{}'", .{ty}); @@ -3034,7 +3034,7 @@ fn archPtrWidthBytes(self: Elf) u8 { /// The reloc offset for the virtual address of a function in its Line Number Program. /// Size is a virtual address integer. const dbg_line_vaddr_reloc_index = 3; -/// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram. +/// The reloc offset for the virtual address of a function in its .debug_info TAG.subprogram. /// Size is a virtual address integer. const dbg_info_low_pc_reloc_index = 1; @@ -3060,7 +3060,7 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 { const root_src_dir_path_len = if (self.base.options.module.?.root_pkg.root_src_directory.path) |p| p.len else 1; // "." return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + directory_count * 8 + file_name_count * 8 + - // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like + // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. root_src_dir_path_len + self.base.options.module.?.root_pkg.root_src_path.len); @@ -3088,8 +3088,8 @@ fn pwriteDbgLineNops( const tracy = trace(@src()); defer tracy.end(); - const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096; - const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 }; + const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096; + const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 }; var vecs: [512]std.os.iovec_const = undefined; var vec_index: usize = 0; { diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 025959793e..5f7b119bdc 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -93,7 +93,7 @@ const abbrev_parameter = 6; /// The reloc offset for the virtual address of a function in its Line Number Program. /// Size is a virtual address integer. const dbg_line_vaddr_reloc_index = 3; -/// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram. +/// The reloc offset for the virtual address of a function in its .debug_info TAG.subprogram. /// Size is a virtual address integer. const dbg_info_low_pc_reloc_index = 1; @@ -299,40 +299,40 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt // These are LEB encoded but since the values are all less than 127 // we can simply append these bytes. const abbrev_buf = [_]u8{ - abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header - DW.AT_stmt_list, DW.FORM_sec_offset, // offset - DW.AT_low_pc, DW.FORM_addr, - DW.AT_high_pc, DW.FORM_addr, - DW.AT_name, DW.FORM_strp, - DW.AT_comp_dir, DW.FORM_strp, - DW.AT_producer, DW.FORM_strp, - DW.AT_language, DW.FORM_data2, + abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header + DW.AT.stmt_list, DW.FORM.sec_offset, // offset + DW.AT.low_pc, DW.FORM.addr, + DW.AT.high_pc, DW.FORM.addr, + DW.AT.name, DW.FORM.strp, + DW.AT.comp_dir, DW.FORM.strp, + DW.AT.producer, DW.FORM.strp, + DW.AT.language, DW.FORM.data2, 0, 0, // table sentinel - abbrev_subprogram, DW.TAG_subprogram, DW.CHILDREN_yes, // header - DW.AT_low_pc, DW.FORM_addr, // start VM address - DW.AT_high_pc, DW.FORM_data4, - DW.AT_type, DW.FORM_ref4, - DW.AT_name, DW.FORM_string, - DW.AT_decl_line, DW.FORM_data4, - DW.AT_decl_file, DW.FORM_data1, + abbrev_subprogram, DW.TAG.subprogram, DW.CHILDREN.yes, // header + DW.AT.low_pc, DW.FORM.addr, // start VM address + DW.AT.high_pc, DW.FORM.data4, + DW.AT.type, DW.FORM.ref4, + DW.AT.name, DW.FORM.string, + DW.AT.decl_line, DW.FORM.data4, + DW.AT.decl_file, DW.FORM.data1, 0, 0, // table sentinel abbrev_subprogram_retvoid, - DW.TAG_subprogram, DW.CHILDREN_yes, // header - DW.AT_low_pc, DW.FORM_addr, - DW.AT_high_pc, DW.FORM_data4, - DW.AT_name, DW.FORM_string, - DW.AT_decl_line, DW.FORM_data4, - DW.AT_decl_file, DW.FORM_data1, + DW.TAG.subprogram, DW.CHILDREN.yes, // header + DW.AT.low_pc, DW.FORM.addr, + DW.AT.high_pc, DW.FORM.data4, + DW.AT.name, DW.FORM.string, + DW.AT.decl_line, DW.FORM.data4, + DW.AT.decl_file, DW.FORM.data1, 0, 0, // table sentinel - abbrev_base_type, DW.TAG_base_type, DW.CHILDREN_no, // header - DW.AT_encoding, DW.FORM_data1, DW.AT_byte_size, - DW.FORM_data1, DW.AT_name, DW.FORM_string, + abbrev_base_type, DW.TAG.base_type, DW.CHILDREN.no, // header + DW.AT.encoding, DW.FORM.data1, DW.AT.byte_size, + DW.FORM.data1, DW.AT.name, DW.FORM.string, 0, 0, // table sentinel - abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header + abbrev_pad1, DW.TAG.unspecified_type, DW.CHILDREN.no, // header 0, 0, // table sentinel - abbrev_parameter, DW.TAG_formal_parameter, DW.CHILDREN_no, // header - DW.AT_location, DW.FORM_exprloc, DW.AT_type, - DW.FORM_ref4, DW.AT_name, DW.FORM_string, + abbrev_parameter, DW.TAG.formal_parameter, DW.CHILDREN.no, // header + DW.AT.location, DW.FORM.exprloc, DW.AT.type, + DW.FORM.ref4, DW.AT.name, DW.FORM.string, 0, 0, // table sentinel 0, 0, 0, // section sentinel }; @@ -397,7 +397,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt const high_pc = text_section.addr + text_section.size; di_buf.appendAssumeCapacity(abbrev_compile_unit); - mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // DW.AT_stmt_list, DW.FORM_sec_offset + mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // DW.AT.stmt_list, DW.FORM.sec_offset mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), low_pc); mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), high_pc); mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, name_strp)); @@ -406,7 +406,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number: // http://dwarfstd.org/ShowIssue.php?issue=171115.1 // Until then we say it is C99. - mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99); + mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99); if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) { // Move the first N decls to the end to make more padding for the header. @@ -514,7 +514,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt di_buf.items.len += @sizeOf(u32); // We will come back and write this. const after_header_len = di_buf.items.len; - const opcode_base = DW.LNS_set_isa + 1; + const opcode_base = DW.LNS.set_isa + 1; di_buf.appendSliceAssumeCapacity(&[_]u8{ 1, // minimum_instruction_length 1, // maximum_operations_per_instruction @@ -525,18 +525,18 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt // Standard opcode lengths. The number of items here is based on `opcode_base`. // The value is the number of LEB128 operands the instruction takes. - 0, // `DW.LNS_copy` - 1, // `DW.LNS_advance_pc` - 1, // `DW.LNS_advance_line` - 1, // `DW.LNS_set_file` - 1, // `DW.LNS_set_column` - 0, // `DW.LNS_negate_stmt` - 0, // `DW.LNS_set_basic_block` - 0, // `DW.LNS_const_add_pc` - 1, // `DW.LNS_fixed_advance_pc` - 0, // `DW.LNS_set_prologue_end` - 0, // `DW.LNS_set_epilogue_begin` - 1, // `DW.LNS_set_isa` + 0, // `DW.LNS.copy` + 1, // `DW.LNS.advance_pc` + 1, // `DW.LNS.advance_line` + 1, // `DW.LNS.set_file` + 1, // `DW.LNS.set_column` + 0, // `DW.LNS.negate_stmt` + 0, // `DW.LNS.set_basic_block` + 0, // `DW.LNS.const_add_pc` + 1, // `DW.LNS.fixed_advance_pc` + 0, // `DW.LNS.set_prologue_end` + 0, // `DW.LNS.set_epilogue_begin` + 1, // `DW.LNS.set_isa` 0, // include_directories (none except the compilation unit cwd) }); // file_names[0] @@ -876,22 +876,22 @@ pub fn initDeclDebugBuffers( const line_off = @intCast(u28, decl.src_line + func.lbrace_line); dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{ - DW.LNS_extended_op, + DW.LNS.extended_op, @sizeOf(u64) + 1, - DW.LNE_set_address, + DW.LNE.set_address, }); // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`. assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len); dbg_line_buffer.items.len += @sizeOf(u64); - dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line); + dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line); // This is the "relocatable" relative line offset from the previous function's end curly // to this function's begin curly. assert(getRelocDbgLineOff() == dbg_line_buffer.items.len); // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later. leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off); - dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file); + dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file); assert(getRelocDbgFileIndex() == dbg_line_buffer.items.len); // Once we support more than one source file, this will have the ability to be more // than one possible value. @@ -900,7 +900,7 @@ pub fn initDeclDebugBuffers( // Emit a line for the begin curly with prologue_end=false. The codegen will // do the work of setting prologue_end=true and epilogue_begin=true. - dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy); + dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy); // .debug_info subprogram const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1]; @@ -917,9 +917,9 @@ pub fn initDeclDebugBuffers( // "relocations" and have to be in this fixed place so that functions can be // moved in virtual address space. assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len); - dbg_info_buffer.items.len += @sizeOf(u64); // DW.AT_low_pc, DW.FORM_addr + dbg_info_buffer.items.len += @sizeOf(u64); // DW.AT.low_pc, DW.FORM.addr assert(getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len); - dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4 + dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4 if (fn_ret_has_bits) { const gop = try dbg_info_type_relocs.getOrPut(allocator, fn_ret_type); if (!gop.found_existing) { @@ -929,11 +929,11 @@ pub fn initDeclDebugBuffers( }; } try gop.value_ptr.relocs.append(allocator, @intCast(u32, dbg_info_buffer.items.len)); - dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4 + dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 } - dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string - mem.writeIntLittle(u32, dbg_info_buffer.addManyAsArrayAssumeCapacity(4), line_off + 1); // DW.AT_decl_line, DW.FORM_data4 - dbg_info_buffer.appendAssumeCapacity(file_index); // DW.AT_decl_file, DW.FORM_data1 + dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string + mem.writeIntLittle(u32, dbg_info_buffer.addManyAsArrayAssumeCapacity(4), line_off + 1); // DW.AT.decl_line, DW.FORM.data4 + dbg_info_buffer.appendAssumeCapacity(file_index); // DW.AT.decl_file, DW.FORM.data1 }, else => { // TODO implement .debug_info for global variables @@ -985,16 +985,16 @@ pub fn commitDeclDebugInfo( { // Advance line and PC. // TODO encapsulate logic in a helper function. - try dbg_line_buffer.append(DW.LNS_advance_pc); + try dbg_line_buffer.append(DW.LNS.advance_pc); try leb.writeULEB128(dbg_line_buffer.writer(), text_block.size); - try dbg_line_buffer.append(DW.LNS_advance_line); + try dbg_line_buffer.append(DW.LNS.advance_line); const func = decl.val.castTag(.function).?.data; const line_off = @intCast(u28, func.rbrace_line - func.lbrace_line); try leb.writeULEB128(dbg_line_buffer.writer(), line_off); } - try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence }); + try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence }); // Now we have the full contents and may allocate a region to store it. @@ -1075,7 +1075,7 @@ pub fn commitDeclDebugInfo( const file_pos = debug_line_sect.offset + src_fn.off; try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos); - // .debug_info - End the TAG_subprogram children. + // .debug_info - End the TAG.subprogram children. try dbg_info_buffer.append(0); }, else => {}, @@ -1128,27 +1128,27 @@ fn addDbgInfoType( .Bool => { try dbg_info_buffer.appendSlice(&[_]u8{ abbrev_base_type, - DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1 - 1, // DW.AT_byte_size, DW.FORM_data1 + DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 + 1, // DW.AT.byte_size, DW.FORM.data1 'b', 'o', 'o', 'l', - 0, // DW.AT_name, DW.FORM_string + 0, // DW.AT.name, DW.FORM.string }); }, .Int => { const info = ty.intInfo(target); try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12); dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); - // DW.AT_encoding, DW.FORM_data1 + // DW.AT.encoding, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { - .signed => DW.ATE_signed, - .unsigned => DW.ATE_unsigned, + .signed => DW.ATE.signed, + .unsigned => DW.ATE.unsigned, }); - // DW.AT_byte_size, DW.FORM_data1 + // DW.AT.byte_size, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); - // DW.AT_name, DW.FORM_string + // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{}\x00", .{ty}); }, else => { @@ -1306,7 +1306,7 @@ fn dbgLineNeededHeaderBytes(self: DebugSymbols, module: *Module) u32 { const root_src_dir_path_len = if (module.root_pkg.root_src_directory.path) |p| p.len else 1; // "." return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + directory_count * 8 + file_name_count * 8 + - // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like + // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. root_src_dir_path_len + module.root_pkg.root_src_path.len); @@ -1332,8 +1332,8 @@ fn pwriteDbgLineNops( const tracy = trace(@src()); defer tracy.end(); - const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096; - const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 }; + const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096; + const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 }; var vecs: [32]std.os.iovec_const = undefined; var vec_index: usize = 0; { diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 06c76b259d..1051cda694 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -836,8 +836,8 @@ pub fn parseDebugInfo(self: *Object, allocator: *Allocator) !void { }, else => |e| return e, }; - const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name); - const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir); + const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.name); + const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.comp_dir); self.debug_info = debug_info; self.tu_name = try allocator.dupe(u8, name); diff --git a/test/behavior.zig b/test/behavior.zig index 7028d6cbf6..80de1880d8 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -10,6 +10,7 @@ test { _ = @import("behavior/if.zig"); _ = @import("behavior/cast.zig"); _ = @import("behavior/array.zig"); + _ = @import("behavior/usingnamespace.zig"); if (!builtin.zig_is_stage2) { // Tests that only pass for stage1. @@ -148,7 +149,7 @@ test { _ = @import("behavior/undefined.zig"); _ = @import("behavior/underscore.zig"); _ = @import("behavior/union.zig"); - _ = @import("behavior/usingnamespace.zig"); + _ = @import("behavior/usingnamespace_stage1.zig"); _ = @import("behavior/var_args.zig"); _ = @import("behavior/vector.zig"); _ = @import("behavior/void.zig"); diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 1b6540cd32..0d103bc49a 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -125,12 +125,6 @@ test "pointer to type" { } } -test "no undeclared identifier error in unanalyzed branches" { - if (false) { - lol_this_doesnt_exist = nonsense; - } -} - test "a type constructed in a global expression" { var l: List = undefined; l.array[0] = 10; diff --git a/test/behavior/usingnamespace.zig b/test/behavior/usingnamespace.zig index b529b24dd4..e819566002 100644 --- a/test/behavior/usingnamespace.zig +++ b/test/behavior/usingnamespace.zig @@ -1,22 +1,13 @@ const std = @import("std"); -fn Foo(comptime T: type) type { - return struct { - usingnamespace T; - }; -} - -test "usingnamespace inside a generic struct" { - const std2 = Foo(std); - const testing2 = Foo(std.testing); - try std2.testing.expect(true); - try testing2.expect(true); -} +const A = struct { + pub const B = bool; +}; -usingnamespace struct { - pub const foo = 42; +const C = struct { + usingnamespace A; }; -test "usingnamespace does not redeclare an imported variable" { - comptime try std.testing.expect(foo == 42); +test "basic usingnamespace" { + try std.testing.expect(C.B == bool); } diff --git a/test/behavior/usingnamespace_stage1.zig b/test/behavior/usingnamespace_stage1.zig new file mode 100644 index 0000000000..b529b24dd4 --- /dev/null +++ b/test/behavior/usingnamespace_stage1.zig @@ -0,0 +1,22 @@ +const std = @import("std"); + +fn Foo(comptime T: type) type { + return struct { + usingnamespace T; + }; +} + +test "usingnamespace inside a generic struct" { + const std2 = Foo(std); + const testing2 = Foo(std.testing); + try std2.testing.expect(true); + try testing2.expect(true); +} + +usingnamespace struct { + pub const foo = 42; +}; + +test "usingnamespace does not redeclare an imported variable" { + comptime try std.testing.expect(foo == 42); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 4c55e39ac7..82ee089970 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -8846,4 +8846,14 @@ pub fn addCases(ctx: *TestContext) !void { , &[_][]const u8{ "error: invalid operands to binary expression: 'f32' and 'f32'", }); + + ctx.objErrStage1("undeclared identifier in unanalyzed branch", + \\export fn a() void { + \\ if (false) { + \\ lol_this_doesnt_exist = nonsense; + \\ } + \\} + , &[_][]const u8{ + "tmp.zig:3:9: error: use of undeclared identifier 'lol_this_doesnt_exist'", + }); } -- cgit v1.2.3 From e41e75a4862e955266a7e760133ea757e5afb8ce Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Aug 2021 18:47:00 -0700 Subject: stage2: update for new usingnamespace semantics --- src/AstGen.zig | 8 -------- src/clang_options_data.zig | 8 +++++++- src/codegen/spirv/spec.zig | 4 ++-- src/libc_installation.zig | 6 +++--- src/link/MachO/DebugSymbols.zig | 5 ++++- src/link/tapi/parse/test.zig | 4 ++-- src/type.zig | 2 +- src/value.zig | 2 +- src/windows_sdk.zig | 21 +++++++++++++-------- tools/update_clang_options.zig | 8 +++++++- 10 files changed, 40 insertions(+), 28 deletions(-) (limited to 'src/codegen') diff --git a/src/AstGen.zig b/src/AstGen.zig index c96a97ddeb..570b2cd943 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -9072,14 +9072,6 @@ const GenZir = struct { return @intCast(u32, gz.decl_line + astgen.source_line); } - fn tokSrcLoc(gz: GenZir, token_index: ast.TokenIndex) LazySrcLoc { - return .{ .token_offset = token_index - gz.srcToken() }; - } - - fn nodeSrcLoc(gz: GenZir, node_index: ast.Node.Index) LazySrcLoc { - return .{ .node_offset = gz.nodeIndexToRelative(node_index) }; - } - fn nodeIndexToRelative(gz: GenZir, node_index: ast.Node.Index) i32 { return @bitCast(i32, node_index) - @bitCast(i32, gz.decl_node_index); } diff --git a/src/clang_options_data.zig b/src/clang_options_data.zig index eb8dfc5753..524374a7e9 100644 --- a/src/clang_options_data.zig +++ b/src/clang_options_data.zig @@ -1,6 +1,12 @@ // This file is generated by tools/update_clang_options.zig. // zig fmt: off -usingnamespace @import("clang_options.zig"); +const clang_options = @import("clang_options.zig"); +const CliArg = clang_options.CliArg; +const flagpd1 = clang_options.flagpd1; +const flagpsl = clang_options.flagpsl; +const joinpd1 = clang_options.joinpd1; +const jspd1 = clang_options.jspd1; +const sepd1 = clang_options.sepd1; pub const data = blk: { @setEvalBranchQuota(6000); break :blk &[_]CliArg{ flagpd1("C"), flagpd1("CC"), diff --git a/src/codegen/spirv/spec.zig b/src/codegen/spirv/spec.zig index 429ed63d23..26d1925646 100644 --- a/src/codegen/spirv/spec.zig +++ b/src/codegen/spirv/spec.zig @@ -582,8 +582,8 @@ pub const Opcode = enum(u16) { OpSpecConstantCompositeContinuedINTEL = 6092, _, - const OpReportIntersectionKHR = OpReportIntersectionNV; - const OpTypeAccelerationStructureKHR = OpTypeAccelerationStructureNV; + const OpReportIntersectionKHR: Opcode = .OpReportIntersectionNV; + const OpTypeAccelerationStructureKHR: Opcode = .OpTypeAccelerationStructureNV; }; pub const ImageOperands = packed struct { Bias: bool align(@alignOf(u32)) = false, diff --git a/src/libc_installation.zig b/src/libc_installation.zig index b639e0f2f8..c011abbfcc 100644 --- a/src/libc_installation.zig +++ b/src/libc_installation.zig @@ -12,7 +12,7 @@ const is_haiku = Target.current.os.tag == .haiku; const log = std.log.scoped(.libc_installation); -usingnamespace @import("windows_sdk.zig"); +const ZigWindowsSDK = @import("windows_sdk.zig").ZigWindowsSDK; /// See the render function implementation for documentation of the fields. pub const LibCInstallation = struct { @@ -183,9 +183,9 @@ pub const LibCInstallation = struct { if (!build_options.have_llvm) return error.WindowsSdkNotFound; var sdk: *ZigWindowsSDK = undefined; - switch (zig_find_windows_sdk(&sdk)) { + switch (ZigWindowsSDK.find(&sdk)) { .None => { - defer zig_free_windows_sdk(sdk); + defer sdk.free(); var batch = Batch(FindError!void, 5, .auto_async).init(); batch.add(&async self.findNativeMsvcIncludeDir(args, sdk)); diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 5f7b119bdc..ee99adafaa 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -20,7 +20,10 @@ const SrcFn = MachO.SrcFn; const TextBlock = MachO.TextBlock; const padToIdeal = MachO.padToIdeal; -usingnamespace @import("commands.zig"); +const commands = @import("commands.zig"); +const emptyHeader = commands.emptyHeader; +const LoadCommand = commands.LoadCommand; +const SegmentCommand = commands.SegmentCommand; const page_size: u16 = 0x1000; diff --git a/src/link/tapi/parse/test.zig b/src/link/tapi/parse/test.zig index b96a71fe97..fc0bed5df6 100644 --- a/src/link/tapi/parse/test.zig +++ b/src/link/tapi/parse/test.zig @@ -1,8 +1,8 @@ const std = @import("std"); const mem = std.mem; const testing = std.testing; - -usingnamespace @import("../parse.zig"); +const Tree = @import("../parse.zig").Tree; +const Node = @import("../parse.zig").Node; test "explicit doc" { const source = diff --git a/src/type.zig b/src/type.zig index aa4517ab05..b6602ba18d 100644 --- a/src/type.zig +++ b/src/type.zig @@ -197,7 +197,7 @@ pub const Type = extern union { /// Prefer `castTag` to this. pub fn cast(self: Type, comptime T: type) ?*T { if (@hasField(T, "base_tag")) { - return base.castTag(T.base_tag); + return self.castTag(T.base_tag); } if (self.tag_if_small_enough < Tag.no_payload_count) { return null; diff --git a/src/value.zig b/src/value.zig index 5ac9f142c4..8aaf70c428 100644 --- a/src/value.zig +++ b/src/value.zig @@ -315,7 +315,7 @@ pub const Value = extern union { /// Prefer `castTag` to this. pub fn cast(self: Value, comptime T: type) ?*T { if (@hasField(T, "base_tag")) { - return base.castTag(T.base_tag); + return self.castTag(T.base_tag); } if (self.tag_if_small_enough < Tag.no_payload_count) { return null; diff --git a/src/windows_sdk.zig b/src/windows_sdk.zig index ca69ff4548..3ff53dc2e1 100644 --- a/src/windows_sdk.zig +++ b/src/windows_sdk.zig @@ -11,12 +11,17 @@ pub const ZigWindowsSDK = extern struct { version81_len: usize, msvc_lib_dir_ptr: ?[*]const u8, msvc_lib_dir_len: usize, + + pub const find = zig_find_windows_sdk; + pub const free = zig_free_windows_sdk; + + pub const FindError = enum(c_int) { + None, + OutOfMemory, + NotFound, + PathTooLong, + }; + + extern fn zig_find_windows_sdk(out_sdk: **ZigWindowsSDK) FindError; + extern fn zig_free_windows_sdk(sdk: *ZigWindowsSDK) void; }; -pub const ZigFindWindowsSdkError = enum(c_int) { - None, - OutOfMemory, - NotFound, - PathTooLong, -}; -pub extern fn zig_find_windows_sdk(out_sdk: **ZigWindowsSDK) ZigFindWindowsSdkError; -pub extern fn zig_free_windows_sdk(sdk: *ZigWindowsSDK) void; diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index 04cdbf44de..407e813ef3 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -473,7 +473,13 @@ pub fn main() anyerror!void { try stdout.writeAll( \\// This file is generated by tools/update_clang_options.zig. \\// zig fmt: off - \\usingnamespace @import("clang_options.zig"); + \\const clang_options = @import("clang_options.zig"); + \\const CliArg = clang_options.CliArg; + \\const flagpd1 = clang_options.flagpd1; + \\const flagpsl = clang_options.flagpsl; + \\const joinpd1 = clang_options.joinpd1; + \\const jspd1 = clang_options.jspd1; + \\const sepd1 = clang_options.sepd1; \\pub const data = blk: { @setEvalBranchQuota(6000); break :blk &[_]CliArg{ \\ ); -- cgit v1.2.3 From 97d69e3352ab50f88580c383b5f375b0edadacfd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Sep 2021 21:37:11 -0700 Subject: stage2: add array_to_slice AIR instruction --- src/Air.zig | 4 +++ src/Liveness.zig | 1 + src/Sema.zig | 3 +- src/codegen.zig | 11 ++++++ src/codegen/c.zig | 18 ++++++++++ src/codegen/llvm.zig | 63 ++++++++++++++++++++++------------ src/codegen/llvm/bindings.zig | 9 +++++ src/print_air.zig | 1 + test/behavior/array.zig | 24 +++++++++++++ test/behavior/array_stage1.zig | 78 +++++------------------------------------- 10 files changed, 120 insertions(+), 92 deletions(-) (limited to 'src/codegen') diff --git a/src/Air.zig b/src/Air.zig index 60e5fdce1c..bc939f6b04 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -306,6 +306,9 @@ pub const Inst = struct { /// Result type is the element type of the inner pointer operand. /// Uses the `bin_op` field. ptr_ptr_elem_val, + /// Given a pointer to an array, return a slice. + /// Uses the `ty_op` field. + array_to_slice, pub fn fromCmpOp(op: std.math.CompareOperator) Tag { return switch (op) { @@ -526,6 +529,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .struct_field_ptr_index_1, .struct_field_ptr_index_2, .struct_field_ptr_index_3, + .array_to_slice, => return air.getRefType(datas[inst].ty_op.ty), .loop, diff --git a/src/Liveness.zig b/src/Liveness.zig index 2f1ecc9c43..f33c44d592 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -287,6 +287,7 @@ fn analyzeInst( .struct_field_ptr_index_1, .struct_field_ptr_index_2, .struct_field_ptr_index_3, + .array_to_slice, => { const o = inst_datas[inst].ty_op; return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none }); diff --git a/src/Sema.zig b/src/Sema.zig index 136a687774..ec163dd0a6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8952,7 +8952,8 @@ fn coerceArrayPtrToSlice( // The comptime Value representation is compatible with both types. return sema.addConstant(dest_type, val); } - return sema.mod.fail(&block.base, inst_src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{}); + try sema.requireRuntimeBlock(block, inst_src); + return block.addTyOp(.array_to_slice, dest_type, inst); } fn coerceArrayPtrToMany( diff --git a/src/codegen.zig b/src/codegen.zig index 0a8797d887..e28192ec1f 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -856,6 +856,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .store => try self.airStore(inst), .struct_field_ptr=> try self.airStructFieldPtr(inst), .struct_field_val=> try self.airStructFieldVal(inst), + .array_to_slice => try self.airArrayToSlice(inst), .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), @@ -4761,6 +4762,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } + fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { + else => return self.fail("TODO implement airArrayToSlice for {}", .{ + self.target.cpu.arch, + }), + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + } + fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // First section of indexes correspond to a set number of constant values. const ref_int = @enumToInt(inst); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index fd964f2829..84bc6ceeb0 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -910,6 +910,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM .switch_br => try airSwitchBr(o, inst), .wrap_optional => try airWrapOptional(o, inst), .struct_field_ptr => try airStructFieldPtr(o, inst), + .array_to_slice => try airArrayToSlice(o, inst), .struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0), .struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1), @@ -1860,6 +1861,23 @@ fn airIsErr( return local; } +fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue { + if (o.liveness.isUnused(inst)) + return CValue.none; + + const inst_ty = o.air.typeOfIndex(inst); + const local = try o.allocLocal(inst_ty, .Const); + const ty_op = o.air.instructions.items(.data)[inst].ty_op; + const writer = o.writer(); + const operand = try o.resolveInst(ty_op.operand); + const array_len = o.air.typeOf(ty_op.operand).elemType().arrayLen(); + + try writer.writeAll(" = { .ptr = "); + try o.writeCValue(writer, operand); + try writer.print(", .len = {d} }};\n", .{array_len}); + return local; +} + fn IndentWriter(comptime UnderlyingWriter: type) type { return struct { const Self = @This(); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 8b7fd5dc54..d39262d62e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1006,28 +1006,29 @@ pub const FuncGen = struct { .is_err => try self.airIsErr(inst, .NE, false), .is_err_ptr => try self.airIsErr(inst, .NE, true), - .alloc => try self.airAlloc(inst), - .arg => try self.airArg(inst), - .bitcast => try self.airBitCast(inst), - .bool_to_int=> try self.airBoolToInt(inst), - .block => try self.airBlock(inst), - .br => try self.airBr(inst), - .switch_br => try self.airSwitchBr(inst), - .breakpoint => try self.airBreakpoint(inst), - .call => try self.airCall(inst), - .cond_br => try self.airCondBr(inst), - .intcast => try self.airIntCast(inst), - .trunc => try self.airTrunc(inst), - .floatcast => try self.airFloatCast(inst), - .ptrtoint => try self.airPtrToInt(inst), - .load => try self.airLoad(inst), - .loop => try self.airLoop(inst), - .not => try self.airNot(inst), - .ret => try self.airRet(inst), - .store => try self.airStore(inst), - .assembly => try self.airAssembly(inst), - .slice_ptr => try self.airSliceField(inst, 0), - .slice_len => try self.airSliceField(inst, 1), + .alloc => try self.airAlloc(inst), + .arg => try self.airArg(inst), + .bitcast => try self.airBitCast(inst), + .bool_to_int => try self.airBoolToInt(inst), + .block => try self.airBlock(inst), + .br => try self.airBr(inst), + .switch_br => try self.airSwitchBr(inst), + .breakpoint => try self.airBreakpoint(inst), + .call => try self.airCall(inst), + .cond_br => try self.airCondBr(inst), + .intcast => try self.airIntCast(inst), + .trunc => try self.airTrunc(inst), + .floatcast => try self.airFloatCast(inst), + .ptrtoint => try self.airPtrToInt(inst), + .load => try self.airLoad(inst), + .loop => try self.airLoop(inst), + .not => try self.airNot(inst), + .ret => try self.airRet(inst), + .store => try self.airStore(inst), + .assembly => try self.airAssembly(inst), + .slice_ptr => try self.airSliceField(inst, 0), + .slice_len => try self.airSliceField(inst, 1), + .array_to_slice => try self.airArrayToSlice(inst), .struct_field_ptr => try self.airStructFieldPtr(inst), .struct_field_val => try self.airStructFieldVal(inst), @@ -1246,6 +1247,24 @@ pub const FuncGen = struct { return null; } + fn airArrayToSlice(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) + return null; + + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const operand = try self.resolveInst(ty_op.operand); + const array_len = self.air.typeOf(ty_op.operand).elemType().arrayLen(); + const usize_llvm_ty = try self.dg.llvmType(Type.initTag(.usize)); + const len = usize_llvm_ty.constInt(array_len, .False); + const slice_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst)); + const indices: [2]*const llvm.Value = .{ + usize_llvm_ty.constNull(), usize_llvm_ty.constNull(), + }; + const ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""); + const partial = self.builder.buildInsertValue(slice_llvm_ty.getUndef(), ptr, 0, ""); + return self.builder.buildInsertValue(partial, len, 1, ""); + } + fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index a10002b5d6..e0283be236 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -484,6 +484,15 @@ pub const Builder = opaque { DestTy: *const Type, Name: [*:0]const u8, ) *const Value; + + pub const buildInsertValue = LLVMBuildInsertValue; + extern fn LLVMBuildInsertValue( + *const Builder, + AggVal: *const Value, + EltVal: *const Value, + Index: c_uint, + Name: [*:0]const u8, + ) *const Value; }; pub const IntPredicate = enum(c_uint) { diff --git a/src/print_air.zig b/src/print_air.zig index 0106d0a0f3..4d9ce1bb36 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -174,6 +174,7 @@ const Writer = struct { .struct_field_ptr_index_1, .struct_field_ptr_index_2, .struct_field_ptr_index_3, + .array_to_slice, => try w.writeTyOp(s, inst), .block, diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 232ba87f55..8cf0042d5f 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -3,3 +3,27 @@ const testing = std.testing; const mem = std.mem; const expect = testing.expect; const expectEqual = testing.expectEqual; + +test "arrays" { + var array: [5]u32 = undefined; + + var i: u32 = 0; + while (i < 5) { + array[i] = i + 1; + i = array[i]; + } + + i = 0; + var accumulator = @as(u32, 0); + while (i < 5) { + accumulator += array[i]; + + i += 1; + } + + try expect(accumulator == 15); + try expect(getArrayLen(&array) == 5); +} +fn getArrayLen(a: []const u32) usize { + return a.len; +} diff --git a/test/behavior/array_stage1.zig b/test/behavior/array_stage1.zig index 84a2cdb36c..d3a97665ac 100644 --- a/test/behavior/array_stage1.zig +++ b/test/behavior/array_stage1.zig @@ -4,30 +4,6 @@ const mem = std.mem; const expect = testing.expect; const expectEqual = testing.expectEqual; -test "arrays" { - var array: [5]u32 = undefined; - - var i: u32 = 0; - while (i < 5) { - array[i] = i + 1; - i = array[i]; - } - - i = 0; - var accumulator = @as(u32, 0); - while (i < 5) { - accumulator += array[i]; - - i += 1; - } - - try expect(accumulator == 15); - try expect(getArrayLen(&array) == 5); -} -fn getArrayLen(a: []const u32) usize { - return a.len; -} - test "array with sentinels" { const S = struct { fn doTheTest(is_ct: bool) !void { @@ -64,12 +40,7 @@ test "void arrays" { } test "array literal" { - const hex_mult = [_]u16{ - 4096, - 256, - 16, - 1, - }; + const hex_mult = [_]u16{ 4096, 256, 16, 1 }; try expect(hex_mult.len == 4); try expect(hex_mult[1] == 256); @@ -84,21 +55,10 @@ test "array dot len const expr" { const ArrayDotLenConstExpr = struct { y: [some_array.len]u8, }; -const some_array = [_]u8{ - 0, - 1, - 2, - 3, -}; +const some_array = [_]u8{ 0, 1, 2, 3 }; test "nested arrays" { - const array_of_strings = [_][]const u8{ - "hello", - "this", - "is", - "my", - "thing", - }; + const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" }; for (array_of_strings) |s, i| { if (i == 0) try expect(mem.eql(u8, s, "hello")); if (i == 1) try expect(mem.eql(u8, s, "this")); @@ -109,12 +69,8 @@ test "nested arrays" { } var s_array: [8]Sub = undefined; -const Sub = struct { - b: u8, -}; -const Str = struct { - a: []Sub, -}; +const Sub = struct { b: u8 }; +const Str = struct { a: []Sub }; test "set global var array via slice embedded in struct" { var s = Str{ .a = s_array[0..] }; @@ -208,26 +164,10 @@ test "runtime initialize array elem and then implicit cast to slice" { test "array literal as argument to function" { const S = struct { fn entry(two: i32) !void { - try foo(&[_]i32{ - 1, - 2, - 3, - }); - try foo(&[_]i32{ - 1, - two, - 3, - }); - try foo2(true, &[_]i32{ - 1, - 2, - 3, - }); - try foo2(true, &[_]i32{ - 1, - two, - 3, - }); + try foo(&[_]i32{ 1, 2, 3 }); + try foo(&[_]i32{ 1, two, 3 }); + try foo2(true, &[_]i32{ 1, 2, 3 }); + try foo2(true, &[_]i32{ 1, two, 3 }); } fn foo(x: []const i32) !void { try expect(x[0] == 1); -- cgit v1.2.3 From 0395b35cee8d4082cc40b0dcd0298f797f42309d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Sep 2021 21:58:22 -0700 Subject: stage2: implement cmpxchg and improve comptime eval * Implement Sema for `@cmpxchgWeak` and `@cmpxchgStrong`. Both runtime and comptime codepaths are implement. * Implement Codegen for LLVM backend and C backend. * Add LazySrcLoc.node_offset_builtin_call_argX 3...5 * Sema: rework comptime control flow. - `error.ComptimeReturn` is used to signal that a comptime function call has returned a result (stored in the Inlining struct). `analyzeCall` notices this and handles the result. - The ZIR instructions `break_inline`, `block_inline`, `condbr_inline` are now redundant and can be deleted. `break`, `block`, and `condbr` function equivalently inside a comptime scope. - The ZIR instructions `loop` and `repeat` also are modified to directly perform comptime control flow inside a comptime scope, skipping an unnecessary mechanism for analysis of runtime code. This makes Zig perform closer to an interpreter when evaluating comptime code. * Sema: zirRetErrValue looks at Sema.ret_fn_ty rather than sema.func for adding to the inferred error set. This fixes a bug for inlined/comptime function calls. * Implement ZIR printing for cmpxchg. * stage1: make cmpxchg respect --single-threaded - Our LLVM C++ API wrapper failed to expose this boolean flag before. * Fix AIR printing for struct fields showing incorrect liveness data. --- src/Air.zig | 23 ++++ src/AstGen.zig | 11 +- src/Liveness.zig | 4 + src/Module.zig | 94 ++++++++------ src/Sema.zig | 258 ++++++++++++++++++++++++++++++++++----- src/Zir.zig | 25 +++- src/codegen.zig | 13 ++ src/codegen/c.zig | 39 ++++++ src/codegen/llvm.zig | 93 ++++++++++++++ src/codegen/llvm/bindings.zig | 39 ++++++ src/link/C/zig.h | 12 ++ src/print_air.zig | 17 ++- src/stage1/codegen.cpp | 2 +- src/target.zig | 69 +++++++++++ src/type.zig | 29 +++++ src/zig_llvm.cpp | 23 +--- src/zig_llvm.h | 2 +- test/behavior/atomics.zig | 22 ++++ test/behavior/atomics_stage1.zig | 22 ---- 19 files changed, 682 insertions(+), 115 deletions(-) (limited to 'src/codegen') diff --git a/src/Air.zig b/src/Air.zig index bc939f6b04..29deb9a523 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -309,6 +309,10 @@ pub const Inst = struct { /// Given a pointer to an array, return a slice. /// Uses the `ty_op` field. array_to_slice, + /// Uses the `ty_pl` field with payload `Cmpxchg`. + cmpxchg_weak, + /// Uses the `ty_pl` field with payload `Cmpxchg`. + cmpxchg_strong, pub fn fromCmpOp(op: std.math.CompareOperator) Tag { return switch (op) { @@ -443,6 +447,23 @@ pub const Asm = struct { zir_index: u32, }; +pub const Cmpxchg = struct { + ptr: Inst.Ref, + expected_value: Inst.Ref, + new_value: Inst.Ref, + /// 0b00000000000000000000000000000XXX - success_order + /// 0b00000000000000000000000000XXX000 - failure_order + flags: u32, + + pub fn successOrder(self: Cmpxchg) std.builtin.AtomicOrder { + return @intToEnum(std.builtin.AtomicOrder, @truncate(u3, self.flags)); + } + + pub fn failureOrder(self: Cmpxchg) std.builtin.AtomicOrder { + return @intToEnum(std.builtin.AtomicOrder, @truncate(u3, self.flags >> 3)); + } +}; + pub fn getMainBody(air: Air) []const Air.Inst.Index { const body_index = air.extra[@enumToInt(ExtraIndex.main_block)]; const extra = air.extraData(Block, body_index); @@ -507,6 +528,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .struct_field_ptr, .struct_field_val, .ptr_elem_ptr, + .cmpxchg_weak, + .cmpxchg_strong, => return air.getRefType(datas[inst].ty_pl.ty), .not, diff --git a/src/AstGen.zig b/src/AstGen.zig index a8b7586f6e..b9d7d6f5be 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7542,6 +7542,7 @@ fn cmpxchg( tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { const int_type = try typeExpr(gz, scope, params[0]); + // TODO: allow this to be volatile const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ .ptr_type_simple = .{ .is_allowzero = false, @@ -7553,11 +7554,11 @@ fn cmpxchg( } }); const result = try gz.addPlNode(tag, node, Zir.Inst.Cmpxchg{ // zig fmt: off - .ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[1]), - .expected_value = try expr(gz, scope, .{ .ty = int_type }, params[2]), - .new_value = try expr(gz, scope, .{ .ty = int_type }, params[3]), - .success_order = try expr(gz, scope, .{ .ty = .atomic_order_type }, params[4]), - .fail_order = try expr(gz, scope, .{ .ty = .atomic_order_type }, params[5]), + .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), + .expected_value = try expr(gz, scope, .{ .coerced_ty = int_type }, params[2]), + .new_value = try expr(gz, scope, .{ .coerced_ty = int_type }, params[3]), + .success_order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]), + .failure_order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[5]), // zig fmt: on }); return rvalue(gz, rl, result, node); diff --git a/src/Liveness.zig b/src/Liveness.zig index f33c44d592..5d8e3eed34 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -340,6 +340,10 @@ fn analyzeInst( const extra = a.air.extraData(Air.Bin, inst_datas[inst].ty_pl.payload).data; return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none }); }, + .cmpxchg_strong, .cmpxchg_weak => { + const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data; + return trackOperands(a, new_set, inst, main_tomb, .{ extra.ptr, extra.expected_value, extra.new_value }); + }, .br => { const br = inst_datas[inst].br; return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none }); diff --git a/src/Module.zig b/src/Module.zig index 07b86c0d51..ec3bb2bbd3 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1321,6 +1321,7 @@ pub const Scope = struct { /// It is shared among all the blocks in an inline or comptime called /// function. pub const Inlining = struct { + comptime_result: Air.Inst.Ref, merges: Merges, }; @@ -1643,36 +1644,12 @@ pub const SrcLoc = struct { const token_starts = tree.tokens.items(.start); return token_starts[tok_index]; }, - .node_offset_builtin_call_arg0 => |node_off| { - const tree = try src_loc.file_scope.getTree(gpa); - const node_datas = tree.nodes.items(.data); - const node_tags = tree.nodes.items(.tag); - const node = src_loc.declRelativeToNodeIndex(node_off); - const param = switch (node_tags[node]) { - .builtin_call_two, .builtin_call_two_comma => node_datas[node].lhs, - .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs], - else => unreachable, - }; - const main_tokens = tree.nodes.items(.main_token); - const tok_index = main_tokens[param]; - const token_starts = tree.tokens.items(.start); - return token_starts[tok_index]; - }, - .node_offset_builtin_call_arg1 => |node_off| { - const tree = try src_loc.file_scope.getTree(gpa); - const node_datas = tree.nodes.items(.data); - const node_tags = tree.nodes.items(.tag); - const node = src_loc.declRelativeToNodeIndex(node_off); - const param = switch (node_tags[node]) { - .builtin_call_two, .builtin_call_two_comma => node_datas[node].rhs, - .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + 1], - else => unreachable, - }; - const main_tokens = tree.nodes.items(.main_token); - const tok_index = main_tokens[param]; - const token_starts = tree.tokens.items(.start); - return token_starts[tok_index]; - }, + .node_offset_builtin_call_arg0 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 0), + .node_offset_builtin_call_arg1 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 1), + .node_offset_builtin_call_arg2 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 2), + .node_offset_builtin_call_arg3 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 3), + .node_offset_builtin_call_arg4 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 4), + .node_offset_builtin_call_arg5 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 5), .node_offset_array_access_index => |node_off| { const tree = try src_loc.file_scope.getTree(gpa); const node_datas = tree.nodes.items(.data); @@ -1965,6 +1942,31 @@ pub const SrcLoc = struct { }, } } + + pub fn byteOffsetBuiltinCallArg( + src_loc: SrcLoc, + gpa: *Allocator, + node_off: i32, + arg_index: u32, + ) !u32 { + const tree = try src_loc.file_scope.getTree(gpa); + const node_datas = tree.nodes.items(.data); + const node_tags = tree.nodes.items(.tag); + const node = src_loc.declRelativeToNodeIndex(node_off); + const param = switch (node_tags[node]) { + .builtin_call_two, .builtin_call_two_comma => switch (arg_index) { + 0 => node_datas[node].lhs, + 1 => node_datas[node].rhs, + else => unreachable, + }, + .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + arg_index], + else => unreachable, + }; + const main_tokens = tree.nodes.items(.main_token); + const tok_index = main_tokens[param]; + const token_starts = tree.tokens.items(.start); + return token_starts[tok_index]; + } }; /// Resolving a source location into a byte offset may require doing work @@ -2032,6 +2034,10 @@ pub const LazySrcLoc = union(enum) { node_offset_builtin_call_arg0: i32, /// Same as `node_offset_builtin_call_arg0` except arg index 1. node_offset_builtin_call_arg1: i32, + node_offset_builtin_call_arg2: i32, + node_offset_builtin_call_arg3: i32, + node_offset_builtin_call_arg4: i32, + node_offset_builtin_call_arg5: i32, /// The source location points to the index expression of an array access /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to an array access AST node. Next, navigate @@ -2157,6 +2163,10 @@ pub const LazySrcLoc = union(enum) { .node_offset_for_cond, .node_offset_builtin_call_arg0, .node_offset_builtin_call_arg1, + .node_offset_builtin_call_arg2, + .node_offset_builtin_call_arg3, + .node_offset_builtin_call_arg4, + .node_offset_builtin_call_arg5, .node_offset_array_access_index, .node_offset_slice_sentinel, .node_offset_call_func, @@ -2205,6 +2215,10 @@ pub const LazySrcLoc = union(enum) { .node_offset_for_cond, .node_offset_builtin_call_arg0, .node_offset_builtin_call_arg1, + .node_offset_builtin_call_arg2, + .node_offset_builtin_call_arg3, + .node_offset_builtin_call_arg4, + .node_offset_builtin_call_arg5, .node_offset_array_access_index, .node_offset_slice_sentinel, .node_offset_call_func, @@ -2246,6 +2260,9 @@ pub const CompileError = error{ /// because the function is generic. This is only seen when analyzing the body of a param /// instruction. GenericPoison, + /// In a comptime scope, a return instruction was encountered. This error is only seen when + /// doing a comptime function call. + ComptimeReturn, }; pub fn deinit(mod: *Module) void { @@ -3928,8 +3945,10 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { log.debug("set {s} to in_progress", .{decl.name}); _ = sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) { + // TODO make these unreachable instead of @panic error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"), error.GenericPoison => @panic("zig compiler bug: GenericPoison"), + error.ComptimeReturn => @panic("zig compiler bug: ComptimeReturn"), else => |e| return e, }; @@ -4534,7 +4553,6 @@ pub const PeerTypeCandidateSrc = union(enum) { self: PeerTypeCandidateSrc, gpa: *Allocator, decl: *Decl, - candidates: usize, candidate_i: usize, ) ?LazySrcLoc { @setCold(true); @@ -4547,12 +4565,14 @@ pub const PeerTypeCandidateSrc = union(enum) { return candidate_srcs[candidate_i]; }, .typeof_builtin_call_node_offset => |node_offset| { - if (candidates <= 2) { - switch (candidate_i) { - 0 => return LazySrcLoc{ .node_offset_builtin_call_arg0 = node_offset }, - 1 => return LazySrcLoc{ .node_offset_builtin_call_arg1 = node_offset }, - else => unreachable, - } + switch (candidate_i) { + 0 => return LazySrcLoc{ .node_offset_builtin_call_arg0 = node_offset }, + 1 => return LazySrcLoc{ .node_offset_builtin_call_arg1 = node_offset }, + 2 => return LazySrcLoc{ .node_offset_builtin_call_arg2 = node_offset }, + 3 => return LazySrcLoc{ .node_offset_builtin_call_arg3 = node_offset }, + 4 => return LazySrcLoc{ .node_offset_builtin_call_arg4 = node_offset }, + 5 => return LazySrcLoc{ .node_offset_builtin_call_arg5 = node_offset }, + else => {}, } const tree = decl.namespace.file_scope.getTree(gpa) catch |err| { diff --git a/src/Sema.zig b/src/Sema.zig index ebfe0361d6..de0d0b7c88 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -159,7 +159,6 @@ pub fn analyzeBody( .bit_or => try sema.zirBitwise(block, inst, .bit_or), .bitcast => try sema.zirBitcast(block, inst), .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, inst), - .block => try sema.zirBlock(block, inst), .suspend_block => try sema.zirSuspendBlock(block, inst), .bool_not => try sema.zirBoolNot(block, inst), .bool_br_and => try sema.zirBoolBr(block, inst, false), @@ -215,7 +214,6 @@ pub fn analyzeBody( .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst), .is_non_null => try sema.zirIsNonNull(block, inst), .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), - .loop => try sema.zirLoop(block, inst), .merge_error_sets => try sema.zirMergeErrorSets(block, inst), .negate => try sema.zirNegate(block, inst, .sub), .negate_wrap => try sema.zirNegate(block, inst, .subwrap), @@ -308,8 +306,8 @@ pub fn analyzeBody( .shr_exact => try sema.zirShrExact(block, inst), .bit_offset_of => try sema.zirBitOffsetOf(block, inst), .offset_of => try sema.zirOffsetOf(block, inst), - .cmpxchg_strong => try sema.zirCmpxchg(block, inst), - .cmpxchg_weak => try sema.zirCmpxchg(block, inst), + .cmpxchg_strong => try sema.zirCmpxchg(block, inst, .cmpxchg_strong), + .cmpxchg_weak => try sema.zirCmpxchg(block, inst, .cmpxchg_weak), .splat => try sema.zirSplat(block, inst), .reduce => try sema.zirReduce(block, inst), .shuffle => try sema.zirShuffle(block, inst), @@ -364,16 +362,12 @@ pub fn analyzeBody( // Instructions that we know to *always* be noreturn based solely on their tag. // These functions match the return type of analyzeBody so that we can // tail call them here. - .break_inline => return inst, - .condbr => return sema.zirCondbr(block, inst), - .@"break" => return sema.zirBreak(block, inst), .compile_error => return sema.zirCompileError(block, inst), .ret_coerce => return sema.zirRetCoerce(block, inst), .ret_node => return sema.zirRetNode(block, inst), .ret_load => return sema.zirRetLoad(block, inst), .ret_err_value => return sema.zirRetErrValue(block, inst), .@"unreachable" => return sema.zirUnreachable(block, inst), - .repeat => return sema.zirRepeat(block, inst), .panic => return sema.zirPanic(block, inst), // zig fmt: on @@ -499,6 +493,28 @@ pub fn analyzeBody( }, // Special case instructions to handle comptime control flow. + .@"break" => { + if (block.is_comptime) { + return inst; // same as break_inline + } else { + return sema.zirBreak(block, inst); + } + }, + .break_inline => return inst, + .repeat => { + if (block.is_comptime) { + // Send comptime control flow back to the beginning of this block. + const src: LazySrcLoc = .{ .node_offset = datas[inst].node }; + try sema.emitBackwardBranch(block, src); + i = 0; + continue; + } else { + const src_node = sema.code.instructions.items(.data)[inst].node; + const src: LazySrcLoc = .{ .node_offset = src_node }; + try sema.requireRuntimeBlock(block, src); + return always_noreturn; + } + }, .repeat_inline => { // Send comptime control flow back to the beginning of this block. const src: LazySrcLoc = .{ .node_offset = datas[inst].node }; @@ -506,6 +522,34 @@ pub fn analyzeBody( i = 0; continue; }, + .loop => blk: { + if (!block.is_comptime) break :blk try sema.zirLoop(block, inst); + // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220 + const inst_data = datas[inst].pl_node; + const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); + const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; + const break_inst = try sema.analyzeBody(block, inline_body); + const break_data = datas[break_inst].@"break"; + if (inst == break_data.block_inst) { + break :blk sema.resolveInst(break_data.operand); + } else { + return break_inst; + } + }, + .block => blk: { + if (!block.is_comptime) break :blk try sema.zirBlock(block, inst); + // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220 + const inst_data = datas[inst].pl_node; + const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); + const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; + const break_inst = try sema.analyzeBody(block, inline_body); + const break_data = datas[break_inst].@"break"; + if (inst == break_data.block_inst) { + break :blk sema.resolveInst(break_data.operand); + } else { + return break_inst; + } + }, .block_inline => blk: { // Directly analyze the block body without introducing a new block. const inst_data = datas[inst].pl_node; @@ -519,6 +563,24 @@ pub fn analyzeBody( return break_inst; } }, + .condbr => blk: { + if (!block.is_comptime) return sema.zirCondbr(block, inst); + // Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220 + const inst_data = datas[inst].pl_node; + const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); + const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len]; + const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; + const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition); + const inline_body = if (cond.val.toBool()) then_body else else_body; + const break_inst = try sema.analyzeBody(block, inline_body); + const break_data = datas[break_inst].@"break"; + if (inst == break_data.block_inst) { + break :blk sema.resolveInst(break_data.operand); + } else { + return break_inst; + } + }, .condbr_inline => blk: { const inst_data = datas[inst].pl_node; const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; @@ -1933,16 +1995,6 @@ fn zirCompileLog( return Air.Inst.Ref.void_value; } -fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { - const tracy = trace(@src()); - defer tracy.end(); - - const src_node = sema.code.instructions.items(.data)[inst].node; - const src: LazySrcLoc = .{ .node_offset = src_node }; - try sema.requireRuntimeBlock(block, src); - return always_noreturn; -} - fn zirPanic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src: LazySrcLoc = inst_data.src(); @@ -2003,7 +2055,6 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Compil _ = try sema.analyzeBody(&loop_block, body); - // Loop repetition is implied so the last instruction may or may not be a noreturn instruction. try child_block.instructions.append(gpa, loop_inst); try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + @@ -2615,6 +2666,7 @@ fn analyzeCall( // This one is shared among sub-blocks within the same callee, but not // shared among the entire inline/comptime call stack. var inlining: Scope.Block.Inlining = .{ + .comptime_result = undefined, .merges = .{ .results = .{}, .br_list = .{}, @@ -2770,8 +2822,13 @@ fn analyzeCall( } } - _ = try sema.analyzeBody(&child_block, fn_info.body); - const result = try sema.analyzeBlockBody(block, call_src, &child_block, merges); + const result = result: { + _ = sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) { + error.ComptimeReturn => break :result inlining.comptime_result, + else => |e| return e, + }; + break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges); + }; if (is_comptime_call) { const result_val = try sema.resolveConstMaybeUndefVal(block, call_src, result); @@ -6662,9 +6719,9 @@ fn zirRetErrValue( const src = inst_data.src(); // Add the error tag to the inferred error set of the in-scope function. - if (sema.func) |func| { - if (func.getInferredErrorSet()) |map| { - _ = try map.getOrPut(sema.gpa, err_name); + if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) { + if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| { + _ = try payload.data.map.getOrPut(sema.gpa, err_name); } } // Return the error code from the function. @@ -6699,6 +6756,10 @@ fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr const operand = sema.resolveInst(inst_data.operand); const src = inst_data.src(); + // TODO: we pass false here for the `need_coercion` boolean, but I'm pretty sure we need + // to remove this parameter entirely. Observe the problem by looking at the incorrect compile + // error that occurs when a behavior test case being executed at comptime fails, e.g. + // `test { comptime foo(); } fn foo() { try expect(false); }` return sema.analyzeRet(block, operand, src, false); } @@ -6730,6 +6791,10 @@ fn analyzeRet( try sema.coerce(block, sema.fn_ret_ty, uncasted_operand, src); if (block.inlining) |inlining| { + if (block.is_comptime) { + inlining.comptime_result = operand; + return error.ComptimeReturn; + } // We are inlining a function call; rewrite the `ret` as a `break`. try inlining.merges.results.append(sema.gpa, operand); _ = try block.addBr(inlining.merges.block_inst, operand); @@ -7425,10 +7490,149 @@ fn zirOffsetOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{}); } -fn zirCmpxchg(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn checkAtomicOperandType( + sema: *Sema, + block: *Scope.Block, + ty_src: LazySrcLoc, + ty: Type, +) CompileError!void { + var buffer: Type.Payload.Bits = undefined; + const target = sema.mod.getTarget(); + const max_atomic_bits = target_util.largestAtomicBits(target); + const int_ty = switch (ty.zigTypeTag()) { + .Int => ty, + .Enum => ty.enumTagType(&buffer), + .Float => { + const bit_count = ty.floatBits(target); + if (bit_count > max_atomic_bits) { + return sema.mod.fail( + &block.base, + ty_src, + "expected {d}-bit float type or smaller; found {d}-bit float type", + .{ max_atomic_bits, bit_count }, + ); + } + return; + }, + .Bool => return, // Will be treated as `u8`. + else => return sema.mod.fail( + &block.base, + ty_src, + "expected bool, integer, float, enum, or pointer type; found {}", + .{ty}, + ), + }; + const bit_count = int_ty.intInfo(target).bits; + if (bit_count > max_atomic_bits) { + return sema.mod.fail( + &block.base, + ty_src, + "expected {d}-bit integer type or smaller; found {d}-bit integer type", + .{ max_atomic_bits, bit_count }, + ); + } +} + +fn resolveAtomicOrder( + sema: *Sema, + block: *Scope.Block, + src: LazySrcLoc, + zir_ref: Zir.Inst.Ref, +) CompileError!std.builtin.AtomicOrder { + const atomic_order_ty = try sema.getBuiltinType(block, src, "AtomicOrder"); + const air_ref = sema.resolveInst(zir_ref); + const coerced = try sema.coerce(block, atomic_order_ty, air_ref, src); + const val = try sema.resolveConstValue(block, src, coerced); + return val.toEnum(std.builtin.AtomicOrder); +} + +fn zirCmpxchg( + sema: *Sema, + block: *Scope.Block, + inst: Zir.Inst.Index, + air_tag: Air.Inst.Tag, +) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const extra = sema.code.extraData(Zir.Inst.Cmpxchg, inst_data.payload_index).data; const src = inst_data.src(); - return sema.mod.fail(&block.base, src, "TODO: Sema.zirCmpxchg", .{}); + // zig fmt: off + const elem_ty_src : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; + const expected_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; + const new_value_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; + const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; + const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = inst_data.src_node }; + // zig fmt: on + const ptr = sema.resolveInst(extra.ptr); + const elem_ty = sema.typeOf(ptr).elemType(); + try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty); + if (elem_ty.zigTypeTag() == .Float) { + return mod.fail( + &block.base, + elem_ty_src, + "expected bool, integer, enum, or pointer type; found '{}'", + .{elem_ty}, + ); + } + const expected_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.expected_value), expected_src); + const new_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.new_value), new_value_src); + const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order); + const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order); + + if (@enumToInt(success_order) < @enumToInt(std.builtin.AtomicOrder.Monotonic)) { + return mod.fail(&block.base, success_order_src, "success atomic ordering must be Monotonic or stricter", .{}); + } + if (@enumToInt(failure_order) < @enumToInt(std.builtin.AtomicOrder.Monotonic)) { + return mod.fail(&block.base, failure_order_src, "failure atomic ordering must be Monotonic or stricter", .{}); + } + if (@enumToInt(failure_order) > @enumToInt(success_order)) { + return mod.fail(&block.base, failure_order_src, "failure atomic ordering must be no stricter than success", .{}); + } + if (failure_order == .Release or failure_order == .AcqRel) { + return mod.fail(&block.base, failure_order_src, "failure atomic ordering must not be Release or AcqRel", .{}); + } + + const result_ty = try Module.optionalType(sema.arena, elem_ty); + + // special case zero bit types + if ((try sema.typeHasOnePossibleValue(block, elem_ty_src, elem_ty)) != null) { + return sema.addConstant(result_ty, Value.initTag(.null_value)); + } + + const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { + if (try sema.resolveMaybeUndefVal(block, expected_src, expected_value)) |expected_val| { + if (try sema.resolveMaybeUndefVal(block, new_value_src, new_value)) |new_val| { + if (expected_val.isUndef() or new_val.isUndef()) { + return sema.addConstUndef(result_ty); + } + const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src; + const result_val = if (stored_val.eql(expected_val, elem_ty)) blk: { + try sema.storePtr(block, src, ptr, new_value); + break :blk Value.initTag(.null_value); + } else try Value.Tag.opt_payload.create(sema.arena, stored_val); + + return sema.addConstant(result_ty, result_val); + } else break :rs new_value_src; + } else break :rs expected_src; + } else ptr_src; + + const flags: u32 = @as(u32, @enumToInt(success_order)) | + (@as(u32, @enumToInt(failure_order)) << 3); + + try sema.requireRuntimeBlock(block, runtime_src); + return block.addInst(.{ + .tag = air_tag, + .data = .{ .ty_pl = .{ + .ty = try sema.addType(result_ty), + .payload = try sema.addExtra(Air.Cmpxchg{ + .ptr = ptr, + .expected_value = expected_value, + .new_value = new_value, + .flags = flags, + }), + } }, + }); } fn zirSplat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -9576,13 +9780,11 @@ fn resolvePeerTypes( const chosen_src = candidate_srcs.resolve( sema.gpa, block.src_decl, - instructions.len, chosen_i, ); const candidate_src = candidate_srcs.resolve( sema.gpa, block.src_decl, - instructions.len, candidate_i + 1, ); diff --git a/src/Zir.zig b/src/Zir.zig index dbc1442364..1f0e4e370b 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2778,7 +2778,7 @@ pub const Inst = struct { expected_value: Ref, new_value: Ref, success_order: Ref, - fail_order: Ref, + failure_order: Ref, }; pub const AtomicRmw = struct { @@ -3054,8 +3054,6 @@ const Writer = struct { .array_init_ref, .array_init_anon_ref, .union_init_ptr, - .cmpxchg_strong, - .cmpxchg_weak, .shuffle, .select, .atomic_rmw, @@ -3072,6 +3070,10 @@ const Writer = struct { .struct_init_ref, => try self.writeStructInit(stream, inst), + .cmpxchg_strong, + .cmpxchg_weak, + => try self.writeCmpxchg(stream, inst), + .struct_init_anon, .struct_init_anon_ref, => try self.writeStructInitAnon(stream, inst), @@ -3474,6 +3476,23 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } + fn writeCmpxchg(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.Cmpxchg, inst_data.payload_index).data; + + try self.writeInstRef(stream, extra.ptr); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.expected_value); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.new_value); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.success_order); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.failure_order); + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Inst.StructInitAnon, inst_data.payload_index); diff --git a/src/codegen.zig b/src/codegen.zig index 1995d8baa7..511d4c2301 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -857,6 +857,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .struct_field_ptr=> try self.airStructFieldPtr(inst), .struct_field_val=> try self.airStructFieldVal(inst), .array_to_slice => try self.airArrayToSlice(inst), + .cmpxchg_strong => try self.airCmpxchg(inst), + .cmpxchg_weak => try self.airCmpxchg(inst), .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), @@ -4751,6 +4753,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } + fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Block, ty_pl.payload); + const result: MCValue = switch (arch) { + else => return self.fail("TODO implement airCmpxchg for {}", .{ + self.target.cpu.arch, + }), + }; + return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); + } + fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // First section of indexes correspond to a set number of constant values. const ref_int = @enumToInt(inst); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 84bc6ceeb0..ff49b18f7b 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -911,6 +911,8 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM .wrap_optional => try airWrapOptional(o, inst), .struct_field_ptr => try airStructFieldPtr(o, inst), .array_to_slice => try airArrayToSlice(o, inst), + .cmpxchg_weak => try airCmpxchg(o, inst, "weak"), + .cmpxchg_strong => try airCmpxchg(o, inst, "strong"), .struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0), .struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1), @@ -1878,6 +1880,43 @@ fn airArrayToSlice(o: *Object, inst: Air.Inst.Index) !CValue { return local; } +fn airCmpxchg(o: *Object, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue { + const ty_pl = o.air.instructions.items(.data)[inst].ty_pl; + const extra = o.air.extraData(Air.Cmpxchg, ty_pl.payload).data; + const inst_ty = o.air.typeOfIndex(inst); + const ptr = try o.resolveInst(extra.ptr); + const expected_value = try o.resolveInst(extra.expected_value); + const new_value = try o.resolveInst(extra.new_value); + const local = try o.allocLocal(inst_ty, .Const); + const writer = o.writer(); + + try writer.print(" = zig_cmpxchg_{s}(", .{flavor}); + try o.writeCValue(writer, ptr); + try writer.writeAll(", "); + try o.writeCValue(writer, expected_value); + try writer.writeAll(", "); + try o.writeCValue(writer, new_value); + try writer.writeAll(", "); + try writeMemoryOrder(writer, extra.successOrder()); + try writer.writeAll(", "); + try writeMemoryOrder(writer, extra.failureOrder()); + try writer.writeAll(");\n"); + + return local; +} + +fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void { + const str = switch (order) { + .Unordered => "memory_order_relaxed", + .Monotonic => "memory_order_consume", + .Acquire => "memory_order_acquire", + .Release => "memory_order_release", + .AcqRel => "memory_order_acq_rel", + .SeqCst => "memory_order_seq_cst", + }; + return w.writeAll(str); +} + fn IndentWriter(comptime UnderlyingWriter: type) type { return struct { const Self = @This(); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index d39262d62e..b28c371466 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -389,6 +389,7 @@ pub const Object = struct { .latest_alloca_inst = null, .llvm_func = llvm_func, .blocks = .{}, + .single_threaded = module.comp.bin_file.options.single_threaded, }; defer fg.deinit(); @@ -906,6 +907,31 @@ pub const DeclGen = struct { // TODO: improve this API, `addAttr(-1, attr_name)` self.addAttr(val, std.math.maxInt(llvm.AttributeIndex), attr_name); } + + /// If the operand type of an atomic operation is not byte sized we need to + /// widen it before using it and then truncate the result. + /// RMW exchange of floating-point values is bitcasted to same-sized integer + /// types to work around a LLVM deficiency when targeting ARM/AArch64. + fn getAtomicAbiType(dg: *DeclGen, ty: Type, is_rmw_xchg: bool) ?*const llvm.Type { + const target = dg.module.getTarget(); + var buffer: Type.Payload.Bits = undefined; + const int_ty = switch (ty.zigTypeTag()) { + .Int => ty, + .Enum => ty.enumTagType(&buffer), + .Float => { + if (!is_rmw_xchg) return null; + return dg.context.intType(@intCast(c_uint, ty.abiSize(target) * 8)); + }, + .Bool => return dg.context.intType(8), + else => return null, + }; + const bit_count = int_ty.intInfo(target).bits; + if (!std.math.isPowerOfTwo(bit_count) or (bit_count % 8) != 0) { + return dg.context.intType(@intCast(c_uint, int_ty.abiSize(target) * 8)); + } else { + return null; + } + } }; pub const FuncGen = struct { @@ -940,6 +966,8 @@ pub const FuncGen = struct { break_vals: *BreakValues, }), + single_threaded: bool, + const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock); const BreakValues = std.ArrayListUnmanaged(*const llvm.Value); @@ -1029,6 +1057,8 @@ pub const FuncGen = struct { .slice_ptr => try self.airSliceField(inst, 0), .slice_len => try self.airSliceField(inst, 1), .array_to_slice => try self.airArrayToSlice(inst), + .cmpxchg_weak => try self.airCmpxchg(inst, true), + .cmpxchg_strong => try self.airCmpxchg(inst, false), .struct_field_ptr => try self.airStructFieldPtr(inst), .struct_field_val => try self.airStructFieldVal(inst), @@ -1975,6 +2005,58 @@ pub const FuncGen = struct { return null; } + fn airCmpxchg(self: *FuncGen, inst: Air.Inst.Index, is_weak: bool) !?*const llvm.Value { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data; + var ptr = try self.resolveInst(extra.ptr); + var expected_value = try self.resolveInst(extra.expected_value); + var new_value = try self.resolveInst(extra.new_value); + const operand_ty = self.air.typeOf(extra.ptr).elemType(); + const opt_abi_ty = self.dg.getAtomicAbiType(operand_ty, false); + if (opt_abi_ty) |abi_ty| { + // operand needs widening and truncating + ptr = self.builder.buildBitCast(ptr, abi_ty.pointerType(0), ""); + if (operand_ty.isSignedInt()) { + expected_value = self.builder.buildSExt(expected_value, abi_ty, ""); + new_value = self.builder.buildSExt(new_value, abi_ty, ""); + } else { + expected_value = self.builder.buildZExt(expected_value, abi_ty, ""); + new_value = self.builder.buildZExt(new_value, abi_ty, ""); + } + } + const success_order = toLlvmAtomicOrdering(extra.successOrder()); + const failure_order = toLlvmAtomicOrdering(extra.failureOrder()); + const result = self.builder.buildCmpXchg( + ptr, + expected_value, + new_value, + success_order, + failure_order, + is_weak, + self.single_threaded, + ); + + const optional_ty = self.air.typeOfIndex(inst); + var buffer: Type.Payload.ElemType = undefined; + const child_ty = optional_ty.optionalChild(&buffer); + + var payload = self.builder.buildExtractValue(result, 0, ""); + if (opt_abi_ty != null) { + payload = self.builder.buildTrunc(payload, try self.dg.llvmType(operand_ty), ""); + } + const success_bit = self.builder.buildExtractValue(result, 1, ""); + + if (optional_ty.isPtrLikeOptional()) { + const child_llvm_ty = try self.dg.llvmType(child_ty); + return self.builder.buildSelect(success_bit, child_llvm_ty.constNull(), payload, ""); + } + + const optional_llvm_ty = try self.dg.llvmType(optional_ty); + const non_null_bit = self.builder.buildNot(success_bit, ""); + const partial = self.builder.buildInsertValue(optional_llvm_ty.getUndef(), payload, 0, ""); + return self.builder.buildInsertValue(partial, non_null_bit, 1, ""); + } + fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value { const id = llvm.lookupIntrinsicID(name.ptr, name.len); assert(id != 0); @@ -2125,3 +2207,14 @@ fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void { .spirv64 => {}, } } + +fn toLlvmAtomicOrdering(atomic_order: std.builtin.AtomicOrder) llvm.AtomicOrdering { + return switch (atomic_order) { + .Unordered => .Unordered, + .Monotonic => .Monotonic, + .Acquire => .Acquire, + .Release => .Release, + .AcqRel => .AcquireRelease, + .SeqCst => .SequentiallyConsistent, + }; +} diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index e0283be236..b4bd91708d 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -298,6 +298,14 @@ pub const Builder = opaque { Name: [*:0]const u8, ) *const Value; + pub const buildSExt = LLVMBuildSExt; + extern fn LLVMBuildSExt( + *const Builder, + Val: *const Value, + DestTy: *const Type, + Name: [*:0]const u8, + ) *const Value; + pub const buildCall = LLVMBuildCall; extern fn LLVMBuildCall( *const Builder, @@ -493,6 +501,27 @@ pub const Builder = opaque { Index: c_uint, Name: [*:0]const u8, ) *const Value; + + pub const buildCmpXchg = ZigLLVMBuildCmpXchg; + extern fn ZigLLVMBuildCmpXchg( + builder: *const Builder, + ptr: *const Value, + cmp: *const Value, + new_val: *const Value, + success_ordering: AtomicOrdering, + failure_ordering: AtomicOrdering, + is_weak: bool, + is_single_threaded: bool, + ) *const Value; + + pub const buildSelect = LLVMBuildSelect; + extern fn LLVMBuildSelect( + *const Builder, + If: *const Value, + Then: *const Value, + Else: *const Value, + Name: [*:0]const u8, + ) *const Value; }; pub const IntPredicate = enum(c_uint) { @@ -854,3 +883,13 @@ pub const Linkage = enum(c_uint) { LinkerPrivate, LinkerPrivateWeak, }; + +pub const AtomicOrdering = enum(c_uint) { + NotAtomic = 0, + Unordered = 1, + Monotonic = 2, + Acquire = 4, + Release = 5, + AcquireRelease = 6, + SequentiallyConsistent = 7, +}; diff --git a/src/link/C/zig.h b/src/link/C/zig.h index 232fb6bd0c..f3fb02b840 100644 --- a/src/link/C/zig.h +++ b/src/link/C/zig.h @@ -60,6 +60,18 @@ #define zig_breakpoint() raise(SIGTRAP) #endif +#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) +#include +#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) +#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail) +#elif __GNUC__ +#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired) +#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) __sync_val_compare_and_swap(obj, expected, desired) +#else +#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) zig_unimplemented() +#define zig_cmpxchg_weak(obj, expected, desired, succ, fail) zig_unimplemented() +#endif + #include #include #include diff --git a/src/print_air.zig b/src/print_air.zig index 4d9ce1bb36..11cf1b7baa 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -191,6 +191,7 @@ const Writer = struct { .br => try w.writeBr(s, inst), .cond_br => try w.writeCondBr(s, inst), .switch_br => try w.writeSwitchBr(s, inst), + .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst), } } @@ -258,7 +259,21 @@ const Writer = struct { try w.writeOperand(s, inst, 0, extra.lhs); try s.writeAll(", "); - try w.writeOperand(s, inst, 0, extra.rhs); + try w.writeOperand(s, inst, 1, extra.rhs); + } + + fn writeCmpxchg(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; + const extra = w.air.extraData(Air.Cmpxchg, ty_pl.payload).data; + + try w.writeOperand(s, inst, 0, extra.ptr); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, extra.expected_value); + try s.writeAll(", "); + try w.writeOperand(s, inst, 2, extra.new_value); + try s.print(", {s}, {s}", .{ + @tagName(extra.successOrder()), @tagName(extra.failureOrder()), + }); } fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 614ed8e26c..3b22101df9 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -5723,7 +5723,7 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, Stage1Air *executable, Stage1A LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order); LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, - success_order, failure_order, instruction->is_weak); + success_order, failure_order, instruction->is_weak, g->is_single_threaded); ZigType *optional_type = instruction->base.value->type; assert(optional_type->id == ZigTypeIdOptional); diff --git a/src/target.zig b/src/target.zig index 25a133f03b..c9d7e1742b 100644 --- a/src/target.zig +++ b/src/target.zig @@ -475,3 +475,72 @@ pub fn clangAssemblerSupportsMcpuArg(target: std.Target) bool { pub fn needUnwindTables(target: std.Target) bool { return target.os.tag == .windows; } + +/// TODO this was ported from stage1 but it does not take into account CPU features, +/// which can affect this value. Audit this! +pub fn largestAtomicBits(target: std.Target) u32 { + return switch (target.cpu.arch) { + .avr, + .msp430, + .spu_2, + => 16, + + .arc, + .arm, + .armeb, + .hexagon, + .le32, + .mips, + .mipsel, + .nvptx, + .powerpc, + .powerpcle, + .r600, + .riscv32, + .sparc, + .sparcel, + .tce, + .tcele, + .thumb, + .thumbeb, + .i386, + .xcore, + .amdil, + .hsail, + .spir, + .kalimba, + .lanai, + .shave, + .wasm32, + .renderscript32, + .csky, + .spirv32, + => 32, + + .aarch64, + .aarch64_be, + .aarch64_32, + .amdgcn, + .bpfel, + .bpfeb, + .le64, + .mips64, + .mips64el, + .nvptx64, + .powerpc64, + .powerpc64le, + .riscv64, + .sparcv9, + .s390x, + .amdil64, + .hsail64, + .spir64, + .wasm64, + .renderscript64, + .ve, + .spirv64, + => 64, + + .x86_64 => 128, + }; +} diff --git a/src/type.zig b/src/type.zig index 4d6ff3be0f..2403893133 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2886,6 +2886,35 @@ pub const Type = extern union { } } + /// Returns the integer tag type of the enum. + pub fn enumTagType(ty: Type, buffer: *Payload.Bits) Type { + switch (ty.tag()) { + .enum_full, .enum_nonexhaustive => { + const enum_full = ty.cast(Payload.EnumFull).?.data; + return enum_full.tag_ty; + }, + .enum_simple => { + const enum_simple = ty.castTag(.enum_simple).?.data; + buffer.* = .{ + .base = .{ .tag = .int_unsigned }, + .data = std.math.log2_int_ceil(usize, enum_simple.fields.count()), + }; + return Type.initPayload(&buffer.base); + }, + .atomic_order, + .atomic_rmw_op, + .calling_convention, + .float_mode, + .reduce_op, + .call_options, + .export_options, + .extern_options, + => @panic("TODO resolve std.builtin types"), + + else => unreachable, + } + } + pub fn isNonexhaustiveEnum(ty: Type) bool { return switch (ty.tag()) { .enum_nonexhaustive => true, diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 2089092c7c..e1ab74f423 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -1087,10 +1087,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) { LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp, LLVMValueRef new_val, LLVMAtomicOrdering success_ordering, - LLVMAtomicOrdering failure_ordering, bool is_weak) + LLVMAtomicOrdering failure_ordering, bool is_weak, bool is_single_threaded) { - AtomicCmpXchgInst *inst = unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), - unwrap(new_val), mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering)); + AtomicCmpXchgInst *inst = unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), + unwrap(cmp), unwrap(new_val), + mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering), + is_single_threaded ? SyncScope::SingleThread : SyncScope::System); inst->setWeak(is_weak); return wrap(inst); } @@ -1308,19 +1310,6 @@ static AtomicRMWInst::BinOp toLLVMRMWBinOp(enum ZigLLVM_AtomicRMWBinOp BinOp) { } } -static AtomicOrdering toLLVMOrdering(LLVMAtomicOrdering Ordering) { - switch (Ordering) { - default: - case LLVMAtomicOrderingNotAtomic: return AtomicOrdering::NotAtomic; - case LLVMAtomicOrderingUnordered: return AtomicOrdering::Unordered; - case LLVMAtomicOrderingMonotonic: return AtomicOrdering::Monotonic; - case LLVMAtomicOrderingAcquire: return AtomicOrdering::Acquire; - case LLVMAtomicOrderingRelease: return AtomicOrdering::Release; - case LLVMAtomicOrderingAcquireRelease: return AtomicOrdering::AcquireRelease; - case LLVMAtomicOrderingSequentiallyConsistent: return AtomicOrdering::SequentiallyConsistent; - } -} - inline LLVMAttributeRef wrap(Attribute Attr) { return reinterpret_cast(Attr.getRawPointer()); } @@ -1335,7 +1324,7 @@ LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp { AtomicRMWInst::BinOp intop = toLLVMRMWBinOp(op); return wrap(unwrap(B)->CreateAtomicRMW(intop, unwrap(PTR), - unwrap(Val), toLLVMOrdering(ordering), + unwrap(Val), mapFromLLVMOrdering(ordering), singleThread ? SyncScope::SingleThread : SyncScope::System)); } diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 91407b7f12..be279d86e1 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -148,7 +148,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSShlSat(LLVMBuilderRef builder, LLVMValueR ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp, LLVMValueRef new_val, LLVMAtomicOrdering success_ordering, - LLVMAtomicOrdering failure_ordering, bool is_weak); + LLVMAtomicOrdering failure_ordering, bool is_weak, bool is_single_threaded); ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char *name); diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig index f6c94f7155..444ff56438 100644 --- a/test/behavior/atomics.zig +++ b/test/behavior/atomics.zig @@ -2,3 +2,25 @@ const std = @import("std"); const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const builtin = @import("builtin"); + +test "cmpxchg" { + try testCmpxchg(); + comptime try testCmpxchg(); +} + +fn testCmpxchg() !void { + var x: i32 = 1234; + if (@cmpxchgWeak(i32, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { + try expect(x1 == 1234); + } else { + @panic("cmpxchg should have failed"); + } + + while (@cmpxchgWeak(i32, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| { + try expect(x1 == 1234); + } + try expect(x == 5678); + + try expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null); + try expect(x == 42); +} diff --git a/test/behavior/atomics_stage1.zig b/test/behavior/atomics_stage1.zig index 18836931aa..6e754e30cd 100644 --- a/test/behavior/atomics_stage1.zig +++ b/test/behavior/atomics_stage1.zig @@ -3,28 +3,6 @@ const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const builtin = @import("builtin"); -test "cmpxchg" { - try testCmpxchg(); - comptime try testCmpxchg(); -} - -fn testCmpxchg() !void { - var x: i32 = 1234; - if (@cmpxchgWeak(i32, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { - try expect(x1 == 1234); - } else { - @panic("cmpxchg should have failed"); - } - - while (@cmpxchgWeak(i32, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| { - try expect(x1 == 1234); - } - try expect(x == 5678); - - try expect(@cmpxchgStrong(i32, &x, 5678, 42, .SeqCst, .SeqCst) == null); - try expect(x == 42); -} - test "fence" { var x: i32 = 1234; @fence(.SeqCst); -- cgit v1.2.3