From 80c9d3f3b13a5e88a4103582f406a698fa8643d1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 8 Aug 2022 14:25:45 +0200 Subject: [PATCH 01/72] update update_clang_options.zig to latest formatting changes --- tools/update_clang_options.zig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index 2c13f1082b73ae34a3309469335cb1596cf9f269..22df0054a2830fbff1d80ee1d8fabffeb7ddb124 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -497,7 +497,7 @@ const cpu_targets = struct { pub const riscv = std.Target.riscv; pub const sparc = std.Target.sparc; pub const spirv = std.Target.spirv; - pub const systemz = std.Target.systemz; + pub const s390x = std.Target.s390x; pub const ve = std.Target.ve; pub const wasm = std.Target.wasm; pub const x86 = std.Target.x86; @@ -647,9 +647,9 @@ pub fn main() anyerror!void { \\ .name = "{s}", \\ .syntax = {s}, \\ .zig_equivalent = .{s}, - \\ .pd1 = {s}, - \\ .pd2 = {s}, - \\ .psl = {s}, + \\ .pd1 = {}, + \\ .pd2 = {}, + \\ .psl = {}, \\}}, \\ , .{ name, final_syntax, ident, pd1, pd2, pslash }); @@ -677,9 +677,9 @@ pub fn main() anyerror!void { \\ .name = "{s}", \\ .syntax = {s}, \\ .zig_equivalent = .other, - \\ .pd1 = {s}, - \\ .pd2 = {s}, - \\ .psl = {s}, + \\ .pd1 = {}, + \\ .pd2 = {}, + \\ .psl = {}, \\}}, \\ , .{ name, syntax, pd1, pd2, pslash }); -- 2.54.0 From a0ec07fdec9efb8364394ad12b4000a9072ed59f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 8 Aug 2022 14:26:10 +0200 Subject: [PATCH 02/72] cc: add support for -M flag --- src/clang_options_data.zig | 13 ++++++++++--- src/main.zig | 5 +++-- tools/update_clang_options.zig | 8 ++++++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/clang_options_data.zig b/src/clang_options_data.zig index d4320b1619a1ed03e981e43f8d2dab2c47f5c6f9..76e687c7d6aba2a3b721e8ba97d0d6062f9c8d6b 100644 --- a/src/clang_options_data.zig +++ b/src/clang_options_data.zig @@ -33,7 +33,14 @@ flagpd1("H"), .psl = false, }, flagpd1("I-"), -flagpd1("M"), +.{ + .name = "M", + .syntax = .flag, + .zig_equivalent = .dep_file_to_stdout, + .pd1 = true, + .pd2 = false, + .psl = false, +}, .{ .name = "MD", .syntax = .flag, @@ -53,7 +60,7 @@ flagpd1("M"), .{ .name = "MM", .syntax = .flag, - .zig_equivalent = .dep_file_mm, + .zig_equivalent = .dep_file_to_stdout, .pd1 = true, .pd2 = false, .psl = false, @@ -1983,7 +1990,7 @@ flagpsl("MT"), .{ .name = "user-dependencies", .syntax = .flag, - .zig_equivalent = .dep_file_mm, + .zig_equivalent = .dep_file_to_stdout, .pd1 = false, .pd2 = true, .psl = false, diff --git a/src/main.zig b/src/main.zig index f192137b3c3f26dd6482159ade66fa690c1677a0..0c6e4a63a3ab978ed59600c003b0ba7740cb3051 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1657,7 +1657,8 @@ fn buildOutputType( disable_c_depfile = true; try clang_argv.appendSlice(it.other_args); }, - .dep_file_mm => { // -MM + .dep_file_to_stdout => { // -M, -MM + // "Like -MD, but also implies -E and writes to stdout by default" // "Like -MMD, but also implies -E and writes to stdout by default" c_out_mode = .preprocessor; disable_c_depfile = true; @@ -4652,7 +4653,7 @@ pub const ClangArgIterator = struct { lib_dir, mcpu, dep_file, - dep_file_mm, + dep_file_to_stdout, framework_dir, framework, nostdlibinc, diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index 22df0054a2830fbff1d80ee1d8fabffeb7ddb124..d261b5ee33325468bd892746e3822b8a9d02974e 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -386,11 +386,15 @@ const known_options = [_]KnownOpt{ }, .{ .name = "MM", - .ident = "dep_file_mm", + .ident = "dep_file_to_stdout", + }, + .{ + .name = "M", + .ident = "dep_file_to_stdout", }, .{ .name = "user-dependencies", - .ident = "dep_file_mm", + .ident = "dep_file_to_stdout", }, .{ .name = "MMD", -- 2.54.0 From 50b36e84fab023ec418eb794d6a5b4510ac6b984 Mon Sep 17 00:00:00 2001 From: "Ryotaro \"Justin\" Kimura" <1560508+ryoppippi@users.noreply.github.com> Date: Mon, 8 Aug 2022 23:13:35 +0100 Subject: [PATCH 03/72] std: fix BoundedArray test checking wrong condition (#12372) --- lib/std/bounded_array.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/bounded_array.zig b/lib/std/bounded_array.zig index 0b0efc55e445c707960105510b0450e66c27e339..8134beb6e32c9011f052e901f71a36346f80a9f6 100644 --- a/lib/std/bounded_array.zig +++ b/lib/std/bounded_array.zig @@ -275,7 +275,7 @@ test "BoundedArray" { try testing.expectEqualSlices(u8, &x, a.constSlice()); var a2 = a; - try testing.expectEqualSlices(u8, a.constSlice(), a.constSlice()); + try testing.expectEqualSlices(u8, a.constSlice(), a2.constSlice()); a2.set(0, 0); try testing.expect(a.get(0) != a2.get(0)); -- 2.54.0 From d769fd0102dee7ea24f957c3c96e2336d1c18839 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 8 Aug 2022 22:51:08 +0300 Subject: [PATCH 04/72] stage2: pass anon name strategy to reify --- src/AstGen.zig | 26 ++++++++++++-- src/Autodoc.zig | 2 +- src/Sema.zig | 24 +++++++------ src/Zir.zig | 9 +++-- src/print_zir.zig | 2 +- ...or_tagged_union_with_extra_union_field.zig | 35 +++++++++++++++++++ 6 files changed, 78 insertions(+), 20 deletions(-) create mode 100644 test/cases/compile_errors/reify_type_for_tagged_union_with_extra_union_field.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 9c8252da55906b12c21a629bb332d3fef2aef6e5..ffe489a216129b6f5e95e495ebe1c353341f56f5 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2454,7 +2454,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .trunc, .round, .tag_name, - .reify, .type_name, .frame_type, .frame_size, @@ -7553,7 +7552,6 @@ fn builtinCall( .trunc => return simpleUnOp(gz, scope, rl, node, .none, params[0], .trunc), .round => return simpleUnOp(gz, scope, rl, node, .none, params[0], .round), .tag_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .tag_name), - .Type => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .type_info_type }, params[0], .reify), .type_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .type_name), .Frame => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_type), .frame_size => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_size), @@ -7568,6 +7566,30 @@ fn builtinCall( .truncate => return typeCast(gz, scope, rl, node, params[0], params[1], .truncate), // zig fmt: on + .Type => { + const operand = try expr(gz, scope, .{ .coerced_ty = .type_info_type }, params[0]); + + const gpa = gz.astgen.gpa; + + try gz.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); + + const payload_index = try gz.astgen.addExtra(Zir.Inst.UnNode{ + .node = gz.nodeIndexToRelative(node), + .operand = operand, + }); + const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); + gz.astgen.instructions.appendAssumeCapacity(.{ + .tag = .extended, + .data = .{ .extended = .{ + .opcode = .reify, + .small = @enumToInt(gz.anon_name_strategy), + .operand = payload_index, + } }, + }); + gz.instructions.appendAssumeCapacity(new_index); + return indexToRef(new_index); + }, .panic => { try emitDbgNode(gz, node); return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], if (gz.force_comptime) .panic_comptime else .panic); diff --git a/src/Autodoc.zig b/src/Autodoc.zig index c2b0359c645259d8690c7fdf07a1cda1d2520cc3..9e3874979d03874a61e14d09515ece43cdf135c3 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -1220,7 +1220,6 @@ fn walkInstruction( .trunc, .round, .tag_name, - .reify, .type_name, .frame_type, .frame_size, @@ -2605,6 +2604,7 @@ fn walkInstruction( }, .error_to_int, .int_to_error, + .reify, => { const extra = file.zir.extraData(Zir.Inst.UnNode, extended.operand).data; const bin_index = self.exprs.items.len; diff --git a/src/Sema.zig b/src/Sema.zig index 2f3c931e16e6483d7784549c805f2dfdf96777ea..c91f4cddb1359e4456dba2b406538fc753a423cb 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -816,7 +816,6 @@ fn analyzeBodyInner( .embed_file => try sema.zirEmbedFile(block, inst), .error_name => try sema.zirErrorName(block, inst), .tag_name => try sema.zirTagName(block, inst), - .reify => try sema.zirReify(block, inst), .type_name => try sema.zirTypeName(block, inst), .frame_type => try sema.zirFrameType(block, inst), .frame_size => try sema.zirFrameSize(block, inst), @@ -951,6 +950,7 @@ fn analyzeBodyInner( .select => try sema.zirSelect( block, extended), .error_to_int => try sema.zirErrorToInt( block, extended), .int_to_error => try sema.zirIntToError( block, extended), + .reify => try sema.zirReify( block, extended, inst), // zig fmt: on .fence => { try sema.zirFence(block, extended); @@ -16023,13 +16023,14 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return block.addUnOp(.tag_name, casted_operand); } -fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const mod = sema.mod; - const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const src = inst_data.src(); + const name_strategy = @intToEnum(Zir.Inst.NameStrategy, extended.small); + const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; + const src = LazySrcLoc.nodeOffset(extra.node); const type_info_ty = try sema.resolveBuiltinTypeFields(block, src, "Type"); - const uncasted_operand = try sema.resolveInst(inst_data.operand); - const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const uncasted_operand = try sema.resolveInst(extra.operand); + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); const val = try sema.resolveConstValue(block, operand_src, type_info, "operand to @Type must be comptime known"); const union_val = val.cast(Value.Payload.Union).?.data; @@ -16289,7 +16290,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I return if (is_tuple_val.toBool()) try sema.reifyTuple(block, src, fields_val) else - try sema.reifyStruct(block, inst, src, layout_val, fields_val); + try sema.reifyStruct(block, inst, src, layout_val, fields_val, name_strategy); }, .Enum => { const struct_val = union_val.val.castTag(.aggregate).?.data; @@ -16338,7 +16339,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = enum_val, - }, .anon, "enum", null); + }, name_strategy, "enum", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); @@ -16435,7 +16436,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = opaque_val, - }, .anon, "opaque", null); + }, name_strategy, "opaque", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); @@ -16494,7 +16495,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = new_union_val, - }, .anon, "union", null); + }, name_strategy, "union", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); @@ -16787,6 +16788,7 @@ fn reifyStruct( src: LazySrcLoc, layout_val: Value, fields_val: Value, + name_strategy: Zir.Inst.NameStrategy, ) CompileError!Air.Inst.Ref { var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); errdefer new_decl_arena.deinit(); @@ -16799,7 +16801,7 @@ fn reifyStruct( const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = new_struct_val, - }, .anon, "struct", null); + }, name_strategy, "struct", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); diff --git a/src/Zir.zig b/src/Zir.zig index 642742084077837c2701200418e4805291eb1878..c5618a58c0b0d275bb6900b4e4e255b7b156e1a1 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -839,8 +839,6 @@ pub const Inst = struct { round, /// Implement builtin `@tagName`. Uses `un_node`. tag_name, - /// Implement builtin `@Type`. Uses `un_node`. - reify, /// Implement builtin `@typeName`. Uses `un_node`. type_name, /// Implement builtin `@Frame`. Uses `un_node`. @@ -1197,7 +1195,6 @@ pub const Inst = struct { .trunc, .round, .tag_name, - .reify, .type_name, .frame_type, .frame_size, @@ -1484,7 +1481,6 @@ pub const Inst = struct { .trunc, .round, .tag_name, - .reify, .type_name, .frame_type, .frame_size, @@ -1759,7 +1755,6 @@ pub const Inst = struct { .trunc = .un_node, .round = .un_node, .tag_name = .un_node, - .reify = .un_node, .type_name = .un_node, .frame_type = .un_node, .frame_size = .un_node, @@ -1980,6 +1975,10 @@ pub const Inst = struct { /// Implement builtin `@intToError`. /// `operand` is payload index to `UnNode`. int_to_error, + /// Implement builtin `@Type`. + /// `operand` is payload index to `UnNode`. + /// `small` contains `NameStrategy + reify, pub const InstData = struct { opcode: Extended, diff --git a/src/print_zir.zig b/src/print_zir.zig index 6e33154bbd8e6215051d52af043ce3ca32c9a3c6..ca2368fe100165150d656567477fb974c3b0b5a5 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -214,7 +214,6 @@ const Writer = struct { .trunc, .round, .tag_name, - .reify, .type_name, .frame_type, .frame_size, @@ -500,6 +499,7 @@ const Writer = struct { .wasm_memory_size, .error_to_int, .int_to_error, + .reify, => { const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(inst_data.node); diff --git a/test/cases/compile_errors/reify_type_for_tagged_union_with_extra_union_field.zig b/test/cases/compile_errors/reify_type_for_tagged_union_with_extra_union_field.zig new file mode 100644 index 0000000000000000000000000000000000000000..9d6170b9d07c9be69eed5608db8f3969b88ee94e --- /dev/null +++ b/test/cases/compile_errors/reify_type_for_tagged_union_with_extra_union_field.zig @@ -0,0 +1,35 @@ +const Tag = @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = u1, + .fields = &.{ + .{ .name = "signed", .value = 0 }, + .{ .name = "unsigned", .value = 1 }, + }, + .decls = &.{}, + .is_exhaustive = true, + }, +}); +const Tagged = @Type(.{ + .Union = .{ + .layout = .Auto, + .tag_type = Tag, + .fields = &.{ + .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, + .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, + .{ .name = "arst", .field_type = f32, .alignment = @alignOf(f32) }, + }, + .decls = &.{}, + }, +}); +export fn entry() void { + var tagged = Tagged{ .signed = -1 }; + tagged = .{ .unsigned = 1 }; +} + +// error +// backend=stage2 +// target=native +// +// :13:16: error: no field named 'arst' in enum 'tmp.Tag' +// :1:13: note: enum declared here -- 2.54.0 From 1a500b969943dc6a4c549fdcbd72659702e867a2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Aug 2022 19:16:21 -0700 Subject: [PATCH 05/72] Sema: avoid error return traces when possible stage2 was adding bogus error return trace frames when an error was not being returned. This commit makes several improvements: * Make a runtime check if necessary to only emit a frame into the error return trace when an actual error is returned. * Use the `analyzeIsNonErrComptimeOnly` machinery to avoid runtime checks when it is compile-time-known that the value is an error, or a non-error. * Make std.builtin.returnError take a non-optional stack trace pointer. closes #12174 --- lib/std/builtin.zig | 3 +- src/Sema.zig | 79 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 047c65439c14c793d0b80de636c924e93e25f483..ef716c6972c4a755a0e077107d76191ff09d2850 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -867,10 +867,9 @@ pub fn panicOutOfBounds(index: usize, len: usize) noreturn { std.debug.panic("attempt to index out of bound: index {d}, len {d}", .{ index, len }); } -pub noinline fn returnError(maybe_st: ?*StackTrace) void { +pub noinline fn returnError(st: *StackTrace) void { @setCold(true); @setRuntimeSafety(false); - const st = maybe_st orelse return; addErrRetTraceAddr(st, @returnAddress()); } diff --git a/src/Sema.zig b/src/Sema.zig index 2f3c931e16e6483d7784549c805f2dfdf96777ea..a540a75d607f6c20482494532186b99e5ec0dd5a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14492,6 +14492,20 @@ fn zirBoolBr( const rhs_result = try sema.resolveBody(rhs_block, body, inst); _ = try rhs_block.addBr(block_inst, rhs_result); + return finishCondBr(sema, parent_block, &child_block, &then_block, &else_block, lhs, block_inst); +} + +fn finishCondBr( + sema: *Sema, + parent_block: *Block, + child_block: *Block, + then_block: *Block, + else_block: *Block, + cond: Air.Inst.Ref, + block_inst: Air.Inst.Index, +) !Air.Inst.Ref { + const gpa = sema.gpa; + try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + then_block.instructions.items.len + else_block.instructions.items.len + @typeInfo(Air.Block).Struct.fields.len + child_block.instructions.items.len + 1); @@ -14504,7 +14518,7 @@ fn zirBoolBr( sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items); _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ - .operand = lhs, + .operand = cond, .payload = cond_br_payload, } } }); @@ -14901,6 +14915,8 @@ fn analyzeRet( uncasted_operand: Air.Inst.Ref, src: LazySrcLoc, ) CompileError!Zir.Inst.Index { + const gpa = sema.gpa; + // Special case for returning an error to an inferred error set; we need to // add the error tag to the inferred error set of the in-scope function, so // that the coercion below works correctly. @@ -14918,11 +14934,13 @@ fn analyzeRet( return error.ComptimeReturn; } // We are inlining a function call; rewrite the `ret` as a `break`. - try inlining.merges.results.append(sema.gpa, operand); + try inlining.merges.results.append(gpa, operand); _ = try block.addBr(inlining.merges.block_inst, operand); return always_noreturn; } + try sema.resolveTypeLayout(block, src, sema.fn_ret_ty); + // TODO implement this feature in all the backends and then delete this check. const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm; @@ -14931,19 +14949,52 @@ fn analyzeRet( sema.mod.comp.bin_file.options.error_return_tracing and backend_supports_error_return_tracing) ret_err: { - if (try sema.resolveMaybeUndefVal(block, src, operand)) |ret_val| { - if (ret_val.tag() != .@"error") break :ret_err; - } - const return_err_fn = try sema.getBuiltin(block, src, "returnError"); + // Avoid adding a frame to the error return trace in case the value is comptime-known + // to be not an error. + const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, src, operand); + const need_check = switch (is_non_err) { + .bool_true => break :ret_err, + .bool_false => false, + else => true, + }; + const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); - const ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty); + const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty); const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); + const return_err_fn = try sema.getBuiltin(block, src, "returnError"); const args: [1]Air.Inst.Ref = .{err_return_trace}; + + if (!need_check) { + _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null); + break :ret_err; + } + + const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len); + try sema.air_instructions.append(gpa, .{ + .tag = .block, + .data = .{ .ty_pl = .{ + .ty = .void_type, + .payload = undefined, + } }, + }); + + var child_block = block.makeSubBlock(); + defer child_block.instructions.deinit(gpa); + + var then_block = child_block.makeSubBlock(); + defer then_block.instructions.deinit(gpa); + _ = try then_block.addUnOp(.ret, operand); + + var else_block = child_block.makeSubBlock(); + defer else_block.instructions.deinit(gpa); _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null); + _ = try else_block.addUnOp(.ret, operand); + + _ = try finishCondBr(sema, block, &child_block, &then_block, &else_block, is_non_err, block_inst); + return always_noreturn; } - try sema.resolveTypeLayout(block, src, sema.fn_ret_ty); _ = try block.addUnOp(.ret, operand); return always_noreturn; } @@ -25436,10 +25487,16 @@ fn analyzeIsNonErrComptimeOnly( assert(ot == .ErrorUnion); if (Air.refToIndex(operand)) |operand_inst| { - const air_tags = sema.air_instructions.items(.tag); - if (air_tags[operand_inst] == .wrap_errunion_payload) { - return Air.Inst.Ref.bool_true; + switch (sema.air_instructions.items(.tag)[operand_inst]) { + .wrap_errunion_payload => return Air.Inst.Ref.bool_true, + .wrap_errunion_err => return Air.Inst.Ref.bool_false, + else => {}, } + } else if (operand == .undef) { + return sema.addConstUndef(Type.bool); + } else { + // None of the ref tags can be errors. + return Air.Inst.Ref.bool_true; } const maybe_operand_val = try sema.resolveMaybeUndefVal(block, src, operand); -- 2.54.0 From 3389890709b8470c59cadc21471e4a6c8dda48a6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Aug 2022 20:19:08 -0700 Subject: [PATCH 06/72] stage2: error return tracing handles ret better Sema: insert an error return trace frame when appropriate when analyzing ret_load. Also optimize the instructions to avoid an unnecessary block sent to the backends. AstGen: always emit a dbg_stmt for return expressions, in between the defer instructions and the return instruction. This improves the following test case: ```zig pub fn main() !void { return foo(); } fn foo() !void { return error.Bad; } ``` The error return trace now points to the return token instead of pointing to the foo() function call, matching stage1. --- src/AstGen.zig | 28 +++++++++ src/Sema.zig | 166 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 141 insertions(+), 53 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 9c8252da55906b12c21a629bb332d3fef2aef6e5..e53c59817ad5cf5374d74ce8f1a6c71a651f3eb0 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -6573,6 +6573,16 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{}); + // Ensure debug line/column information is emitted for this return expression. + // Then we will save the line/column so that we can emit another one that goes + // "backwards" because we want to evaluate the operand, but then put the debug + // info back at the return keyword for error return tracing. + if (!gz.force_comptime) { + try emitDbgNode(gz, node); + } + const ret_line = astgen.source_line - gz.decl_line; + const ret_column = astgen.source_column; + const defer_outer = &astgen.fn_block.?.base; const operand_node = node_datas[node].lhs; @@ -6591,11 +6601,13 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref const defer_counts = countDefers(astgen, defer_outer, scope); if (!defer_counts.need_err_code) { try genDefers(gz, defer_outer, scope, .both_sans_err); + try emitRetDbgStmt(gz, ret_line, ret_column); _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token); return Zir.Inst.Ref.unreachable_value; } const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token); try genDefers(gz, defer_outer, scope, .{ .both = err_code }); + try emitRetDbgStmt(gz, ret_line, ret_column); _ = try gz.addUnNode(.ret_node, err_code, node); return Zir.Inst.Ref.unreachable_value; } @@ -6614,6 +6626,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref .never => { // Returning a value that cannot be an error; skip error defers. try genDefers(gz, defer_outer, scope, .normal_only); + try emitRetDbgStmt(gz, ret_line, ret_column); try gz.addRet(rl, operand, node); return Zir.Inst.Ref.unreachable_value; }, @@ -6621,6 +6634,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref // Value is always an error. Emit both error defers and regular defers. const err_code = if (rl == .ptr) try gz.addUnNode(.load, rl.ptr, node) else operand; try genDefers(gz, defer_outer, scope, .{ .both = err_code }); + try emitRetDbgStmt(gz, ret_line, ret_column); try gz.addRet(rl, operand, node); return Zir.Inst.Ref.unreachable_value; }, @@ -6629,6 +6643,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref if (!defer_counts.have_err) { // Only regular defers; no branch needed. try genDefers(gz, defer_outer, scope, .normal_only); + try emitRetDbgStmt(gz, ret_line, ret_column); try gz.addRet(rl, operand, node); return Zir.Inst.Ref.unreachable_value; } @@ -6642,6 +6657,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref defer then_scope.unstack(); try genDefers(&then_scope, defer_outer, scope, .normal_only); + try emitRetDbgStmt(gz, ret_line, ret_column); try then_scope.addRet(rl, operand, node); var else_scope = gz.makeSubBlock(scope); @@ -6651,6 +6667,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref .both = try else_scope.addUnNode(.err_union_code, result, node), }; try genDefers(&else_scope, defer_outer, scope, which_ones); + try emitRetDbgStmt(gz, ret_line, ret_column); try else_scope.addRet(rl, operand, node); try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0); @@ -11667,3 +11684,14 @@ fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 { } return @intCast(u32, count); } + +fn emitRetDbgStmt(gz: *GenZir, line: u32, column: u32) !void { + if (gz.force_comptime) return; + + _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ + .dbg_stmt = .{ + .line = line, + .column = column, + }, + } }); +} diff --git a/src/Sema.zig b/src/Sema.zig index a540a75d607f6c20482494532186b99e5ec0dd5a..13570a509a42555fde757adc8c5d087d28a3abfc 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14888,10 +14888,83 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir const operand = try sema.analyzeLoad(block, src, ret_ptr, src); return sema.analyzeRet(block, operand, src); } + + if (sema.wantErrorReturnTracing()) { + const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr); + return retWithErrTracing(sema, block, src, is_non_err, .ret_load, ret_ptr); + } + _ = try block.addUnOp(.ret_load, ret_ptr); return always_noreturn; } +fn retWithErrTracing( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + is_non_err: Air.Inst.Ref, + ret_tag: Air.Inst.Tag, + operand: Air.Inst.Ref, +) CompileError!Zir.Inst.Index { + const need_check = switch (is_non_err) { + .bool_true => { + _ = try block.addUnOp(ret_tag, operand); + return always_noreturn; + }, + .bool_false => false, + else => true, + }; + const gpa = sema.gpa; + const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); + const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); + const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty); + const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); + const return_err_fn = try sema.getBuiltin(block, src, "returnError"); + const args: [1]Air.Inst.Ref = .{err_return_trace}; + + if (!need_check) { + _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null); + _ = try block.addUnOp(ret_tag, operand); + return always_noreturn; + } + + var then_block = block.makeSubBlock(); + defer then_block.instructions.deinit(gpa); + _ = try then_block.addUnOp(ret_tag, operand); + + var else_block = block.makeSubBlock(); + defer else_block.instructions.deinit(gpa); + _ = try sema.analyzeCall(&else_block, return_err_fn, src, src, .never_inline, false, &args, null); + _ = try else_block.addUnOp(ret_tag, operand); + + try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + + then_block.instructions.items.len + else_block.instructions.items.len + + @typeInfo(Air.Block).Struct.fields.len + 1); + + const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ + .then_body_len = @intCast(u32, then_block.instructions.items.len), + .else_body_len = @intCast(u32, else_block.instructions.items.len), + }); + sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items); + sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items); + + _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ + .operand = is_non_err, + .payload = cond_br_payload, + } } }); + + return always_noreturn; +} + +fn wantErrorReturnTracing(sema: *Sema) bool { + // TODO implement this feature in all the backends and then delete this check. + const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm; + + return sema.fn_ret_ty.isError() and + sema.mod.comp.bin_file.options.error_return_tracing and + backend_supports_error_return_tracing; +} + fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion); @@ -14915,8 +14988,6 @@ fn analyzeRet( uncasted_operand: Air.Inst.Ref, src: LazySrcLoc, ) CompileError!Zir.Inst.Index { - const gpa = sema.gpa; - // Special case for returning an error to an inferred error set; we need to // add the error tag to the inferred error set of the in-scope function, so // that the coercion below works correctly. @@ -14934,65 +15005,18 @@ fn analyzeRet( return error.ComptimeReturn; } // We are inlining a function call; rewrite the `ret` as a `break`. - try inlining.merges.results.append(gpa, operand); + try inlining.merges.results.append(sema.gpa, operand); _ = try block.addBr(inlining.merges.block_inst, operand); return always_noreturn; } try sema.resolveTypeLayout(block, src, sema.fn_ret_ty); - // TODO implement this feature in all the backends and then delete this check. - const backend_supports_error_return_tracing = - sema.mod.comp.bin_file.options.use_llvm; - - if (sema.fn_ret_ty.isError() and - sema.mod.comp.bin_file.options.error_return_tracing and - backend_supports_error_return_tracing) - ret_err: { + if (sema.wantErrorReturnTracing()) { // Avoid adding a frame to the error return trace in case the value is comptime-known // to be not an error. - const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, src, operand); - const need_check = switch (is_non_err) { - .bool_true => break :ret_err, - .bool_false => false, - else => true, - }; - - const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); - const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); - const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty); - const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); - const return_err_fn = try sema.getBuiltin(block, src, "returnError"); - const args: [1]Air.Inst.Ref = .{err_return_trace}; - - if (!need_check) { - _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null); - break :ret_err; - } - - const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len); - try sema.air_instructions.append(gpa, .{ - .tag = .block, - .data = .{ .ty_pl = .{ - .ty = .void_type, - .payload = undefined, - } }, - }); - - var child_block = block.makeSubBlock(); - defer child_block.instructions.deinit(gpa); - - var then_block = child_block.makeSubBlock(); - defer then_block.instructions.deinit(gpa); - _ = try then_block.addUnOp(.ret, operand); - - var else_block = child_block.makeSubBlock(); - defer else_block.instructions.deinit(gpa); - _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null); - _ = try else_block.addUnOp(.ret, operand); - - _ = try finishCondBr(sema, block, &child_block, &then_block, &else_block, is_non_err, block_inst); - return always_noreturn; + const is_non_err = try sema.analyzeIsNonErr(block, src, operand); + return retWithErrTracing(sema, block, src, is_non_err, .ret, operand); } _ = try block.addUnOp(.ret, operand); @@ -25474,6 +25498,27 @@ fn analyzeIsNull( return block.addUnOp(air_tag, operand); } +fn analyzePtrIsNonErrComptimeOnly( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + operand: Air.Inst.Ref, +) CompileError!Air.Inst.Ref { + const ptr_ty = sema.typeOf(operand); + assert(ptr_ty.zigTypeTag() == .Pointer); + const child_ty = ptr_ty.childType(); + + const child_tag = child_ty.zigTypeTag(); + if (child_tag != .ErrorSet and child_tag != .ErrorUnion) return Air.Inst.Ref.bool_true; + if (child_tag == .ErrorSet) return Air.Inst.Ref.bool_false; + assert(child_tag == .ErrorUnion); + + _ = block; + _ = src; + + return Air.Inst.Ref.none; +} + fn analyzeIsNonErrComptimeOnly( sema: *Sema, block: *Block, @@ -25572,6 +25617,21 @@ fn analyzeIsNonErr( } } +fn analyzePtrIsNonErr( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + operand: Air.Inst.Ref, +) CompileError!Air.Inst.Ref { + const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand); + if (result == .none) { + try sema.requireRuntimeBlock(block, src, null); + return block.addUnOp(.is_non_err_ptr, operand); + } else { + return result; + } +} + fn analyzeSlice( sema: *Sema, block: *Block, -- 2.54.0 From 0648177ed7a1ad5364c478b63ec28a6de15e4ad9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Aug 2022 20:29:22 -0700 Subject: [PATCH 07/72] AstGen: avoid multiple dbg_stmt instructions in a row This is purely an optimization to emit fewer ZIR instructions. --- src/AstGen.zig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/AstGen.zig b/src/AstGen.zig index e53c59817ad5cf5374d74ce8f1a6c71a651f3eb0..5087ea629330f42c4bf471afa3437e4954f96dec 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -3071,6 +3071,19 @@ fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void { const line = astgen.source_line - gz.decl_line; const column = astgen.source_column; + if (gz.instructions.items.len > 0) { + const last = gz.instructions.items[gz.instructions.items.len - 1]; + const zir_tags = astgen.instructions.items(.tag); + if (zir_tags[last] == .dbg_stmt) { + const zir_datas = astgen.instructions.items(.data); + zir_datas[last].dbg_stmt = .{ + .line = line, + .column = column, + }; + return; + } + } + _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ .dbg_stmt = .{ .line = line, -- 2.54.0 From feb90f6ed4f64eb0faef8fbbcf8c51fad5392b39 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Aug 2022 20:53:06 -0700 Subject: [PATCH 08/72] AstGen: emit debug stmt for try This improves the following test case: ```zig pub fn main() !void { try foo(); } fn foo() !void { return error.Bad; } ``` The error return trace now points to the `try` token instead of pointing to the foo() function call, matching stage1. Closes #12308. --- src/AstGen.zig | 27 +++++++++++++++++++-------- test/stack_traces.zig | 1 + 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 5087ea629330f42c4bf471afa3437e4954f96dec..4e08b397772840f9b455e940bbe59c7ff917656d 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -5051,6 +5051,16 @@ fn tryExpr( if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{}); + // Ensure debug line/column information is emitted for this try expression. + // Then we will save the line/column so that we can emit another one that goes + // "backwards" because we want to evaluate the operand, but then put the debug + // info back at the try keyword for error return tracing. + if (!parent_gz.force_comptime) { + try emitDbgNode(parent_gz, node); + } + const try_line = astgen.source_line - parent_gz.decl_line; + const try_column = astgen.source_column; + const operand_rl: ResultLoc = switch (rl) { .ref => .ref, else => .none, @@ -5080,6 +5090,7 @@ fn tryExpr( }; const err_code = try else_scope.addUnNode(err_tag, operand, node); try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); + try emitDbgStmt(&else_scope, try_line, try_column); _ = try else_scope.addUnNode(.ret_node, err_code, node); try else_scope.setTryBody(try_inst, operand); @@ -6614,13 +6625,13 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref const defer_counts = countDefers(astgen, defer_outer, scope); if (!defer_counts.need_err_code) { try genDefers(gz, defer_outer, scope, .both_sans_err); - try emitRetDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_line, ret_column); _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token); return Zir.Inst.Ref.unreachable_value; } const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token); try genDefers(gz, defer_outer, scope, .{ .both = err_code }); - try emitRetDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_line, ret_column); _ = try gz.addUnNode(.ret_node, err_code, node); return Zir.Inst.Ref.unreachable_value; } @@ -6639,7 +6650,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref .never => { // Returning a value that cannot be an error; skip error defers. try genDefers(gz, defer_outer, scope, .normal_only); - try emitRetDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_line, ret_column); try gz.addRet(rl, operand, node); return Zir.Inst.Ref.unreachable_value; }, @@ -6647,7 +6658,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref // Value is always an error. Emit both error defers and regular defers. const err_code = if (rl == .ptr) try gz.addUnNode(.load, rl.ptr, node) else operand; try genDefers(gz, defer_outer, scope, .{ .both = err_code }); - try emitRetDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_line, ret_column); try gz.addRet(rl, operand, node); return Zir.Inst.Ref.unreachable_value; }, @@ -6656,7 +6667,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref if (!defer_counts.have_err) { // Only regular defers; no branch needed. try genDefers(gz, defer_outer, scope, .normal_only); - try emitRetDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(gz, ret_line, ret_column); try gz.addRet(rl, operand, node); return Zir.Inst.Ref.unreachable_value; } @@ -6670,7 +6681,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref defer then_scope.unstack(); try genDefers(&then_scope, defer_outer, scope, .normal_only); - try emitRetDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(&then_scope, ret_line, ret_column); try then_scope.addRet(rl, operand, node); var else_scope = gz.makeSubBlock(scope); @@ -6680,7 +6691,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref .both = try else_scope.addUnNode(.err_union_code, result, node), }; try genDefers(&else_scope, defer_outer, scope, which_ones); - try emitRetDbgStmt(gz, ret_line, ret_column); + try emitDbgStmt(&else_scope, ret_line, ret_column); try else_scope.addRet(rl, operand, node); try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0); @@ -11698,7 +11709,7 @@ fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 { return @intCast(u32, count); } -fn emitRetDbgStmt(gz: *GenZir, line: u32, column: u32) !void { +fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void { if (gz.force_comptime) return; _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{ diff --git a/test/stack_traces.zig b/test/stack_traces.zig index c40413f936c6f0f95012476f7425ad76ee3d6f0a..ea27b5beff7e94a170f6be4edbd065a7013e26b9 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -22,6 +22,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { .ReleaseSafe = .{ .exclude_os = .{ .windows, // segfault + .linux, // defeated by aggressive inlining }, .expect = \\error: TheSkyIsFalling -- 2.54.0 From c76b5c1a31ebe09726cd29e7a6da69613eabfd10 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 3 Aug 2022 19:55:27 +0300 Subject: [PATCH 09/72] stage2: correct node offset of nested declarations --- src/AstGen.zig | 21 ++++-- src/Module.zig | 33 +++------ src/Sema.zig | 69 ++++++++----------- src/print_zir.zig | 16 +++++ src/type.zig | 44 ------------ .../non_constant_expression_in_array_size.zig | 2 +- .../cases/compile_errors/not_an_enum_type.zig | 2 +- ...n_invalid_value_of_non-exhaustive_enum.zig | 2 +- test/cases/error_in_nested_declaration.zig | 31 +++++++++ 9 files changed, 106 insertions(+), 114 deletions(-) create mode 100644 test/cases/error_in_nested_declaration.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index ffe489a216129b6f5e95e495ebe1c353341f56f5..3a30ab7b9d3ec64eb857fc6836881c19f4822c70 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -4278,7 +4278,7 @@ fn structDeclInner( var known_non_opv = false; var known_comptime_only = false; for (container_decl.ast.members) |member_node| { - const member = switch (try containerMember(gz, &namespace.base, &wip_members, member_node)) { + const member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { .decl => continue, .field => |field| field, }; @@ -4445,7 +4445,7 @@ fn unionDeclInner( defer wip_members.deinit(); for (members) |member_node| { - const member = switch (try containerMember(gz, &namespace.base, &wip_members, member_node)) { + const member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { .decl => continue, .field => |field| field, }; @@ -4732,7 +4732,7 @@ fn containerDecl( for (container_decl.ast.members) |member_node| { if (member_node == counts.nonexhaustive_node) continue; - const member = switch (try containerMember(gz, &namespace.base, &wip_members, member_node)) { + const member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { .decl => continue, .field => |field| field, }; @@ -4810,13 +4810,26 @@ fn containerDecl( }; defer namespace.deinit(gpa); + astgen.advanceSourceCursorToNode(node); + var block_scope: GenZir = .{ + .parent = &namespace.base, + .decl_node_index = node, + .decl_line = astgen.source_line, + .astgen = astgen, + .force_comptime = true, + .in_defer = false, + .instructions = gz.instructions, + .instructions_top = gz.instructions.items.len, + }; + defer block_scope.unstack(); + const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members); var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0); defer wip_members.deinit(); for (container_decl.ast.members) |member_node| { - const res = try containerMember(gz, &namespace.base, &wip_members, member_node); + const res = try containerMember(&block_scope, &namespace.base, &wip_members, member_node); if (res == .field) { return astgen.failNode(member_node, "opaque types cannot have fields", .{}); } diff --git a/src/Module.zig b/src/Module.zig index ab394af0ad3db8cbd30abfcd730121d3a16d64f9..8b195eff2de02674457ea2d786f0fdc9fc0da264 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -853,8 +853,6 @@ pub const EmitH = struct { pub const ErrorSet = struct { /// The Decl that corresponds to the error set itself. owner_decl: Decl.Index, - /// Offset from Decl node index, points to the error set AST node. - node_offset: i32, /// The string bytes are stored in the owner Decl arena. /// These must be in sorted order. See sortNames. names: NameMap, @@ -866,7 +864,7 @@ pub const ErrorSet = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(self.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } @@ -893,8 +891,6 @@ pub const Struct = struct { namespace: Namespace, /// The Decl that corresponds to the struct itself. owner_decl: Decl.Index, - /// Offset from `owner_decl`, points to the struct AST node. - node_offset: i32, /// Index of the struct_decl ZIR instruction. zir_index: Zir.Inst.Index, @@ -953,7 +949,7 @@ pub const Struct = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(s.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } @@ -968,7 +964,7 @@ pub const Struct = struct { }); return s.srcLoc(mod); }; - const node = owner_decl.relativeToNodeIndex(s.node_offset); + const node = owner_decl.relativeToNodeIndex(0); const node_tags = tree.nodes.items(.tag); switch (node_tags[node]) { .container_decl, @@ -1060,8 +1056,6 @@ pub const Struct = struct { pub const EnumSimple = struct { /// The Decl that corresponds to the enum itself. owner_decl: Decl.Index, - /// Offset from `owner_decl`, points to the enum decl AST node. - node_offset: i32, /// Set of field names in declaration order. fields: NameMap, @@ -1072,7 +1066,7 @@ pub const EnumSimple = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(self.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } }; @@ -1083,8 +1077,6 @@ pub const EnumSimple = struct { pub const EnumNumbered = struct { /// The Decl that corresponds to the enum itself. owner_decl: Decl.Index, - /// Offset from `owner_decl`, points to the enum decl AST node. - node_offset: i32, /// An integer type which is used for the numerical value of the enum. /// Whether zig chooses this type or the user specifies it, it is stored here. tag_ty: Type, @@ -1103,7 +1095,7 @@ pub const EnumNumbered = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(self.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } }; @@ -1113,8 +1105,6 @@ pub const EnumNumbered = struct { pub const EnumFull = struct { /// The Decl that corresponds to the enum itself. owner_decl: Decl.Index, - /// Offset from `owner_decl`, points to the enum decl AST node. - node_offset: i32, /// An integer type which is used for the numerical value of the enum. /// Whether zig chooses this type or the user specifies it, it is stored here. tag_ty: Type, @@ -1137,7 +1127,7 @@ pub const EnumFull = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(self.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } }; @@ -1155,8 +1145,6 @@ pub const Union = struct { namespace: Namespace, /// The Decl that corresponds to the union itself. owner_decl: Decl.Index, - /// Offset from `owner_decl`, points to the union decl AST node. - node_offset: i32, /// Index of the union_decl ZIR instruction. zir_index: Zir.Inst.Index, @@ -1203,7 +1191,7 @@ pub const Union = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(self.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } @@ -1218,7 +1206,7 @@ pub const Union = struct { }); return u.srcLoc(mod); }; - const node = owner_decl.relativeToNodeIndex(u.node_offset); + const node = owner_decl.relativeToNodeIndex(0); const node_tags = tree.nodes.items(.tag); var buf: [2]Ast.Node.Index = undefined; switch (node_tags[node]) { @@ -1410,8 +1398,6 @@ pub const Union = struct { pub const Opaque = struct { /// The Decl that corresponds to the opaque itself. owner_decl: Decl.Index, - /// Offset from `owner_decl`, points to the opaque decl AST node. - node_offset: i32, /// Represents the declarations inside this opaque. namespace: Namespace, @@ -1420,7 +1406,7 @@ pub const Opaque = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(self.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } @@ -4337,7 +4323,6 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { struct_obj.* = .{ .owner_decl = undefined, // set below .fields = .{}, - .node_offset = 0, // it's the struct for the root file .zir_index = undefined, // set below .layout = .Auto, .status = .none, diff --git a/src/Sema.zig b/src/Sema.zig index c91f4cddb1359e4456dba2b406538fc753a423cb..34d64ff9191de810459afca51c37e86170eb2070 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1818,10 +1818,10 @@ fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazyS const tree = try sema.getAstTree(block); const decl = sema.mod.declPtr(decl_index); - const field_src = enumFieldSrcLoc(decl, tree.*, container_ty.getNodeOffset(), field_index); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_index); const default_value_src: LazySrcLoc = .{ .node_offset_field_default = field_src.node_offset.x }; - try sema.errNote(block, default_value_src, msg, "default value set here", .{}); + try sema.mod.errNoteNonLazy(default_value_src.toSrcLoc(decl), msg, "default value set here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); @@ -1866,7 +1866,7 @@ fn addFieldErrNote( const decl_index = container_ty.getOwnerDecl(); const decl = mod.declPtr(decl_index); const tree = try sema.getAstTree(block); - const field_src = enumFieldSrcLoc(decl, tree.*, container_ty.getNodeOffset(), field_index); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_index); try mod.errNoteNonLazy(field_src.toSrcLoc(decl), parent, format, args); } @@ -2262,7 +2262,7 @@ fn zirStructDecl( const struct_obj = try new_decl_arena_allocator.create(Module.Struct); const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj); const struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty); - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = struct_val, }, small.name_strategy, "struct", inst); @@ -2272,7 +2272,6 @@ fn zirStructDecl( struct_obj.* = .{ .owner_decl = new_decl_index, .fields = .{}, - .node_offset = src.node_offset.x, .zir_index = inst, .layout = small.layout, .status = .none, @@ -2294,6 +2293,7 @@ fn zirStructDecl( fn createAnonymousDeclTypeNamed( sema: *Sema, block: *Block, + src: LazySrcLoc, typed_value: TypedValue, name_strategy: Zir.Inst.NameStrategy, anon_prefix: []const u8, @@ -2303,7 +2303,8 @@ fn createAnonymousDeclTypeNamed( const namespace = block.namespace; const src_scope = block.wip_capture_scope; const src_decl = mod.declPtr(block.src_decl); - const new_decl_index = try mod.allocateNewDecl(namespace, src_decl.src_node, src_scope); + const src_node = src_decl.relativeToNodeIndex(src.node_offset.x); + const new_decl_index = try mod.allocateNewDecl(namespace, src_node, src_scope); errdefer mod.destroyDecl(new_decl_index); switch (name_strategy) { @@ -2378,7 +2379,7 @@ fn createAnonymousDeclTypeNamed( }, else => {}, }; - return sema.createAnonymousDeclTypeNamed(block, typed_value, .anon, anon_prefix, null); + return sema.createAnonymousDeclTypeNamed(block, src, typed_value, .anon, anon_prefix, null); }, } } @@ -2442,7 +2443,7 @@ fn zirEnumDecl( }; const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = enum_val, }, small.name_strategy, "enum", inst); @@ -2456,7 +2457,6 @@ fn zirEnumDecl( .tag_ty_inferred = true, .fields = .{}, .values = .{}, - .node_offset = src.node_offset.x, .namespace = .{ .parent = block.namespace, .ty = enum_ty, @@ -2684,7 +2684,7 @@ fn zirUnionDecl( const union_ty = Type.initPayload(&union_payload.base); const union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty); const mod = sema.mod; - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = union_val, }, small.name_strategy, "union", inst); @@ -2695,7 +2695,6 @@ fn zirUnionDecl( .owner_decl = new_decl_index, .tag_ty = Type.initTag(.@"null"), .fields = .{}, - .node_offset = src.node_offset.x, .zir_index = inst, .layout = small.layout, .status = .none, @@ -2753,7 +2752,7 @@ fn zirOpaqueDecl( }; const opaque_ty = Type.initPayload(&opaque_ty_payload.base); const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty); - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = opaque_val, }, small.name_strategy, "opaque", inst); @@ -2763,7 +2762,6 @@ fn zirOpaqueDecl( opaque_obj.* = .{ .owner_decl = new_decl_index, - .node_offset = src.node_offset.x, .namespace = .{ .parent = block.namespace, .ty = opaque_ty, @@ -2802,7 +2800,7 @@ fn zirErrorSetDecl( const error_set_ty = try Type.Tag.error_set.create(new_decl_arena_allocator, error_set); const error_set_val = try Value.Tag.ty.create(new_decl_arena_allocator, error_set_ty); const mod = sema.mod; - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = error_set_val, }, name_strategy, "error", inst); @@ -2827,7 +2825,6 @@ fn zirErrorSetDecl( error_set.* = .{ .owner_decl = new_decl_index, - .node_offset = inst_data.src_node, .names = names, }; try new_decl.finalizeNewArena(&new_decl_arena); @@ -16336,7 +16333,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in }; const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = enum_val, }, name_strategy, "enum", inst); @@ -16350,7 +16347,6 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in .tag_ty_inferred = false, .fields = .{}, .values = .{}, - .node_offset = src.node_offset.x, .namespace = .{ .parent = block.namespace, .ty = enum_ty, @@ -16433,7 +16429,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in }; const opaque_ty = Type.initPayload(&opaque_ty_payload.base); const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty); - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = opaque_val, }, name_strategy, "opaque", inst); @@ -16443,7 +16439,6 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in opaque_obj.* = .{ .owner_decl = new_decl_index, - .node_offset = src.node_offset.x, .namespace = .{ .parent = block.namespace, .ty = opaque_ty, @@ -16492,7 +16487,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in }; const union_ty = Type.initPayload(&union_payload.base); const new_union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty); - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = new_union_val, }, name_strategy, "union", inst); @@ -16503,7 +16498,6 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in .owner_decl = new_decl_index, .tag_ty = Type.initTag(.@"null"), .fields = .{}, - .node_offset = src.node_offset.x, .zir_index = inst, .layout = layout, .status = .have_field_types, @@ -16798,7 +16792,7 @@ fn reifyStruct( const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj); const new_struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty); const mod = sema.mod; - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = new_struct_val, }, name_strategy, "struct", inst); @@ -16808,7 +16802,6 @@ fn reifyStruct( struct_obj.* = .{ .owner_decl = new_decl_index, .fields = .{}, - .node_offset = src.node_offset.x, .zir_index = inst, .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout), .status = .have_field_types, @@ -27241,7 +27234,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); var extra_index: usize = extended.operand; - const src = LazySrcLoc.nodeOffset(struct_obj.node_offset); + const src = LazySrcLoc.nodeOffset(0); extra_index += @boolToInt(small.has_src_node); const fields_len = if (small.has_fields_len) blk: { @@ -27360,12 +27353,12 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void if (gop.found_existing) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, field_i); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i); const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{s}'", .{field_name}); errdefer msg.destroy(gpa); const prev_field_index = struct_obj.fields.getIndex(field_name).?; - const prev_field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, prev_field_index); + const prev_field_src = enumFieldSrcLoc(decl, tree.*, 0, prev_field_index); try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{}); try sema.errNote(&block_scope, src, msg, "struct declared here", .{}); break :msg msg; @@ -27422,7 +27415,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void if (field_ty.zigTypeTag() == .Opaque) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, i); const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); errdefer msg.destroy(sema.gpa); @@ -27434,7 +27427,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void if (struct_obj.layout == .Extern and !sema.validateExternType(field.ty, .other)) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i); + const fields_src = enumFieldSrcLoc(decl, tree.*, 0, i); const msg = try sema.errMsg(&block_scope, fields_src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -27447,7 +27440,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void } else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty))) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i); + const fields_src = enumFieldSrcLoc(decl, tree.*, 0, i); const msg = try sema.errMsg(&block_scope, fields_src, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -27504,7 +27497,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); var extra_index: usize = extended.operand; - const src = LazySrcLoc.nodeOffset(union_obj.node_offset); + const src = LazySrcLoc.nodeOffset(0); extra_index += @boolToInt(small.has_src_node); const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: { @@ -27728,12 +27721,12 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { if (gop.found_existing) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i); const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{s}'", .{field_name}); errdefer msg.destroy(gpa); const prev_field_index = union_obj.fields.getIndex(field_name).?; - const prev_field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, prev_field_index); + const prev_field_src = enumFieldSrcLoc(decl, tree.*, 0, prev_field_index); try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{}); try sema.errNote(&block_scope, src, msg, "union declared here", .{}); break :msg msg; @@ -27746,7 +27739,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { if (!enum_has_field) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i); const msg = try sema.errMsg(&block_scope, field_src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(sema.mod) }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_obj.tag_ty); @@ -27759,7 +27752,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { if (field_ty.zigTypeTag() == .Opaque) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i); const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); errdefer msg.destroy(sema.gpa); @@ -27771,7 +27764,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { if (union_obj.layout == .Extern and !sema.validateExternType(field_ty, .union_field)) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i); const msg = try sema.errMsg(&block_scope, field_src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -27784,7 +27777,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty))) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const fields_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const fields_src = enumFieldSrcLoc(decl, tree.*, 0, field_i); const msg = try sema.errMsg(&block_scope, fields_src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -27876,7 +27869,6 @@ fn generateUnionTagTypeNumbered( .tag_ty = int_ty, .fields = .{}, .values = .{}, - .node_offset = 0, }; // Here we pre-allocate the maps using the decl arena. try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); @@ -27934,7 +27926,6 @@ fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: usize, may enum_obj.* = .{ .owner_decl = new_decl_index, .fields = .{}, - .node_offset = 0, }; // Here we pre-allocate the maps using the decl arena. try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); @@ -28245,7 +28236,7 @@ fn enumFieldSrcLoc( => tree.containerDeclArg(enum_node), // Container was constructed with `@Type`. - else => return LazySrcLoc.nodeOffset(node_offset), + else => return LazySrcLoc.nodeOffset(0), }; var it_index: usize = 0; for (container_decl.ast.members) |member_node| { diff --git a/src/print_zir.zig b/src/print_zir.zig index ca2368fe100165150d656567477fb974c3b0b5a5..6503e029fd134854c977b256411d24302e560b7f 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -1245,6 +1245,10 @@ const Writer = struct { if (decls_len == 0) { try stream.writeAll("{}, "); } else { + const prev_parent_decl_node = self.parent_decl_node; + if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); + defer self.parent_decl_node = prev_parent_decl_node; + try stream.writeAll("{\n"); self.indent += 2; extra_index = try self.writeDecls(stream, decls_len, extra_index); @@ -1415,6 +1419,10 @@ const Writer = struct { if (decls_len == 0) { try stream.writeAll("{}, "); } else { + const prev_parent_decl_node = self.parent_decl_node; + if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); + defer self.parent_decl_node = prev_parent_decl_node; + try stream.writeAll("{\n"); self.indent += 2; extra_index = try self.writeDecls(stream, decls_len, extra_index); @@ -1662,6 +1670,10 @@ const Writer = struct { if (decls_len == 0) { try stream.writeAll("{}, "); } else { + const prev_parent_decl_node = self.parent_decl_node; + if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); + defer self.parent_decl_node = prev_parent_decl_node; + try stream.writeAll("{\n"); self.indent += 2; extra_index = try self.writeDecls(stream, decls_len, extra_index); @@ -1755,6 +1767,10 @@ const Writer = struct { if (decls_len == 0) { try stream.writeAll("{})"); } else { + const prev_parent_decl_node = self.parent_decl_node; + if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); + defer self.parent_decl_node = prev_parent_decl_node; + try stream.writeAll("{\n"); self.indent += 2; _ = try self.writeDecls(stream, decls_len, extra_index); diff --git a/src/type.zig b/src/type.zig index d53158735a4221a8f8c6bead4669db9e788b5980..85d77303c9e7c9c7677b9fbf066ff409a42162a7 100644 --- a/src/type.zig +++ b/src/type.zig @@ -5771,50 +5771,6 @@ pub const Type = extern union { } } - pub fn getNodeOffset(ty: Type) i32 { - switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => { - const enum_full = ty.cast(Payload.EnumFull).?.data; - return enum_full.node_offset; - }, - .enum_numbered => return ty.castTag(.enum_numbered).?.data.node_offset, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - return enum_simple.node_offset; - }, - .@"struct" => { - const struct_obj = ty.castTag(.@"struct").?.data; - return struct_obj.node_offset; - }, - .error_set => { - const error_set = ty.castTag(.error_set).?.data; - return error_set.node_offset; - }, - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - return union_obj.node_offset; - }, - .@"opaque" => { - const opaque_obj = ty.cast(Payload.Opaque).?.data; - return opaque_obj.node_offset; - }, - .atomic_order, - .atomic_rmw_op, - .calling_convention, - .address_space, - .float_mode, - .reduce_op, - .call_options, - .prefetch_options, - .export_options, - .extern_options, - .type_info, - => unreachable, // These need to be resolved earlier. - - else => unreachable, - } - } - /// This enum does not directly correspond to `std.builtin.TypeId` because /// it has extra enum tags in it, as a way of using less memory. For example, /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types diff --git a/test/cases/compile_errors/non_constant_expression_in_array_size.zig b/test/cases/compile_errors/non_constant_expression_in_array_size.zig index e702246001d2c613c5a6c988cfe7eab73bbd17b3..1dc8b50464620ac15cf0ad0149ebcf6de8692881 100644 --- a/test/cases/compile_errors/non_constant_expression_in_array_size.zig +++ b/test/cases/compile_errors/non_constant_expression_in_array_size.zig @@ -11,4 +11,4 @@ export fn entry() usize { return @offsetOf(Foo, "y"); } // target=native // // :5:25: error: cannot load runtime value in comptime block -// :2:15: note: called from here +// :2:12: note: called from here diff --git a/test/cases/compile_errors/not_an_enum_type.zig b/test/cases/compile_errors/not_an_enum_type.zig index 063ee8a8d858ceaece41caab08b89784d116e54a..bdd03c8db7fe54490e4342f4e3d64af7b4b79899 100644 --- a/test/cases/compile_errors/not_an_enum_type.zig +++ b/test/cases/compile_errors/not_an_enum_type.zig @@ -17,4 +17,4 @@ const ExpectedVarDeclOrFn = struct {}; // target=native // // :4:9: error: expected type '@typeInfo(tmp.Error).Union.tag_type.?', found 'type' -// :8:1: note: enum declared here +// :8:15: note: enum declared here diff --git a/test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig b/test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig index e9e63f0d7ac1884bcc7bb0e300baa954cc9d6c26..1de0d1c1450163981eb7dde7188ba15be59e5b52 100644 --- a/test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig +++ b/test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig @@ -9,4 +9,4 @@ test "enum" { // is_test=1 // // :3:9: error: no field with value '5' in enum 'test.enum.E' -// :1:1: note: declared here +// :2:15: note: declared here diff --git a/test/cases/error_in_nested_declaration.zig b/test/cases/error_in_nested_declaration.zig new file mode 100644 index 0000000000000000000000000000000000000000..3fff746909a5f431fa3ca9da784c4b72ceba7fcd --- /dev/null +++ b/test/cases/error_in_nested_declaration.zig @@ -0,0 +1,31 @@ +const S = struct { + b: u32, + c: i32, + a: struct { + pub fn str(_: @This(), extra: []u32) []i32 { + return @bitCast([]i32, extra); + } + }, +}; + +pub export fn entry() void { + var s: S = undefined; + _ = s.a.str(undefined); +} + +const S2 = struct { + a: [*c]anyopaque, +}; + +pub export fn entry2() void { + var s: S2 = undefined; + _ = s; +} + +// error +// backend=llvm +// target=native +// +// :17:12: error: C pointers cannot point to opaque types +// :6:29: error: cannot @bitCast to '[]i32' +// :6:29: note: use @ptrCast to cast from '[]u32' -- 2.54.0 From 0fd90749d10052c40d5a91881621af55e80135b3 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 7 Aug 2022 19:55:31 +0300 Subject: [PATCH 10/72] stage2: generate call arguments in separate blocks --- src/AstGen.zig | 52 ++++-- src/Sema.zig | 156 ++++++++++++------ src/Zir.zig | 20 +-- src/print_zir.zig | 37 +++-- test/behavior/call.zig | 15 ++ .../int_literal_passed_as_variadic_arg.zig | 11 ++ .../member_function_arg_mismatch.zig | 14 ++ 7 files changed, 209 insertions(+), 96 deletions(-) create mode 100644 test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig create mode 100644 test/cases/compile_errors/member_function_arg_mismatch.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index ead9517ef04a2723ac4686dfcea9cdc58bcd1e54..efa8690b55ce96d7f2fd116972165c8e26a3432c 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2500,7 +2500,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .closure_get, .array_base_ptr, .field_base_ptr, - .param_type, .ret_ptr, .ret_type, .@"try", @@ -8228,6 +8227,33 @@ fn callExpr( assert(callee != .none); assert(node != 0); + const call_index = @intCast(Zir.Inst.Index, astgen.instructions.len); + const call_inst = Zir.indexToRef(call_index); + try gz.astgen.instructions.append(astgen.gpa, undefined); + try gz.instructions.append(astgen.gpa, call_index); + + const scratch_top = astgen.scratch.items.len; + defer astgen.scratch.items.len = scratch_top; + + var scratch_index = scratch_top; + try astgen.scratch.resize(astgen.gpa, scratch_top + call.ast.params.len); + + for (call.ast.params) |param_node| { + var arg_block = gz.makeSubBlock(scope); + defer arg_block.unstack(); + + // `call_inst` is reused to provide the param type. + const arg_ref = try expr(&arg_block, &arg_block.base, .{ .coerced_ty = call_inst }, param_node); + _ = try arg_block.addBreak(.break_inline, call_index, arg_ref); + + const body = arg_block.instructionsSlice(); + try astgen.scratch.ensureUnusedCapacity(astgen.gpa, countBodyLenAfterFixups(astgen, body)); + appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); + + astgen.scratch.items[scratch_index] = @intCast(u32, astgen.scratch.items.len - scratch_top); + scratch_index += 1; + } + const payload_index = try addExtra(astgen, Zir.Inst.Call{ .callee = callee, .flags = .{ @@ -8235,22 +8261,16 @@ fn callExpr( .args_len = @intCast(Zir.Inst.Call.Flags.PackedArgsLen, call.ast.params.len), }, }); - var extra_index = try reserveExtra(astgen, call.ast.params.len); - - for (call.ast.params) |param_node, i| { - const param_type = try gz.add(.{ - .tag = .param_type, - .data = .{ .param_type = .{ - .callee = callee, - .param_index = @intCast(u32, i), - } }, - }); - const arg_ref = try expr(gz, scope, .{ .coerced_ty = param_type }, param_node); - astgen.extra.items[extra_index] = @enumToInt(arg_ref); - extra_index += 1; + if (call.ast.params.len != 0) { + try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]); } - - const call_inst = try gz.addPlNodePayloadIndex(.call, node, payload_index); + gz.astgen.instructions.set(call_index, .{ + .tag = .call, + .data = .{ .pl_node = .{ + .src_node = gz.nodeIndexToRelative(node), + .payload_index = payload_index, + } }, + }); return rvalue(gz, rl, call_inst, node); // TODO function call with result location } diff --git a/src/Sema.zig b/src/Sema.zig index 4995d8786633d7e6fd57edcdf78ea6beb1eb792e..68409a5d05cb3a03bddf651714cc62e51ca3b1d0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -772,7 +772,6 @@ fn analyzeBodyInner( .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false), .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false), .optional_type => try sema.zirOptionalType(block, inst), - .param_type => try sema.zirParamType(block, inst), .ptr_type => try sema.zirPtrType(block, inst), .overflow_arithmetic_ptr => try sema.zirOverflowArithmeticPtr(block, inst), .ref => try sema.zirRef(block, inst), @@ -4441,43 +4440,6 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v return sema.storePtr2(block, src, ptr, src, operand, src, if (is_ret) .ret_ptr else .store); } -fn zirParamType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const callee_src = sema.src; - - const inst_data = sema.code.instructions.items(.data)[inst].param_type; - const callee = try sema.resolveInst(inst_data.callee); - const callee_ty = sema.typeOf(callee); - var param_index = inst_data.param_index; - - const fn_ty = if (callee_ty.tag() == .bound_fn) fn_ty: { - const bound_fn_val = try sema.resolveConstValue(block, .unneeded, callee, undefined); - const bound_fn = bound_fn_val.castTag(.bound_fn).?.data; - const fn_ty = sema.typeOf(bound_fn.func_inst); - param_index += 1; - break :fn_ty fn_ty; - } else callee_ty; - - const fn_info = if (fn_ty.zigTypeTag() == .Pointer) - fn_ty.childType().fnInfo() - else - fn_ty.fnInfo(); - - if (param_index >= fn_info.param_types.len) { - if (fn_info.is_var_args) { - return sema.addType(Type.initTag(.var_args_param)); - } - // TODO implement begin_call/end_call Zir instructions and check - // argument count before casting arguments to parameter types. - return sema.fail(block, callee_src, "wrong number of arguments", .{}); - } - - if (fn_info.param_types[param_index].tag() == .generic_poison) { - return sema.addType(Type.initTag(.var_args_param)); - } - - return sema.addType(fn_info.param_types[param_index]); -} - fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -5459,13 +5421,14 @@ fn zirCall( const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; const call_src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index); - const args = sema.code.refSlice(extra.end, extra.data.flags.args_len); + const args_len = extra.data.flags.args_len; const modifier = @intToEnum(std.builtin.CallOptions.Modifier, extra.data.flags.packed_modifier); const ensure_result_used = extra.data.flags.ensure_result_used; var func = try sema.resolveInst(extra.data.callee); var resolved_args: []Air.Inst.Ref = undefined; + var arg_index: u32 = 0; const func_type = sema.typeOf(func); @@ -5476,16 +5439,99 @@ fn zirCall( const bound_func = try sema.resolveValue(block, .unneeded, func, undefined); const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data; func = bound_data.func_inst; - resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len + 1); - resolved_args[0] = bound_data.arg0_inst; - for (args) |zir_arg, i| { - resolved_args[i + 1] = try sema.resolveInst(zir_arg); - } + resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len + 1); + resolved_args[arg_index] = bound_data.arg0_inst; + arg_index += 1; } else { - resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len); - for (args) |zir_arg, i| { - resolved_args[i] = try sema.resolveInst(zir_arg); + resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len); + } + const total_args = args_len + @boolToInt(bound_arg_src != null); + + const callee_ty = sema.typeOf(func); + const func_ty = func_ty: { + switch (callee_ty.zigTypeTag()) { + .Fn => break :func_ty callee_ty, + .Pointer => { + const ptr_info = callee_ty.ptrInfo().data; + if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Fn) { + break :func_ty ptr_info.pointee_type; + } + }, + else => {}, } + return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(sema.mod)}); + }; + const func_ty_info = func_ty.fnInfo(); + + const fn_params_len = func_ty_info.param_types.len; + if (func_ty_info.is_var_args) { + assert(func_ty_info.cc == .C); + if (total_args < fn_params_len) { + // TODO add error note: declared here + if (bound_arg_src != null) { + return sema.fail( + block, + call_src, + "member function expected at least {d} argument(s), found {d}", + .{ fn_params_len - 1, args_len }, + ); + } + return sema.fail( + block, + func_src, + "expected at least {d} argument(s), found {d}", + .{ fn_params_len, args_len }, + ); + } + } else if (fn_params_len != total_args) { + // TODO add error note: declared here + if (bound_arg_src != null) { + return sema.fail( + block, + call_src, + "member function expected {d} argument(s), found {d}", + .{ fn_params_len - 1, args_len }, + ); + } + return sema.fail( + block, + call_src, + "expected {d} argument(s), found {d}", + .{ fn_params_len, args_len }, + ); + } + + const args_body = sema.code.extra[extra.end..]; + + const parent_comptime = block.is_comptime; + // `extra_index` and `arg_index` are separate since the bound function is passed as the first argument. + var extra_index: usize = 0; + var arg_start: u32 = args_len; + while (extra_index < args_len) : ({ + extra_index += 1; + arg_index += 1; + }) { + const arg_end = sema.code.extra[extra.end + extra_index]; + defer arg_start = arg_end; + + const param_ty = if (arg_index >= fn_params_len or + func_ty_info.param_types[arg_index].tag() == .generic_poison) + Type.initTag(.var_args_param) + else + func_ty_info.param_types[arg_index]; + + const old_comptime = block.is_comptime; + defer block.is_comptime = old_comptime; + // Generate args to comptime params in comptime block. + block.is_comptime = parent_comptime; + if (arg_index < fn_params_len and func_ty_info.comptime_params[arg_index]) { + block.is_comptime = true; + } + + const param_ty_inst = try sema.addType(param_ty); + try sema.inst_map.put(sema.gpa, inst, param_ty_inst); + + resolved_args[arg_index] = try sema.resolveBody(block, args_body[arg_start..arg_end], inst); } return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src); @@ -5964,7 +6010,18 @@ fn analyzeCall( else => |e| return e, }; } else { - args[i] = uncasted_arg; + args[i] = sema.coerceVarArgParam(block, uncasted_arg, .unneeded) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + _ = try sema.coerceVarArgParam( + block, + uncasted_arg, + Module.argSrc(call_src.node_offset.x, sema.gpa, decl, i, bound_arg_src), + ); + return error.AnalysisFail; + }, + else => |e| return e, + }; } } @@ -23534,7 +23591,10 @@ fn coerceVarArgParam( inst_src: LazySrcLoc, ) !Air.Inst.Ref { const inst_ty = sema.typeOf(inst); + if (block.is_typeof) return inst; + switch (inst_ty.zigTypeTag()) { + // TODO consider casting to c_int/f64 if they fit .ComptimeInt, .ComptimeFloat => return sema.fail(block, inst_src, "integer and float literals in var args function must be casted", .{}), else => {}, } diff --git a/src/Zir.zig b/src/Zir.zig index c5618a58c0b0d275bb6900b4e4e255b7b156e1a1..c62e6d02bb550d6d3fa9fe522214c9a8366b3f35 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -490,14 +490,6 @@ pub const Inst = struct { /// Merge two error sets into one, `E1 || E2`. /// Uses the `pl_node` field with payload `Bin`. merge_error_sets, - /// Given a reference to a function and a parameter index, returns the - /// type of the parameter. The only usage of this instruction is for the - /// result location of parameters of function calls. In the case of a function's - /// parameter type being `anytype`, it is the type coercion's job to detect this - /// scenario and skip the coercion, so that semantic analysis of this instruction - /// is not in a position where it must create an invalid type. - /// Uses the `param_type` union field. - param_type, /// Turns an R-Value into a const L-Value. In other words, it takes a value, /// stores it in a memory location, and returns a const pointer to it. If the value /// is `comptime`, the memory location is global static constant data. Otherwise, @@ -1095,7 +1087,6 @@ pub const Inst = struct { .mul, .mulwrap, .mul_sat, - .param_type, .ref, .shl, .shl_sat, @@ -1397,7 +1388,6 @@ pub const Inst = struct { .mul, .mulwrap, .mul_sat, - .param_type, .ref, .shl, .shl_sat, @@ -1569,7 +1559,6 @@ pub const Inst = struct { .mulwrap = .pl_node, .mul_sat = .pl_node, - .param_type = .param_type, .param = .pl_tok, .param_comptime = .pl_tok, .param_anytype = .str_tok, @@ -2540,10 +2529,6 @@ pub const Inst = struct { /// Points to a `Block`. payload_index: u32, }, - param_type: struct { - callee: Ref, - param_index: u32, - }, @"unreachable": struct { /// Offset from Decl AST node index. /// `Tag` determines which kind of AST node this points to. @@ -2614,7 +2599,6 @@ pub const Inst = struct { ptr_type, int_type, bool_br, - param_type, @"unreachable", @"break", switch_capture, @@ -2794,7 +2778,9 @@ pub const Inst = struct { }; /// Stored inside extra, with trailing arguments according to `args_len`. - /// Each argument is a `Ref`. + /// Implicit 0. arg_0_start: u32, // always same as `args_len` + /// 1. arg_end: u32, // for each `args_len` + /// arg_N_start is the same as arg_N-1_end pub const Call = struct { // Note: Flags *must* come first so that unusedResultExpr // can find it when it goes to modify them. diff --git a/src/print_zir.zig b/src/print_zir.zig index 6503e029fd134854c977b256411d24302e560b7f..2b70799c367b798db5f77431ee7819863867319b 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -246,7 +246,6 @@ const Writer = struct { .validate_array_init_ty => try self.writeValidateArrayInitTy(stream, inst), .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst), - .param_type => try self.writeParamType(stream, inst), .ptr_type => try self.writePtrType(stream, inst), .int => try self.writeInt(stream, inst), .int_big => try self.writeIntBig(stream, inst), @@ -605,16 +604,6 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } - fn writeParamType( - self: *Writer, - stream: anytype, - inst: Zir.Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].param_type; - try self.writeInstRef(stream, inst_data.callee); - try stream.print(", {d})", .{inst_data.param_index}); - } - fn writePtrType( self: *Writer, stream: anytype, @@ -1158,7 +1147,8 @@ const Writer = struct { fn writeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.Call, inst_data.payload_index); - const args = self.code.refSlice(extra.end, extra.data.flags.args_len); + const args_len = extra.data.flags.args_len; + const body = self.code.extra[extra.end..]; if (extra.data.flags.ensure_result_used) { try stream.writeAll("nodiscard "); @@ -1166,10 +1156,27 @@ const Writer = struct { try stream.print(".{s}, ", .{@tagName(@intToEnum(std.builtin.CallOptions.Modifier, extra.data.flags.packed_modifier))}); try self.writeInstRef(stream, extra.data.callee); try stream.writeAll(", ["); - for (args) |arg, i| { - if (i != 0) try stream.writeAll(", "); - try self.writeInstRef(stream, arg); + + self.indent += 2; + if (args_len != 0) { + try stream.writeAll("\n"); } + var i: usize = 0; + var arg_start: u32 = args_len; + while (i < args_len) : (i += 1) { + try stream.writeByteNTimes(' ', self.indent); + const arg_end = self.code.extra[extra.end + i]; + defer arg_start = arg_end; + const arg_body = body[arg_start..arg_end]; + try self.writeBracedBody(stream, arg_body); + + try stream.writeAll(",\n"); + } + self.indent -= 2; + if (args_len != 0) { + try stream.writeByteNTimes(' ', self.indent); + } + try stream.writeAll("]) "); try self.writeSrc(stream, inst_data.src()); } diff --git a/test/behavior/call.zig b/test/behavior/call.zig index 0297582234d752230d5de2bdd4e811030e43846c..eafd2ef4e9168fd379c3b430c21f8e63b5642159 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -246,3 +246,18 @@ test "function call with 40 arguments" { }; try S.doTheTest(39); } + +test "arguments to comptime parameters generated in comptime blocks" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + + const S = struct { + fn fortyTwo() i32 { + return 42; + } + + fn foo(comptime x: i32) void { + if (x != 42) @compileError("bad"); + } + }; + S.foo(S.fortyTwo()); +} diff --git a/test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig b/test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig new file mode 100644 index 0000000000000000000000000000000000000000..be9ffaa884c8741db68089a1253cd0cb562ff2f8 --- /dev/null +++ b/test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig @@ -0,0 +1,11 @@ +extern fn printf([*:0]const u8, ...) c_int; + +pub export fn entry() void { + _ = printf("%d %d %d %d\n", 1, 2, 3, 4); +} + +// error +// backend=stage2 +// target=native +// +// :4:33: error: integer and float literals in var args function must be casted diff --git a/test/cases/compile_errors/member_function_arg_mismatch.zig b/test/cases/compile_errors/member_function_arg_mismatch.zig new file mode 100644 index 0000000000000000000000000000000000000000..aaefac58b4f0fe5b724584f963987cc2f2c42a88 --- /dev/null +++ b/test/cases/compile_errors/member_function_arg_mismatch.zig @@ -0,0 +1,14 @@ +const S = struct { + a: u32, + fn foo(_: *S, _: u32, _: bool) void {} +}; +pub export fn entry() void { + var s: S = undefined; + s.foo(true); +} + +// error +// backend=stage2 +// target=native +// +// :7:10: error: member function expected 2 argument(s), found 1 -- 2.54.0 From 19af4b5cadaad24fd886578da5b5840dd3499d2d Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 7 Aug 2022 23:01:12 +0300 Subject: [PATCH 11/72] std: add workaround for stage2 bug --- lib/std/crypto/25519/ed25519.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig index 2a7671863eb91fe083b4de6f872ecea7c3f21b19..7066b1a154198c779ac2698702bd7c2c520042ef 100644 --- a/lib/std/crypto/25519/ed25519.zig +++ b/lib/std/crypto/25519/ed25519.zig @@ -355,7 +355,9 @@ test "ed25519 batch verification" { try Ed25519.verifyBatch(2, signature_batch); signature_batch[1].sig = sig1; - try std.testing.expectError(error.SignatureVerificationFailed, Ed25519.verifyBatch(signature_batch.len, signature_batch)); + // TODO https://github.com/ziglang/zig/issues/12240 + const sig_len = signature_batch.len; + try std.testing.expectError(error.SignatureVerificationFailed, Ed25519.verifyBatch(sig_len, signature_batch)); } } -- 2.54.0 From 0778490283a8129755e411877453a173759ab4f4 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 8 Aug 2022 22:28:48 +0300 Subject: [PATCH 12/72] AutoDoc: update to Zir call inst changes --- src/Autodoc.zig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 9e3874979d03874a61e14d09515ece43cdf135c3..3fcc28d74274540579abd5c3dc163bf920393742 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -2078,14 +2078,19 @@ fn walkInstruction( const args_len = extra.data.flags.args_len; var args = try self.arena.alloc(DocData.Expr, args_len); - const arg_refs = file.zir.refSlice(extra.end, args_len); - for (arg_refs) |ref, idx| { + const body = file.zir.extra[extra.end..]; + + var i: usize = 0; + while (i < args_len) : (i += 1) { + const arg_end = file.zir.extra[extra.end + i]; + const break_index = body[arg_end - 1]; + const ref = data[break_index].@"break".operand; // TODO: consider toggling need_type to true if we ever want // to show discrepancies between the types of provided // arguments and the types declared in the function // signature for its parameters. const wr = try self.walkRef(file, parent_scope, ref, false); - args[idx] = wr.expr; + args[i] = wr.expr; } const cte_slot_index = self.comptime_exprs.items.len; -- 2.54.0 From b757a96d5c885442c38e217006d75c85a28daf09 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 9 Aug 2022 23:37:26 +0300 Subject: [PATCH 13/72] Sema: add declared here note to function call errors --- src/Sema.zig | 92 +++++++++++-------- ...function_with_naked_calling_convention.zig | 11 +++ .../member_function_arg_mismatch.zig | 3 +- ...function_with_naked_calling_convention.zig | 11 --- ...number_of_arguments_for_method_fn_call.zig | 14 --- .../wrong_number_of_arguments.zig | 3 +- ...number_of_arguments_for_method_fn_call.zig | 15 +++ 7 files changed, 83 insertions(+), 66 deletions(-) create mode 100644 test/cases/compile_errors/calling_function_with_naked_calling_convention.zig delete mode 100644 test/cases/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig delete mode 100644 test/cases/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig create mode 100644 test/cases/compile_errors/wrong_number_of_arguments_for_method_fn_call.zig diff --git a/src/Sema.zig b/src/Sema.zig index 68409a5d05cb3a03bddf651714cc62e51ca3b1d0..acdce0e9b09739e52f83b7bf3eb62f6ed3406ef3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5409,6 +5409,19 @@ fn lookupInNamespace( return null; } +fn funcDeclSrc(sema: *Sema, block: *Block, src: LazySrcLoc, func_inst: Air.Inst.Ref) !?Module.SrcLoc { + const func_val = (try sema.resolveMaybeUndefVal(block, src, func_inst)) orelse return null; + if (func_val.isUndef()) return null; + const owner_decl_index = switch (func_val.tag()) { + .extern_fn => func_val.castTag(.extern_fn).?.data.owner_decl, + .function => func_val.castTag(.function).?.data.owner_decl, + .decl_ref => sema.mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data.owner_decl, + else => return null, + }; + const owner_decl = sema.mod.declPtr(owner_decl_index); + return owner_decl.srcLoc(); +} + fn zirCall( sema: *Sema, block: *Block, @@ -5464,41 +5477,35 @@ fn zirCall( const func_ty_info = func_ty.fnInfo(); const fn_params_len = func_ty_info.param_types.len; - if (func_ty_info.is_var_args) { - assert(func_ty_info.cc == .C); - if (total_args < fn_params_len) { - // TODO add error note: declared here - if (bound_arg_src != null) { - return sema.fail( - block, - call_src, - "member function expected at least {d} argument(s), found {d}", - .{ fn_params_len - 1, args_len }, - ); - } - return sema.fail( + check_args: { + if (func_ty_info.is_var_args) { + assert(func_ty_info.cc == .C); + if (total_args >= fn_params_len) break :check_args; + } else if (fn_params_len == total_args) { + break :check_args; + } + + const decl_src = try sema.funcDeclSrc(block, func_src, func); + const member_str = if (bound_arg_src != null) "member function " else ""; + const variadic_str = if (func_ty_info.is_var_args) "at least " else ""; + const msg = msg: { + const msg = try sema.errMsg( block, func_src, - "expected at least {d} argument(s), found {d}", - .{ fn_params_len, args_len }, + "{s}expected {s}{d} argument(s), found {d}", + .{ + member_str, + variadic_str, + fn_params_len - @boolToInt(bound_arg_src != null), + args_len, + }, ); - } - } else if (fn_params_len != total_args) { - // TODO add error note: declared here - if (bound_arg_src != null) { - return sema.fail( - block, - call_src, - "member function expected {d} argument(s), found {d}", - .{ fn_params_len - 1, args_len }, - ); - } - return sema.fail( - block, - call_src, - "expected {d} argument(s), found {d}", - .{ fn_params_len, args_len }, - ); + errdefer msg.destroy(sema.gpa); + + if (decl_src) |some| try sema.mod.errNoteNonLazy(some, msg, "function declared here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); } const args_body = sema.code.extra[extra.end..]; @@ -5625,13 +5632,20 @@ fn analyzeCall( const func_ty_info = func_ty.fnInfo(); const cc = func_ty_info.cc; if (cc == .Naked) { - // TODO add error note: declared here - return sema.fail( - block, - func_src, - "unable to call function with naked calling convention", - .{}, - ); + const decl_src = try sema.funcDeclSrc(block, func_src, func); + const msg = msg: { + const msg = try sema.errMsg( + block, + func_src, + "unable to call function with naked calling convention", + .{}, + ); + errdefer msg.destroy(sema.gpa); + + if (decl_src) |some| try sema.mod.errNoteNonLazy(some, msg, "function declared here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); } const fn_params_len = func_ty_info.param_types.len; if (func_ty_info.is_var_args) { diff --git a/test/cases/compile_errors/calling_function_with_naked_calling_convention.zig b/test/cases/compile_errors/calling_function_with_naked_calling_convention.zig new file mode 100644 index 0000000000000000000000000000000000000000..54bf58542542ec1f43e168f53dbb32c5cf5ca738 --- /dev/null +++ b/test/cases/compile_errors/calling_function_with_naked_calling_convention.zig @@ -0,0 +1,11 @@ +export fn entry() void { + foo(); +} +fn foo() callconv(.Naked) void { } + +// error +// backend=llvm +// target=native +// +// :2:5: error: unable to call function with naked calling convention +// :4:1: note: function declared here diff --git a/test/cases/compile_errors/member_function_arg_mismatch.zig b/test/cases/compile_errors/member_function_arg_mismatch.zig index aaefac58b4f0fe5b724584f963987cc2f2c42a88..b739be95440be4e74235e96989b3efba52acf1a6 100644 --- a/test/cases/compile_errors/member_function_arg_mismatch.zig +++ b/test/cases/compile_errors/member_function_arg_mismatch.zig @@ -11,4 +11,5 @@ pub export fn entry() void { // backend=stage2 // target=native // -// :7:10: error: member function expected 2 argument(s), found 1 +// :7:6: error: member function expected 2 argument(s), found 1 +// :3:5: note: function declared here diff --git a/test/cases/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig b/test/cases/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig deleted file mode 100644 index 401f84e687da591d8ea817f5f1c27f4bfd02a563..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig +++ /dev/null @@ -1,11 +0,0 @@ -export fn entry() void { - foo(); -} -fn foo() callconv(.Naked) void { } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: unable to call function with naked calling convention -// tmp.zig:4:1: note: declared here diff --git a/test/cases/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig b/test/cases/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig deleted file mode 100644 index 7371223863c0f43a33a8bd1ae068053cbd691e32..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig +++ /dev/null @@ -1,14 +0,0 @@ -const Foo = struct { - fn method(self: *const Foo, a: i32) void {_ = self; _ = a;} -}; -fn f(foo: *const Foo) void { - - foo.method(1, 2); -} -export fn entry() usize { return @sizeOf(@TypeOf(f)); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:6:15: error: expected 2 argument(s), found 3 diff --git a/test/cases/compile_errors/wrong_number_of_arguments.zig b/test/cases/compile_errors/wrong_number_of_arguments.zig index 64eb11650a9c0762021a1b834c4fd2e61ab53078..05d761de18ca5e4a2a42a52404c361d3fc202d51 100644 --- a/test/cases/compile_errors/wrong_number_of_arguments.zig +++ b/test/cases/compile_errors/wrong_number_of_arguments.zig @@ -7,4 +7,5 @@ fn c(d: i32, e: i32, f: i32) void { _ = d; _ = e; _ = f; } // backend=stage2 // target=native // -// :2:6: error: expected 3 argument(s), found 1 +// :2:5: error: expected 3 argument(s), found 1 +// :4:1: note: function declared here diff --git a/test/cases/compile_errors/wrong_number_of_arguments_for_method_fn_call.zig b/test/cases/compile_errors/wrong_number_of_arguments_for_method_fn_call.zig new file mode 100644 index 0000000000000000000000000000000000000000..da6a7be4fa453dd682f372cd510f2881b5283cf8 --- /dev/null +++ b/test/cases/compile_errors/wrong_number_of_arguments_for_method_fn_call.zig @@ -0,0 +1,15 @@ +const Foo = struct { + fn method(self: *const Foo, a: i32) void {_ = self; _ = a;} +}; +fn f(foo: *const Foo) void { + + foo.method(1, 2); +} +export fn entry() usize { return @sizeOf(@TypeOf(&f)); } + +// error +// backend=stage2 +// target=native +// +// :6:8: error: member function expected 1 argument(s), found 2 +// :2:5: note: function declared here -- 2.54.0 From bb1c3e8b7e2be201221e14719d2d39e6298cc66c Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 10 Aug 2022 13:21:47 +0200 Subject: [PATCH 14/72] stage2: Handle lazy values for the % operator --- src/Sema.zig | 59 ++++++++++++++++++++++++++++++++++++++++-- src/value.zig | 38 --------------------------- test/behavior/math.zig | 15 +++++++++++ 3 files changed, 72 insertions(+), 40 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index acdce0e9b09739e52f83b7bf3eb62f6ed3406ef3..6d95b46c7c6ac949fc42eb08429eae698de89881 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -78,6 +78,7 @@ post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{}, err: ?*Module.ErrorMsg = null, const std = @import("std"); +const math = std.math; const mem = std.mem; const Allocator = std.mem.Allocator; const assert = std.debug.assert; @@ -11824,7 +11825,7 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. return sema.failWithDivideByZero(block, rhs_src); } if (maybe_lhs_val) |lhs_val| { - const rem_result = try lhs_val.intRem(rhs_val, resolved_type, sema.arena, target); + const rem_result = try sema.intRem(block, resolved_type, lhs_val, lhs_src, rhs_val, rhs_src); // If this answer could possibly be different by doing `intMod`, // we must emit a compile error. Otherwise, it's OK. if ((try rhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) != (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) and @@ -11886,6 +11887,60 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. return block.addBinOp(air_tag, casted_lhs, casted_rhs); } +fn intRem( + sema: *Sema, + block: *Block, + ty: Type, + lhs: Value, + lhs_src: LazySrcLoc, + rhs: Value, + rhs_src: LazySrcLoc, +) CompileError!Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try sema.arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try sema.intRemScalar(block, lhs.indexVectorlike(i), lhs_src, rhs.indexVectorlike(i), rhs_src); + } + return Value.Tag.aggregate.create(sema.arena, result_data); + } + return sema.intRemScalar(block, lhs, lhs_src, rhs, rhs_src); +} + +fn intRemScalar( + sema: *Sema, + block: *Block, + lhs: Value, + lhs_src: LazySrcLoc, + rhs: Value, + rhs_src: LazySrcLoc, +) CompileError!Value { + const target = sema.mod.getTarget(); + // TODO is this a performance issue? maybe we should try the operation without + // resorting to BigInt first. + var lhs_space: Value.BigIntSpace = undefined; + var rhs_space: Value.BigIntSpace = undefined; + const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema.kit(block, lhs_src)); + const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema.kit(block, rhs_src)); + const limbs_q = try sema.arena.alloc( + math.big.Limb, + lhs_bigint.limbs.len, + ); + const limbs_r = try sema.arena.alloc( + math.big.Limb, + // TODO: consider reworking Sema to re-use Values rather than + // always producing new Value objects. + rhs_bigint.limbs.len, + ); + const limbs_buffer = try sema.arena.alloc( + math.big.Limb, + math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len), + ); + var result_q = math.big.int.Mutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; + var result_r = math.big.int.Mutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; + result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer); + return Value.fromBigInt(sema.arena, result_r.toConst()); +} + fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; @@ -12050,7 +12105,7 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins if (maybe_lhs_val) |lhs_val| { return sema.addConstant( resolved_type, - try lhs_val.intRem(rhs_val, resolved_type, sema.arena, target), + try sema.intRem(block, resolved_type, lhs_val, lhs_src, rhs_val, rhs_src), ); } break :rs lhs_src; diff --git a/src/value.zig b/src/value.zig index 3994040ba656d90097f4d7c5af17580137b6660f..f156651eaac605546291e5edb6b3bdbf04bff648 100644 --- a/src/value.zig +++ b/src/value.zig @@ -3472,44 +3472,6 @@ pub const Value = extern union { return fromBigInt(allocator, result_q.toConst()); } - pub fn intRem(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { - if (ty.zigTypeTag() == .Vector) { - const result_data = try allocator.alloc(Value, ty.vectorLen()); - for (result_data) |*scalar, i| { - scalar.* = try intRemScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); - } - return Value.Tag.aggregate.create(allocator, result_data); - } - return intRemScalar(lhs, rhs, allocator, target); - } - - pub fn intRemScalar(lhs: Value, rhs: Value, allocator: Allocator, target: Target) !Value { - // TODO is this a performance issue? maybe we should try the operation without - // resorting to BigInt first. - var lhs_space: Value.BigIntSpace = undefined; - var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space, target); - const rhs_bigint = rhs.toBigInt(&rhs_space, target); - const limbs_q = try allocator.alloc( - std.math.big.Limb, - lhs_bigint.limbs.len, - ); - const limbs_r = try allocator.alloc( - std.math.big.Limb, - // TODO: consider reworking Sema to re-use Values rather than - // always producing new Value objects. - rhs_bigint.limbs.len, - ); - const limbs_buffer = try allocator.alloc( - std.math.big.Limb, - std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len), - ); - var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; - var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; - result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer); - return fromBigInt(allocator, result_r.toConst()); - } - pub fn intMod(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index c8d0becbd6552535e689b4c12e85af920fb6fbc5..ce476fea04e583379978fa6fe93ad9ea520114ed 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1721,3 +1721,18 @@ fn testAbsFloat() !void { fn testAbsFloatOne(in: f32, out: f32) !void { try expect(@fabs(@as(f32, in)) == @as(f32, out)); } + +test "mod lazy values" { + { + const X = struct { x: u32 }; + const x = @sizeOf(X); + const y = 1 % x; + _ = y; + } + { + const X = struct { x: u32 }; + const x = @sizeOf(X); + const y = x % 1; + _ = y; + } +} -- 2.54.0 From 0d32b73078aa4579187f7d5c67343a6036eed277 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 8 Aug 2022 18:39:14 +0200 Subject: [PATCH 15/72] stage2: Implement explicit backing integers for packed structs Now the backing integer of a packed struct type may be explicitly specified with e.g. `packed struct(u32) { ... }`. --- lib/std/builtin.zig | 2 + lib/std/zig/Ast.zig | 2 +- lib/std/zig/parse.zig | 10 +- lib/std/zig/parser_test.zig | 7 + src/AstGen.zig | 56 ++++- src/Autodoc.zig | 11 + src/Module.zig | 21 +- src/Sema.zig | 207 +++++++++++++++++- src/Zir.zig | 16 +- src/codegen/llvm.zig | 15 +- src/print_zir.zig | 25 ++- src/stage1/all_types.hpp | 1 + src/stage1/analyze.cpp | 6 + src/stage1/ir.cpp | 40 +++- src/stage1/parser.cpp | 11 +- src/type.zig | 23 +- test/behavior.zig | 1 + .../packed_struct_explicit_backing_int.zig | 53 +++++ test/behavior/type_info.zig | 4 +- .../packed_struct_backing_int_wrong.zig | 55 +++++ 20 files changed, 493 insertions(+), 73 deletions(-) create mode 100644 test/behavior/packed_struct_explicit_backing_int.zig create mode 100644 test/cases/compile_errors/packed_struct_backing_int_wrong.zig diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index ef716c6972c4a755a0e077107d76191ff09d2850..2c2bc92c96022ec7b2c150d3bcdd77b89cf3740a 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -294,6 +294,8 @@ pub const Type = union(enum) { /// therefore must be kept in sync with the compiler implementation. pub const Struct = struct { layout: ContainerLayout, + /// Only valid if layout is .Packed + backing_integer: ?type = null, fields: []const StructField, decls: []const Declaration, is_tuple: bool, diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index 9bffcb3df202376c0d663aec5233c1829c4c14d5..016cefb2559256f0d0fd69c4ee4552b1e6358d98 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -2967,7 +2967,7 @@ pub const Node = struct { /// Same as ContainerDeclTwo except there is known to be a trailing comma /// or semicolon before the rbrace. container_decl_two_trailing, - /// `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`. + /// `struct(lhs)` / `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`. container_decl_arg, /// Same as container_decl_arg but there is known to be a trailing /// comma or semicolon before the rbrace. diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 2a7d2623efb28514ab88a69693be88cda5db5c7d..a03764a91cc6b53e3879bd7a87c30c5774ea319d 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -3356,16 +3356,18 @@ const Parser = struct { } /// Caller must have already verified the first token. + /// ContainerDeclAuto <- ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE + /// /// ContainerDeclType - /// <- KEYWORD_struct + /// <- KEYWORD_struct (LPAREN Expr RPAREN)? + /// / KEYWORD_opaque /// / KEYWORD_enum (LPAREN Expr RPAREN)? /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? - /// / KEYWORD_opaque fn parseContainerDeclAuto(p: *Parser) !Node.Index { const main_token = p.nextToken(); const arg_expr = switch (p.token_tags[main_token]) { - .keyword_struct, .keyword_opaque => null_node, - .keyword_enum => blk: { + .keyword_opaque => null_node, + .keyword_struct, .keyword_enum => blk: { if (p.eatToken(.l_paren)) |_| { const expr = try p.expectExpr(); _ = try p.expectToken(.r_paren); diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index a74d53f21c0d444c862c54785dfa3879b6a366cc..bee9375b5ae188a089e03de1ebbf7368f7fe52da 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -3064,6 +3064,13 @@ test "zig fmt: struct declaration" { \\ c: u8, \\}; \\ + \\const Ps = packed struct(u32) { + \\ a: u1, + \\ b: u2, + \\ + \\ c: u29, + \\}; + \\ \\const Es = extern struct { \\ a: u8, \\ b: u8, diff --git a/src/AstGen.zig b/src/AstGen.zig index efa8690b55ce96d7f2fd116972165c8e26a3432c..af77cdacc466df99613f90e561aa4fd52e2fa60d 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -152,6 +152,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { 0, tree.containerDeclRoot(), .Auto, + 0, )) |struct_decl_ref| { assert(refToIndex(struct_decl_ref).? == 0); } else |err| switch (err) { @@ -4223,15 +4224,18 @@ fn structDeclInner( node: Ast.Node.Index, container_decl: Ast.full.ContainerDecl, layout: std.builtin.Type.ContainerLayout, + backing_int_node: Ast.Node.Index, ) InnerError!Zir.Inst.Ref { const decl_inst = try gz.reserveInstructionIndex(); - if (container_decl.ast.members.len == 0) { + if (container_decl.ast.members.len == 0 and backing_int_node == 0) { try gz.setStruct(decl_inst, .{ .src_node = node, .layout = layout, .fields_len = 0, .decls_len = 0, + .backing_int_ref = .none, + .backing_int_body_len = 0, .known_non_opv = false, .known_comptime_only = false, }); @@ -4266,6 +4270,35 @@ fn structDeclInner( }; defer block_scope.unstack(); + const scratch_top = astgen.scratch.items.len; + defer astgen.scratch.items.len = scratch_top; + + var backing_int_body_len: usize = 0; + const backing_int_ref: Zir.Inst.Ref = blk: { + if (backing_int_node != 0) { + if (layout != .Packed) { + return astgen.failNode(backing_int_node, "non-packed struct does not support backing integer type", .{}); + } else { + const backing_int_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node); + if (!block_scope.isEmpty()) { + if (!block_scope.endsWithNoReturn()) { + _ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref); + } + + const body = block_scope.instructionsSlice(); + const old_scratch_len = astgen.scratch.items.len; + try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); + appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); + backing_int_body_len = astgen.scratch.items.len - old_scratch_len; + block_scope.instructions.items.len = block_scope.instructions_top; + } + break :blk backing_int_ref; + } + } else { + break :blk .none; + } + }; + const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members); const field_count = @intCast(u32, container_decl.ast.members.len - decl_count); @@ -4378,6 +4411,8 @@ fn structDeclInner( .layout = layout, .fields_len = field_count, .decls_len = decl_count, + .backing_int_ref = backing_int_ref, + .backing_int_body_len = @intCast(u32, backing_int_body_len), .known_non_opv = known_non_opv, .known_comptime_only = known_comptime_only, }); @@ -4386,7 +4421,9 @@ fn structDeclInner( const decls_slice = wip_members.declsSlice(); const fields_slice = wip_members.fieldsSlice(); const bodies_slice = astgen.scratch.items[bodies_start..]; - try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + fields_slice.len + bodies_slice.len); + try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len + + decls_slice.len + fields_slice.len + bodies_slice.len); + astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]); astgen.extra.appendSliceAssumeCapacity(decls_slice); astgen.extra.appendSliceAssumeCapacity(fields_slice); astgen.extra.appendSliceAssumeCapacity(bodies_slice); @@ -4582,9 +4619,7 @@ fn containerDecl( else => unreachable, } else std.builtin.Type.ContainerLayout.Auto; - assert(container_decl.ast.arg == 0); - - const result = try structDeclInner(gz, scope, node, container_decl, layout); + const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg); return rvalue(gz, rl, result, node); }, .keyword_union => { @@ -11254,6 +11289,8 @@ const GenZir = struct { src_node: Ast.Node.Index, fields_len: u32, decls_len: u32, + backing_int_ref: Zir.Inst.Ref, + backing_int_body_len: u32, layout: std.builtin.Type.ContainerLayout, known_non_opv: bool, known_comptime_only: bool, @@ -11261,7 +11298,7 @@ const GenZir = struct { const astgen = gz.astgen; const gpa = astgen.gpa; - try astgen.extra.ensureUnusedCapacity(gpa, 4); + try astgen.extra.ensureUnusedCapacity(gpa, 6); const payload_index = @intCast(u32, astgen.extra.items.len); if (args.src_node != 0) { @@ -11274,6 +11311,12 @@ const GenZir = struct { if (args.decls_len != 0) { astgen.extra.appendAssumeCapacity(args.decls_len); } + if (args.backing_int_ref != .none) { + astgen.extra.appendAssumeCapacity(args.backing_int_body_len); + if (args.backing_int_body_len == 0) { + astgen.extra.appendAssumeCapacity(@enumToInt(args.backing_int_ref)); + } + } astgen.instructions.set(inst, .{ .tag = .extended, .data = .{ .extended = .{ @@ -11282,6 +11325,7 @@ const GenZir = struct { .has_src_node = args.src_node != 0, .has_fields_len = args.fields_len != 0, .has_decls_len = args.decls_len != 0, + .has_backing_int = args.backing_int_ref != .none, .known_non_opv = args.known_non_opv, .known_comptime_only = args.known_comptime_only, .name_strategy = gz.anon_name_strategy, diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 3fcc28d74274540579abd5c3dc163bf920393742..ef2579a8e4182be3e1132749b662d85cec31ceec 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -2536,6 +2536,17 @@ fn walkInstruction( break :blk decls_len; } else 0; + // TODO: Expose explicit backing integer types in some way. + if (small.has_backing_int) { + const backing_int_body_len = file.zir.extra[extra_index]; + extra_index += 1; // backing_int_body_len + if (backing_int_body_len == 0) { + extra_index += 1; // backing_int_ref + } else { + extra_index += backing_int_body_len; // backing_int_body_inst + } + } + var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; diff --git a/src/Module.zig b/src/Module.zig index 8b195eff2de02674457ea2d786f0fdc9fc0da264..7e877a2f4ad6394442565ceffacf4824b0c7a130 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -895,6 +895,11 @@ pub const Struct = struct { zir_index: Zir.Inst.Index, layout: std.builtin.Type.ContainerLayout, + /// If the layout is not packed, this is the noreturn type. + /// If the layout is packed, this is the backing integer type of the packed struct. + /// Whether zig chooses this type or the user specifies it, it is stored here. + /// This will be set to the noreturn type until status is `have_layout`. + backing_int_ty: Type = Type.initTag(.noreturn), status: enum { none, field_types_wip, @@ -1025,7 +1030,7 @@ pub const Struct = struct { pub fn packedFieldBitOffset(s: Struct, target: Target, index: usize) u16 { assert(s.layout == .Packed); - assert(s.haveFieldTypes()); + assert(s.haveLayout()); var bit_sum: u64 = 0; for (s.fields.values()) |field, i| { if (i == index) { @@ -1033,19 +1038,7 @@ pub const Struct = struct { } bit_sum += field.ty.bitSize(target); } - return @intCast(u16, bit_sum); - } - - pub fn packedIntegerBits(s: Struct, target: Target) u16 { - return s.packedFieldBitOffset(target, s.fields.count()); - } - - pub fn packedIntegerType(s: Struct, target: Target, buf: *Type.Payload.Bits) Type { - buf.* = .{ - .base = .{ .tag = .int_unsigned }, - .data = s.packedIntegerBits(target), - }; - return Type.initPayload(&buf.base); + unreachable; // index out of bounds } }; diff --git a/src/Sema.zig b/src/Sema.zig index 6d95b46c7c6ac949fc42eb08429eae698de89881..faf16926539f22c29feef1957ea5638032190182 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2239,6 +2239,16 @@ pub fn analyzeStructDecl( break :blk decls_len; } else 0; + if (small.has_backing_int) { + const backing_int_body_len = sema.code.extra[extra_index]; + extra_index += 1; // backing_int_body_len + if (backing_int_body_len == 0) { + extra_index += 1; // backing_int_ref + } else { + extra_index += backing_int_body_len; // backing_int_body_inst + } + } + _ = try sema.mod.scanNamespace(&struct_obj.namespace, extra_index, decls_len, new_decl); } @@ -14285,13 +14295,27 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespace()); - const field_values = try sema.arena.create([4]Value); + const backing_integer_val = blk: { + if (layout == .Packed) { + const struct_obj = struct_ty.castTag(.@"struct").?.data; + assert(struct_obj.haveLayout()); + assert(struct_obj.backing_int_ty.isInt()); + const backing_int_ty_val = try Value.Tag.ty.create(sema.arena, struct_obj.backing_int_ty); + break :blk try Value.Tag.opt_payload.create(sema.arena, backing_int_ty_val); + } else { + break :blk Value.initTag(.null_value); + } + }; + + const field_values = try sema.arena.create([5]Value); field_values.* = .{ // layout: ContainerLayout, try Value.Tag.enum_field_index.create( sema.arena, @enumToInt(layout), ), + // backing_integer: ?type, + backing_integer_val, // fields: []const StructField, fields_val, // decls: []const Declaration, @@ -16308,7 +16332,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in if (!try sema.intFitsInType(block, src, alignment_val, Type.u32, null)) { return sema.fail(block, src, "alignment must fit in 'u32'", .{}); } - const abi_align = @intCast(u29, alignment_val.toUnsignedInt(target)); + const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?); var buffer: Value.ToTypeBuffer = undefined; const unresolved_elem_ty = child_val.toType(&buffer); @@ -16473,22 +16497,31 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in const struct_val = union_val.val.castTag(.aggregate).?.data; // layout: containerlayout, const layout_val = struct_val[0]; + // backing_int: ?type, + const backing_int_val = struct_val[1]; // fields: []const enumfield, - const fields_val = struct_val[1]; + const fields_val = struct_val[2]; // decls: []const declaration, - const decls_val = struct_val[2]; + const decls_val = struct_val[3]; // is_tuple: bool, - const is_tuple_val = struct_val[3]; + const is_tuple_val = struct_val[4]; + assert(struct_val.len == 5); + + const layout = layout_val.toEnum(std.builtin.Type.ContainerLayout); // Decls if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified structs must have no decls", .{}); } + if (layout != .Packed and !backing_int_val.isNull()) { + return sema.fail(block, src, "non-packed struct does not support backing integer type", .{}); + } + return if (is_tuple_val.toBool()) try sema.reifyTuple(block, src, fields_val) else - try sema.reifyStruct(block, inst, src, layout_val, fields_val, name_strategy); + try sema.reifyStruct(block, inst, src, layout, backing_int_val, fields_val, name_strategy); }, .Enum => { const struct_val = union_val.val.castTag(.aggregate).?.data; @@ -16981,7 +17014,8 @@ fn reifyStruct( block: *Block, inst: Zir.Inst.Index, src: LazySrcLoc, - layout_val: Value, + layout: std.builtin.Type.ContainerLayout, + backing_int_val: Value, fields_val: Value, name_strategy: Zir.Inst.NameStrategy, ) CompileError!Air.Inst.Ref { @@ -17004,7 +17038,7 @@ fn reifyStruct( .owner_decl = new_decl_index, .fields = .{}, .zir_index = inst, - .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout), + .layout = layout, .status = .have_field_types, .known_non_opv = false, .namespace = .{ @@ -17070,6 +17104,41 @@ fn reifyStruct( }; } + if (layout == .Packed) { + struct_obj.status = .layout_wip; + + for (struct_obj.fields.values()) |field, index| { + sema.resolveTypeLayout(block, src, field.ty) catch |err| switch (err) { + error.AnalysisFail => { + const msg = sema.err orelse return err; + try sema.addFieldErrNote(block, struct_ty, index, msg, "while checking this field", .{}); + return err; + }, + else => return err, + }; + } + + var fields_bit_sum: u64 = 0; + for (struct_obj.fields.values()) |field| { + fields_bit_sum += field.ty.bitSize(target); + } + + if (backing_int_val.optionalValue()) |payload| { + var buf: Value.ToTypeBuffer = undefined; + const backing_int_ty = payload.toType(&buf); + try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum); + struct_obj.backing_int_ty = try backing_int_ty.copy(new_decl_arena_allocator); + } else { + var buf: Type.Payload.Bits = .{ + .base = .{ .tag = .int_unsigned }, + .data = @intCast(u16, fields_bit_sum), + }; + struct_obj.backing_int_ty = try Type.initPayload(&buf.base).copy(new_decl_arena_allocator); + } + + struct_obj.status = .have_layout; + } + try new_decl.finalizeNewArena(&new_decl_arena); return sema.analyzeDeclVal(block, src, new_decl_index); } @@ -27154,6 +27223,11 @@ fn resolveStructLayout( else => return err, }; } + + if (struct_obj.layout == .Packed) { + try semaBackingIntType(sema.mod, struct_obj); + } + struct_obj.status = .have_layout; // In case of querying the ABI alignment of this struct, we will ask @@ -27173,6 +27247,109 @@ fn resolveStructLayout( // otherwise it's a tuple; no need to resolve anything } +fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!void { + const gpa = mod.gpa; + const target = mod.getTarget(); + + var fields_bit_sum: u64 = 0; + for (struct_obj.fields.values()) |field| { + fields_bit_sum += field.ty.bitSize(target); + } + + const decl_index = struct_obj.owner_decl; + const decl = mod.declPtr(decl_index); + var decl_arena = decl.value_arena.?.promote(gpa); + defer decl.value_arena.?.* = decl_arena.state; + const decl_arena_allocator = decl_arena.allocator(); + + const zir = struct_obj.namespace.file_scope.zir; + const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; + assert(extended.opcode == .struct_decl); + const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); + + if (small.has_backing_int) { + var extra_index: usize = extended.operand; + extra_index += @boolToInt(small.has_src_node); + extra_index += @boolToInt(small.has_fields_len); + extra_index += @boolToInt(small.has_decls_len); + + const backing_int_body_len = zir.extra[extra_index]; + extra_index += 1; + + var analysis_arena = std.heap.ArenaAllocator.init(gpa); + defer analysis_arena.deinit(); + + var sema: Sema = .{ + .mod = mod, + .gpa = gpa, + .arena = analysis_arena.allocator(), + .perm_arena = decl_arena_allocator, + .code = zir, + .owner_decl = decl, + .owner_decl_index = decl_index, + .func = null, + .fn_ret_ty = Type.void, + .owner_func = null, + }; + defer sema.deinit(); + + var wip_captures = try WipCaptureScope.init(gpa, decl_arena_allocator, decl.src_scope); + defer wip_captures.deinit(); + + var block: Block = .{ + .parent = null, + .sema = &sema, + .src_decl = decl_index, + .namespace = &struct_obj.namespace, + .wip_capture_scope = wip_captures.scope, + .instructions = .{}, + .inlining = null, + .is_comptime = true, + }; + defer { + assert(block.instructions.items.len == 0); + block.params.deinit(gpa); + } + + const backing_int_src: LazySrcLoc = .{ .node_offset_container_tag = 0 }; + const backing_int_ty = blk: { + if (backing_int_body_len == 0) { + const backing_int_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); + break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref); + } else { + const body = zir.extra[extra_index..][0..backing_int_body_len]; + const ty_ref = try sema.resolveBody(&block, body, struct_obj.zir_index); + break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref); + } + }; + + try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum); + struct_obj.backing_int_ty = try backing_int_ty.copy(decl_arena_allocator); + } else { + var buf: Type.Payload.Bits = .{ + .base = .{ .tag = .int_unsigned }, + .data = @intCast(u16, fields_bit_sum), + }; + struct_obj.backing_int_ty = try Type.initPayload(&buf.base).copy(decl_arena_allocator); + } +} + +fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void { + const target = sema.mod.getTarget(); + + if (!backing_int_ty.isInt()) { + return sema.fail(block, src, "expected backing integer type, found '{}'", .{backing_int_ty.fmt(sema.mod)}); + } + if (backing_int_ty.bitSize(target) != fields_bit_sum) { + return sema.fail( + block, + src, + "backing integer type '{}' has bit size {} but the struct fields have a total bit size of {}", + .{ backing_int_ty.fmt(sema.mod), backing_int_ty.bitSize(target), fields_bit_sum }, + ); + } +} + fn resolveUnionLayout( sema: *Sema, block: *Block, @@ -27495,12 +27672,26 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void break :decls_len decls_len; } else 0; + // The backing integer cannot be handled until `resolveStructLayout()`. + if (small.has_backing_int) { + const backing_int_body_len = zir.extra[extra_index]; + extra_index += 1; // backing_int_body_len + if (backing_int_body_len == 0) { + extra_index += 1; // backing_int_ref + } else { + extra_index += backing_int_body_len; // backing_int_body_inst + } + } + // Skip over decls. var decls_it = zir.declIteratorInner(extra_index, decls_len); while (decls_it.next()) |_| {} extra_index = decls_it.extra_index; if (fields_len == 0) { + if (struct_obj.layout == .Packed) { + try semaBackingIntType(mod, struct_obj); + } struct_obj.status = .have_layout; return; } diff --git a/src/Zir.zig b/src/Zir.zig index c62e6d02bb550d6d3fa9fe522214c9a8366b3f35..538ef6aaf8c35b12a3e4586e47103283d2b29cd6 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -3085,13 +3085,16 @@ pub const Inst = struct { /// 0. src_node: i32, // if has_src_node /// 1. fields_len: u32, // if has_fields_len /// 2. decls_len: u32, // if has_decls_len - /// 3. decl_bits: u32 // for every 8 decls + /// 3. backing_int_body_len: u32, // if has_backing_int + /// 4. backing_int_ref: Ref, // if has_backing_int and backing_int_body_len is 0 + /// 5. backing_int_body_inst: Inst, // if has_backing_int and backing_int_body_len is > 0 + /// 6. decl_bits: u32 // for every 8 decls /// - sets of 4 bits: /// 0b000X: whether corresponding decl is pub /// 0b00X0: whether corresponding decl is exported /// 0b0X00: whether corresponding decl has an align expression /// 0bX000: whether corresponding decl has a linksection or an address space expression - /// 4. decl: { // for every decls_len + /// 7. decl: { // for every decls_len /// src_hash: [4]u32, // hash of source bytes /// line: u32, // line number of decl, relative to parent /// name: u32, // null terminated string index @@ -3109,13 +3112,13 @@ pub const Inst = struct { /// address_space: Ref, /// } /// } - /// 5. flags: u32 // for every 8 fields + /// 8. flags: u32 // for every 8 fields /// - sets of 4 bits: /// 0b000X: whether corresponding field has an align expression /// 0b00X0: whether corresponding field has a default expression /// 0b0X00: whether corresponding field is comptime /// 0bX000: whether corresponding field has a type expression - /// 6. fields: { // for every fields_len + /// 9. fields: { // for every fields_len /// field_name: u32, /// doc_comment: u32, // 0 if no doc comment /// field_type: Ref, // if corresponding bit is not set. none means anytype. @@ -3123,7 +3126,7 @@ pub const Inst = struct { /// align_body_len: u32, // if corresponding bit is set /// init_body_len: u32, // if corresponding bit is set /// } - /// 7. bodies: { // for every fields_len + /// 10. bodies: { // for every fields_len /// field_type_body_inst: Inst, // for each field_type_body_len /// align_body_inst: Inst, // for each align_body_len /// init_body_inst: Inst, // for each init_body_len @@ -3133,11 +3136,12 @@ pub const Inst = struct { has_src_node: bool, has_fields_len: bool, has_decls_len: bool, + has_backing_int: bool, known_non_opv: bool, known_comptime_only: bool, name_strategy: NameStrategy, layout: std.builtin.Type.ContainerLayout, - _: u7 = undefined, + _: u6 = undefined, }; }; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 9c3efa18cd9f4ab556922af6f5c7fddb57681205..6d2922ea3fe557dd2411dba5539fd333331e07ad 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1683,8 +1683,7 @@ pub const Object = struct { if (ty.castTag(.@"struct")) |payload| { const struct_obj = payload.data; if (struct_obj.layout == .Packed) { - var buf: Type.Payload.Bits = undefined; - const info = struct_obj.packedIntegerType(target, &buf).intInfo(target); + const info = struct_obj.backing_int_ty.intInfo(target); const dwarf_encoding: c_uint = switch (info.signedness) { .signed => DW.ATE.signed, .unsigned => DW.ATE.unsigned, @@ -2679,9 +2678,7 @@ pub const DeclGen = struct { const struct_obj = t.castTag(.@"struct").?.data; if (struct_obj.layout == .Packed) { - var buf: Type.Payload.Bits = undefined; - const int_ty = struct_obj.packedIntegerType(target, &buf); - const int_llvm_ty = try dg.lowerType(int_ty); + const int_llvm_ty = try dg.lowerType(struct_obj.backing_int_ty); gop.value_ptr.* = int_llvm_ty; return int_llvm_ty; } @@ -3330,8 +3327,8 @@ pub const DeclGen = struct { const struct_obj = tv.ty.castTag(.@"struct").?.data; if (struct_obj.layout == .Packed) { - const big_bits = struct_obj.packedIntegerBits(target); - const int_llvm_ty = dg.context.intType(big_bits); + const big_bits = struct_obj.backing_int_ty.bitSize(target); + const int_llvm_ty = dg.context.intType(@intCast(c_uint, big_bits)); const fields = struct_obj.fields.values(); comptime assert(Type.packed_struct_layout_version == 2); var running_int: *const llvm.Value = int_llvm_ty.constNull(); @@ -8243,8 +8240,8 @@ pub const FuncGen = struct { .Struct => { if (result_ty.containerLayout() == .Packed) { const struct_obj = result_ty.castTag(.@"struct").?.data; - const big_bits = struct_obj.packedIntegerBits(target); - const int_llvm_ty = self.dg.context.intType(big_bits); + const big_bits = struct_obj.backing_int_ty.bitSize(target); + const int_llvm_ty = self.dg.context.intType(@intCast(c_uint, big_bits)); const fields = struct_obj.fields.values(); comptime assert(Type.packed_struct_layout_version == 2); var running_int: *const llvm.Value = int_llvm_ty.constNull(); diff --git a/src/print_zir.zig b/src/print_zir.zig index 2b70799c367b798db5f77431ee7819863867319b..4bc96c42591cf6e01c66bde67c3305e21b76535d 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -1245,9 +1245,28 @@ const Writer = struct { try self.writeFlag(stream, "known_non_opv, ", small.known_non_opv); try self.writeFlag(stream, "known_comptime_only, ", small.known_comptime_only); - try stream.print("{s}, {s}, ", .{ - @tagName(small.name_strategy), @tagName(small.layout), - }); + + try stream.print("{s}, ", .{@tagName(small.name_strategy)}); + + if (small.layout == .Packed and small.has_backing_int) { + const backing_int_body_len = self.code.extra[extra_index]; + extra_index += 1; + try stream.writeAll("Packed("); + if (backing_int_body_len == 0) { + const backing_int_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + try self.writeInstRef(stream, backing_int_ref); + } else { + const body = self.code.extra[extra_index..][0..backing_int_body_len]; + extra_index += backing_int_body_len; + self.indent += 2; + try self.writeBracedDecl(stream, body); + self.indent -= 2; + } + try stream.writeAll("), "); + } else { + try stream.print("{s}, ", .{@tagName(small.layout)}); + } if (decls_len == 0) { try stream.writeAll("{}, "); diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index 4028c3872d0892058555b83749ef471ea23880d6..9f9a6151b8d57f97cd0babe142f849c4c93c9179 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -1116,6 +1116,7 @@ struct AstNodeContainerDecl { ContainerLayout layout; bool auto_enum, is_root; // union(enum) + bool unsupported_explicit_backing_int; }; struct AstNodeErrorSetField { diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index 08aa8bbf062f31c3552f159b23cfba8bb0d3dfee..90173f384e9ec5c8adefa1959c1db10102f1f2b3 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -3034,6 +3034,12 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { AstNode *decl_node = struct_type->data.structure.decl_node; + if (decl_node->data.container_decl.unsupported_explicit_backing_int) { + add_node_error(g, decl_node, buf_create_from_str( + "the stage1 compiler does not support explicit backing integer types on packed structs")); + return ErrorSemanticAnalyzeFail; + } + if (struct_type->data.structure.resolve_loop_flag_zero_bits) { if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) { struct_type->data.structure.resolve_status = ResolveStatusInvalid; diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index e31715030cec1b3071cfc58ca5e75c4bf835c075..a5428945a99d9bd7dbcb7215aa8a99b375c6886d 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -18640,7 +18640,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Struct", nullptr); - ZigValue **fields = alloc_const_vals_ptrs(g, 4); + ZigValue **fields = alloc_const_vals_ptrs(g, 5); result->data.x_struct.fields = fields; // layout: ContainerLayout @@ -18648,8 +18648,17 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour fields[0]->special = ConstValSpecialStatic; fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.structure.layout); + + // backing_integer: ?type + ensure_field_index(result->type, "backing_integer", 1); + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = get_optional_type(g, g->builtin_types.entry_type); + // This is always null in stage1, as stage1 does not support explicit backing integers + // for packed structs. + fields[1]->data.x_optional = nullptr; + // fields: []Type.StructField - ensure_field_index(result->type, "fields", 1); + ensure_field_index(result->type, "fields", 2); ZigType *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField", nullptr); if ((err = type_resolve(g, type_info_struct_field_type, ResolveStatusSizeKnown))) { @@ -18663,7 +18672,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour struct_field_array->data.x_array.special = ConstArraySpecialNone; struct_field_array->data.x_array.data.s_none.elements = g->pass1_arena->allocate(struct_field_count); - init_const_slice(g, fields[1], struct_field_array, 0, struct_field_count, false, nullptr); + init_const_slice(g, fields[2], struct_field_array, 0, struct_field_count, false, nullptr); for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) { TypeStructField *struct_field = type_entry->data.structure.fields[struct_field_index]; @@ -18710,18 +18719,18 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour struct_field_val->parent.data.p_array.elem_index = struct_field_index; } // decls: []Type.Declaration - ensure_field_index(result->type, "decls", 2); - if ((err = ir_make_type_info_decls(ira, source_node, fields[2], + ensure_field_index(result->type, "decls", 3); + if ((err = ir_make_type_info_decls(ira, source_node, fields[3], type_entry->data.structure.decls_scope, false))) { return err; } // is_tuple: bool - ensure_field_index(result->type, "is_tuple", 3); - fields[3]->special = ConstValSpecialStatic; - fields[3]->type = g->builtin_types.entry_bool; - fields[3]->data.x_bool = is_tuple(type_entry); + ensure_field_index(result->type, "is_tuple", 4); + fields[4]->special = ConstValSpecialStatic; + fields[4]->type = g->builtin_types.entry_bool; + fields[4]->data.x_bool = is_tuple(type_entry); break; } @@ -19313,7 +19322,14 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_ assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr)); ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); - ZigValue *fields_value = get_const_field(ira, source_node, payload, "fields", 1); + ZigType *tag_type = get_const_field_meta_type_optional(ira, source_node, payload, "backing_integer", 1); + if (tag_type != nullptr) { + ir_add_error_node(ira, source_node, buf_create_from_str( + "the stage1 compiler does not support explicit backing integer types on packed structs")); + return ira->codegen->invalid_inst_gen->value->type; + } + + ZigValue *fields_value = get_const_field(ira, source_node, payload, "fields", 2); if (fields_value == nullptr) return ira->codegen->invalid_inst_gen->value->type; assert(fields_value->special == ConstValSpecialStatic); @@ -19322,7 +19338,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_ ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index]; size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint); - ZigValue *decls_value = get_const_field(ira, source_node, payload, "decls", 2); + ZigValue *decls_value = get_const_field(ira, source_node, payload, "decls", 3); if (decls_value == nullptr) return ira->codegen->invalid_inst_gen->value->type; assert(decls_value->special == ConstValSpecialStatic); @@ -19335,7 +19351,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_ } bool is_tuple; - if ((err = get_const_field_bool(ira, source_node, payload, "is_tuple", 3, &is_tuple))) + if ((err = get_const_field_bool(ira, source_node, payload, "is_tuple", 4, &is_tuple))) return ira->codegen->invalid_inst_gen->value->type; ZigType *entry = new_type_table_entry(ZigTypeIdStruct); diff --git a/src/stage1/parser.cpp b/src/stage1/parser.cpp index fdc0777aff1c2938c575e8bf6a0f538d0cb2758b..bd778484cb9fedf017137a2a11891674b340afc7 100644 --- a/src/stage1/parser.cpp +++ b/src/stage1/parser.cpp @@ -2902,16 +2902,25 @@ static AstNode *ast_parse_container_decl_auto(ParseContext *pc) { } // ContainerDeclType -// <- KEYWORD_struct +// <- KEYWORD_struct (LPAREN Expr RPAREN)? // / KEYWORD_enum (LPAREN Expr RPAREN)? // / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? // / KEYWORD_opaque static AstNode *ast_parse_container_decl_type(ParseContext *pc) { TokenIndex first = eat_token_if(pc, TokenIdKeywordStruct); if (first != 0) { + bool explicit_backing_int = false; + if (eat_token_if(pc, TokenIdLParen) != 0) { + explicit_backing_int = true; + ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + } AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); res->data.container_decl.init_arg_expr = nullptr; res->data.container_decl.kind = ContainerKindStruct; + // We want this to be an error in semantic analysis not parsing to make sharing + // the test suite between stage1 and self hosted easier. + res->data.container_decl.unsupported_explicit_backing_int = explicit_backing_int; return res; } diff --git a/src/type.zig b/src/type.zig index 85d77303c9e7c9c7677b9fbf066ff409a42162a7..1b71f4e9b1d90f29517c7a1bf21709714c27a2f7 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3000,9 +3000,17 @@ pub const Type = extern union { .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, }; if (struct_obj.layout == .Packed) { - var buf: Type.Payload.Bits = undefined; - const int_ty = struct_obj.packedIntegerType(target, &buf); - return AbiAlignmentAdvanced{ .scalar = int_ty.abiAlignment(target) }; + switch (strat) { + .sema_kit => |sk| try sk.sema.resolveTypeLayout(sk.block, sk.src, ty), + .lazy => |arena| { + if (!struct_obj.haveLayout()) { + return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }; + } + }, + .eager => {}, + } + assert(struct_obj.haveLayout()); + return AbiAlignmentAdvanced{ .scalar = struct_obj.backing_int_ty.abiAlignment(target) }; } const fields = ty.structFields(); @@ -3192,17 +3200,16 @@ pub const Type = extern union { .Packed => { const struct_obj = ty.castTag(.@"struct").?.data; switch (strat) { - .sema_kit => |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty), + .sema_kit => |sk| try sk.sema.resolveTypeLayout(sk.block, sk.src, ty), .lazy => |arena| { - if (!struct_obj.haveFieldTypes()) { + if (!struct_obj.haveLayout()) { return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) }; } }, .eager => {}, } - var buf: Type.Payload.Bits = undefined; - const int_ty = struct_obj.packedIntegerType(target, &buf); - return AbiSizeAdvanced{ .scalar = int_ty.abiSize(target) }; + assert(struct_obj.haveLayout()); + return AbiSizeAdvanced{ .scalar = struct_obj.backing_int_ty.abiSize(target) }; }, else => { switch (strat) { diff --git a/test/behavior.zig b/test/behavior.zig index fee61f5e0921ad3809aa3927a63fa8df402b3e52..e9c4ec779bb5e6bc4dca8ea89eccddb61be1ab5a 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -165,6 +165,7 @@ test { if (builtin.zig_backend != .stage1) { _ = @import("behavior/decltest.zig"); + _ = @import("behavior/packed_struct_explicit_backing_int.zig"); } if (builtin.os.tag != .wasi) { diff --git a/test/behavior/packed_struct_explicit_backing_int.zig b/test/behavior/packed_struct_explicit_backing_int.zig new file mode 100644 index 0000000000000000000000000000000000000000..165e94fd4e731c476264d22b01cfbccd415640f2 --- /dev/null +++ b/test/behavior/packed_struct_explicit_backing_int.zig @@ -0,0 +1,53 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const expectEqual = std.testing.expectEqual; +const native_endian = builtin.cpu.arch.endian(); + +test "packed struct explicit backing integer" { + assert(builtin.zig_backend != .stage1); + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S1 = packed struct { a: u8, b: u8, c: u8 }; + + const S2 = packed struct(i24) { d: u8, e: u8, f: u8 }; + + const S3 = packed struct { x: S1, y: S2 }; + const S3Padded = packed struct(u64) { s3: S3, pad: u16 }; + + try expectEqual(48, @bitSizeOf(S3)); + try expectEqual(@sizeOf(u48), @sizeOf(S3)); + + try expectEqual(3, @offsetOf(S3, "y")); + try expectEqual(24, @bitOffsetOf(S3, "y")); + + if (native_endian == .Little) { + const s3 = @bitCast(S3Padded, @as(u64, 0xe952d5c71ff4)).s3; + try expectEqual(@as(u8, 0xf4), s3.x.a); + try expectEqual(@as(u8, 0x1f), s3.x.b); + try expectEqual(@as(u8, 0xc7), s3.x.c); + try expectEqual(@as(u8, 0xd5), s3.y.d); + try expectEqual(@as(u8, 0x52), s3.y.e); + try expectEqual(@as(u8, 0xe9), s3.y.f); + } + + const S4 = packed struct { a: i32, b: i8 }; + const S5 = packed struct(u80) { a: i32, b: i8, c: S4 }; + const S6 = packed struct(i80) { a: i32, b: S4, c: i8 }; + + const expectedBitSize = 80; + const expectedByteSize = @sizeOf(u80); + try expectEqual(expectedBitSize, @bitSizeOf(S5)); + try expectEqual(expectedByteSize, @sizeOf(S5)); + try expectEqual(expectedBitSize, @bitSizeOf(S6)); + try expectEqual(expectedByteSize, @sizeOf(S6)); + + try expectEqual(5, @offsetOf(S5, "c")); + try expectEqual(40, @bitOffsetOf(S5, "c")); + try expectEqual(9, @offsetOf(S6, "c")); + try expectEqual(72, @bitOffsetOf(S6, "c")); +} diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index b1012e69c8b714a1831be4e0a8beedbf8e96bb58..968c3e7490b70b1700607f5fb2c6199d539694a3 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -293,6 +293,7 @@ test "type info: struct info" { fn testStruct() !void { const unpacked_struct_info = @typeInfo(TestStruct); try expect(unpacked_struct_info.Struct.is_tuple == false); + try expect(unpacked_struct_info.Struct.backing_integer == null); try expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32)); try expect(@ptrCast(*const u32, unpacked_struct_info.Struct.fields[0].default_value.?).* == 4); try expect(mem.eql(u8, "foobar", @ptrCast(*const *const [6:0]u8, unpacked_struct_info.Struct.fields[1].default_value.?).*)); @@ -315,6 +316,7 @@ fn testPackedStruct() !void { try expect(struct_info == .Struct); try expect(struct_info.Struct.is_tuple == false); try expect(struct_info.Struct.layout == .Packed); + try expect(struct_info.Struct.backing_integer == u128); try expect(struct_info.Struct.fields.len == 4); try expect(struct_info.Struct.fields[0].alignment == 0); try expect(struct_info.Struct.fields[2].field_type == f32); @@ -326,7 +328,7 @@ fn testPackedStruct() !void { } const TestPackedStruct = packed struct { - fieldA: usize, + fieldA: u64, fieldB: void, fieldC: f32, fieldD: u32 = 4, diff --git a/test/cases/compile_errors/packed_struct_backing_int_wrong.zig b/test/cases/compile_errors/packed_struct_backing_int_wrong.zig new file mode 100644 index 0000000000000000000000000000000000000000..cd1b4ec11cb73b99b3625e3fcc09aea377d29175 --- /dev/null +++ b/test/cases/compile_errors/packed_struct_backing_int_wrong.zig @@ -0,0 +1,55 @@ +export fn entry1() void { + _ = @sizeOf(packed struct(u32) { + x: u1, + y: u24, + z: u4, + }); +} +export fn entry2() void { + _ = @sizeOf(packed struct(i31) { + x: u4, + y: u24, + z: u4, + }); +} + +export fn entry3() void { + _ = @sizeOf(packed struct(void) { + x: void, + }); +} + +export fn entry4() void { + _ = @sizeOf(packed struct(void) {}); +} + +export fn entry5() void { + _ = @sizeOf(packed struct(noreturn) {}); +} + +export fn entry6() void { + _ = @sizeOf(packed struct(f64) { + x: u32, + y: f32, + }); +} + +export fn entry7() void { + _ = @sizeOf(packed struct(*u32) { + x: u4, + y: u24, + z: u4, + }); +} + +// error +// backend=llvm +// target=native +// +// :2:31: error: backing integer type 'u32' has bit size 32 but the struct fields have a total bit size of 29 +// :9:31: error: backing integer type 'i31' has bit size 31 but the struct fields have a total bit size of 32 +// :17:31: error: expected backing integer type, found 'void' +// :23:31: error: expected backing integer type, found 'void' +// :27:31: error: expected backing integer type, found 'noreturn' +// :31:31: error: expected backing integer type, found 'f64' +// :38:31: error: expected backing integer type, found '*u32' -- 2.54.0 From 40447b25e87ece9eee2d83d1c78c365ad7ab40f1 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 10 Aug 2022 10:59:26 +0300 Subject: [PATCH 16/72] Sema: fix expansion of repeated value Closes #12386 --- src/Sema.zig | 5 ++++- test/behavior/eval.zig | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index acdce0e9b09739e52f83b7bf3eb62f6ed3406ef3..fc8a182508acde178b711e8eb338be64b41bd21d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -23990,7 +23990,10 @@ fn beginComptimePtrMutation( const array_len_including_sentinel = try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel()); const elems = try arena.alloc(Value, array_len_including_sentinel); - mem.set(Value, elems, repeated_val); + if (elems.len > 0) elems[0] = repeated_val; + for (elems[1..]) |*elem| { + elem.* = try repeated_val.copy(arena); + } val_ptr.* = try Value.Tag.aggregate.create(arena, elems); diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index d2a75e18dfe158c4880f832d754c6d1b307f51a9..8a872f63781374b4edd56224b6ab1586369ca1e7 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1293,3 +1293,20 @@ test "mutate through pointer-like optional at comptime" { try expect(payload_ptr.*.* == 16); } } + +test "repeated value is correctly expanded" { + const S = struct { x: [4]i8 = std.mem.zeroes([4]i8) }; + const M = struct { x: [4]S = std.mem.zeroes([4]S) }; + + comptime { + var res = M{}; + for (.{ 1, 2, 3 }) |i| res.x[i].x[i] = i; + + try expectEqual(M{ .x = .{ + .{ .x = .{ 0, 0, 0, 0 } }, + .{ .x = .{ 0, 1, 0, 0 } }, + .{ .x = .{ 0, 0, 2, 0 } }, + .{ .x = .{ 0, 0, 0, 3 } }, + } }, res); + } +} -- 2.54.0 From 8fd20a5eb015fda4b27cef14c9e3149575861994 Mon Sep 17 00:00:00 2001 From: zooster Date: Wed, 10 Aug 2022 18:40:18 +0200 Subject: [PATCH 17/72] fix: disallow newline in char literal --- lib/std/zig/tokenizer.zig | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig index 8a0885ab7b712cfd32e708a53cd243f3f1cefe42..5c6d7324193148a39fa6e8e62f0a7c11c89f1680 100644 --- a/lib/std/zig/tokenizer.zig +++ b/lib/std/zig/tokenizer.zig @@ -797,6 +797,10 @@ pub const Tokenizer = struct { remaining_code_units = 3; state = .char_literal_unicode; }, + '\n' => { + result.tag = .invalid; + break; + }, else => { state = .char_literal_end; }, @@ -1507,6 +1511,20 @@ test "tokenizer - code point literal with hex escape" { , &.{ .invalid, .invalid }); } +test "tokenizer - newline in char literal" { + try testTokenize( + \\' + \\' + , &.{ .invalid, .invalid }); +} + +test "tokenizer - newline in string literal" { + try testTokenize( + \\" + \\" + , &.{ .invalid, .string_literal }); +} + test "tokenizer - code point literal with unicode escapes" { // Valid unicode escapes try testTokenize( -- 2.54.0 From 0e118ed0aca3d852d2499fa37d04adef62b03ead Mon Sep 17 00:00:00 2001 From: LeRoyce Pearson Date: Tue, 9 Aug 2022 16:08:59 -0600 Subject: [PATCH 18/72] stage2: add compile error for shlExact overflow - moves a stage1 test case and makes it target `llvm` backend instead of `stage1` backend --- src/Sema.zig | 12 +++++------- .../{stage1/obj => }/shlExact_shifts_out_1_bits.zig | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/shlExact_shifts_out_1_bits.zig (60%) diff --git a/src/Sema.zig b/src/Sema.zig index fc8a182508acde178b711e8eb338be64b41bd21d..4fefff7c26a17f2ad234d99a8f285ac5576b91ea 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10269,16 +10269,14 @@ fn zirShl( const val = switch (air_tag) { .shl_exact => val: { - const shifted = try lhs_val.shl(rhs_val, lhs_ty, sema.arena, target); + const shifted = try lhs_val.shlWithOverflow(rhs_val, lhs_ty, sema.arena, target); if (scalar_ty.zigTypeTag() == .ComptimeInt) { - break :val shifted; + break :val shifted.wrapped_result; } - const int_info = scalar_ty.intInfo(target); - const truncated = try shifted.intTrunc(lhs_ty, sema.arena, int_info.signedness, int_info.bits, target); - if (try sema.compare(block, src, truncated, .eq, shifted, lhs_ty)) { - break :val shifted; + if (shifted.overflowed.compareWithZero(.eq)) { + break :val shifted.wrapped_result; } - return sema.addConstUndef(lhs_ty); + return sema.fail(block, src, "operation caused overflow", .{}); }, .shl_sat => if (scalar_ty.zigTypeTag() == .ComptimeInt) diff --git a/test/cases/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig b/test/cases/compile_errors/shlExact_shifts_out_1_bits.zig similarity index 60% rename from test/cases/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig rename to test/cases/compile_errors/shlExact_shifts_out_1_bits.zig index 953c5fec50199c59dd2669b4e8601d026f4d70f8..b2ab45a8e9233958df13e7a60867ca77d0af1914 100644 --- a/test/cases/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig +++ b/test/cases/compile_errors/shlExact_shifts_out_1_bits.zig @@ -4,7 +4,7 @@ comptime { } // error -// backend=stage1 +// backend=llvm // target=native // -// tmp.zig:2:15: error: operation caused overflow +// :2:15: error: operation caused overflow -- 2.54.0 From e218b7ea0c2a907c5728006b38e7ca492e19ccb6 Mon Sep 17 00:00:00 2001 From: InKryption Date: Wed, 10 Aug 2022 17:09:27 +0200 Subject: [PATCH 19/72] stage2: add compile error for invalid null/undefined pointer cast --- src/Sema.zig | 9 +++++++++ .../compile_errors/compile_time_null_ptr_cast.zig | 11 +++++++++++ .../compile_errors/compile_time_undef_ptr_cast.zig | 11 +++++++++++ 3 files changed, 31 insertions(+) create mode 100644 test/cases/compile_errors/compile_time_null_ptr_cast.zig create mode 100644 test/cases/compile_errors/compile_time_undef_ptr_cast.zig diff --git a/src/Sema.zig b/src/Sema.zig index 4fefff7c26a17f2ad234d99a8f285ac5576b91ea..fe19de39ddd9b5e8a97ce3bf56aa3417d6da26b0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17272,6 +17272,15 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air else operand; + if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| { + if (!dest_ty.ptrAllowsZero() and operand_val.isUndef()) { + return sema.failWithUseOfUndef(block, operand_src); + } + if (!dest_ty.ptrAllowsZero() and operand_val.isNull()) { + return sema.fail(block, operand_src, "null pointer casted to type {}", .{dest_ty.fmt(sema.mod)}); + } + } + const dest_elem_ty = dest_ty.elemType2(); try sema.resolveTypeLayout(block, dest_ty_src, dest_elem_ty); const dest_align = dest_ty.ptrAlignment(target); diff --git a/test/cases/compile_errors/compile_time_null_ptr_cast.zig b/test/cases/compile_errors/compile_time_null_ptr_cast.zig new file mode 100644 index 0000000000000000000000000000000000000000..d3750c865407633c2b40c7fb04d8ee1b9113b836 --- /dev/null +++ b/test/cases/compile_errors/compile_time_null_ptr_cast.zig @@ -0,0 +1,11 @@ +comptime { + var opt_ptr: ?*i32 = null; + const ptr = @ptrCast(*i32, opt_ptr); + _ = ptr; +} + +// error +// backend=llvm +// target=native +// +// :3:32: error: null pointer casted to type *i32 diff --git a/test/cases/compile_errors/compile_time_undef_ptr_cast.zig b/test/cases/compile_errors/compile_time_undef_ptr_cast.zig new file mode 100644 index 0000000000000000000000000000000000000000..14edd293de286852b7e5a0bf61d77ccb763e3c5a --- /dev/null +++ b/test/cases/compile_errors/compile_time_undef_ptr_cast.zig @@ -0,0 +1,11 @@ +comptime { + var undef_ptr: *i32 = undefined; + const ptr = @ptrCast(*i32, undef_ptr); + _ = ptr; +} + +// error +// backend=llvm +// target=native +// +// :3:32: error: use of undefined value here causes undefined behavior -- 2.54.0 From 45c444ff18b43d30a7277e346174ba6eca4a6193 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 11 Aug 2022 00:06:31 +0200 Subject: [PATCH 20/72] macho: allow unaligned offsets in object files --- src/link/MachO/Object.zig | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 996a85ed4b75a732c5f5a995f327e62d0a0e4374..098917e81f60002c473f56d6e371294b626218ab 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -99,10 +99,11 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) }, .SYMTAB => { const symtab = cmd.cast(macho.symtab_command).?; - self.in_symtab = @ptrCast( - [*]const macho.nlist_64, - @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]), - )[0..symtab.nsyms]; + // Sadly, SYMTAB may be at an unaligned offset within the object file. + self.in_symtab = @alignCast(@alignOf(macho.nlist_64), @ptrCast( + [*]align(1) const macho.nlist_64, + self.contents.ptr + symtab.symoff, + ))[0..symtab.nsyms]; self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize]; try self.symtab.appendSlice(allocator, self.in_symtab); }, @@ -302,10 +303,10 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) const code: ?[]const u8 = if (!sect.isZerofill()) try self.getSectionContents(sect) else null; // Read section's list of relocations - const relocs = @ptrCast( - [*]const macho.relocation_info, - @alignCast(@alignOf(macho.relocation_info), &self.contents[sect.reloff]), - )[0..sect.nreloc]; + const relocs = @alignCast(@alignOf(macho.relocation_info), @ptrCast( + [*]align(1) const macho.relocation_info, + self.contents.ptr + sect.reloff, + ))[0..sect.nreloc]; // Symbols within this section only. const filtered_syms = filterSymbolsByAddress( @@ -548,10 +549,10 @@ pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry { .DATA_IN_CODE => { const dice = cmd.cast(macho.linkedit_data_command).?; const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry)); - return @ptrCast( - [*]const macho.data_in_code_entry, - @alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]), - )[0..ndice]; + return @alignCast(@alignOf(macho.data_in_code_entry), @ptrCast( + [*]align(1) const macho.data_in_code_entry, + self.contents.ptr + dice.dataoff, + ))[0..ndice]; }, else => {}, } -- 2.54.0 From 173952f4b82713712fde873ffa31decb53ad4450 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Aug 2022 14:37:49 -0700 Subject: [PATCH 21/72] disable failing behavior test see tracking issue #12396 --- test/behavior/cast.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index e899def4aa9db1751d8bdaaa18d1f12aae4bb7b8..ac3a4daeab443157dd0f7e640739af680dd68f82 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1431,6 +1431,11 @@ test "coerce between pointers of compatible differently-named floats" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.os.tag == .windows) { + // https://github.com/ziglang/zig/issues/12396 + return error.SkipZigTest; + } + const F = switch (@typeInfo(c_longdouble).Float.bits) { 16 => f16, 32 => f32, -- 2.54.0 From 9656ec271c3ca1009c48c26a9a5c407d98bbcacd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Aug 2022 16:24:04 -0700 Subject: [PATCH 22/72] better default for use_stage1 when -ofmt=c is provided --- src/Compilation.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Compilation.zig b/src/Compilation.zig index d3816e542cfecf8969f9b37ed9975a214e5851e3..af39154a3f233e43fbe7c80c19599f43312f4f08 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1041,6 +1041,10 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { } } + // If LLVM does not support the target, then we can't use it. + if (!target_util.hasLlvmSupport(options.target, ofmt)) + break :blk false; + break :blk build_options.is_stage1; }; -- 2.54.0 From e8d02c5b90c0833401a70cc797a28da57a356927 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Aug 2022 14:49:06 -0700 Subject: [PATCH 23/72] add ZIG_GLOBAL_CACHE_DIR env var --- src/main.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index 0c6e4a63a3ab978ed59600c003b0ba7740cb3051..c103cddcd45c6504b4354cfcaaf0fad21bdd1184 100644 --- a/src/main.zig +++ b/src/main.zig @@ -718,7 +718,7 @@ fn buildOutputType( var test_filter: ?[]const u8 = null; var test_name_prefix: ?[]const u8 = null; var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR"); - var override_global_cache_dir: ?[]const u8 = null; + var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR"); var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR"); var main_pkg_path: ?[]const u8 = null; var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; -- 2.54.0 From d267f0f968c97d77d8434ef9918c71fb4c11f45d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Aug 2022 14:08:09 -0700 Subject: [PATCH 24/72] LLVM: respect linksection for exported variables --- src/Sema.zig | 1 + src/codegen/llvm.zig | 11 +++++++++++ src/codegen/llvm/bindings.zig | 3 +++ 3 files changed, 15 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index 9aaa761a035053235c71fb0079094a5fd1850f7f..f9b37f5b495eb26d578978a14e95cf9e14d1253d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6351,6 +6351,7 @@ fn instantiateGenericCall( new_decl.is_exported = fn_owner_decl.is_exported; new_decl.has_align = fn_owner_decl.has_align; new_decl.has_linksection_or_addrspace = fn_owner_decl.has_linksection_or_addrspace; + new_decl.@"linksection" = fn_owner_decl.@"linksection"; new_decl.@"addrspace" = fn_owner_decl.@"addrspace"; new_decl.zir_decl_index = fn_owner_decl.zir_decl_index; new_decl.alive = true; // This Decl is called at runtime. diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6d2922ea3fe557dd2411dba5539fd333331e07ad..aa846f4247e2b1c406b0edece45243b252ffdf0d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -722,6 +722,10 @@ pub const Object = struct { dg.addFnAttrString(llvm_func, "no-stack-arg-probe", ""); } + if (decl.@"linksection") |section| { + llvm_func.setSection(section); + } + // Remove all the basic blocks of a function in order to start over, generating // LLVM IR from an empty function body. while (llvm_func.getFirstBasicBlock()) |bb| { @@ -1107,6 +1111,11 @@ pub const Object = struct { .hidden => llvm_global.setVisibility(.Hidden), .protected => llvm_global.setVisibility(.Protected), } + if (exports[0].options.section) |section| { + const section_z = try module.gpa.dupeZ(u8, section); + defer module.gpa.free(section_z); + llvm_global.setSection(section_z); + } if (decl.val.castTag(.variable)) |variable| { if (variable.data.is_threadlocal) { llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel); @@ -2183,6 +2192,7 @@ pub const DeclGen = struct { const target = dg.module.getTarget(); var global = try dg.resolveGlobalDecl(decl_index); global.setAlignment(decl.getAlignment(target)); + if (decl.@"linksection") |section| global.setSection(section); assert(decl.has_tv); const init_val = if (decl.val.castTag(.variable)) |payload| init_val: { const variable = payload.data; @@ -2216,6 +2226,7 @@ pub const DeclGen = struct { new_global.setLinkage(global.getLinkage()); new_global.setUnnamedAddr(global.getUnnamedAddress()); new_global.setAlignment(global.getAlignment()); + if (decl.@"linksection") |section| new_global.setSection(section); new_global.setInitializer(llvm_init); // replaceAllUsesWith requires the type to be unchanged. So we bitcast // the new global to the old type and use that as the thing to replace diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index e4357b80609bf5c1b4b483b2a9a4b11ec25e8fb8..baf67b15aa42b7563cacaf5d1a1a69f07b790efd 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -126,6 +126,9 @@ pub const Value = opaque { pub const setThreadLocalMode = LLVMSetThreadLocalMode; extern fn LLVMSetThreadLocalMode(Global: *const Value, Mode: ThreadLocalMode) void; + pub const setSection = LLVMSetSection; + extern fn LLVMSetSection(Global: *const Value, Section: [*:0]const u8) void; + pub const deleteGlobal = LLVMDeleteGlobal; extern fn LLVMDeleteGlobal(GlobalVar: *const Value) void; -- 2.54.0 From 3c4e9b5983e49fa3179adec8e41e3198f3fd66ae Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 8 Aug 2022 15:54:07 -0700 Subject: [PATCH 25/72] disable failing behavior test See #12360 --- test/behavior/type.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/behavior/type.zig b/test/behavior/type.zig index 4aec553527fa5ffc5b73dc7e7e4a43ef6e9cc7d5..8cef86b5dd36c88ae30afa8ddcc0cf951314b16a 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -513,6 +513,11 @@ test "Type.Fn" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (true) { + // https://github.com/ziglang/zig/issues/12360 + return error.SkipZigTest; + } + const some_opaque = opaque {}; const some_ptr = *some_opaque; const T = fn (c_int, some_ptr) callconv(.C) void; -- 2.54.0 From 4d7f5a1917f569506682bbb6ee5af672ab1fd8e8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 7 Aug 2022 22:48:34 -0700 Subject: [PATCH 26/72] stage2: fix crash with comptime vector reduce --- src/value.zig | 10 ++++++++++ test/behavior/vector.zig | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/value.zig b/src/value.zig index f156651eaac605546291e5edb6b3bdbf04bff648..677a459afe77a4af576060241d89dae2069a4e55 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1194,6 +1194,16 @@ pub const Value = extern union { return switch (self.tag()) { .bool_true, .one => true, .bool_false, .zero => false, + .int_u64 => switch (self.castTag(.int_u64).?.data) { + 0 => false, + 1 => true, + else => unreachable, + }, + .int_i64 => switch (self.castTag(.int_i64).?.data) { + 0 => false, + 1 => true, + else => unreachable, + }, else => unreachable, }; } diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index c13db2e262712525c2826819d33ed2e5e96f3774..863a8030c02671921db812e7074b84b96178798f 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -807,6 +807,23 @@ test "vector reduce operation" { comptime try S.doTheTest(); } +test "vector @reduce comptime" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const value = @Vector(4, i32){ 1, -1, 1, -1 }; + const result = value > @splat(4, @as(i32, 0)); + // result is { true, false, true, false }; + comptime try expect(@TypeOf(result) == @Vector(4, bool)); + const is_all_true = @reduce(.And, result); + comptime try expect(@TypeOf(is_all_true) == bool); + try expect(is_all_true == false); +} + test "mask parameter of @shuffle is comptime scope" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO -- 2.54.0 From 74673b7f69b27dc39a653f92eb58bba71e289f39 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 10 Aug 2022 22:45:46 +0300 Subject: [PATCH 27/72] stage2 llvm: implement more C ABI --- src/codegen/llvm.zig | 124 +++++++++++++++++++++++++++++++--- src/codegen/llvm/bindings.zig | 3 + test/standalone.zig | 2 +- 3 files changed, 119 insertions(+), 10 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index aa846f4247e2b1c406b0edece45243b252ffdf0d..38f7d285aed18678a4340e7a47cd5fe395639bcb 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -921,6 +921,40 @@ pub const Object = struct { }; try args.append(loaded); }, + .multiple_llvm_float => { + const llvm_floats = it.llvm_types_buffer[0..it.llvm_types_len]; + const param_ty = fn_info.param_types[it.zig_index - 1]; + const param_llvm_ty = try dg.lowerType(param_ty); + const param_alignment = param_ty.abiAlignment(target); + const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty); + arg_ptr.setAlignment(param_alignment); + var field_types_buf: [8]*const llvm.Type = undefined; + const field_types = field_types_buf[0..llvm_floats.len]; + for (llvm_floats) |float_bits, i| { + switch (float_bits) { + 64 => field_types[i] = dg.context.doubleType(), + 80 => field_types[i] = dg.context.x86FP80Type(), + else => {}, + } + } + const ints_llvm_ty = dg.context.structType(field_types.ptr, @intCast(c_uint, field_types.len), .False); + const casted_ptr = builder.buildBitCast(arg_ptr, ints_llvm_ty.pointerType(0), ""); + for (llvm_floats) |_, i_usize| { + const i = @intCast(c_uint, i_usize); + const param = llvm_func.getParam(i); + const field_ptr = builder.buildStructGEP(casted_ptr, i, ""); + const store_inst = builder.buildStore(param, field_ptr); + store_inst.setAlignment(target.cpu.arch.ptrBitWidth() / 8); + } + + const is_by_ref = isByRef(param_ty); + const loaded = if (is_by_ref) arg_ptr else l: { + const load_inst = builder.buildLoad(arg_ptr, ""); + load_inst.setAlignment(param_alignment); + break :l load_inst; + }; + try args.append(loaded); + }, .as_u16 => { const param = llvm_func.getParam(llvm_arg_i); llvm_arg_i += 1; @@ -2341,6 +2375,14 @@ pub const DeclGen = struct { dg.addFnAttr(llvm_fn, "noreturn"); } + var llvm_arg_i = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing); + var it = iterateParamTypes(dg, fn_info); + while (it.next()) |_| : (llvm_arg_i += 1) { + if (!it.byval_attr) continue; + const param = llvm_fn.getParam(llvm_arg_i); + llvm_fn.addByValAttr(llvm_arg_i, param.typeOf().getElementType()); + } + return llvm_fn; } @@ -2894,6 +2936,18 @@ pub const DeclGen = struct { llvm_params.appendAssumeCapacity(big_int_ty); } }, + .multiple_llvm_float => { + const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len]; + try llvm_params.ensureUnusedCapacity(it.llvm_types_len); + for (llvm_ints) |float_bits| { + const float_ty = switch (float_bits) { + 64 => dg.context.doubleType(), + 80 => dg.context.x86FP80Type(), + else => unreachable, + }; + llvm_params.appendAssumeCapacity(float_ty); + } + }, .as_u16 => { try llvm_params.append(dg.context.intType(16)); }, @@ -4402,6 +4456,39 @@ pub const FuncGen = struct { llvm_args.appendAssumeCapacity(load_inst); } }, + .multiple_llvm_float => { + const arg = args[it.zig_index - 1]; + const param_ty = self.air.typeOf(arg); + const llvm_floats = it.llvm_types_buffer[0..it.llvm_types_len]; + const llvm_arg = try self.resolveInst(arg); + const is_by_ref = isByRef(param_ty); + const arg_ptr = if (is_by_ref) llvm_arg else p: { + const p = self.buildAlloca(llvm_arg.typeOf()); + const store_inst = self.builder.buildStore(llvm_arg, p); + store_inst.setAlignment(param_ty.abiAlignment(target)); + break :p p; + }; + + var field_types_buf: [8]*const llvm.Type = undefined; + const field_types = field_types_buf[0..llvm_floats.len]; + for (llvm_floats) |float_bits, i| { + switch (float_bits) { + 64 => field_types[i] = self.dg.context.doubleType(), + 80 => field_types[i] = self.dg.context.x86FP80Type(), + else => {}, + } + } + const ints_llvm_ty = self.dg.context.structType(field_types.ptr, @intCast(c_uint, field_types.len), .False); + const casted_ptr = self.builder.buildBitCast(arg_ptr, ints_llvm_ty.pointerType(0), ""); + try llvm_args.ensureUnusedCapacity(it.llvm_types_len); + for (llvm_floats) |_, i_usize| { + const i = @intCast(c_uint, i_usize); + const field_ptr = self.builder.buildStructGEP(casted_ptr, i, ""); + const load_inst = self.builder.buildLoad(field_ptr, ""); + load_inst.setAlignment(target.cpu.arch.ptrBitWidth() / 8); + llvm_args.appendAssumeCapacity(load_inst); + } + }, .as_u16 => { const arg = args[it.zig_index - 1]; const llvm_arg = try self.resolveInst(arg); @@ -9367,16 +9454,20 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. llvm_types_index += 1; }, .sse => { - @panic("TODO"); + llvm_types_buffer[llvm_types_index] = dg.context.doubleType(); + llvm_types_index += 1; }, .sseup => { - @panic("TODO"); + llvm_types_buffer[llvm_types_index] = dg.context.doubleType(); + llvm_types_index += 1; }, .x87 => { - @panic("TODO"); + llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type(); + llvm_types_index += 1; }, .x87up => { - @panic("TODO"); + llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type(); + llvm_types_index += 1; }, .complex_x87 => { @panic("TODO"); @@ -9422,6 +9513,7 @@ const ParamTypeIterator = struct { target: std.Target, llvm_types_len: u32, llvm_types_buffer: [8]u16, + byval_attr: bool, const Lowering = enum { no_bits, @@ -9429,6 +9521,7 @@ const ParamTypeIterator = struct { byref, abi_sized_int, multiple_llvm_ints, + multiple_llvm_float, slice, as_u16, }; @@ -9436,6 +9529,7 @@ const ParamTypeIterator = struct { pub fn next(it: *ParamTypeIterator) ?Lowering { if (it.zig_index >= it.fn_info.param_types.len) return null; const ty = it.fn_info.param_types[it.zig_index]; + it.byval_attr = false; return nextInner(it, ty); } @@ -9521,6 +9615,7 @@ const ParamTypeIterator = struct { .memory => { it.zig_index += 1; it.llvm_index += 1; + it.byval_attr = true; return .byref; }, .sse => { @@ -9540,6 +9635,7 @@ const ParamTypeIterator = struct { if (classes[0] == .memory) { it.zig_index += 1; it.llvm_index += 1; + it.byval_attr = true; return .byref; } var llvm_types_buffer: [8]u16 = undefined; @@ -9551,16 +9647,20 @@ const ParamTypeIterator = struct { llvm_types_index += 1; }, .sse => { - @panic("TODO"); + llvm_types_buffer[llvm_types_index] = 64; + llvm_types_index += 1; }, .sseup => { - @panic("TODO"); + llvm_types_buffer[llvm_types_index] = 64; + llvm_types_index += 1; }, .x87 => { - @panic("TODO"); + llvm_types_buffer[llvm_types_index] = 80; + llvm_types_index += 1; }, .x87up => { - @panic("TODO"); + llvm_types_buffer[llvm_types_index] = 80; + llvm_types_index += 1; }, .complex_x87 => { @panic("TODO"); @@ -9574,11 +9674,16 @@ const ParamTypeIterator = struct { it.llvm_index += 1; return .abi_sized_int; } + if (classes[0] == .sse and classes[1] == .none) { + it.zig_index += 1; + it.llvm_index += 1; + return .byval; + } it.llvm_types_buffer = llvm_types_buffer; it.llvm_types_len = llvm_types_index; it.llvm_index += llvm_types_index; it.zig_index += 1; - return .multiple_llvm_ints; + return if (classes[0] == .integer) .multiple_llvm_ints else .multiple_llvm_float; }, }, .wasm32 => { @@ -9619,6 +9724,7 @@ fn iterateParamTypes(dg: *DeclGen, fn_info: Type.Payload.Function.Data) ParamTyp .target = dg.module.getTarget(), .llvm_types_buffer = undefined, .llvm_types_len = 0, + .byval_attr = false, }; } diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index baf67b15aa42b7563cacaf5d1a1a69f07b790efd..9daa96eb8ff0397c4e04e801ca60e8d13b71eaec 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -248,6 +248,9 @@ pub const Value = opaque { pub const addFunctionAttr = ZigLLVMAddFunctionAttr; extern fn ZigLLVMAddFunctionAttr(Fn: *const Value, attr_name: [*:0]const u8, attr_value: [*:0]const u8) void; + + pub const addByValAttr = ZigLLVMAddByValAttr; + extern fn ZigLLVMAddByValAttr(Fn: *const Value, ArgNo: c_uint, type: *const Type) void; }; pub const Type = opaque { diff --git a/test/standalone.zig b/test/standalone.zig index 76e46a1b62f37f6b3644015f3a8cd8fe05bb840a..2b0667e89c988f3bd88d7fe757132e5c209491fc 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -40,7 +40,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { } // C ABI compatibility issue: https://github.com/ziglang/zig/issues/1481 if (builtin.cpu.arch == .x86_64) { - if (builtin.zig_backend == .stage1) { // https://github.com/ziglang/zig/issues/12222 + if (builtin.zig_backend == .stage1 or builtin.zig_backend == .stage2_llvm) { // https://github.com/ziglang/zig/issues/12222 cases.addBuildFile("test/c_abi/build.zig", .{}); } } -- 2.54.0 From cc56400e62ae7d59cccf78d0cfe2e4b990d53abc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Aug 2022 18:56:08 -0700 Subject: [PATCH 28/72] Revert "macho: allow unaligned offsets in object files" This reverts commit 45c444ff18b43d30a7277e346174ba6eca4a6193. --- src/link/MachO/Object.zig | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 098917e81f60002c473f56d6e371294b626218ab..996a85ed4b75a732c5f5a995f327e62d0a0e4374 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -99,11 +99,10 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) }, .SYMTAB => { const symtab = cmd.cast(macho.symtab_command).?; - // Sadly, SYMTAB may be at an unaligned offset within the object file. - self.in_symtab = @alignCast(@alignOf(macho.nlist_64), @ptrCast( - [*]align(1) const macho.nlist_64, - self.contents.ptr + symtab.symoff, - ))[0..symtab.nsyms]; + self.in_symtab = @ptrCast( + [*]const macho.nlist_64, + @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]), + )[0..symtab.nsyms]; self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize]; try self.symtab.appendSlice(allocator, self.in_symtab); }, @@ -303,10 +302,10 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) const code: ?[]const u8 = if (!sect.isZerofill()) try self.getSectionContents(sect) else null; // Read section's list of relocations - const relocs = @alignCast(@alignOf(macho.relocation_info), @ptrCast( - [*]align(1) const macho.relocation_info, - self.contents.ptr + sect.reloff, - ))[0..sect.nreloc]; + const relocs = @ptrCast( + [*]const macho.relocation_info, + @alignCast(@alignOf(macho.relocation_info), &self.contents[sect.reloff]), + )[0..sect.nreloc]; // Symbols within this section only. const filtered_syms = filterSymbolsByAddress( @@ -549,10 +548,10 @@ pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry { .DATA_IN_CODE => { const dice = cmd.cast(macho.linkedit_data_command).?; const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry)); - return @alignCast(@alignOf(macho.data_in_code_entry), @ptrCast( - [*]align(1) const macho.data_in_code_entry, - self.contents.ptr + dice.dataoff, - ))[0..ndice]; + return @ptrCast( + [*]const macho.data_in_code_entry, + @alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]), + )[0..ndice]; }, else => {}, } -- 2.54.0 From fa620ef710bbbf7f642fd1d6d24a188d02902988 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Aug 2022 22:26:29 -0700 Subject: [PATCH 29/72] link.MachO: use accurate alignment attribute on pointers Also adds a new method to ArrayList: appendUnalignedSlice --- lib/std/array_list.zig | 63 +++++++++++++++++++++++++++++++++++++++ src/link/MachO.zig | 6 ++-- src/link/MachO/Atom.zig | 2 +- src/link/MachO/Object.zig | 25 ++++++++-------- 4 files changed, 80 insertions(+), 16 deletions(-) diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index 655bdeaa42c2803023a2c07eb452799060734b52..a8c53c314244ad7e810aaf8133eee2fae4b4d3d6 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -221,6 +221,30 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { mem.copy(T, self.items[old_len..], items); } + /// Append an unaligned slice of items to the list. Allocates more + /// memory as necessary. Only call this function if calling + /// `appendSlice` instead would be a compile error. + pub fn appendUnalignedSlice(self: *Self, items: []align(1) const T) Allocator.Error!void { + try self.ensureUnusedCapacity(items.len); + self.appendUnalignedSliceAssumeCapacity(items); + } + + /// Append the slice of items to the list, asserting the capacity is already + /// enough to store the new items. **Does not** invalidate pointers. + /// Only call this function if calling `appendSliceAssumeCapacity` instead + /// would be a compile error. + pub fn appendUnalignedSliceAssumeCapacity(self: *Self, items: []align(1) const T) void { + const old_len = self.items.len; + const new_len = old_len + items.len; + assert(new_len <= self.capacity); + self.items.len = new_len; + @memcpy( + @ptrCast([*]align(@alignOf(T)) u8, self.items.ptr + old_len), + @ptrCast([*]const u8, items.ptr), + items.len * @sizeOf(T), + ); + } + pub const Writer = if (T != u8) @compileError("The Writer interface is only defined for ArrayList(u8) " ++ "but the given type is ArrayList(" ++ @typeName(T) ++ ")") @@ -592,6 +616,29 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ mem.copy(T, self.items[old_len..], items); } + /// Append the slice of items to the list. Allocates more + /// memory as necessary. Only call this function if a call to `appendSlice` instead would + /// be a compile error. + pub fn appendUnalignedSlice(self: *Self, allocator: Allocator, items: []align(1) const T) Allocator.Error!void { + try self.ensureUnusedCapacity(allocator, items.len); + self.appendUnalignedSliceAssumeCapacity(items); + } + + /// Append an unaligned slice of items to the list, asserting the capacity is enough + /// to store the new items. Only call this function if a call to `appendSliceAssumeCapacity` + /// instead would be a compile error. + pub fn appendUnalignedSliceAssumeCapacity(self: *Self, items: []align(1) const T) void { + const old_len = self.items.len; + const new_len = old_len + items.len; + assert(new_len <= self.capacity); + self.items.len = new_len; + @memcpy( + @ptrCast([*]align(@alignOf(T)) u8, self.items.ptr + old_len), + @ptrCast([*]const u8, items.ptr), + items.len * @sizeOf(T), + ); + } + pub const WriterContext = struct { self: *Self, allocator: Allocator, @@ -899,6 +946,14 @@ test "std.ArrayList/ArrayListUnmanaged.basic" { try testing.expect(list.pop() == 1); try testing.expect(list.items.len == 9); + var unaligned: [3]i32 align(1) = [_]i32{ 4, 5, 6 }; + list.appendUnalignedSlice(&unaligned) catch unreachable; + try testing.expect(list.items.len == 12); + try testing.expect(list.pop() == 6); + try testing.expect(list.pop() == 5); + try testing.expect(list.pop() == 4); + try testing.expect(list.items.len == 9); + list.appendSlice(&[_]i32{}) catch unreachable; try testing.expect(list.items.len == 9); @@ -941,6 +996,14 @@ test "std.ArrayList/ArrayListUnmanaged.basic" { try testing.expect(list.pop() == 1); try testing.expect(list.items.len == 9); + var unaligned: [3]i32 align(1) = [_]i32{ 4, 5, 6 }; + list.appendUnalignedSlice(a, &unaligned) catch unreachable; + try testing.expect(list.items.len == 12); + try testing.expect(list.pop() == 6); + try testing.expect(list.pop() == 5); + try testing.expect(list.pop() == 4); + try testing.expect(list.items.len == 9); + list.appendSlice(a, &[_]i32{}) catch unreachable; try testing.expect(list.items.len == 9); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index db207af5f54c990e5e0f5a34c45921e14fd437a6..3da086a382fc38179011e8c6587f18806f4d2e3b 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -5315,10 +5315,10 @@ fn writeFunctionStarts(self: *MachO, ncmds: *u32, lc_writer: anytype) !void { } fn filterDataInCode( - dices: []const macho.data_in_code_entry, + dices: []align(1) const macho.data_in_code_entry, start_addr: u64, end_addr: u64, -) []const macho.data_in_code_entry { +) []align(1) const macho.data_in_code_entry { const Predicate = struct { addr: u64, @@ -5825,7 +5825,7 @@ pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc { return global; } -pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate: anytype) usize { +pub fn findFirst(comptime T: type, haystack: []align(1) const T, start: usize, predicate: anytype) usize { if (!@hasDecl(@TypeOf(predicate), "predicate")) @compileError("Predicate is required to define fn predicate(@This(), T) bool"); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 4871276f3c2729f1ad1fba42b636a17f0bcedd3f..dd818ea936f4cf0396145d349dcbc3e4b6df4fe7 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -218,7 +218,7 @@ const RelocContext = struct { base_offset: i32 = 0, }; -pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context: RelocContext) !void { +pub fn parseRelocs(self: *Atom, relocs: []align(1) const macho.relocation_info, context: RelocContext) !void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 996a85ed4b75a732c5f5a995f327e62d0a0e4374..935183bbc6f1b1da3cc58be997b77bc574145140 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -24,7 +24,7 @@ mtime: u64, contents: []align(@alignOf(u64)) const u8, header: macho.mach_header_64 = undefined, -in_symtab: []const macho.nlist_64 = undefined, +in_symtab: []align(1) const macho.nlist_64 = undefined, in_strtab: []const u8 = undefined, symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, @@ -99,12 +99,13 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) }, .SYMTAB => { const symtab = cmd.cast(macho.symtab_command).?; + // Sadly, SYMTAB may be at an unaligned offset within the object file. self.in_symtab = @ptrCast( - [*]const macho.nlist_64, - @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]), + [*]align(1) const macho.nlist_64, + self.contents.ptr + symtab.symoff, )[0..symtab.nsyms]; self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize]; - try self.symtab.appendSlice(allocator, self.in_symtab); + try self.symtab.appendUnalignedSlice(allocator, self.in_symtab); }, else => {}, } @@ -196,10 +197,10 @@ fn filterSymbolsByAddress( } fn filterRelocs( - relocs: []const macho.relocation_info, + relocs: []align(1) const macho.relocation_info, start_addr: u64, end_addr: u64, -) []const macho.relocation_info { +) []align(1) const macho.relocation_info { const Predicate = struct { addr: u64, @@ -303,8 +304,8 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) // Read section's list of relocations const relocs = @ptrCast( - [*]const macho.relocation_info, - @alignCast(@alignOf(macho.relocation_info), &self.contents[sect.reloff]), + [*]align(1) const macho.relocation_info, + self.contents.ptr + sect.reloff, )[0..sect.nreloc]; // Symbols within this section only. @@ -472,7 +473,7 @@ fn createAtomFromSubsection( size: u64, alignment: u32, code: ?[]const u8, - relocs: []const macho.relocation_info, + relocs: []align(1) const macho.relocation_info, indexes: []const SymbolAtIndex, match: u8, sect: macho.section_64, @@ -538,7 +539,7 @@ pub fn getSourceSection(self: Object, index: u16) macho.section_64 { return self.sections.items[index]; } -pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry { +pub fn parseDataInCode(self: Object) ?[]align(1) const macho.data_in_code_entry { var it = LoadCommandIterator{ .ncmds = self.header.ncmds, .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds], @@ -549,8 +550,8 @@ pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry { const dice = cmd.cast(macho.linkedit_data_command).?; const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry)); return @ptrCast( - [*]const macho.data_in_code_entry, - @alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]), + [*]align(1) const macho.data_in_code_entry, + self.contents.ptr + dice.dataoff, )[0..ndice]; }, else => {}, -- 2.54.0 From 933436dc52b9be0a3e5d81b014fba6df2124fe20 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Aug 2022 20:39:57 -0700 Subject: [PATCH 30/72] stage2: remove destroyed functions from maps This is likely the cause of the flaky test failures in master branch. Since we have some test coverage for incremental compilation, it's not OK to leave proper memory management of Fn objects as "TODO". --- src/Module.zig | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 7e877a2f4ad6394442565ceffacf4824b0c7a130..995fdda7ea24d16437f29442e004681580883af5 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -84,7 +84,6 @@ string_literal_bytes: std.ArrayListUnmanaged(u8) = .{}, /// The set of all the generic function instantiations. This is used so that when a generic /// function is called twice with the same comptime parameter arguments, both calls dispatch /// to the same function. -/// TODO: remove functions from this set when they are destroyed. monomorphed_funcs: MonomorphedFuncsSet = .{}, /// The set of all comptime function calls that have been cached so that future calls /// with the same parameters will get the same return value. @@ -92,7 +91,6 @@ memoized_calls: MemoizedCallSet = .{}, /// Contains the values from `@setAlignStack`. A sparse table is used here /// instead of a field of `Fn` because usage of `@setAlignStack` is rare, while /// functions are many. -/// TODO: remove functions from this set when they are destroyed. align_stack_fns: std.AutoHashMapUnmanaged(*const Fn, SetAlignStack) = .{}, /// We optimize memory usage for a compilation with no compile errors by storing the @@ -560,6 +558,8 @@ pub const Decl = struct { gpa.destroy(extern_fn); } if (decl.getFunction()) |func| { + _ = mod.align_stack_fns.remove(func); + _ = mod.monomorphed_funcs.remove(func); func.deinit(gpa); gpa.destroy(func); } @@ -4094,6 +4094,12 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { // The exports this Decl performs will be re-discovered, so we remove them here // prior to re-analysis. mod.deleteDeclExports(decl_index); + + // Similarly, `@setAlignStack` invocations will be re-discovered. + if (decl.getFunction()) |func| { + _ = mod.align_stack_fns.remove(func); + } + // Dependencies will be re-discovered, so we remove them here prior to re-analysis. for (decl.dependencies.keys()) |dep_index| { const dep = mod.declPtr(dep_index); -- 2.54.0 From 9f1f60fd4311b37493a5297ccaac167d0270f521 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 11 Aug 2022 00:46:44 -0700 Subject: [PATCH 31/72] test-cases: remove multi-threading This effectively reverts 22690efcc2378222503cb8aaad26a6f4a539f5aa, re-opening #11818. This had the following problems: * Buggy on some targets (macOS, Windows) * Messy output to the terminal These problems need to be solved before moving forward with this. --- src/test.zig | 48 ++++++++---------------------------------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/src/test.zig b/src/test.zig index 5f4107a402d41954e188b40e885d32e800778382..266b4181e0113d9f84e4e6bf946703b578adf8f6 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1224,10 +1224,6 @@ pub const TestContext = struct { try aux_thread_pool.init(self.gpa); defer aux_thread_pool.deinit(); - var case_thread_pool: ThreadPool = undefined; - try case_thread_pool.init(self.gpa); - defer case_thread_pool.deinit(); - // Use the same global cache dir for all the tests, such that we for example don't have to // rebuild musl libc for every case (when LLVM backend is enabled). var global_tmp = std.testing.tmpDir(.{}); @@ -1245,9 +1241,6 @@ pub const TestContext = struct { defer self.gpa.free(global_cache_directory.path.?); { - var wait_group: WaitGroup = .{}; - defer wait_group.wait(); - for (self.cases.items) |*case| { if (build_options.skip_non_native) { if (case.target.getCpuArch() != builtin.cpu.arch) @@ -1267,17 +1260,19 @@ pub const TestContext = struct { if (std.mem.indexOf(u8, case.name, test_filter) == null) continue; } - wait_group.start(); - try case_thread_pool.spawn(workerRunOneCase, .{ + var prg_node = root_node.start(case.name, case.updates.items.len); + prg_node.activate(); + defer prg_node.end(); + + case.result = runOneCase( self.gpa, - root_node, - case, + &prg_node, + case.*, zig_lib_directory, &aux_thread_pool, global_cache_directory, host, - &wait_group, - }); + ); } } @@ -1295,33 +1290,6 @@ pub const TestContext = struct { } } - fn workerRunOneCase( - gpa: Allocator, - root_node: *std.Progress.Node, - case: *Case, - zig_lib_directory: Compilation.Directory, - thread_pool: *ThreadPool, - global_cache_directory: Compilation.Directory, - host: std.zig.system.NativeTargetInfo, - wait_group: *WaitGroup, - ) void { - defer wait_group.finish(); - - var prg_node = root_node.start(case.name, case.updates.items.len); - prg_node.activate(); - defer prg_node.end(); - - case.result = runOneCase( - gpa, - &prg_node, - case.*, - zig_lib_directory, - thread_pool, - global_cache_directory, - host, - ); - } - fn runOneCase( allocator: Allocator, root_node: *std.Progress.Node, -- 2.54.0 From 7d6a7f513b0053be4e1629de492cbde9215c442c Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 11 Aug 2022 11:04:26 +0200 Subject: [PATCH 32/72] std: Don't pass undefined memory to the kernel in os.abort() --- lib/std/os.zig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 1192c726298e8919ab6dce959a1c26409ccb8494..b068a11229b54c93de90afed384f2321b0dbc041 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -476,9 +476,8 @@ pub fn abort() noreturn { // Install default handler so that the tkill below will terminate. const sigact = Sigaction{ .handler = .{ .sigaction = SIG.DFL }, - .mask = undefined, - .flags = undefined, - .restorer = undefined, + .mask = empty_sigset, + .flags = 0, }; sigaction(SIG.ABRT, &sigact, null) catch |err| switch (err) { error.OperationNotSupported => unreachable, -- 2.54.0 From 7e6dbd63989ed4f62b9d12fb03119b0bec1bda84 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 23 Jul 2022 19:49:26 +0200 Subject: [PATCH 33/72] wasm: Use free-lists for unused locals When a local is no longer needed (for instance, it was used as a temporary during arithmetic), it can be appended to one of the typed freelists. This allows us to re-use locals and therefore require less locals, reducing the binary size, as well as runtime initialization. --- src/arch/wasm/CodeGen.zig | 49 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index ef92361de2c03e2844524904525a1b6a729c9161..2048a456d74f96f7725c133fbee529de4a7f9a7c 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -601,6 +601,21 @@ stack_size: u32 = 0, /// However, local variables or the usage of `@setAlignStack` can overwrite this default. stack_alignment: u32 = 16, +// For each individual Wasm valtype we store a seperate free list which +// allows us to re-use locals that are no longer used. e.g. a temporary local. +/// A list of indexes which represents a local of valtype `i32`. +/// It is illegal to store a non-i32 valtype in this list. +free_locals_i32: std.ArrayListUnmanaged(u32) = .{}, +/// A list of indexes which represents a local of valtype `i64`. +/// It is illegal to store a non-i32 valtype in this list. +free_locals_i64: std.ArrayListUnmanaged(u32) = .{}, +/// A list of indexes which represents a local of valtype `f32`. +/// It is illegal to store a non-i32 valtype in this list. +free_locals_f32: std.ArrayListUnmanaged(u32) = .{}, +/// A list of indexes which represents a local of valtype `f64`. +/// It is illegal to store a non-i32 valtype in this list. +free_locals_f64: std.ArrayListUnmanaged(u32) = .{}, + const InnerError = error{ OutOfMemory, /// An error occurred when trying to lower AIR to MIR. @@ -781,13 +796,43 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void { /// Creates one locals for a given `Type`. /// Returns a corresponding `Wvalue` with `local` as active tag fn allocLocal(self: *Self, ty: Type) InnerError!WValue { + const valtype = typeToValtype(ty, self.target); + switch (valtype) { + .i32 => if (self.free_locals_i32.popOrNull()) |index| { + return WValue{ .local = index }; + }, + .i64 => if (self.free_locals_i64.popOrNull()) |index| { + return WValue{ .local = index }; + }, + .f32 => if (self.free_locals_f32.popOrNull()) |index| { + return WValue{ .local = index }; + }, + .f64 => if (self.free_locals_f64.popOrNull()) |index| { + return WValue{ .local = index }; + }, + } + // no local was free to be re-used, so allocate a new local instead + try self.locals.append(self.gpa, wasm.valtype(valtype)); const initial_index = self.local_index; - const valtype = genValtype(ty, self.target); - try self.locals.append(self.gpa, valtype); self.local_index += 1; return WValue{ .local = initial_index }; } +/// Marks a local as no longer being referenced and essentially allows +/// us to re-use it somewhere else within the function. +/// The valtype of the local is deducted by using the index of the given. +/// Asserts given `WValue` is a `local`. +fn freeLocal(self: *Self, value: WValue) InnerError!WValue { + const index = value.local; + const valtype = wasm.valtype(self.locals.items[index]); + switch (valtype) { + .i32 => self.free_locals_i32.append(index) catch {}, // It's ok to fail any of those, a new local can be allocated instead + .i64 => self.free_locals_i64.append(index) catch {}, + .f32 => self.free_locals_f32.append(index) catch {}, + .f64 => self.free_locals_f64.append(index) catch {}, + } +} + /// Generates a `wasm.Type` from a given function type. /// Memory is owned by the caller. fn genFunctype(gpa: Allocator, cc: std.builtin.CallingConvention, params: []const Type, return_type: Type, target: std.Target) !wasm.Type { -- 2.54.0 From cc6f2b67c68ece7577e27e23846670d6566ad5f2 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 24 Jul 2022 15:48:12 +0200 Subject: [PATCH 34/72] wasm: `binOp` leave value on stack Rather than always creating a new local and storing the result of a binary operation into said local, we now leave it on top of the stack. This allows for better codegen as we need less instructions, as well as less total amount of locals. --- src/arch/wasm/CodeGen.zig | 147 ++++++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 63 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 2048a456d74f96f7725c133fbee529de4a7f9a7c..cbe4b558610b603b3368614310a98a679456951b 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -29,6 +29,8 @@ const errUnionErrorOffset = codegen.errUnionErrorOffset; const WValue = union(enum) { /// May be referenced but is unused none: void, + /// The value lives on top of the stack + stack: void, /// Index of the local variable local: u32, /// An immediate 32bit value @@ -55,7 +57,7 @@ const WValue = union(enum) { /// In wasm function pointers are indexes into a function table, /// rather than an address in the data section. function_index: u32, - /// Offset from the bottom of the stack, with the offset + /// Offset from the bottom of the virtual stack, with the offset /// pointing to where the value lives. stack_offset: u32, @@ -71,6 +73,21 @@ const WValue = union(enum) { else => return 0, } } + + /// Promotes a `WValue` to a local when given value is on top of the stack. + /// When encountering a `local` or `stack_offset` this is essentially a no-op. + /// All other tags are illegal. + fn toLocal(self: WValue, gen: *Self, ty: Type) InnerError!WValue { + switch (self) { + .stack => { + const local = try gen.allocLocal(ty); + try gen.addLabel(.local_set, local.local); + return local; + }, + .local, .stack_offset => return self, + else => unreachable, + } + } }; /// Wasm ops, but without input/output/signedness information @@ -774,7 +791,7 @@ fn genBlockType(ty: Type, target: std.Target) u8 { /// Writes the bytecode depending on the given `WValue` in `val` fn emitWValue(self: *Self, value: WValue) InnerError!void { switch (value) { - .none => {}, // no-op + .none, .stack => {}, // no-op .local => |idx| try self.addLabel(.local_get, idx), .imm32 => |val| try self.addImm32(@bitCast(i32, val)), .imm64 => |val| try self.addImm64(val), @@ -1892,6 +1909,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void { + assert(!(lhs != .stack and rhs == .stack)); switch (ty.zigTypeTag()) { .ErrorUnion => { const pl_ty = ty.errorUnionPayload(); @@ -2070,10 +2088,14 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const rhs = try self.resolveInst(bin_op.rhs); const ty = self.air.typeOf(bin_op.lhs); - return self.binOp(lhs, rhs, ty, op); + const stack_value = try self.binOp(lhs, rhs, ty, op); + return stack_value.toLocal(self, ty); } +/// Performs a binary operation on the given `WValue`'s +/// NOTE: THis leaves the value on top of the stack. fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { + assert(!(lhs != .stack and rhs == .stack)); if (isByRef(ty, self.target)) { if (ty.zigTypeTag() == .Int) { return self.binOpBigInt(lhs, rhs, ty, op); @@ -2099,10 +2121,7 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); - // save the result in a temporary - const bin_local = try self.allocLocal(ty); - try self.addLabel(.local_set, bin_local.local); - return bin_local; + return WValue{ .stack = {} }; } fn binOpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue { @@ -2134,8 +2153,8 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr const rhs_high_bit = try self.load(rhs, Type.u64, 0); const rhs_low_bit = try self.load(rhs, Type.u64, 8); - const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); - const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); + const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64); + const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); const lt = if (op == .add) blk: { break :blk try self.cmp(high_op_res, rhs_high_bit, Type.u64, .lt); @@ -2143,7 +2162,7 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); } else unreachable; const tmp = try self.intcast(lt, Type.u32, Type.u64); - const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); + const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64); try self.store(result, high_op_res, Type.u64, 0); try self.store(result, tmp_op, Type.u64, 8); @@ -2202,6 +2221,7 @@ fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { if (wasm_bits == bitsize) return operand; if (wasm_bits == 128) { + assert(operand != .stack); const msb = try self.load(operand, Type.u64, 0); const lsb = try self.load(operand, Type.u64, 8); @@ -2772,19 +2792,19 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { switch (wasm_bits) { 32 => { const bin_op = try self.binOp(operand, .{ .imm32 = ~@as(u32, 0) }, operand_ty, .xor); - return self.wrapOperand(bin_op, operand_ty); + return (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty); }, 64 => { const bin_op = try self.binOp(operand, .{ .imm64 = ~@as(u64, 0) }, operand_ty, .xor); - return self.wrapOperand(bin_op, operand_ty); + return (try self.wrapOperand(bin_op, operand_ty)).toLocal(self, operand_ty); }, 128 => { const result_ptr = try self.allocStack(operand_ty); const msb = try self.load(operand, Type.u64, 0); const lsb = try self.load(operand, Type.u64, 8); - const msb_xor = try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); - const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); + const msb_xor = try (try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty); + const lsb_xor = try (try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty); try self.store(result_ptr, msb_xor, Type.u64, 0); try self.store(result_ptr, lsb_xor, Type.u64, 8); return result_ptr; @@ -3215,7 +3235,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value if (wanted.isSignedInt()) { - const shr = try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr); + const shr = try (try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr)).toLocal(self, Type.i64); try self.store(stack_ptr, shr, Type.u64, 8); } else { // Ensure memory of lsb is zero'd @@ -4320,7 +4340,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W break :blk try self.signAbsValue(rhs_op, lhs_ty); } else rhs_op; - const bin_op = try self.binOp(lhs, rhs, lhs_ty, op); + const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty); const result = if (wasm_bits != int_info.bits) blk: { break :blk try self.wrapOperand(bin_op, lhs_ty); } else bin_op; @@ -4330,7 +4350,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W if (wasm_bits == int_info.bits) { const cmp_zero = try self.cmp(rhs, zero, lhs_ty, cmp_op); const lt = try self.cmp(bin_op, lhs, lhs_ty, .lt); - break :blk try self.binOp(cmp_zero, lt, Type.u32, .xor); // result of cmp_zero and lt is always 32bit + break :blk try (try self.binOp(cmp_zero, lt, Type.u32, .xor)).toLocal(self, Type.u32); // result of cmp_zero and lt is always 32bit } const abs = try self.signAbsValue(bin_op, lhs_ty); break :blk try self.cmp(abs, bin_op, lhs_ty, .neq); @@ -4360,8 +4380,8 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, const rhs_high_bit = try self.load(rhs, Type.u64, 0); const rhs_low_bit = try self.load(rhs, Type.u64, 8); - const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); - const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); + const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64); + const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); const lt = if (op == .add) blk: { break :blk try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt); @@ -4369,14 +4389,14 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); } else unreachable; const tmp = try self.intcast(lt, Type.u32, Type.u64); - const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); + const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64); const overflow_bit = if (is_signed) blk: { - const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor); const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor); const to_wrap = if (op == .add) wrap: { break :wrap try self.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); } else xor_low; + const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor); const wrap = try self.binOp(to_wrap, xor_op, Type.u64, .@"and"); break :blk try self.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed } else blk: { @@ -4422,7 +4442,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits}); }; - const shl = try self.binOp(lhs, rhs, lhs_ty, .shl); + const shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty); const result = if (wasm_bits != int_info.bits) blk: { break :blk try self.wrapOperand(shl, lhs_ty); } else shl; @@ -4432,7 +4452,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr); break :blk try self.cmp(lhs, wrapped, lhs_ty, .neq); } else blk: { - const shr = try self.binOp(result, rhs, lhs_ty, .shr); + const shr = try (try self.binOp(result, rhs, lhs_ty, .shr)).toLocal(self, lhs_ty); break :blk try self.cmp(lhs, shr, lhs_ty, .neq); }; @@ -4478,7 +4498,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64; const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty); const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty); - const bin_op = try self.binOp(lhs_upcast, rhs_upcast, new_ty, .mul); + const bin_op = try (try self.binOp(lhs_upcast, rhs_upcast, new_ty, .mul)).toLocal(self, new_ty); if (int_info.signedness == .unsigned) { const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); const wrap = try self.intcast(shr, new_ty, lhs_ty); @@ -4488,7 +4508,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { break :blk try self.intcast(bin_op, new_ty, lhs_ty); } else { const down_cast = try self.intcast(bin_op, new_ty, lhs_ty); - const shr = try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr); + const shr = try (try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(self, lhs_ty); const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty); @@ -4500,14 +4520,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } else if (int_info.signedness == .signed) blk: { const lhs_abs = try self.signAbsValue(lhs, lhs_ty); const rhs_abs = try self.signAbsValue(rhs, lhs_ty); - const bin_op = try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul); + const bin_op = try (try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul)).toLocal(self, lhs_ty); const mul_abs = try self.signAbsValue(bin_op, lhs_ty); const cmp_op = try self.cmp(mul_abs, bin_op, lhs_ty, .neq); try self.emitWValue(cmp_op); try self.addLabel(.local_set, overflow_bit.local); break :blk try self.wrapOperand(bin_op, lhs_ty); } else blk: { - const bin_op = try self.binOp(lhs, rhs, lhs_ty, .mul); + const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(self, lhs_ty); const shift_imm = if (wasm_bits == 32) WValue{ .imm32 = int_info.bits } else @@ -4587,7 +4607,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } const mul_result = try self.binOp(lhs, rhs, ty, .mul); - return self.binOp(mul_result, addend, ty, .add); + return (try self.binOp(mul_result, addend, ty, .add)).toLocal(self, ty); } fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -4663,16 +4683,16 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { 32 => { if (wasm_bits != int_info.bits) { const val: u32 = @as(u32, 1) << @intCast(u5, int_info.bits); - const bin_op = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or"); - try self.emitWValue(bin_op); + // leave value on the stack + _ = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or"); } else try self.emitWValue(operand); try self.addTag(.i32_ctz); }, 64 => { if (wasm_bits != int_info.bits) { const val: u64 = @as(u64, 1) << @intCast(u6, int_info.bits); - const bin_op = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or"); - try self.emitWValue(bin_op); + // leave value on the stack + _ = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or"); } else try self.emitWValue(operand); try self.addTag(.i64_ctz); try self.addTag(.i32_wrap_i64); @@ -4847,45 +4867,45 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { switch (int_info.bits) { 16 => { const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl); - const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and"); - const shr_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr); + const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and")).toLocal(self, ty); + const shr_res = try (try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); const res = if (int_info.signedness == .signed) blk: { break :blk try self.wrapOperand(shr_res, Type.u8); } else shr_res; - return self.binOp(lhs, res, ty, .@"or"); + return (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty); }, 24 => { const msb = try self.wrapOperand(operand, Type.u16); const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr); const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl); - const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and"); - const shr_res = try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr); + const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and")).toLocal(self, Type.u16); + const shr_res = try (try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); const res = if (int_info.signedness == .signed) blk: { break :blk try self.wrapOperand(shr_res, Type.u8); } else shr_res; const lhs_tmp = try self.binOp(lhs, res, ty, .@"or"); - const lhs_result = try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr); + const lhs_result = try (try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); const rhs_wrap = try self.wrapOperand(msb, Type.u8); - const rhs_result = try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl); + const rhs_result = try (try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl)).toLocal(self, ty); const tmp = try self.binOp(lhs_result, rhs_result, ty, .@"or"); - return self.binOp(tmp, lsb, ty, .@"or"); + return (try self.binOp(tmp, lsb, ty, .@"or")).toLocal(self, ty); }, 32 => { const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl); - const lhs = try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and"); + const lhs = try (try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and")).toLocal(self, ty); const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr); - const rhs = try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and"); - const tmp_or = try self.binOp(lhs, rhs, ty, .@"or"); + const rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty); + const tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty); - const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl); - const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr); + const shr = try (try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr)).toLocal(self, ty); const res = if (int_info.signedness == .signed) blk: { break :blk try self.wrapOperand(shr, Type.u16); } else shr; - return self.binOp(shl, res, ty, .@"or"); + const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl); + return (try self.binOp(shl, res, ty, .@"or")).toLocal(self, ty); }, else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}), } @@ -4902,7 +4922,7 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (ty.isSignedInt()) { return self.divSigned(lhs, rhs, ty); } - return self.binOp(lhs, rhs, ty, .div); + return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty); } fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -4914,7 +4934,7 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const rhs = try self.resolveInst(bin_op.rhs); if (ty.isUnsignedInt()) { - return self.binOp(lhs, rhs, ty, .div); + return (try self.binOp(lhs, rhs, ty, .div)).toLocal(self, ty); } else if (ty.isSignedInt()) { const int_bits = ty.intInfo(self.target).bits; const wasm_bits = toWasmBits(int_bits) orelse { @@ -4927,9 +4947,6 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { break :blk try self.signAbsValue(rhs, ty); } else rhs; - const div_result = try self.binOp(lhs_res, rhs_res, ty, .div); - const rem_result = try self.binOp(lhs_res, rhs_res, ty, .rem); - const zero = switch (wasm_bits) { 32 => WValue{ .imm32 = 0 }, 64 => WValue{ .imm64 = 0 }, @@ -4938,7 +4955,10 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const lhs_less_than_zero = try self.cmp(lhs_res, zero, ty, .lt); const rhs_less_than_zero = try self.cmp(rhs_res, zero, ty, .lt); - try self.emitWValue(div_result); + const div_result = try self.allocLocal(ty); + // leave on stack + _ = try self.binOp(lhs_res, rhs_res, ty, .div); + try self.addLabel(.local_tee, div_result.local); try self.emitWValue(lhs_less_than_zero); try self.emitWValue(rhs_less_than_zero); switch (wasm_bits) { @@ -4953,7 +4973,8 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { else => unreachable, } try self.emitWValue(div_result); - try self.emitWValue(rem_result); + // leave value on the stack + _ = try self.binOp(lhs_res, rhs_res, ty, .rem); try self.addTag(.select); } else { const float_bits = ty.floatBits(self.target); @@ -5110,7 +5131,7 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { } const wasm_bits = toWasmBits(int_info.bits).?; - const bin_result = try self.binOp(lhs, rhs, ty, op); + const bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty); if (wasm_bits != int_info.bits and op == .add) { const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1); const imm_val = switch (wasm_bits) { @@ -5161,7 +5182,7 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op else => unreachable, }; - const bin_result = try self.binOp(lhs, rhs, ty, op); + const bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty); if (!is_wasm_bits) { const cmp_result_lt = try self.cmp(bin_result, max_wvalue, ty, .lt); try self.emitWValue(bin_result); @@ -5185,14 +5206,14 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op }; const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt); const cmp_zero_result = try self.cmp(rhs, zero, ty, if (op == .add) .lt else .gt); - const xor = try self.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor. const cmp_bin_zero_result = try self.cmp(bin_result, zero, ty, .lt); try self.emitWValue(max_wvalue); try self.emitWValue(min_wvalue); try self.emitWValue(cmp_bin_zero_result); try self.addTag(.select); try self.emitWValue(bin_result); - try self.emitWValue(xor); + // leave on stack + _ = try self.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor. try self.addTag(.select); try self.addLabel(.local_set, bin_result.local); // re-use local return bin_result; @@ -5216,8 +5237,8 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const result = try self.allocLocal(ty); if (wasm_bits == int_info.bits) { - const shl = try self.binOp(lhs, rhs, ty, .shl); - const shr = try self.binOp(shl, rhs, ty, .shr); + const shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty); + const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty); const cmp_result = try self.cmp(lhs, shr, ty, .neq); switch (wasm_bits) { @@ -5258,9 +5279,9 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { else => unreachable, }; - const shl_res = try self.binOp(lhs, shift_value, ty, .shl); - const shl = try self.binOp(shl_res, rhs, ty, .shl); - const shr = try self.binOp(shl, rhs, ty, .shr); + const shl_res = try (try self.binOp(lhs, shift_value, ty, .shl)).toLocal(self, ty); + const shl = try (try self.binOp(shl_res, rhs, ty, .shl)).toLocal(self, ty); + const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty); const cmp_result = try self.cmp(shl_res, shr, ty, .neq); switch (wasm_bits) { @@ -5294,7 +5315,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.emitWValue(cmp_result); try self.addTag(.select); try self.addLabel(.local_set, result.local); - const shift_result = try self.binOp(result, shift_value, ty, .shr); + const shift_result = try (try self.binOp(result, shift_value, ty, .shr)).toLocal(self, ty); if (is_signed) { return self.wrapOperand(shift_result, ty); } -- 2.54.0 From 305b113a53cd5905d837ea620e2bcae8912c5938 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 25 Jul 2022 07:10:56 +0200 Subject: [PATCH 35/72] wasm: keep result of `cmp` on the stack By keeping the result on the stack, we prevent codegen from generating unneccesary locals when we have subsequent instructions that do not have to be re-used. --- src/arch/wasm/CodeGen.zig | 133 ++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 79 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index cbe4b558610b603b3368614310a98a679456951b..4e88e044900e96eb20680457e159d4587b51632c 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2659,10 +2659,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const operand_ty = self.air.typeOf(bin_op.lhs); - return self.cmp(lhs, rhs, operand_ty, op); + return (try self.cmp(lhs, rhs, operand_ty, op)).toLocal(self, Type.u32); // comparison result is always 32 bits } +/// Compares two operands. +/// Asserts rhs is not a stack value when the lhs isn't a stack value either +/// NOTE: This leaves the result on top of the stack, rather than a new local. fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue { + assert(!(lhs != .stack and rhs == .stack)); if (ty.zigTypeTag() == .Optional and !ty.optionalReprIsPayload()) { var buf: Type.Payload.ElemType = undefined; const payload_ty = ty.optionalChild(&buf); @@ -2704,9 +2708,7 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper }); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); - const cmp_tmp = try self.allocLocal(Type.initTag(.i32)); // bool is always i32 - try self.addLabel(.local_set, cmp_tmp.local); - return cmp_tmp; + return WValue{ .stack = {} }; } fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperator) InnerError!WValue { @@ -2729,9 +2731,7 @@ fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperato try self.emitWValue(ext_rhs); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); - const result = try self.allocLocal(Type.initTag(.i32)); // bool is always i32 - try self.addLabel(.local_set, result.local); - return result; + return WValue{ .stack = {} }; } fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3982,13 +3982,16 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std try self.addImm32(0); try self.addTag(if (op == .eq) .i32_ne else .i32_eq); try self.addLabel(.local_set, result.local); - return result; + try self.emitWValue(result); + return WValue{ .stack = {} }; } /// Compares big integers by checking both its high bits and low bits. +/// NOTE: Leaves the result of the comparison on top of the stack. /// TODO: Lower this to compiler_rt call when bitsize > 128 fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { assert(operand_ty.abiSize(self.target) >= 16); + assert(!(lhs != .stack and rhs == .stack)); if (operand_ty.intInfo(self.target).bits > 128) { return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits}); } @@ -4012,20 +4015,15 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma }, else => { const ty = if (operand_ty.isSignedInt()) Type.i64 else Type.u64; - const high_bit_eql = try self.cmp(lhs_high_bit, rhs_high_bit, ty, .eq); - const high_bit_cmp = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op); - const low_bit_cmp = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op); - - try self.emitWValue(low_bit_cmp); - try self.emitWValue(high_bit_cmp); - try self.emitWValue(high_bit_eql); + // leave those value on top of the stack for '.select' + _ = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op); + _ = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op); + _ = try self.cmp(lhs_high_bit, rhs_high_bit, ty, .eq); try self.addTag(.select); }, } - const result = try self.allocLocal(Type.initTag(.i32)); - try self.addLabel(.local_set, result.local); - return result; + return WValue{ .stack = {} }; } fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -4350,7 +4348,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W if (wasm_bits == int_info.bits) { const cmp_zero = try self.cmp(rhs, zero, lhs_ty, cmp_op); const lt = try self.cmp(bin_op, lhs, lhs_ty, .lt); - break :blk try (try self.binOp(cmp_zero, lt, Type.u32, .xor)).toLocal(self, Type.u32); // result of cmp_zero and lt is always 32bit + break :blk try self.binOp(cmp_zero, lt, Type.u32, .xor); } const abs = try self.signAbsValue(bin_op, lhs_ty); break :blk try self.cmp(abs, bin_op, lhs_ty, .neq); @@ -4358,11 +4356,12 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W try self.cmp(bin_op, lhs, lhs_ty, cmp_op) else try self.cmp(bin_op, result, lhs_ty, .neq); + const overflow_local = try overflow_bit.toLocal(self, Type.u32); const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); try self.store(result_ptr, result, lhs_ty, 0); const offset = @intCast(u32, lhs_ty.abiSize(self.target)); - try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); + try self.store(result_ptr, overflow_local, Type.initTag(.u1), offset); return result_ptr; } @@ -4384,9 +4383,9 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); const lt = if (op == .add) blk: { - break :blk try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt); + break :blk try (try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt)).toLocal(self, Type.u32); } else if (op == .sub) blk: { - break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); + break :blk try (try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt)).toLocal(self, Type.u32); } else unreachable; const tmp = try self.intcast(lt, Type.u32, Type.u64); const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64); @@ -4400,27 +4399,23 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, const wrap = try self.binOp(to_wrap, xor_op, Type.u64, .@"and"); break :blk try self.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed } else blk: { - const eq = try self.cmp(tmp_op, lhs_low_bit, Type.u64, .eq); - const op_eq = try self.cmp(tmp_op, lhs_low_bit, Type.u64, if (op == .add) .lt else .gt); - const first_arg = if (op == .sub) arg: { break :arg try self.cmp(high_op_res, lhs_high_bit, Type.u64, .gt); } else lt; try self.emitWValue(first_arg); - try self.emitWValue(op_eq); - try self.emitWValue(eq); + _ = try self.cmp(tmp_op, lhs_low_bit, Type.u64, if (op == .add) .lt else .gt); + _ = try self.cmp(tmp_op, lhs_low_bit, Type.u64, .eq); try self.addTag(.select); - const overflow_bit = try self.allocLocal(Type.initTag(.u1)); - try self.addLabel(.local_set, overflow_bit.local); - break :blk overflow_bit; + break :blk WValue{ .stack = {} }; }; + const overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1)); const result_ptr = try self.allocStack(result_ty); try self.store(result_ptr, high_op_res, Type.u64, 0); try self.store(result_ptr, tmp_op, Type.u64, 8); - try self.store(result_ptr, overflow_bit, Type.initTag(.u1), 16); + try self.store(result_ptr, overflow_local, Type.initTag(.u1), 16); return result_ptr; } @@ -4455,11 +4450,12 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const shr = try (try self.binOp(result, rhs, lhs_ty, .shr)).toLocal(self, lhs_ty); break :blk try self.cmp(lhs, shr, lhs_ty, .neq); }; + const overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1)); const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); try self.store(result_ptr, result, lhs_ty, 0); const offset = @intCast(u32, lhs_ty.abiSize(self.target)); - try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); + try self.store(result_ptr, overflow_local, Type.initTag(.u1), offset); return result_ptr; } @@ -4502,8 +4498,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (int_info.signedness == .unsigned) { const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); const wrap = try self.intcast(shr, new_ty, lhs_ty); - const cmp_res = try self.cmp(wrap, zero, lhs_ty, .neq); - try self.emitWValue(cmp_res); + _ = try self.cmp(wrap, zero, lhs_ty, .neq); try self.addLabel(.local_set, overflow_bit.local); break :blk try self.intcast(bin_op, new_ty, lhs_ty); } else { @@ -4512,8 +4507,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty); - const cmp_res = try self.cmp(down_shr_res, shr, lhs_ty, .neq); - try self.emitWValue(cmp_res); + _ = try self.cmp(down_shr_res, shr, lhs_ty, .neq); try self.addLabel(.local_set, overflow_bit.local); break :blk down_cast; } @@ -4522,8 +4516,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const rhs_abs = try self.signAbsValue(rhs, lhs_ty); const bin_op = try (try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul)).toLocal(self, lhs_ty); const mul_abs = try self.signAbsValue(bin_op, lhs_ty); - const cmp_op = try self.cmp(mul_abs, bin_op, lhs_ty, .neq); - try self.emitWValue(cmp_op); + _ = try self.cmp(mul_abs, bin_op, lhs_ty, .neq); try self.addLabel(.local_set, overflow_bit.local); break :blk try self.wrapOperand(bin_op, lhs_ty); } else blk: { @@ -4533,8 +4526,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { else WValue{ .imm64 = int_info.bits }; const shr = try self.binOp(bin_op, shift_imm, lhs_ty, .shr); - const cmp_op = try self.cmp(shr, zero, lhs_ty, .neq); - try self.emitWValue(cmp_op); + _ = try self.cmp(shr, zero, lhs_ty, .neq); try self.addLabel(.local_set, overflow_bit.local); break :blk try self.wrapOperand(bin_op, lhs_ty); }; @@ -4562,12 +4554,10 @@ fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerErro const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const cmp_result = try self.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt); - // operands to select from try self.lowerToStack(lhs); try self.lowerToStack(rhs); - try self.emitWValue(cmp_result); + _ = try self.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt); // based on the result from comparison, return operand 0 or 1. try self.addTag(.select); @@ -4638,7 +4628,6 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { 128 => { const msb = try self.load(operand, Type.u64, 0); const lsb = try self.load(operand, Type.u64, 8); - const neq = try self.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq); try self.emitWValue(lsb); try self.addTag(.i64_clz); @@ -4646,7 +4635,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addTag(.i64_clz); try self.emitWValue(.{ .imm64 = 64 }); try self.addTag(.i64_add); - try self.emitWValue(neq); + _ = try self.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq); try self.addTag(.select); try self.addTag(.i32_wrap_i64); }, @@ -4700,7 +4689,6 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { 128 => { const msb = try self.load(operand, Type.u64, 0); const lsb = try self.load(operand, Type.u64, 8); - const neq = try self.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq); try self.emitWValue(msb); try self.addTag(.i64_ctz); @@ -4716,7 +4704,7 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } else { try self.addTag(.i64_add); } - try self.emitWValue(neq); + _ = try self.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq); try self.addTag(.select); try self.addTag(.i32_wrap_i64); }, @@ -4952,15 +4940,13 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { 64 => WValue{ .imm64 = 0 }, else => unreachable, }; - const lhs_less_than_zero = try self.cmp(lhs_res, zero, ty, .lt); - const rhs_less_than_zero = try self.cmp(rhs_res, zero, ty, .lt); const div_result = try self.allocLocal(ty); // leave on stack _ = try self.binOp(lhs_res, rhs_res, ty, .div); try self.addLabel(.local_tee, div_result.local); - try self.emitWValue(lhs_less_than_zero); - try self.emitWValue(rhs_less_than_zero); + _ = try self.cmp(lhs_res, zero, ty, .lt); + _ = try self.cmp(rhs_res, zero, ty, .lt); switch (wasm_bits) { 32 => { try self.addTag(.i32_xor); @@ -5140,19 +5126,17 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { else => unreachable, }; - const cmp_result = try self.cmp(bin_result, imm_val, ty, .lt); try self.emitWValue(bin_result); try self.emitWValue(imm_val); - try self.emitWValue(cmp_result); + _ = try self.cmp(bin_result, imm_val, ty, .lt); } else { - const cmp_result = try self.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt); switch (wasm_bits) { 32 => try self.addImm32(if (op == .add) @as(i32, -1) else 0), 64 => try self.addImm64(if (op == .add) @bitCast(u64, @as(i64, -1)) else 0), else => unreachable, } try self.emitWValue(bin_result); - try self.emitWValue(cmp_result); + _ = try self.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt); } try self.addTag(.select); @@ -5184,17 +5168,15 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op const bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty); if (!is_wasm_bits) { - const cmp_result_lt = try self.cmp(bin_result, max_wvalue, ty, .lt); try self.emitWValue(bin_result); try self.emitWValue(max_wvalue); - try self.emitWValue(cmp_result_lt); + _ = try self.cmp(bin_result, max_wvalue, ty, .lt); try self.addTag(.select); try self.addLabel(.local_set, bin_result.local); // re-use local - const cmp_result_gt = try self.cmp(bin_result, min_wvalue, ty, .gt); try self.emitWValue(bin_result); try self.emitWValue(min_wvalue); - try self.emitWValue(cmp_result_gt); + _ = try self.cmp(bin_result, min_wvalue, ty, .gt); try self.addTag(.select); try self.addLabel(.local_set, bin_result.local); // re-use local return self.wrapOperand(bin_result, ty); @@ -5204,15 +5186,14 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op 64 => WValue{ .imm64 = 0 }, else => unreachable, }; - const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt); + try self.emitWValue(max_wvalue); + try self.emitWValue(min_wvalue); + _ = try self.cmp(bin_result, zero, ty, .lt); + try self.addTag(.select); + try self.emitWValue(bin_result); + // leave on stack const cmp_zero_result = try self.cmp(rhs, zero, ty, if (op == .add) .lt else .gt); - const cmp_bin_zero_result = try self.cmp(bin_result, zero, ty, .lt); - try self.emitWValue(max_wvalue); - try self.emitWValue(min_wvalue); - try self.emitWValue(cmp_bin_zero_result); - try self.addTag(.select); - try self.emitWValue(bin_result); - // leave on stack + const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt); _ = try self.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor. try self.addTag(.select); try self.addLabel(.local_set, bin_result.local); // re-use local @@ -5239,7 +5220,6 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (wasm_bits == int_info.bits) { const shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty); const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty); - const cmp_result = try self.cmp(lhs, shr, ty, .neq); switch (wasm_bits) { 32 => blk: { @@ -5247,10 +5227,9 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addImm32(-1); break :blk; } - const less_than_zero = try self.cmp(lhs, .{ .imm32 = 0 }, ty, .lt); try self.addImm32(std.math.minInt(i32)); try self.addImm32(std.math.maxInt(i32)); - try self.emitWValue(less_than_zero); + _ = try self.cmp(lhs, .{ .imm32 = 0 }, ty, .lt); try self.addTag(.select); }, 64 => blk: { @@ -5258,16 +5237,15 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addImm64(@bitCast(u64, @as(i64, -1))); break :blk; } - const less_than_zero = try self.cmp(lhs, .{ .imm64 = 0 }, ty, .lt); try self.addImm64(@bitCast(u64, @as(i64, std.math.minInt(i64)))); try self.addImm64(@bitCast(u64, @as(i64, std.math.maxInt(i64)))); - try self.emitWValue(less_than_zero); + _ = try self.cmp(lhs, .{ .imm64 = 0 }, ty, .lt); try self.addTag(.select); }, else => unreachable, } try self.emitWValue(shl); - try self.emitWValue(cmp_result); + _ = try self.cmp(lhs, shr, ty, .neq); try self.addTag(.select); try self.addLabel(.local_set, result.local); return result; @@ -5282,7 +5260,6 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const shl_res = try (try self.binOp(lhs, shift_value, ty, .shl)).toLocal(self, ty); const shl = try (try self.binOp(shl_res, rhs, ty, .shl)).toLocal(self, ty); const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty); - const cmp_result = try self.cmp(shl_res, shr, ty, .neq); switch (wasm_bits) { 32 => blk: { @@ -5291,10 +5268,9 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { break :blk; } - const less_than_zero = try self.cmp(shl_res, .{ .imm32 = 0 }, ty, .lt); try self.addImm32(std.math.minInt(i32)); try self.addImm32(std.math.maxInt(i32)); - try self.emitWValue(less_than_zero); + _ = try self.cmp(shl_res, .{ .imm32 = 0 }, ty, .lt); try self.addTag(.select); }, 64 => blk: { @@ -5303,16 +5279,15 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { break :blk; } - const less_than_zero = try self.cmp(shl_res, .{ .imm64 = 0 }, ty, .lt); try self.addImm64(@bitCast(u64, @as(i64, std.math.minInt(i64)))); try self.addImm64(@bitCast(u64, @as(i64, std.math.maxInt(i64)))); - try self.emitWValue(less_than_zero); + _ = try self.cmp(shl_res, .{ .imm64 = 0 }, ty, .lt); try self.addTag(.select); }, else => unreachable, } try self.emitWValue(shl); - try self.emitWValue(cmp_result); + _ = try self.cmp(shl_res, shr, ty, .neq); try self.addTag(.select); try self.addLabel(.local_set, result.local); const shift_result = try (try self.binOp(result, shift_value, ty, .shr)).toLocal(self, ty); -- 2.54.0 From 699bc6171dcc18d681de754ca2eeb63645aea889 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 25 Jul 2022 16:27:26 +0200 Subject: [PATCH 36/72] wasm: Keep `intcast` values on stack --- src/arch/wasm/CodeGen.zig | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 4e88e044900e96eb20680457e159d4587b51632c..c91edca37d9122d1b7cbece626f520acb358d1b8 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2153,8 +2153,8 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr const rhs_high_bit = try self.load(rhs, Type.u64, 0); const rhs_low_bit = try self.load(rhs, Type.u64, 8); - const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64); const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); + const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); const lt = if (op == .add) blk: { break :blk try self.cmp(high_op_res, rhs_high_bit, Type.u64, .lt); @@ -3190,12 +3190,13 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.fail("todo Wasm intcast for bitsize > 128", .{}); } - return self.intcast(operand, operand_ty, ty); + return (try self.intcast(operand, operand_ty, ty)).toLocal(self, ty); } /// Upcasts or downcasts an integer based on the given and wanted types, /// and stores the result in a new operand. /// Asserts type's bitsize <= 128 +/// NOTE: May leave the result on the top of the stack. fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue { const given_info = given.intInfo(self.target); const wanted_info = wanted.intInfo(self.target); @@ -3227,7 +3228,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W given, if (wanted.isSignedInt()) Type.i64 else Type.u64, ); - break :blk tmp; + break :blk try tmp.toLocal(self, Type.u64); } else operand; // store msb first @@ -3244,9 +3245,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W return stack_ptr; } else return self.load(operand, wanted, 0); - const result = try self.allocLocal(wanted); - try self.addLabel(.local_set, result.local); - return result; + return WValue{ .stack = {} }; } fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!WValue { @@ -3478,7 +3477,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (wasm_bits != wanted_bits) { return self.wrapOperand(result, wanted_ty); } - return result; + return result.toLocal(self, wanted_ty); } fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -4387,7 +4386,7 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, } else if (op == .sub) blk: { break :blk try (try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt)).toLocal(self, Type.u32); } else unreachable; - const tmp = try self.intcast(lt, Type.u32, Type.u64); + const tmp = try (try self.intcast(lt, Type.u32, Type.u64)).toLocal(self, Type.u64); const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64); const overflow_bit = if (is_signed) blk: { @@ -4500,9 +4499,9 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const wrap = try self.intcast(shr, new_ty, lhs_ty); _ = try self.cmp(wrap, zero, lhs_ty, .neq); try self.addLabel(.local_set, overflow_bit.local); - break :blk try self.intcast(bin_op, new_ty, lhs_ty); + break :blk try (try self.intcast(bin_op, new_ty, lhs_ty)).toLocal(self, lhs_ty); } else { - const down_cast = try self.intcast(bin_op, new_ty, lhs_ty); + const down_cast = try (try self.intcast(bin_op, new_ty, lhs_ty)).toLocal(self, lhs_ty); const shr = try (try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(self, lhs_ty); const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); -- 2.54.0 From cde16f61eb41f2beda85e838331a30869e420c68 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 26 Jul 2022 16:46:58 +0200 Subject: [PATCH 37/72] wasm: `wrapOperand` - leave value on the stack This also does it for `wrapBinOp` which internally uses the already refactored `binOp` and `wrapOperand` heavily simplifying this function and not duplicate the logic from `binOp` --- src/arch/wasm/CodeGen.zig | 85 +++++++++++++++------------------------ 1 file changed, 33 insertions(+), 52 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index c91edca37d9122d1b7cbece626f520acb358d1b8..75e989764ff065f924966aac348616fa9a731b1a 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2179,40 +2179,22 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { return self.fail("TODO: Implement wrapping arithmetic for vectors", .{}); } - return self.wrapBinOp(lhs, rhs, ty, op); + return (try self.wrapBinOp(lhs, rhs, ty, op)).toLocal(self, ty); } +/// Performs a wrapping binary operation. +/// Asserts rhs is not a stack value when lhs also isn't. +/// NOTE: Leaves the result on the stack when its Type is <= 64 bits fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { - const bit_size = ty.intInfo(self.target).bits; - var wasm_bits = toWasmBits(bit_size) orelse { - return self.fail("TODO: Implement wrapping arithmetic for integers with bitsize: {d}\n", .{bit_size}); - }; - - if (wasm_bits == 128) { - const bin_op = try self.binOpBigInt(lhs, rhs, ty, op); - return self.wrapOperand(bin_op, ty); - } - - const opcode: wasm.Opcode = buildOpcode(.{ - .op = op, - .valtype1 = typeToValtype(ty, self.target), - .signedness = if (ty.isSignedInt()) .signed else .unsigned, - }); - - try self.emitWValue(lhs); - try self.emitWValue(rhs); - try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); - const bin_local = try self.allocLocal(ty); - try self.addLabel(.local_set, bin_local.local); - + const bin_local = try self.binOp(lhs, rhs, ty, op); return self.wrapOperand(bin_local, ty); } /// Wraps an operand based on a given type's bitsize. /// Asserts `Type` is <= 128 bits. +/// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack. fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { assert(ty.abiSize(self.target) <= 16); - const result_local = try self.allocLocal(ty); const bitsize = ty.intInfo(self.target).bits; const wasm_bits = toWasmBits(bitsize) orelse { return self.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize}); @@ -2246,8 +2228,7 @@ fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { try self.addTag(.i64_and); } else unreachable; - try self.addLabel(.local_set, result_local.local); - return result_local; + return WValue{ .stack = {} }; } fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue { @@ -3471,11 +3452,11 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{int_info.bits}); } - const result = try self.intcast(operand, op_ty, wanted_ty); + var result = try self.intcast(operand, op_ty, wanted_ty); const wanted_bits = wanted_ty.intInfo(self.target).bits; const wasm_bits = toWasmBits(wanted_bits).?; if (wasm_bits != wanted_bits) { - return self.wrapOperand(result, wanted_ty); + result = try self.wrapOperand(result, wanted_ty); } return result.toLocal(self, wanted_ty); } @@ -3749,11 +3730,8 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { .signedness = if (dest_ty.isSignedInt()) .signed else .unsigned, }); try self.addTag(Mir.Inst.Tag.fromOpcode(op)); - - const result = try self.allocLocal(dest_ty); - try self.addLabel(.local_set, result.local); - - return self.wrapOperand(result, dest_ty); + const wrapped = try self.wrapOperand(.{ .stack = {} }, dest_ty); + return wrapped.toLocal(self, dest_ty); } fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -4339,7 +4317,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty); const result = if (wasm_bits != int_info.bits) blk: { - break :blk try self.wrapOperand(bin_op, lhs_ty); + break :blk try (try self.wrapOperand(bin_op, lhs_ty)).toLocal(self, lhs_ty); } else bin_op; const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt; @@ -4438,13 +4416,15 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty); const result = if (wasm_bits != int_info.bits) blk: { - break :blk try self.wrapOperand(shl, lhs_ty); + break :blk try (try self.wrapOperand(shl, lhs_ty)).toLocal(self, lhs_ty); } else shl; const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: { const abs = try self.signAbsValue(shl, lhs_ty); + // emit lhs to stack to we can keep 'wrapped' on the stack also + try self.emitWValue(lhs); const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr); - break :blk try self.cmp(lhs, wrapped, lhs_ty, .neq); + break :blk try self.cmp(.{ .stack = {} }, wrapped, lhs_ty, .neq); } else blk: { const shr = try (try self.binOp(result, rhs, lhs_ty, .shr)).toLocal(self, lhs_ty); break :blk try self.cmp(lhs, shr, lhs_ty, .neq); @@ -4499,7 +4479,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const wrap = try self.intcast(shr, new_ty, lhs_ty); _ = try self.cmp(wrap, zero, lhs_ty, .neq); try self.addLabel(.local_set, overflow_bit.local); - break :blk try (try self.intcast(bin_op, new_ty, lhs_ty)).toLocal(self, lhs_ty); + break :blk try self.intcast(bin_op, new_ty, lhs_ty); } else { const down_cast = try (try self.intcast(bin_op, new_ty, lhs_ty)).toLocal(self, lhs_ty); const shr = try (try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(self, lhs_ty); @@ -4529,9 +4509,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addLabel(.local_set, overflow_bit.local); break :blk try self.wrapOperand(bin_op, lhs_ty); }; + const bin_op_local = try bin_op.toLocal(self, lhs_ty); const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); - try self.store(result_ptr, bin_op, lhs_ty, 0); + try self.store(result_ptr, bin_op_local, lhs_ty, 0); const offset = @intCast(u32, lhs_ty.abiSize(self.target)); try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); @@ -4854,29 +4835,29 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { switch (int_info.bits) { 16 => { const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl); - const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and")).toLocal(self, ty); - const shr_res = try (try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); + const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and"); + const shr_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr); const res = if (int_info.signedness == .signed) blk: { break :blk try self.wrapOperand(shr_res, Type.u8); } else shr_res; return (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty); }, 24 => { - const msb = try self.wrapOperand(operand, Type.u16); - const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr); + const msb = try (try self.wrapOperand(operand, Type.u16)).toLocal(self, Type.u16); const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl); - const lhs = try (try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and")).toLocal(self, Type.u16); - const shr_res = try (try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); + const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and"); + const shr_res = try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr); const res = if (int_info.signedness == .signed) blk: { break :blk try self.wrapOperand(shr_res, Type.u8); } else shr_res; const lhs_tmp = try self.binOp(lhs, res, ty, .@"or"); - const lhs_result = try (try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr)).toLocal(self, ty); + const lhs_result = try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr); const rhs_wrap = try self.wrapOperand(msb, Type.u8); - const rhs_result = try (try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl)).toLocal(self, ty); + const rhs_result = try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl); + const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr); const tmp = try self.binOp(lhs_result, rhs_result, ty, .@"or"); return (try self.binOp(tmp, lsb, ty, .@"or")).toLocal(self, ty); }, @@ -4887,11 +4868,11 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty); const tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty); - const shr = try (try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr)).toLocal(self, ty); + const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl); + const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr); const res = if (int_info.signedness == .signed) blk: { break :blk try self.wrapOperand(shr, Type.u16); } else shr; - const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl); return (try self.binOp(shl, res, ty, .@"or")).toLocal(self, ty); }, else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}), @@ -5178,7 +5159,7 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op _ = try self.cmp(bin_result, min_wvalue, ty, .gt); try self.addTag(.select); try self.addLabel(.local_set, bin_result.local); // re-use local - return self.wrapOperand(bin_result, ty); + return (try self.wrapOperand(bin_result, ty)).toLocal(self, ty); } else { const zero = switch (wasm_bits) { 32 => WValue{ .imm32 = 0 }, @@ -5289,11 +5270,11 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { _ = try self.cmp(shl_res, shr, ty, .neq); try self.addTag(.select); try self.addLabel(.local_set, result.local); - const shift_result = try (try self.binOp(result, shift_value, ty, .shr)).toLocal(self, ty); + var shift_result = try self.binOp(result, shift_value, ty, .shr); if (is_signed) { - return self.wrapOperand(shift_result, ty); + shift_result = try self.wrapOperand(shift_result, ty); } - return shift_result; + return try shift_result.toLocal(self, ty); } } -- 2.54.0 From 3cd0cd12a08435fca5f5b2b6788ff519abfc6184 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 26 Jul 2022 17:24:18 +0200 Subject: [PATCH 38/72] wasm: leave `signedAbsValue` values on the stack --- src/arch/wasm/CodeGen.zig | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 75e989764ff065f924966aac348616fa9a731b1a..2ee6a76401a3d5f4222c6c0169bdfbd715c09062 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -4309,10 +4309,10 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W // for signed integers, we first apply signed shifts by the difference in bits // to get the signed value, as we store it internally as 2's complement. const lhs = if (wasm_bits != int_info.bits and is_signed) blk: { - break :blk try self.signAbsValue(lhs_op, lhs_ty); + break :blk try (try self.signAbsValue(lhs_op, lhs_ty)).toLocal(self, lhs_ty); } else lhs_op; const rhs = if (wasm_bits != int_info.bits and is_signed) blk: { - break :blk try self.signAbsValue(rhs_op, lhs_ty); + break :blk try (try self.signAbsValue(rhs_op, lhs_ty)).toLocal(self, lhs_ty); } else rhs_op; const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty); @@ -4420,9 +4420,9 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } else shl; const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: { - const abs = try self.signAbsValue(shl, lhs_ty); // emit lhs to stack to we can keep 'wrapped' on the stack also try self.emitWValue(lhs); + const abs = try self.signAbsValue(shl, lhs_ty); const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr); break :blk try self.cmp(.{ .stack = {} }, wrapped, lhs_ty, .neq); } else blk: { @@ -4909,10 +4909,10 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.fail("TODO: `@divFloor` for signed integers larger than '{d}' bits", .{int_bits}); }; const lhs_res = if (wasm_bits != int_bits) blk: { - break :blk try self.signAbsValue(lhs, ty); + break :blk try (try self.signAbsValue(lhs, ty)).toLocal(self, ty); } else lhs; const rhs_res = if (wasm_bits != int_bits) blk: { - break :blk try self.signAbsValue(rhs, ty); + break :blk try (try self.signAbsValue(rhs, ty)).toLocal(self, ty); } else rhs; const zero = switch (wasm_bits) { @@ -4994,10 +4994,9 @@ fn divSigned(self: *Self, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue } if (wasm_bits != int_bits) { - const lhs_abs = try self.signAbsValue(lhs, ty); - const rhs_abs = try self.signAbsValue(rhs, ty); - try self.emitWValue(lhs_abs); - try self.emitWValue(rhs_abs); + // Leave both values on the stack + _ = try self.signAbsValue(lhs, ty); + _ = try self.signAbsValue(rhs, ty); } else { try self.emitWValue(lhs); try self.emitWValue(rhs); @@ -5009,6 +5008,8 @@ fn divSigned(self: *Self, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue return result; } +/// Retrieves the absolute value of a signed integer +/// NOTE: Leaves the result value on the stack. fn signAbsValue(self: *Self, operand: WValue, ty: Type) InnerError!WValue { const int_bits = ty.intInfo(self.target).bits; const wasm_bits = toWasmBits(int_bits) orelse { @@ -5037,9 +5038,8 @@ fn signAbsValue(self: *Self, operand: WValue, ty: Type) InnerError!WValue { }, else => unreachable, } - const result = try self.allocLocal(ty); - try self.addLabel(.local_set, result.local); - return result; + + return WValue{ .stack = {} }; } fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { @@ -5130,8 +5130,12 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op const wasm_bits = toWasmBits(int_info.bits).?; const is_wasm_bits = wasm_bits == int_info.bits; - const lhs = if (!is_wasm_bits) try self.signAbsValue(lhs_operand, ty) else lhs_operand; - const rhs = if (!is_wasm_bits) try self.signAbsValue(rhs_operand, ty) else rhs_operand; + const lhs = if (!is_wasm_bits) lhs: { + break :lhs try (try self.signAbsValue(lhs_operand, ty)).toLocal(self, ty); + } else lhs_operand; + const rhs = if (!is_wasm_bits) rhs: { + break :rhs try (try self.signAbsValue(rhs_operand, ty)).toLocal(self, ty); + } else rhs_operand; const max_val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits - 1)) - 1); const min_val: i64 = (-@intCast(i64, @intCast(u63, max_val))) - 1; -- 2.54.0 From a5e4fd7ef66bbd16ddad1a12d378eefcb740da1d Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 27 Jul 2022 19:14:42 +0200 Subject: [PATCH 39/72] wasm: keep `load` values on the stack We internally use a lot of `load`'s that used to put the result in a newly created local. For instance, when is considered byRef or when we need a specific field/element/bytes from a larger type. However, sometimes we want to directly use this value and then forget about it, which means storing it in a local first is wasted instructions as well as wasted locals that shouldn't be generated in the first place. With this change it's explicit and requires the usage of `toLocal`. --- src/arch/wasm/CodeGen.zig | 229 +++++++++++++++++++------------------- 1 file changed, 116 insertions(+), 113 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 2ee6a76401a3d5f4222c6c0169bdfbd715c09062..728b4af651dae876cc797fb269c195d6bde3275b 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1789,8 +1789,8 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const fn_info = self.decl.ty.fnInfo(); if (!firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) { - const result = try self.load(operand, ret_ty, 0); - try self.emitWValue(result); + // leave on the stack + _ = try self.load(operand, ret_ty, 0); } try self.restoreStackPointer(); @@ -1943,20 +1943,26 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro .Pointer => { if (ty.isSlice()) { // store pointer first + // lower it to the stack so we do not have to store rhs into a local first + try self.emitWValue(lhs); const ptr_local = try self.load(rhs, Type.usize, 0); - try self.store(lhs, ptr_local, Type.usize, 0); + try self.store(.{ .stack = {} }, ptr_local, Type.usize, 0 + lhs.offset()); // retrieve length from rhs, and store that alongside lhs as well + try self.emitWValue(lhs); const len_local = try self.load(rhs, Type.usize, self.ptrSize()); - try self.store(lhs, len_local, Type.usize, self.ptrSize()); + try self.store(.{ .stack = {} }, len_local, Type.usize, self.ptrSize() + lhs.offset()); return; } }, .Int => if (ty.intInfo(self.target).bits > 64) { + try self.emitWValue(lhs); const lsb = try self.load(rhs, Type.u64, 0); + try self.store(.{ .stack = {} }, lsb, Type.u64, 0 + lhs.offset()); + + try self.emitWValue(lhs); const msb = try self.load(rhs, Type.u64, 8); - try self.store(lhs, lsb, Type.u64, 0); - try self.store(lhs, msb, Type.u64, 8); + try self.store(.{ .stack = {} }, msb, Type.u64, 8 + lhs.offset()); return; }, else => {}, @@ -1995,9 +2001,12 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return new_local; } - return self.load(operand, ty, 0); + const stack_loaded = try self.load(operand, ty, 0); + return stack_loaded.toLocal(self, ty); } +/// Loads an operand from the linear memory section. +/// NOTE: Leaves the value on the stack. fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { // load local's value from memory by its stack position try self.emitWValue(operand); @@ -2015,10 +2024,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { .{ .offset = offset + operand.offset(), .alignment = ty.abiAlignment(self.target) }, ); - // store the result in a local - const result = try self.allocLocal(ty); - try self.addLabel(.local_set, result.local); - return result; + return WValue{ .stack = {} }; } fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -2124,18 +2130,15 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa return WValue{ .stack = {} }; } +/// Performs a binary operation for 16-bit floats. +/// NOTE: Leaves the result value on the stack fn binOpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue { - const ext_lhs = try self.fpext(lhs, Type.f16, Type.f32); - const ext_rhs = try self.fpext(rhs, Type.f16, Type.f32); - const opcode: wasm.Opcode = buildOpcode(.{ .op = op, .valtype1 = .f32, .signedness = .unsigned }); - try self.emitWValue(ext_lhs); - try self.emitWValue(ext_rhs); + _ = try self.fpext(lhs, Type.f16, Type.f32); + _ = try self.fpext(rhs, Type.f16, Type.f32); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); - // re-use temporary local - try self.addLabel(.local_set, ext_lhs.local); - return self.fptrunc(ext_lhs, Type.f32, Type.f16); + return self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16); } fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { @@ -2148,12 +2151,12 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr } const result = try self.allocStack(ty); - const lhs_high_bit = try self.load(lhs, Type.u64, 0); + const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); + const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64); + const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); + const lhs_low_bit = try self.load(lhs, Type.u64, 8); - const rhs_high_bit = try self.load(rhs, Type.u64, 0); const rhs_low_bit = try self.load(rhs, Type.u64, 8); - - const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); const lt = if (op == .add) blk: { @@ -2204,14 +2207,14 @@ fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { if (wasm_bits == 128) { assert(operand != .stack); - const msb = try self.load(operand, Type.u64, 0); const lsb = try self.load(operand, Type.u64, 8); const result_ptr = try self.allocStack(ty); - try self.store(result_ptr, lsb, Type.u64, 8); + try self.emitWValue(result_ptr); + try self.store(.{ .stack = {} }, lsb, Type.u64, 8 + result_ptr.offset()); const result = (@as(u64, 1) << @intCast(u6, 64 - (wasm_bits - bitsize))) - 1; try self.emitWValue(result_ptr); - try self.emitWValue(msb); + _ = try self.load(operand, Type.u64, 0); try self.addImm64(result); try self.addTag(.i64_and); try self.addMemArg(.i64_store, .{ .offset = result_ptr.offset(), .alignment = 8 }); @@ -2692,10 +2695,9 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper return WValue{ .stack = {} }; } +/// Compares 16-bit floats +/// NOTE: The result value remains on top of the stack. fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperator) InnerError!WValue { - const ext_lhs = try self.fpext(lhs, Type.f16, Type.f32); - const ext_rhs = try self.fpext(rhs, Type.f16, Type.f32); - const opcode: wasm.Opcode = buildOpcode(.{ .op = switch (op) { .lt => .lt, @@ -2708,8 +2710,8 @@ fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperato .valtype1 = .f32, .signedness = .unsigned, }); - try self.emitWValue(ext_lhs); - try self.emitWValue(ext_rhs); + _ = try self.fpext(lhs, Type.f16, Type.f32); + _ = try self.fpext(rhs, Type.f16, Type.f32); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); return WValue{ .stack = {} }; @@ -2781,13 +2783,15 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { }, 128 => { const result_ptr = try self.allocStack(operand_ty); + try self.emitWValue(result_ptr); const msb = try self.load(operand, Type.u64, 0); + const msb_xor = try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); + try self.store(.{ .stack = {} }, msb_xor, Type.u64, 0 + result_ptr.offset()); + + try self.emitWValue(result_ptr); const lsb = try self.load(operand, Type.u64, 8); - - const msb_xor = try (try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty); - const lsb_xor = try (try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty); - try self.store(result_ptr, msb_xor, Type.u64, 0); - try self.store(result_ptr, lsb_xor, Type.u64, 8); + const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); + try self.store(result_ptr, lsb_xor, Type.u64, 8 + result_ptr.offset()); return result_ptr; }, else => unreachable, @@ -2875,7 +2879,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } } - return self.load(operand, field_ty, offset); + const field = try self.load(operand, field_ty, offset); + return field.toLocal(self, field_ty); } fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3085,7 +3090,9 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) if (op_is_ptr or isByRef(payload_ty, self.target)) { return self.buildPointerOffset(operand, pl_offset, .new); } - return self.load(operand, payload_ty, pl_offset); + + const payload = try self.load(operand, payload_ty, pl_offset); + return payload.toLocal(self, payload_ty); } fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!WValue { @@ -3105,7 +3112,8 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In return operand; } - return self.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, self.target))); + const error_val = try self.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, self.target))); + return error_val.toLocal(self, Type.anyerror); } fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3235,9 +3243,12 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en const op_ty = self.air.typeOf(un_op); const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty; - return self.isNull(operand, optional_ty, opcode); + const is_null = try self.isNull(operand, optional_ty, opcode); + return is_null.toLocal(self, optional_ty); } +/// For a given type and operand, checks if it's considered `null`. +/// NOTE: Leaves the result on the stack fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { try self.emitWValue(operand); if (!optional_ty.optionalReprIsPayload()) { @@ -3254,9 +3265,7 @@ fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) try self.addImm32(0); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); - const is_null_tmp = try self.allocLocal(Type.initTag(.i32)); - try self.addLabel(.local_set, is_null_tmp.local); - return is_null_tmp; + return WValue{ .stack = {} }; } fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3274,7 +3283,8 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.buildPointerOffset(operand, offset, .new); } - return self.load(operand, payload_ty, @intCast(u32, offset)); + const payload = try self.load(operand, payload_ty, @intCast(u32, offset)); + return payload.toLocal(self, payload_ty); } fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3377,7 +3387,8 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); - return self.load(operand, Type.usize, self.ptrSize()); + const len = try self.load(operand, Type.usize, self.ptrSize()); + return len.toLocal(self, Type.usize); } fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3391,8 +3402,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const elem_size = elem_ty.abiSize(self.target); // load pointer onto stack - const slice_ptr = try self.load(slice, Type.usize, 0); - try self.addLabel(.local_get, slice_ptr.local); + _ = try self.load(slice, Type.usize, 0); // calculate index into slice try self.emitWValue(index); @@ -3406,7 +3416,9 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (isByRef(elem_ty, self.target)) { return result; } - return self.load(result, elem_ty, 0); + + const elem_val = try self.load(result, elem_ty, 0); + return elem_val.toLocal(self, elem_ty); } fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3419,8 +3431,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const slice = try self.resolveInst(bin_op.lhs); const index = try self.resolveInst(bin_op.rhs); - const slice_ptr = try self.load(slice, Type.usize, 0); - try self.addLabel(.local_get, slice_ptr.local); + _ = try self.load(slice, Type.usize, 0); // calculate index into slice try self.emitWValue(index); @@ -3428,7 +3439,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addTag(.i32_mul); try self.addTag(.i32_add); - const result = try self.allocLocal(Type.initTag(.i32)); + const result = try self.allocLocal(Type.i32); try self.addLabel(.local_set, result.local); return result; } @@ -3437,7 +3448,8 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); - return self.load(operand, Type.usize, 0); + const ptr = try self.load(operand, Type.usize, 0); + return ptr.toLocal(self, Type.usize); } fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3511,8 +3523,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // load pointer onto the stack if (ptr_ty.isSlice()) { - const ptr_local = try self.load(ptr, Type.usize, 0); - try self.addLabel(.local_get, ptr_local.local); + _ = try self.load(ptr, Type.usize, 0); } else { try self.lowerToStack(ptr); } @@ -3528,7 +3539,9 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (isByRef(elem_ty, self.target)) { return result; } - return self.load(result, elem_ty, 0); + + const elem_val = try self.load(result, elem_ty, 0); + return elem_val.toLocal(self, elem_ty); } fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3544,8 +3557,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // load pointer onto the stack if (ptr_ty.isSlice()) { - const ptr_local = try self.load(ptr, Type.usize, 0); - try self.addLabel(.local_get, ptr_local.local); + _ = try self.load(ptr, Type.usize, 0); } else { try self.lowerToStack(ptr); } @@ -3556,7 +3568,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addTag(.i32_mul); try self.addTag(.i32_add); - const result = try self.allocLocal(Type.initTag(.i32)); + const result = try self.allocLocal(Type.i32); try self.addLabel(.local_set, result.local); return result; } @@ -3707,7 +3719,8 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (isByRef(elem_ty, self.target)) { return result; } - return self.load(result, elem_ty, 0); + const elem_val = try self.load(result, elem_ty, 0); + return elem_val.toLocal(self, elem_ty); } fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3929,24 +3942,18 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std const payload_ty = operand_ty.optionalChild(&buf); const offset = @intCast(u32, operand_ty.abiSize(self.target) - payload_ty.abiSize(self.target)); - const lhs_is_null = try self.isNull(lhs, operand_ty, .i32_eq); - const rhs_is_null = try self.isNull(rhs, operand_ty, .i32_eq); - // We store the final result in here that will be validated // if the optional is truly equal. const result = try self.allocLocal(Type.initTag(.i32)); try self.startBlock(.block, wasm.block_empty); - try self.emitWValue(lhs_is_null); - try self.emitWValue(rhs_is_null); + _ = try self.isNull(lhs, operand_ty, .i32_eq); + _ = try self.isNull(rhs, operand_ty, .i32_eq); try self.addTag(.i32_ne); // inverse so we can exit early try self.addLabel(.br_if, 0); - const lhs_pl = try self.load(lhs, payload_ty, offset); - const rhs_pl = try self.load(rhs, payload_ty, offset); - - try self.emitWValue(lhs_pl); - try self.emitWValue(rhs_pl); + _ = try self.load(lhs, payload_ty, offset); + _ = try self.load(rhs, payload_ty, offset); const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, self.target) }); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); try self.addLabel(.br_if, 0); @@ -3973,14 +3980,14 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits}); } - const lhs_high_bit = try self.load(lhs, Type.u64, 0); - const lhs_low_bit = try self.load(lhs, Type.u64, 8); - const rhs_high_bit = try self.load(rhs, Type.u64, 0); - const rhs_low_bit = try self.load(rhs, Type.u64, 8); + const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); + const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64); switch (op) { .eq, .neq => { const xor_high = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor); + const lhs_low_bit = try self.load(lhs, Type.u64, 8); + const rhs_low_bit = try self.load(rhs, Type.u64, 8); const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor); const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or"); @@ -3993,6 +4000,8 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma else => { const ty = if (operand_ty.isSignedInt()) Type.i64 else Type.u64; // leave those value on top of the stack for '.select' + const lhs_low_bit = try self.load(lhs, Type.u64, 8); + const rhs_low_bit = try self.load(rhs, Type.u64, 8); _ = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op); _ = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op); _ = try self.cmp(lhs_high_bit, rhs_high_bit, ty, .eq); @@ -4040,7 +4049,8 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const offset = if (layout.tag_align < layout.payload_align) blk: { break :blk @intCast(u32, layout.payload_size); } else @as(u32, 0); - return self.load(operand, tag_ty, offset); + const tag = try self.load(operand, tag_ty, offset); + return tag.toLocal(self, tag_ty); } fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -4050,19 +4060,20 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const dest_ty = self.air.typeOfIndex(inst); const operand = try self.resolveInst(ty_op.operand); - return self.fpext(operand, self.air.typeOf(ty_op.operand), dest_ty); + const extended = try self.fpext(operand, self.air.typeOf(ty_op.operand), dest_ty); + return extended.toLocal(self, dest_ty); } +/// Extends a float from a given `Type` to a larger wanted `Type` +/// NOTE: Leaves the result on the stack fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue { const given_bits = given.floatBits(self.target); const wanted_bits = wanted.floatBits(self.target); if (wanted_bits == 64 and given_bits == 32) { - const result = try self.allocLocal(wanted); try self.emitWValue(operand); try self.addTag(.f64_promote_f32); - try self.addLabel(.local_set, result.local); - return result; + return WValue{ .stack = {} }; } else if (given_bits == 16) { // call __extendhfsf2(f16) f32 const f32_result = try self.callIntrinsic( @@ -4076,11 +4087,9 @@ fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WVa return f32_result; } if (wanted_bits == 64) { - const result = try self.allocLocal(wanted); try self.emitWValue(f32_result); try self.addTag(.f64_promote_f32); - try self.addLabel(.local_set, result.local); - return result; + return WValue{ .stack = {} }; } return self.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits}); } else { @@ -4095,26 +4104,25 @@ fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const dest_ty = self.air.typeOfIndex(inst); const operand = try self.resolveInst(ty_op.operand); - return self.fptrunc(operand, self.air.typeOf(ty_op.operand), dest_ty); + const trunc = try self.fptrunc(operand, self.air.typeOf(ty_op.operand), dest_ty); + return trunc.toLocal(self, dest_ty); } +/// Truncates a float from a given `Type` to its wanted `Type` +/// NOTE: The result value remains on the stack fn fptrunc(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue { const given_bits = given.floatBits(self.target); const wanted_bits = wanted.floatBits(self.target); if (wanted_bits == 32 and given_bits == 64) { - const result = try self.allocLocal(wanted); try self.emitWValue(operand); try self.addTag(.f32_demote_f64); - try self.addLabel(.local_set, result.local); - return result; + return WValue{ .stack = {} }; } else if (wanted_bits == 16) { const op: WValue = if (given_bits == 64) blk: { - const tmp = try self.allocLocal(Type.f32); try self.emitWValue(operand); try self.addTag(.f32_demote_f64); - try self.addLabel(.local_set, tmp.local); - break :blk tmp; + break :blk WValue{ .stack = {} }; } else operand; // call __truncsfhf2(f32) f16 @@ -4199,12 +4207,9 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue { switch (wasm_bits) { 128 => { - const msb = try self.load(operand, Type.u64, 0); - const lsb = try self.load(operand, Type.u64, 8); - - try self.emitWValue(msb); + _ = try self.load(operand, Type.u64, 0); try self.addTag(.i64_popcnt); - try self.emitWValue(lsb); + _ = try self.load(operand, Type.u64, 8); try self.addTag(.i64_popcnt); try self.addTag(.i64_add); try self.addTag(.i32_wrap_i64); @@ -4351,10 +4356,10 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, return self.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits}); } - const lhs_high_bit = try self.load(lhs, Type.u64, 0); - const lhs_low_bit = try self.load(lhs, Type.u64, 8); - const rhs_high_bit = try self.load(rhs, Type.u64, 0); - const rhs_low_bit = try self.load(rhs, Type.u64, 8); + const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); + const lhs_low_bit = try (try self.load(lhs, Type.u64, 8)).toLocal(self, Type.u64); + const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64); + const rhs_low_bit = try (try self.load(rhs, Type.u64, 8)).toLocal(self, Type.u64); const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64); const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); @@ -4563,9 +4568,9 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const rhs = try self.resolveInst(bin_op.rhs); if (ty.floatBits(self.target) == 16) { - const addend_ext = try self.fpext(addend, ty, Type.f32); - const lhs_ext = try self.fpext(lhs, ty, Type.f32); const rhs_ext = try self.fpext(rhs, ty, Type.f32); + const lhs_ext = try self.fpext(lhs, ty, Type.f32); + const addend_ext = try self.fpext(addend, ty, Type.f32); // call to compiler-rt `fn fmaf(f32, f32, f32) f32` const result = try self.callIntrinsic( "fmaf", @@ -4573,7 +4578,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue { Type.f32, &.{ rhs_ext, lhs_ext, addend_ext }, ); - return try self.fptrunc(result, Type.f32, ty); + return try (try self.fptrunc(result, Type.f32, ty)).toLocal(self, ty); } const mul_result = try self.binOp(lhs, rhs, ty, .mul); @@ -4606,12 +4611,11 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addTag(.i32_wrap_i64); }, 128 => { - const msb = try self.load(operand, Type.u64, 0); - const lsb = try self.load(operand, Type.u64, 8); + const lsb = try (try self.load(operand, Type.u64, 8)).toLocal(self, Type.u64); try self.emitWValue(lsb); try self.addTag(.i64_clz); - try self.emitWValue(msb); + _ = try self.load(operand, Type.u64, 0); try self.addTag(.i64_clz); try self.emitWValue(.{ .imm64 = 64 }); try self.addTag(.i64_add); @@ -4667,12 +4671,11 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addTag(.i32_wrap_i64); }, 128 => { - const msb = try self.load(operand, Type.u64, 0); - const lsb = try self.load(operand, Type.u64, 8); + const msb = try (try self.load(operand, Type.u64, 0)).toLocal(self, Type.u64); try self.emitWValue(msb); try self.addTag(.i64_ctz); - try self.emitWValue(lsb); + _ = try self.load(operand, Type.u64, 8); if (wasm_bits != int_info.bits) { try self.addImm64(@as(u64, 1) << @intCast(u6, int_info.bits - 64)); try self.addTag(.i64_or); @@ -4810,7 +4813,8 @@ fn lowerTry( if (isByRef(pl_ty, self.target)) { return buildPointerOffset(self, err_union, pl_offset, .new); } - return self.load(err_union, pl_ty, pl_offset); + const payload = try self.load(err_union, pl_ty, pl_offset); + return payload.toLocal(self, pl_ty); } fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -4972,9 +4976,7 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } if (is_f16) { - // we can re-use temporary local - try self.addLabel(.local_set, lhs_operand.local); - return self.fptrunc(lhs_operand, Type.f32, Type.f16); + _ = try self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16); } } @@ -5066,9 +5068,7 @@ fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValu try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); if (is_f16) { - // re-use temporary to save locals - try self.addLabel(.local_set, op_to_lower.local); - return self.fptrunc(op_to_lower, Type.f32, Type.f16); + _ = try self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16); } const result = try self.allocLocal(ty); @@ -5285,6 +5285,8 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { /// Calls a compiler-rt intrinsic by creating an undefined symbol, /// then lowering the arguments and calling the symbol as a function call. /// This function call assumes the C-ABI. +/// Asserts arguments are not stack values when the return value is +/// passed as the first parameter. fn callIntrinsic( self: *Self, name: []const u8, @@ -5314,6 +5316,7 @@ fn callIntrinsic( // Lower all arguments to the stack before we call our function for (args) |arg, arg_i| { + assert(!(want_sret_param and arg == .stack)); assert(param_types[arg_i].hasRuntimeBitsIgnoreComptime()); try self.lowerArg(.C, param_types[arg_i], arg); } -- 2.54.0 From b42ba7c3d411cde31ede290b3915150c3e8acfbb Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 27 Jul 2022 21:04:58 +0200 Subject: [PATCH 40/72] wasm: free unused locals When a local is no longer referenced or used, free it so the local can be re-used by another instruction. This means we generate less locals. Freeing this local is a manual action and must only be used on temporaries or where we are sure the local is not referenced by a different AIR instruction, as that creates UB. We now also no longer store a `WValue` when its tag is set to `none` as those may never be referenced by any AIR instruction. An assertion is done to make sure we never store a reference to a `stack` value in our resolved instructions. --- src/arch/wasm/CodeGen.zig | 237 ++++++++++++++++++++++++-------------- 1 file changed, 151 insertions(+), 86 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 728b4af651dae876cc797fb269c195d6bde3275b..53b7b7fb6bae6441f46534606cc4810e0342cfeb 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -77,17 +77,34 @@ const WValue = union(enum) { /// Promotes a `WValue` to a local when given value is on top of the stack. /// When encountering a `local` or `stack_offset` this is essentially a no-op. /// All other tags are illegal. - fn toLocal(self: WValue, gen: *Self, ty: Type) InnerError!WValue { - switch (self) { + fn toLocal(value: WValue, gen: *Self, ty: Type) InnerError!WValue { + switch (value) { .stack => { const local = try gen.allocLocal(ty); try gen.addLabel(.local_set, local.local); return local; }, - .local, .stack_offset => return self, + .local, .stack_offset => return value, else => unreachable, } } + + /// Marks a local as no longer being referenced and essentially allows + /// us to re-use it somewhere else within the function. + /// The valtype of the local is deducted by using the index of the given. + fn free(value: *WValue, gen: *Self) void { + if (value.* != .local) return; + const local_value = value.local; + const index = local_value - gen.args.len - @boolToInt(gen.return_value != .none); + const valtype = @intToEnum(wasm.Valtype, gen.locals.items[index]); + switch (valtype) { + .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead + .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return, + .f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return, + .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return, + } + value.* = WValue{ .none = {} }; + } }; /// Wasm ops, but without input/output/signedness information @@ -829,27 +846,18 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue { }, } // no local was free to be re-used, so allocate a new local instead - try self.locals.append(self.gpa, wasm.valtype(valtype)); + return self.ensureAllocLocal(ty); +} + +/// Ensures a new local will be created. This is useful when it's useful +/// to use a zero-initialized local. +fn ensureAllocLocal(self: *Self, ty: Type) InnerError!WValue { + try self.locals.append(self.gpa, genValtype(ty, self.target)); const initial_index = self.local_index; self.local_index += 1; return WValue{ .local = initial_index }; } -/// Marks a local as no longer being referenced and essentially allows -/// us to re-use it somewhere else within the function. -/// The valtype of the local is deducted by using the index of the given. -/// Asserts given `WValue` is a `local`. -fn freeLocal(self: *Self, value: WValue) InnerError!WValue { - const index = value.local; - const valtype = wasm.valtype(self.locals.items[index]); - switch (valtype) { - .i32 => self.free_locals_i32.append(index) catch {}, // It's ok to fail any of those, a new local can be allocated instead - .i64 => self.free_locals_i64.append(index) catch {}, - .f32 => self.free_locals_f32.append(index) catch {}, - .f64 => self.free_locals_f64.append(index) catch {}, - } -} - /// Generates a `wasm.Type` from a given function type. /// Memory is owned by the caller. fn genFunctype(gpa: Allocator, cc: std.builtin.CallingConvention, params: []const Type, return_type: Type, target: std.Target) !wasm.Type { @@ -1197,9 +1205,9 @@ fn initializeStack(self: *Self) !void { // Reserve a local to store the current stack pointer // We can later use this local to set the stack pointer back to the value // we have stored here. - self.initial_stack_value = try self.allocLocal(Type.usize); + self.initial_stack_value = try self.ensureAllocLocal(Type.usize); // Also reserve a local to store the bottom stack value - self.bottom_stack_value = try self.allocLocal(Type.usize); + self.bottom_stack_value = try self.ensureAllocLocal(Type.usize); } /// Reads the stack pointer from `Context.initial_stack_value` and writes it @@ -1330,7 +1338,9 @@ fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void { else => { // TODO: We should probably lower this to a call to compiler_rt // But for now, we implement it manually - const offset = try self.allocLocal(Type.usize); // local for counter + var offset = try self.ensureAllocLocal(Type.usize); // local for counter + defer offset.free(self); + // outer block to jump to when loop is done try self.startBlock(.block, wasm.block_empty); try self.startBlock(.loop, wasm.block_empty); @@ -1467,7 +1477,7 @@ fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64, action: enum // do not perform arithmetic when offset is 0. if (offset == 0 and ptr_value.offset() == 0 and action == .modify) return ptr_value; const result_ptr: WValue = switch (action) { - .new => try self.allocLocal(Type.usize), + .new => try self.ensureAllocLocal(Type.usize), .modify => ptr_value, }; try self.emitWValue(ptr_value); @@ -1715,7 +1725,10 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { for (body) |inst| { const result = try self.genInst(inst); - try self.values.putNoClobber(self.gpa, Air.indexToRef(inst), result); + if (result != .none) { + assert(result != .stack); // not allowed to store stack values as we cannot keep track of where they are on the stack + try self.values.putNoClobber(self.gpa, Air.indexToRef(inst), result); + } } } @@ -2151,9 +2164,12 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr } const result = try self.allocStack(ty); - const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); - const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64); - const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); + var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); + defer lhs_high_bit.free(self); + var rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64); + defer rhs_high_bit.free(self); + var high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); + defer high_op_res.free(self); const lhs_low_bit = try self.load(lhs, Type.u64, 8); const rhs_low_bit = try self.load(rhs, Type.u64, 8); @@ -2165,7 +2181,8 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); } else unreachable; const tmp = try self.intcast(lt, Type.u32, Type.u64); - const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64); + var tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64); + defer tmp_op.free(self); try self.store(result, high_op_res, Type.u64, 0); try self.store(result, tmp_op, Type.u64, 8); @@ -3208,25 +3225,22 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W } else if (wanted_bits == 128) { // for 128bit integers we store the integer in the virtual stack, rather than a local const stack_ptr = try self.allocStack(wanted); + try self.emitWValue(stack_ptr); // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it // meaning less store operations are required. const lhs = if (op_bits == 32) blk: { - const tmp = try self.intcast( - operand, - given, - if (wanted.isSignedInt()) Type.i64 else Type.u64, - ); - break :blk try tmp.toLocal(self, Type.u64); + break :blk try self.intcast(operand, given, if (wanted.isSignedInt()) Type.i64 else Type.u64); } else operand; // store msb first - try self.store(stack_ptr, lhs, Type.u64, 0); + try self.store(.{ .stack = {} }, lhs, Type.u64, 0 + stack_ptr.offset()); // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value if (wanted.isSignedInt()) { - const shr = try (try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr)).toLocal(self, Type.i64); - try self.store(stack_ptr, shr, Type.u64, 8); + try self.emitWValue(stack_ptr); + const shr = try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr); + try self.store(.{ .stack = {} }, shr, Type.u64, 8 + stack_ptr.offset()); } else { // Ensure memory of lsb is zero'd try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8); @@ -3534,11 +3548,12 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addTag(.i32_mul); try self.addTag(.i32_add); - const result = try self.allocLocal(elem_ty); + var result = try self.allocLocal(elem_ty); try self.addLabel(.local_set, result.local); if (isByRef(elem_ty, self.target)) { return result; } + defer result.free(self); // only free if it's not returned like above const elem_val = try self.load(result, elem_ty, 0); return elem_val.toLocal(self, elem_ty); @@ -3656,7 +3671,7 @@ fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void else => { // TODO: We should probably lower this to a call to compiler_rt // But for now, we implement it manually - const offset = try self.allocLocal(Type.usize); // local for counter + const offset = try self.ensureAllocLocal(Type.usize); // local for counter // outer block to jump to when loop is done try self.startBlock(.block, wasm.block_empty); try self.startBlock(.loop, wasm.block_empty); @@ -3713,12 +3728,14 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addTag(.i32_mul); try self.addTag(.i32_add); - const result = try self.allocLocal(Type.usize); + var result = try self.allocLocal(Type.usize); try self.addLabel(.local_set, result.local); if (isByRef(elem_ty, self.target)) { return result; } + defer result.free(self); // only free if no longer needed and not returned like above + const elem_val = try self.load(result, elem_ty, 0); return elem_val.toLocal(self, elem_ty); } @@ -3944,7 +3961,8 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std // We store the final result in here that will be validated // if the optional is truly equal. - const result = try self.allocLocal(Type.initTag(.i32)); + var result = try self.ensureAllocLocal(Type.initTag(.i32)); + defer result.free(self); try self.startBlock(.block, wasm.block_empty); _ = try self.isNull(lhs, operand_ty, .i32_eq); @@ -3965,8 +3983,6 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std try self.emitWValue(result); try self.addImm32(0); try self.addTag(if (op == .eq) .i32_ne else .i32_eq); - try self.addLabel(.local_set, result.local); - try self.emitWValue(result); return WValue{ .stack = {} }; } @@ -3980,8 +3996,10 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits}); } - const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); - const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64); + var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); + defer lhs_high_bit.free(self); + var rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64); + defer rhs_high_bit.free(self); switch (op) { .eq, .neq => { @@ -4313,17 +4331,19 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W // for signed integers, we first apply signed shifts by the difference in bits // to get the signed value, as we store it internally as 2's complement. - const lhs = if (wasm_bits != int_info.bits and is_signed) blk: { + var lhs = if (wasm_bits != int_info.bits and is_signed) blk: { break :blk try (try self.signAbsValue(lhs_op, lhs_ty)).toLocal(self, lhs_ty); } else lhs_op; - const rhs = if (wasm_bits != int_info.bits and is_signed) blk: { + var rhs = if (wasm_bits != int_info.bits and is_signed) blk: { break :blk try (try self.signAbsValue(rhs_op, lhs_ty)).toLocal(self, lhs_ty); } else rhs_op; - const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty); - const result = if (wasm_bits != int_info.bits) blk: { + var bin_op = try (try self.binOp(lhs, rhs, lhs_ty, op)).toLocal(self, lhs_ty); + defer bin_op.free(self); + var result = if (wasm_bits != int_info.bits) blk: { break :blk try (try self.wrapOperand(bin_op, lhs_ty)).toLocal(self, lhs_ty); } else bin_op; + defer result.free(self); // no-op when wasm_bits == int_info.bits const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt; const overflow_bit: WValue = if (is_signed) blk: { @@ -4338,13 +4358,23 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W try self.cmp(bin_op, lhs, lhs_ty, cmp_op) else try self.cmp(bin_op, result, lhs_ty, .neq); - const overflow_local = try overflow_bit.toLocal(self, Type.u32); + var overflow_local = try overflow_bit.toLocal(self, Type.u32); + defer overflow_local.free(self); const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); try self.store(result_ptr, result, lhs_ty, 0); const offset = @intCast(u32, lhs_ty.abiSize(self.target)); try self.store(result_ptr, overflow_local, Type.initTag(.u1), offset); + // in this case, we performed a signAbsValue which created a temporary local + // so let's free this so it can be re-used instead. + // In the other case we do not want to free it, because that would free the + // resolved instructions which may be referenced by other instructions. + if (wasm_bits != int_info.bits and is_signed) { + lhs.free(self); + rhs.free(self); + } + return result_ptr; } @@ -4356,21 +4386,30 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, return self.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits}); } - const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); - const lhs_low_bit = try (try self.load(lhs, Type.u64, 8)).toLocal(self, Type.u64); - const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64); - const rhs_low_bit = try (try self.load(rhs, Type.u64, 8)).toLocal(self, Type.u64); + var lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); + defer lhs_high_bit.free(self); + var lhs_low_bit = try (try self.load(lhs, Type.u64, 8)).toLocal(self, Type.u64); + defer lhs_low_bit.free(self); + var rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64); + defer rhs_high_bit.free(self); + var rhs_low_bit = try (try self.load(rhs, Type.u64, 8)).toLocal(self, Type.u64); + defer rhs_low_bit.free(self); - const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64); - const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); + var low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64); + defer low_op_res.free(self); + var high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); + defer high_op_res.free(self); - const lt = if (op == .add) blk: { + var lt = if (op == .add) blk: { break :blk try (try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt)).toLocal(self, Type.u32); } else if (op == .sub) blk: { break :blk try (try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt)).toLocal(self, Type.u32); } else unreachable; - const tmp = try (try self.intcast(lt, Type.u32, Type.u64)).toLocal(self, Type.u64); - const tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64); + defer lt.free(self); + var tmp = try (try self.intcast(lt, Type.u32, Type.u64)).toLocal(self, Type.u64); + defer tmp.free(self); + var tmp_op = try (try self.binOp(low_op_res, tmp, Type.u64, op)).toLocal(self, Type.u64); + defer tmp_op.free(self); const overflow_bit = if (is_signed) blk: { const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor); @@ -4392,7 +4431,8 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, break :blk WValue{ .stack = {} }; }; - const overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1)); + var overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1)); + defer overflow_local.free(self); const result_ptr = try self.allocStack(result_ty); try self.store(result_ptr, high_op_res, Type.u64, 0); @@ -4419,10 +4459,12 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits}); }; - const shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty); - const result = if (wasm_bits != int_info.bits) blk: { + var shl = try (try self.binOp(lhs, rhs, lhs_ty, .shl)).toLocal(self, lhs_ty); + defer shl.free(self); + var result = if (wasm_bits != int_info.bits) blk: { break :blk try (try self.wrapOperand(shl, lhs_ty)).toLocal(self, lhs_ty); } else shl; + defer result.free(self); // it's a no-op to free the same local twice (when wasm_bits == int_info.bits) const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: { // emit lhs to stack to we can keep 'wrapped' on the stack also @@ -4431,10 +4473,12 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr); break :blk try self.cmp(.{ .stack = {} }, wrapped, lhs_ty, .neq); } else blk: { - const shr = try (try self.binOp(result, rhs, lhs_ty, .shr)).toLocal(self, lhs_ty); - break :blk try self.cmp(lhs, shr, lhs_ty, .neq); + try self.emitWValue(lhs); + const shr = try self.binOp(result, rhs, lhs_ty, .shr); + break :blk try self.cmp(.{ .stack = {} }, shr, lhs_ty, .neq); }; - const overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1)); + var overflow_local = try overflow_bit.toLocal(self, Type.initTag(.u1)); + defer overflow_local.free(self); const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); try self.store(result_ptr, result, lhs_ty, 0); @@ -4457,7 +4501,9 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // We store the bit if it's overflowed or not in this. As it's zero-initialized // we only need to update it if an overflow (or underflow) occurred. - const overflow_bit = try self.allocLocal(Type.initTag(.u1)); + var overflow_bit = try self.ensureAllocLocal(Type.initTag(.u1)); + defer overflow_bit.free(self); + const int_info = lhs_ty.intInfo(self.target); const wasm_bits = toWasmBits(int_info.bits) orelse { return self.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits}); @@ -4487,7 +4533,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { break :blk try self.intcast(bin_op, new_ty, lhs_ty); } else { const down_cast = try (try self.intcast(bin_op, new_ty, lhs_ty)).toLocal(self, lhs_ty); - const shr = try (try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(self, lhs_ty); + var shr = try (try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr)).toLocal(self, lhs_ty); + defer shr.free(self); const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty); @@ -4504,7 +4551,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addLabel(.local_set, overflow_bit.local); break :blk try self.wrapOperand(bin_op, lhs_ty); } else blk: { - const bin_op = try (try self.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(self, lhs_ty); + var bin_op = try (try self.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(self, lhs_ty); + defer bin_op.free(self); const shift_imm = if (wasm_bits == 32) WValue{ .imm32 = int_info.bits } else @@ -4514,7 +4562,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addLabel(.local_set, overflow_bit.local); break :blk try self.wrapOperand(bin_op, lhs_ty); }; - const bin_op_local = try bin_op.toLocal(self, lhs_ty); + var bin_op_local = try bin_op.toLocal(self, lhs_ty); + defer bin_op_local.free(self); const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); try self.store(result_ptr, bin_op_local, lhs_ty, 0); @@ -4572,12 +4621,13 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const lhs_ext = try self.fpext(lhs, ty, Type.f32); const addend_ext = try self.fpext(addend, ty, Type.f32); // call to compiler-rt `fn fmaf(f32, f32, f32) f32` - const result = try self.callIntrinsic( + var result = try self.callIntrinsic( "fmaf", &.{ Type.f32, Type.f32, Type.f32 }, Type.f32, &.{ rhs_ext, lhs_ext, addend_ext }, ); + defer result.free(self); return try (try self.fptrunc(result, Type.f32, ty)).toLocal(self, ty); } @@ -4611,7 +4661,8 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addTag(.i32_wrap_i64); }, 128 => { - const lsb = try (try self.load(operand, Type.u64, 8)).toLocal(self, Type.u64); + var lsb = try (try self.load(operand, Type.u64, 8)).toLocal(self, Type.u64); + defer lsb.free(self); try self.emitWValue(lsb); try self.addTag(.i64_clz); @@ -4671,7 +4722,8 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addTag(.i32_wrap_i64); }, 128 => { - const msb = try (try self.load(operand, Type.u64, 0)).toLocal(self, Type.u64); + var msb = try (try self.load(operand, Type.u64, 0)).toLocal(self, Type.u64); + defer msb.free(self); try self.emitWValue(msb); try self.addTag(.i64_ctz); @@ -4847,7 +4899,8 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return (try self.binOp(lhs, res, ty, .@"or")).toLocal(self, ty); }, 24 => { - const msb = try (try self.wrapOperand(operand, Type.u16)).toLocal(self, Type.u16); + var msb = try (try self.wrapOperand(operand, Type.u16)).toLocal(self, Type.u16); + defer msb.free(self); const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl); const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and"); @@ -4867,10 +4920,13 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { }, 32 => { const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl); - const lhs = try (try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and")).toLocal(self, ty); + var lhs = try (try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and")).toLocal(self, ty); + defer lhs.free(self); const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr); - const rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty); - const tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty); + var rhs = try (try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and")).toLocal(self, ty); + defer rhs.free(self); + var tmp_or = try (try self.binOp(lhs, rhs, ty, .@"or")).toLocal(self, ty); + defer tmp_or.free(self); const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl); const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr); @@ -5097,7 +5153,8 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { } const wasm_bits = toWasmBits(int_info.bits).?; - const bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty); + var bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty); + defer bin_result.free(self); if (wasm_bits != int_info.bits and op == .add) { const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1); const imm_val = switch (wasm_bits) { @@ -5130,10 +5187,10 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op const wasm_bits = toWasmBits(int_info.bits).?; const is_wasm_bits = wasm_bits == int_info.bits; - const lhs = if (!is_wasm_bits) lhs: { + var lhs = if (!is_wasm_bits) lhs: { break :lhs try (try self.signAbsValue(lhs_operand, ty)).toLocal(self, ty); } else lhs_operand; - const rhs = if (!is_wasm_bits) rhs: { + var rhs = if (!is_wasm_bits) rhs: { break :rhs try (try self.signAbsValue(rhs_operand, ty)).toLocal(self, ty); } else rhs_operand; @@ -5150,8 +5207,11 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op else => unreachable, }; - const bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty); + var bin_result = try (try self.binOp(lhs, rhs, ty, op)).toLocal(self, ty); if (!is_wasm_bits) { + defer bin_result.free(self); // not returned in this branch + defer lhs.free(self); // uses temporary local for absvalue + defer rhs.free(self); // uses temporary local for absvalue try self.emitWValue(bin_result); try self.emitWValue(max_wvalue); _ = try self.cmp(bin_result, max_wvalue, ty, .lt); @@ -5202,8 +5262,10 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const result = try self.allocLocal(ty); if (wasm_bits == int_info.bits) { - const shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty); - const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty); + var shl = try (try self.binOp(lhs, rhs, ty, .shl)).toLocal(self, ty); + defer shl.free(self); + var shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty); + defer shr.free(self); switch (wasm_bits) { 32 => blk: { @@ -5241,9 +5303,12 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { else => unreachable, }; - const shl_res = try (try self.binOp(lhs, shift_value, ty, .shl)).toLocal(self, ty); - const shl = try (try self.binOp(shl_res, rhs, ty, .shl)).toLocal(self, ty); - const shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty); + var shl_res = try (try self.binOp(lhs, shift_value, ty, .shl)).toLocal(self, ty); + defer shl_res.free(self); + var shl = try (try self.binOp(shl_res, rhs, ty, .shl)).toLocal(self, ty); + defer shl.free(self); + var shr = try (try self.binOp(shl, rhs, ty, .shr)).toLocal(self, ty); + defer shr.free(self); switch (wasm_bits) { 32 => blk: { @@ -5278,7 +5343,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (is_signed) { shift_result = try self.wrapOperand(shift_result, ty); } - return try shift_result.toLocal(self, ty); + return shift_result.toLocal(self, ty); } } -- 2.54.0 From b78f3bf1f17c7d67ae30d95047e40efc164bf8af Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 11 Aug 2022 10:56:21 +0200 Subject: [PATCH 41/72] std: fix definition of SIG_IGN, SIG_DFL, etc. POSIX specifies that the sa_handler field of the sigaction struct may be set to SIG_IGN or SIG_DFL. However, the current constants in the standard library use the function pointer signature corresponding to the sa_sigaction field instead. This may not cause issues in practice because the fields usually occupy the same memory in a union, but this isn't required by POSIX and there may be systems we do not yet support that do this differently. Fixing this also makes the Zig interface less confusing to use after reading the man page. --- lib/std/c/darwin.zig | 8 ++++---- lib/std/c/dragonfly.zig | 6 +++--- lib/std/c/freebsd.zig | 6 +++--- lib/std/c/netbsd.zig | 6 +++--- lib/std/c/openbsd.zig | 10 +++++----- lib/std/c/solaris.zig | 8 ++++---- lib/std/debug.zig | 2 +- lib/std/os.zig | 2 +- lib/std/os/linux.zig | 18 +++++++++--------- lib/std/os/test.zig | 2 +- 10 files changed, 34 insertions(+), 34 deletions(-) diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index e7fd3cde44e82d80518f0c66bbf37e0ba5ff520e..0a65fa52429ea9a6f23fef3e08e2d56b0cf4c9b7 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -814,10 +814,10 @@ pub const sigset_t = u32; pub const empty_sigset: sigset_t = 0; pub const SIG = struct { - pub const ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); - pub const DFL = @intToPtr(?Sigaction.sigaction_fn, 0); - pub const IGN = @intToPtr(?Sigaction.sigaction_fn, 1); - pub const HOLD = @intToPtr(?Sigaction.sigaction_fn, 5); + pub const ERR = @intToPtr(?Sigaction.handler_fn, maxInt(usize)); + pub const DFL = @intToPtr(?Sigaction.handler_fn, 0); + pub const IGN = @intToPtr(?Sigaction.handler_fn, 1); + pub const HOLD = @intToPtr(?Sigaction.handler_fn, 5); /// block specified signal set pub const _BLOCK = 1; diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig index 1a60f94a1ec2a402343e3bb2fb3b0527573a221a..5d7822e8e9b925e12524ee7cbca4a786bf144a5c 100644 --- a/lib/std/c/dragonfly.zig +++ b/lib/std/c/dragonfly.zig @@ -609,9 +609,9 @@ pub const S = struct { pub const BADSIG = SIG.ERR; pub const SIG = struct { - pub const DFL = @intToPtr(?Sigaction.sigaction_fn, 0); - pub const IGN = @intToPtr(?Sigaction.sigaction_fn, 1); - pub const ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); + pub const DFL = @intToPtr(?Sigaction.handler_fn, 0); + pub const IGN = @intToPtr(?Sigaction.handler_fn, 1); + pub const ERR = @intToPtr(?Sigaction.handler_fn, maxInt(usize)); pub const BLOCK = 1; pub const UNBLOCK = 2; diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index dac239094e4a1a640f4a72a39d205be561df3c23..6f0188698662308f2f91093c48096495c252bc7d 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -670,9 +670,9 @@ pub const SIG = struct { pub const UNBLOCK = 2; pub const SETMASK = 3; - pub const DFL = @intToPtr(?Sigaction.sigaction_fn, 0); - pub const IGN = @intToPtr(?Sigaction.sigaction_fn, 1); - pub const ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); + pub const DFL = @intToPtr(?Sigaction.handler_fn, 0); + pub const IGN = @intToPtr(?Sigaction.handler_fn, 1); + pub const ERR = @intToPtr(?Sigaction.handler_fn, maxInt(usize)); pub const WORDS = 4; pub const MAXSIG = 128; diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index 3de14da7a26af0184e14aa898ca718f2307df8ac..a8287033d75e9fa4adce7231e6917632ff80cb48 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -910,9 +910,9 @@ pub const winsize = extern struct { const NSIG = 32; pub const SIG = struct { - pub const DFL = @intToPtr(?Sigaction.sigaction_fn, 0); - pub const IGN = @intToPtr(?Sigaction.sigaction_fn, 1); - pub const ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); + pub const DFL = @intToPtr(?Sigaction.handler_fn, 0); + pub const IGN = @intToPtr(?Sigaction.handler_fn, 1); + pub const ERR = @intToPtr(?Sigaction.handler_fn, maxInt(usize)); pub const WORDS = 4; pub const MAXSIG = 128; diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index 0863cc5a5e794819c09cf0aa7ec25c8339566a1a..5c259bc41ae3c929cda4d4f56c9591dca48f218c 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -982,11 +982,11 @@ pub const winsize = extern struct { const NSIG = 33; pub const SIG = struct { - pub const DFL = @intToPtr(?Sigaction.sigaction_fn, 0); - pub const IGN = @intToPtr(?Sigaction.sigaction_fn, 1); - pub const ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); - pub const CATCH = @intToPtr(?Sigaction.sigaction_fn, 2); - pub const HOLD = @intToPtr(?Sigaction.sigaction_fn, 3); + pub const DFL = @intToPtr(?Sigaction.handler_fn, 0); + pub const IGN = @intToPtr(?Sigaction.handler_fn, 1); + pub const ERR = @intToPtr(?Sigaction.handler_fn, maxInt(usize)); + pub const CATCH = @intToPtr(?Sigaction.handler_fn, 2); + pub const HOLD = @intToPtr(?Sigaction.handler_fn, 3); pub const HUP = 1; pub const INT = 2; diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig index 61e52cfe301d24ed9064f808f05ca5eb78556310..1e726beb75c9f4a71333de44efa35b6e24883f29 100644 --- a/lib/std/c/solaris.zig +++ b/lib/std/c/solaris.zig @@ -879,10 +879,10 @@ pub const winsize = extern struct { const NSIG = 75; pub const SIG = struct { - pub const DFL = @intToPtr(?Sigaction.sigaction_fn, 0); - pub const ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); - pub const IGN = @intToPtr(?Sigaction.sigaction_fn, 1); - pub const HOLD = @intToPtr(?Sigaction.sigaction_fn, 2); + pub const DFL = @intToPtr(?Sigaction.handler_fn, 0); + pub const ERR = @intToPtr(?Sigaction.handler_fn, maxInt(usize)); + pub const IGN = @intToPtr(?Sigaction.handler_fn, 1); + pub const HOLD = @intToPtr(?Sigaction.handler_fn, 2); pub const WORDS = 4; pub const MAXSIG = 75; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 847298e54b5ab8459583586af79360d3df04db65..0738b5af0b63754bd20bc1efe653fa66cd49beb3 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1787,7 +1787,7 @@ fn resetSegfaultHandler() void { return; } var act = os.Sigaction{ - .handler = .{ .sigaction = os.SIG.DFL }, + .handler = .{ .handler = os.SIG.DFL }, .mask = os.empty_sigset, .flags = 0, }; diff --git a/lib/std/os.zig b/lib/std/os.zig index b068a11229b54c93de90afed384f2321b0dbc041..46b1bed7bdc941cdb93f6a8f3582c9512236a9aa 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -475,7 +475,7 @@ pub fn abort() noreturn { // Install default handler so that the tkill below will terminate. const sigact = Sigaction{ - .handler = .{ .sigaction = SIG.DFL }, + .handler = .{ .handler = SIG.DFL }, .mask = empty_sigset, .flags = 0, }; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index ae9b441b60ef78abb72aaae579291e9d1c2b98b4..539f3612f91878967433f182d2339d32978f9e23 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1945,9 +1945,9 @@ pub const SIG = if (is_mips) struct { pub const SYS = 31; pub const UNUSED = SIG.SYS; - pub const ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); - pub const DFL = @intToPtr(?Sigaction.sigaction_fn, 0); - pub const IGN = @intToPtr(?Sigaction.sigaction_fn, 1); + pub const ERR = @intToPtr(?Sigaction.handler_fn, maxInt(usize)); + pub const DFL = @intToPtr(?Sigaction.handler_fn, 0); + pub const IGN = @intToPtr(?Sigaction.handler_fn, 1); } else if (is_sparc) struct { pub const BLOCK = 1; pub const UNBLOCK = 2; @@ -1989,9 +1989,9 @@ pub const SIG = if (is_mips) struct { pub const PWR = LOST; pub const IO = SIG.POLL; - pub const ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); - pub const DFL = @intToPtr(?Sigaction.sigaction_fn, 0); - pub const IGN = @intToPtr(?Sigaction.sigaction_fn, 1); + pub const ERR = @intToPtr(?Sigaction.handler_fn, maxInt(usize)); + pub const DFL = @intToPtr(?Sigaction.handler_fn, 0); + pub const IGN = @intToPtr(?Sigaction.handler_fn, 1); } else struct { pub const BLOCK = 0; pub const UNBLOCK = 1; @@ -2032,9 +2032,9 @@ pub const SIG = if (is_mips) struct { pub const SYS = 31; pub const UNUSED = SIG.SYS; - pub const ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); - pub const DFL = @intToPtr(?Sigaction.sigaction_fn, 0); - pub const IGN = @intToPtr(?Sigaction.sigaction_fn, 1); + pub const ERR = @intToPtr(?Sigaction.handler_fn, maxInt(usize)); + pub const DFL = @intToPtr(?Sigaction.handler_fn, 0); + pub const IGN = @intToPtr(?Sigaction.handler_fn, 1); }; pub const kernel_rwf = u32; diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 44f8b16b9e0c97bcd8aa5f2e97bdb946dbf91ab0..a8497586f96999ec061dfbbf60cc88d394cb0fff 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -785,7 +785,7 @@ test "sigaction" { try testing.expect(signal_test_failed == false); // Check if the handler has been correctly reset to SIG_DFL try os.sigaction(os.SIG.USR1, null, &old_sa); - try testing.expectEqual(os.SIG.DFL, old_sa.handler.sigaction); + try testing.expectEqual(os.SIG.DFL, old_sa.handler.handler); } test "dup & dup2" { -- 2.54.0 From a82d7c20631b90de2f2298d351353041ac41650d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Aug 2022 23:02:04 -0700 Subject: [PATCH 42/72] std: add missing error to windows.WriteFile I encountered this error today when testing the self-hosted compiler on Windows. --- lib/std/os.zig | 4 ++++ lib/std/os/windows.zig | 4 ++++ src/link.zig | 1 + src/main.zig | 1 + 4 files changed, 10 insertions(+) diff --git a/lib/std/os.zig b/lib/std/os.zig index 1192c726298e8919ab6dce959a1c26409ccb8494..2836ddf9c9111fe2f81ef614e5b71a9c93fcefa6 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -953,6 +953,10 @@ pub const WriteError = error{ OperationAborted, NotOpenForWriting, + /// The process cannot access the file because another process has locked + /// a portion of the file. Windows-only. + LockViolation, + /// This error occurs when no global event loop is configured, /// and reading from the file descriptor would block. WouldBlock, diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 3e42ee5f2d08cc28acea971949ed5dca62604189..c79ccb511375d5127a21062d497f897d1ba5754d 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -517,6 +517,9 @@ pub const WriteFileError = error{ OperationAborted, BrokenPipe, NotOpenForWriting, + /// The process cannot access the file because another process has locked + /// a portion of the file. + LockViolation, Unexpected, }; @@ -597,6 +600,7 @@ pub fn WriteFile( .IO_PENDING => unreachable, .BROKEN_PIPE => return error.BrokenPipe, .INVALID_HANDLE => return error.NotOpenForWriting, + .LOCK_VIOLATION => return error.LockViolation, else => |err| return unexpectedError(err), } } diff --git a/src/link.zig b/src/link.zig index a69dcc4c6eae8f3677519c55bb8871a33d20d4db..14ae142a3fbe4a8eba828f82de53fc9a6ff92c79 100644 --- a/src/link.zig +++ b/src/link.zig @@ -435,6 +435,7 @@ pub const File = struct { EmitFail, NameTooLong, CurrentWorkingDirectoryUnlinked, + LockViolation, }; /// Called from within the CodeGen to lower a local variable instantion as an unnamed diff --git a/src/main.zig b/src/main.zig index c103cddcd45c6504b4354cfcaaf0fad21bdd1184..971fe19e36c6c3f384d4dc0ce55c590e5b18ecac 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4227,6 +4227,7 @@ const FmtError = error{ NotOpenForWriting, UnsupportedEncoding, ConnectionResetByPeer, + LockViolation, } || fs.File.OpenError; fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void { -- 2.54.0 From bbcc26a9b6d8de5971f26cda01350022a1049e08 Mon Sep 17 00:00:00 2001 From: LordMZTE Date: Thu, 11 Aug 2022 12:16:36 +0200 Subject: [PATCH 43/72] fix error message when providing slice to `std.hash.autoHash` --- lib/std/hash/auto_hash.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/hash/auto_hash.zig b/lib/std/hash/auto_hash.zig index c4060e9182b5f6b0c119e7779614312b0bb23f07..951c0148d9d03f1f3a1835de2583ec9c5555488b 100644 --- a/lib/std/hash/auto_hash.zig +++ b/lib/std/hash/auto_hash.zig @@ -193,8 +193,8 @@ fn typeContainsSlice(comptime K: type) bool { pub fn autoHash(hasher: anytype, key: anytype) void { const Key = @TypeOf(key); if (comptime typeContainsSlice(Key)) { - @compileError("std.auto_hash.autoHash does not allow slices as well as unions and structs containing slices here (" ++ @typeName(Key) ++ - ") because the intent is unclear. Consider using std.auto_hash.hash or providing your own hash function instead."); + @compileError("std.hash.autoHash does not allow slices as well as unions and structs containing slices here (" ++ @typeName(Key) ++ + ") because the intent is unclear. Consider using std.hash.autoHashStrat or providing your own hash function instead."); } hash(hasher, key, .Shallow); -- 2.54.0 From bcc6cdc4fb7784895bd3f508a8a642f3dae5d81e Mon Sep 17 00:00:00 2001 From: Hadrien Dorio Date: Tue, 9 Aug 2022 04:16:54 +0200 Subject: [PATCH 44/72] ci: azure: stop when exe exits with an error --- ci/azure/pipelines.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/ci/azure/pipelines.yml b/ci/azure/pipelines.yml index 75e9e7ef8eee9f7f61d8c8772f892362a05038c8..27e95c6842361e65c8d5c7c674001933742dbd4c 100644 --- a/ci/azure/pipelines.yml +++ b/ci/azure/pipelines.yml @@ -41,6 +41,13 @@ jobs: Set-Variable -Name ZIGINSTALLDIR -Value "${ZIGBUILDDIR}\dist" Set-Variable -Name ZIGPREFIXPATH -Value "$(Get-Location)\$(ZIG_LLVM_CLANG_LLD_NAME)" + function CheckLastExitCode { + if (!$?) { + exit 1 + } + return 0 + } + # Make the `zig version` number consistent. # This will affect the `zig build` command below which uses `git describe`. git config core.abbrev 9 @@ -69,6 +76,7 @@ jobs: -Dstrip ` -Duse-zig-libcxx ` -Dtarget=$(TARGET) + CheckLastExitCode cd - @@ -83,19 +91,37 @@ jobs: - pwsh: | Set-Variable -Name ZIGINSTALLDIR -Value "$(Get-Location)\build\dist" + function CheckLastExitCode { + if (!$?) { + exit 1 + } + return 0 + } + # Sadly, stage2 is omitted from this build to save memory on the CI server. Once self-hosted is # built with itself and does not gobble as much memory, we can enable these tests. #& "$ZIGINSTALLDIR\bin\zig.exe" test "..\test\behavior.zig" -fno-stage1 -fLLVM -I "..\test" 2>&1 + #CheckLastExitCode & "$ZIGINSTALLDIR\bin\zig.exe" build test-toolchain -Dskip-non-native -Dskip-stage2-tests 2>&1 + CheckLastExitCode & "$ZIGINSTALLDIR\bin\zig.exe" build test-std -Dskip-non-native 2>&1 + CheckLastExitCode name: test displayName: 'Test' - pwsh: | Set-Variable -Name ZIGINSTALLDIR -Value "$(Get-Location)\build\dist" + function CheckLastExitCode { + if (!$?) { + exit 1 + } + return 0 + } + & "$ZIGINSTALLDIR\bin\zig.exe" build docs + CheckLastExitCode timeoutInMinutes: 60 name: doc displayName: 'Documentation' -- 2.54.0 From 64307c28211a7d402bd7f4363dff5b65ab79bd34 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Aug 2022 23:55:10 -0700 Subject: [PATCH 45/72] CI: update windows tarball --- ci/azure/pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/azure/pipelines.yml b/ci/azure/pipelines.yml index 27e95c6842361e65c8d5c7c674001933742dbd4c..5ff0a5d61b1506c1605ebe347988d430c5c52c8c 100644 --- a/ci/azure/pipelines.yml +++ b/ci/azure/pipelines.yml @@ -27,7 +27,7 @@ jobs: vmImage: 'windows-2019' variables: TARGET: 'x86_64-windows-gnu' - ZIG_LLVM_CLANG_LLD_NAME: 'zig+llvm+lld+clang-${{ variables.TARGET }}-0.10.0-dev.2931+bdf3fa12f' + ZIG_LLVM_CLANG_LLD_NAME: 'zig+llvm+lld+clang-${{ variables.TARGET }}-0.10.0-dev.3524+74673b7f6' ZIG_LLVM_CLANG_LLD_URL: 'https://ziglang.org/deps/${{ variables.ZIG_LLVM_CLANG_LLD_NAME }}.zip' steps: - pwsh: | -- 2.54.0 From dba758da18a706efbdbeb1e1bbbabdfe49e1931d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 11 Aug 2022 12:56:46 -0700 Subject: [PATCH 46/72] disable failing C backend behavior test on Windows See tracking issue #12415 --- test/tests.zig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/tests.zig b/test/tests.zig index dcc891f8786bd90c50a384160e228ede9011bcc6..0bca9a9d16030ceae1322a01008281722f9f536e 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -641,6 +641,13 @@ pub fn addPkgTests( } else false; if (!want_this_mode) continue; + if (test_target.backend) |backend| { + if (backend == .stage2_c and builtin.os.tag == .windows) { + // https://github.com/ziglang/zig/issues/12415 + continue; + } + } + const libc_prefix = if (test_target.target.getOs().requiresLibC()) "" else if (test_target.link_libc) -- 2.54.0 From 23a227a0736d2ef749c41b43857a7aabc8919597 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 11 Aug 2022 16:55:24 -0700 Subject: [PATCH 47/72] disable failing standalone test: issue_11595 See tracking issue #12419 --- test/standalone.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/standalone.zig b/test/standalone.zig index 2b0667e89c988f3bd88d7fe757132e5c209491fc..2a7902dfb25835c9e3f81d7df9df628830dfacae 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -34,7 +34,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void { if (builtin.zig_backend == .stage1) { // https://github.com/ziglang/zig/issues/12194 cases.addBuildFile("test/standalone/issue_9812/build.zig", .{}); } - cases.addBuildFile("test/standalone/issue_11595/build.zig", .{}); + if (builtin.os.tag != .windows) { + // https://github.com/ziglang/zig/issues/12419 + cases.addBuildFile("test/standalone/issue_11595/build.zig", .{}); + } if (builtin.os.tag != .wasi) { cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{}); } -- 2.54.0 From 486626beb73936a10c3a566607b1c6633292fb8f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 11 Aug 2022 17:51:57 -0700 Subject: [PATCH 48/72] disable failing link tests on windows See tracking issue #12421 --- test/link.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/link.zig b/test/link.zig index 1e75620919ca2976f45316f29121c97d582ae692..01edd07261d05ba943e449528ba410625951e68e 100644 --- a/test/link.zig +++ b/test/link.zig @@ -3,6 +3,11 @@ const builtin = @import("builtin"); const tests = @import("tests.zig"); pub fn addCases(cases: *tests.StandaloneContext) void { + if (builtin.os.tag == .windows) { + // https://github.com/ziglang/zig/issues/12421 + return; + } + cases.addBuildFile("test/link/bss/build.zig", .{ .build_modes = false, // we only guarantee zerofill for undefined in Debug }); -- 2.54.0 From 5e42b0821a5aa6af2327b4380fd796be274cb024 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 11 Aug 2022 19:19:23 -0700 Subject: [PATCH 49/72] disable failing stack traces tests on windows See tracking issue #12422 --- test/stack_traces.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/stack_traces.zig b/test/stack_traces.zig index ea27b5beff7e94a170f6be4edbd065a7013e26b9..e514e0fd881a419893cb9dacf56928c891fa171f 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -3,6 +3,11 @@ const os = std.os; const tests = @import("tests.zig"); pub fn addCases(cases: *tests.StackTracesContext) void { + if (@import("builtin").os.tag == .windows) { + // https://github.com/ziglang/zig/issues/12422 + return; + } + cases.addCase(.{ .name = "return", .source = -- 2.54.0 From 7c9979a02e830a4383995e66ff623a7d07cac091 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 11 Aug 2022 22:45:15 +0300 Subject: [PATCH 50/72] stage2: generate a switch for `@errSetCast` safety --- src/Air.zig | 5 ++++ src/Liveness.zig | 2 ++ src/Sema.zig | 13 +++------- src/arch/aarch64/CodeGen.zig | 1 + src/arch/arm/CodeGen.zig | 1 + src/arch/riscv64/CodeGen.zig | 1 + src/arch/sparc64/CodeGen.zig | 1 + src/arch/wasm/CodeGen.zig | 1 + src/arch/x86_64/CodeGen.zig | 1 + src/codegen/c.zig | 1 + src/codegen/llvm.zig | 48 ++++++++++++++++++++++++++++++++++++ src/print_air.zig | 1 + 12 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index c5734278d338458e549749bccf5462b301d28e97..e08993bbed5ab8b2b07efafd1d719e97e5b69460 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -673,6 +673,10 @@ pub const Inst = struct { /// Uses the `un_op` field. error_name, + /// Returns true if error set has error with value. + /// Uses the `ty_op` field. + error_set_has_value, + /// Constructs a vector, tuple, struct, or array value out of runtime-known elements. /// Some of the elements may be comptime-known. /// Uses the `ty_pl` field, payload is index of an array of elements, each of which @@ -1062,6 +1066,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .is_err_ptr, .is_non_err_ptr, .is_named_enum_value, + .error_set_has_value, => return Type.bool, .const_ty => return Type.type, diff --git a/src/Liveness.zig b/src/Liveness.zig index 748016d5845d689cd69d0d43a39ee05991c08327..5a4bd2265e18e0854da76183e3f69e111995264c 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -267,6 +267,7 @@ pub fn categorizeOperand( .byte_swap, .bit_reverse, .splat, + .error_set_has_value, => { const o = air_datas[inst].ty_op; if (o.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); @@ -842,6 +843,7 @@ fn analyzeInst( .byte_swap, .bit_reverse, .splat, + .error_set_has_value, => { 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 f9b37f5b495eb26d578978a14e95cf9e14d1253d..3e62344a0436948a708a944201de9a7aa1baf568 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17359,17 +17359,10 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat } try sema.requireRuntimeBlock(block, src, operand_src); - if (block.wantSafety() and !dest_ty.isAnyError()) { + if (block.wantSafety() and !dest_ty.isAnyError() and sema.mod.comp.bin_file.options.use_llvm) { const err_int_inst = try block.addBitCast(Type.u16, operand); - // TODO: Output a switch instead of chained OR's. - var found_match: Air.Inst.Ref = undefined; - for (dest_ty.errorSetNames()) |dest_err_name, i| { - const dest_err_int = (try sema.mod.getErrorValue(dest_err_name)).value; - const dest_err_int_inst = try sema.addIntUnsigned(Type.u16, dest_err_int); - const next_match = try block.addBinOp(.cmp_eq, dest_err_int_inst, err_int_inst); - found_match = if (i == 0) next_match else try block.addBinOp(.bool_or, found_match, next_match); - } - try sema.addSafetyCheck(block, found_match, .invalid_error_code); + const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst); + try sema.addSafetyCheck(block, ok, .invalid_error_code); } return block.addBitCast(dest_ty, operand); } diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 988317284bd64cc9646fd0eaebbabf8759eb3a16..d256f9a55856b24f42d3426121446ffa86f9d26d 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -778,6 +778,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { => return self.fail("TODO implement optimized float mode", .{}), .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), + .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), .wasm_memory_size => unreachable, .wasm_memory_grow => unreachable, diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index be2891ef6fc0116e15af4099387ebd2d1eafddb6..60008ec1d1927aa864028ccd35bfc225e00dd16f 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -769,6 +769,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { => return self.fail("TODO implement optimized float mode", .{}), .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), + .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), .wasm_memory_size => unreachable, .wasm_memory_grow => unreachable, diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index a5007928b639e9a785cafc922b6a4d1819ab215e..06adcff6d45f1bed059a932a5066bcbe3baf62aa 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -694,6 +694,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { => return self.fail("TODO implement optimized float mode", .{}), .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), + .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), .wasm_memory_size => unreachable, .wasm_memory_grow => unreachable, diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index bf834a36d97a2db27df9e7b993800630a5f034e6..cd891f0fa3a0e8e9c23cb5a32a7357fca85a5a09 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -706,6 +706,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { => @panic("TODO implement optimized float mode"), .is_named_enum_value => @panic("TODO implement is_named_enum_value"), + .error_set_has_value => @panic("TODO implement error_set_has_value"), .wasm_memory_size => unreachable, .wasm_memory_grow => unreachable, diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 53b7b7fb6bae6441f46534606cc4810e0342cfeb..8eb3e2175b9a292f54bdc9cee85d8d70d5133c3c 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1694,6 +1694,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .err_return_trace, .set_err_return_trace, .is_named_enum_value, + .error_set_has_value, => |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), .add_optimized, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7f7473bc666484c8b6e3f7fce6995a17df75a1ee..8d19768bbb278aa560091048727ba5f2c894b0b9 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -776,6 +776,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { => return self.fail("TODO implement optimized float mode", .{}), .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), + .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), .wasm_memory_size => unreachable, .wasm_memory_grow => unreachable, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 74e4404bce2bc3fd86ab1daef83f8dc9ff33885a..81a892183f1dca201878af7fc1ddff0b83b89a62 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1954,6 +1954,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO => return f.fail("TODO implement optimized float mode", .{}), .is_named_enum_value => return f.fail("TODO: C backend: implement is_named_enum_value", .{}), + .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}), // zig fmt: on }; switch (result_value) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 38f7d285aed18678a4340e7a47cd5fe395639bcb..5da3e7e32790fb323edf9e9d9c2d8722bd03c1fd 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4247,6 +4247,7 @@ pub const FuncGen = struct { .prefetch => try self.airPrefetch(inst), .is_named_enum_value => try self.airIsNamedEnumValue(inst), + .error_set_has_value => try self.airErrorSetHasValue(inst), .reduce => try self.airReduce(inst, false), .reduce_optimized => try self.airReduce(inst, true), @@ -7983,6 +7984,53 @@ pub const FuncGen = struct { } } + fn airErrorSetHasValue(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 error_set_ty = self.air.getRefType(ty_op.ty); + + const names = error_set_ty.errorSetNames(); + const valid_block = self.dg.context.appendBasicBlock(self.llvm_func, "Valid"); + const invalid_block = self.dg.context.appendBasicBlock(self.llvm_func, "Invalid"); + const end_block = self.context.appendBasicBlock(self.llvm_func, "End"); + const switch_instr = self.builder.buildSwitch(operand, invalid_block, @intCast(c_uint, names.len)); + + for (names) |name| { + const err_int = self.dg.module.global_error_set.get(name).?; + const this_tag_int_value = int: { + var tag_val_payload: Value.Payload.U64 = .{ + .base = .{ .tag = .int_u64 }, + .data = err_int, + }; + break :int try self.dg.lowerValue(.{ + .ty = Type.u16, + .val = Value.initPayload(&tag_val_payload.base), + }); + }; + switch_instr.addCase(this_tag_int_value, valid_block); + } + self.builder.positionBuilderAtEnd(valid_block); + _ = self.builder.buildBr(end_block); + + self.builder.positionBuilderAtEnd(invalid_block); + _ = self.builder.buildBr(end_block); + + self.builder.positionBuilderAtEnd(end_block); + + const llvm_type = self.dg.context.intType(1); + const incoming_values: [2]*const llvm.Value = .{ + llvm_type.constInt(1, .False), llvm_type.constInt(0, .False), + }; + const incoming_blocks: [2]*const llvm.BasicBlock = .{ + valid_block, invalid_block, + }; + const phi_node = self.builder.buildPhi(llvm_type, ""); + phi_node.addIncoming(&incoming_values, &incoming_blocks, 2); + return phi_node; + } + fn airIsNamedEnumValue(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; diff --git a/src/print_air.zig b/src/print_air.zig index 23107946f6fee77767631ced2b2032988b1a77ec..04dec25f5f51e431a0c69158c29d0b1c7c100d19 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -243,6 +243,7 @@ const Writer = struct { .popcount, .byte_swap, .bit_reverse, + .error_set_has_value, => try w.writeTyOp(s, inst), .block, -- 2.54.0 From 09f273136c74639d18695f63ba9c0fdc68ad678c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 11 Aug 2022 23:06:50 +0300 Subject: [PATCH 51/72] stage2: check for zero in `@intToError` safety --- src/Sema.zig | 22 ++++++++++++---------- src/codegen/llvm.zig | 2 +- src/type.zig | 2 ++ test/cases/safety/zero casted to error.zig | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 test/cases/safety/zero casted to error.zig diff --git a/src/Sema.zig b/src/Sema.zig index 3e62344a0436948a708a944201de9a7aa1baf568..55b519d9003fdcce6669a5115325cb72f4e36820 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6804,11 +6804,10 @@ fn zirErrorToInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const uncasted_operand = try sema.resolveInst(extra.operand); const operand = try sema.coerce(block, Type.anyerror, uncasted_operand, operand_src); - const result_ty = Type.u16; if (try sema.resolveMaybeUndefVal(block, src, operand)) |val| { if (val.isUndef()) { - return sema.addConstUndef(result_ty); + return sema.addConstUndef(Type.err_int); } switch (val.tag()) { .@"error" => { @@ -6817,14 +6816,14 @@ fn zirErrorToInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat .base = .{ .tag = .int_u64 }, .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value, }; - return sema.addConstant(result_ty, Value.initPayload(&payload.base)); + return sema.addConstant(Type.err_int, Value.initPayload(&payload.base)); }, // This is not a valid combination with the type `anyerror`. .the_only_possible_value => unreachable, // Assume it's already encoded as an integer. - else => return sema.addConstant(result_ty, val), + else => return sema.addConstant(Type.err_int, val), } } @@ -6833,14 +6832,14 @@ fn zirErrorToInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat if (!op_ty.isAnyError()) { const names = op_ty.errorSetNames(); switch (names.len) { - 0 => return sema.addConstant(result_ty, Value.zero), - 1 => return sema.addIntUnsigned(result_ty, sema.mod.global_error_set.get(names[0]).?), + 0 => return sema.addConstant(Type.err_int, Value.zero), + 1 => return sema.addIntUnsigned(Type.err_int, sema.mod.global_error_set.get(names[0]).?), else => {}, } } try sema.requireRuntimeBlock(block, src, operand_src); - return block.addBitCast(result_ty, operand); + return block.addBitCast(Type.err_int, operand); } fn zirIntToError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { @@ -6851,7 +6850,7 @@ fn zirIntToError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat const src = LazySrcLoc.nodeOffset(extra.node); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const uncasted_operand = try sema.resolveInst(extra.operand); - const operand = try sema.coerce(block, Type.u16, uncasted_operand, operand_src); + const operand = try sema.coerce(block, Type.err_int, uncasted_operand, operand_src); const target = sema.mod.getTarget(); if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| { @@ -6868,7 +6867,10 @@ fn zirIntToError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety()) { const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand); - try sema.addSafetyCheck(block, is_lt_len, .invalid_error_code); + const zero_val = try sema.addConstant(Type.err_int, Value.zero); + const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val); + const ok = try block.addBinOp(.bit_and, is_lt_len, is_non_zero); + try sema.addSafetyCheck(block, ok, .invalid_error_code); } return block.addInst(.{ .tag = .bitcast, @@ -17360,7 +17362,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety() and !dest_ty.isAnyError() and sema.mod.comp.bin_file.options.use_llvm) { - const err_int_inst = try block.addBitCast(Type.u16, operand); + const err_int_inst = try block.addBitCast(Type.err_int, operand); const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst); try sema.addSafetyCheck(block, ok, .invalid_error_code); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 5da3e7e32790fb323edf9e9d9c2d8722bd03c1fd..0586c994320b91708d92d8962cccf7c487b6b5d2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -8005,7 +8005,7 @@ pub const FuncGen = struct { .data = err_int, }; break :int try self.dg.lowerValue(.{ - .ty = Type.u16, + .ty = Type.err_int, .val = Value.initPayload(&tag_val_payload.base), }); }; diff --git a/src/type.zig b/src/type.zig index 1b71f4e9b1d90f29517c7a1bf21709714c27a2f7..582ea230ef99fb64f2cd040ada393840bdda58a9 100644 --- a/src/type.zig +++ b/src/type.zig @@ -6303,6 +6303,8 @@ pub const Type = extern union { pub const @"anyopaque" = initTag(.anyopaque); pub const @"null" = initTag(.@"null"); + pub const err_int = Type.u16; + pub fn ptr(arena: Allocator, mod: *Module, data: Payload.Pointer.Data) !Type { const target = mod.getTarget(); diff --git a/test/cases/safety/zero casted to error.zig b/test/cases/safety/zero casted to error.zig new file mode 100644 index 0000000000000000000000000000000000000000..3a2edf834a1c7b2c7371c541d5c383cbbbf20a48 --- /dev/null +++ b/test/cases/safety/zero casted to error.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "invalid error code")) { + std.process.exit(0); + } + std.process.exit(1); +} +pub fn main() !void { + bar(0) catch {}; + return error.TestFailed; +} +fn bar(x: u16) anyerror { + return @intToError(x); +} +// run +// backend=llvm +// target=native -- 2.54.0 From 92568a0097a6aeb0cd59f1f3dae43d0ecd7a18d0 Mon Sep 17 00:00:00 2001 From: martinhath Date: Fri, 12 Aug 2022 10:45:11 +0200 Subject: [PATCH 52/72] Sema: add error for signed integer division stage1 error reads error: division with 'i32' and 'comptime_int': signed integers must use @divTrunc, @divFloor, or @divExact Fixes: #12339 --- src/Sema.zig | 7 ++++++- test/cases/compile_errors/signed_integer_division.zig | 9 +++++++++ .../stage1/obj/signed_integer_division.zig | 9 --------- 3 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 test/cases/compile_errors/signed_integer_division.zig delete mode 100644 test/cases/compile_errors/stage1/obj/signed_integer_division.zig diff --git a/src/Sema.zig b/src/Sema.zig index f9b37f5b495eb26d578978a14e95cf9e14d1253d..d697ab0a9958fea7647be7e20d9e1385ce063be8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11261,7 +11261,12 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int); } - const air_tag = if (is_int) Air.Inst.Tag.div_trunc else switch (block.float_mode) { + const air_tag = if (is_int) blk: { + if (lhs_ty.isSignedInt() or rhs_ty.isSignedInt()) { + return sema.fail(block, src, "division with '{s}' and '{s}': signed integers must use @divTrunc, @divFloor, or @divExact", .{ @tagName(lhs_ty.tag()), @tagName(rhs_ty.tag()) }); + } + break :blk Air.Inst.Tag.div_trunc; + } else switch (block.float_mode) { .Optimized => Air.Inst.Tag.div_float_optimized, .Strict => Air.Inst.Tag.div_float, }; diff --git a/test/cases/compile_errors/signed_integer_division.zig b/test/cases/compile_errors/signed_integer_division.zig new file mode 100644 index 0000000000000000000000000000000000000000..7e968ac77eb1984c65ed4aa938366a24accacb3c --- /dev/null +++ b/test/cases/compile_errors/signed_integer_division.zig @@ -0,0 +1,9 @@ +export fn foo(a: i32, b: i32) i32 { + return a / b; +} + +// error +// backend=stage2 +// target=native +// +// :2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact diff --git a/test/cases/compile_errors/stage1/obj/signed_integer_division.zig b/test/cases/compile_errors/stage1/obj/signed_integer_division.zig deleted file mode 100644 index 3eebbf2248dbb2acd204d755c00346e2abbbdf6a..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/stage1/obj/signed_integer_division.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn foo(a: i32, b: i32) i32 { - return a / b; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact -- 2.54.0 From f2f1bb7cb675fc14dc7754f06329b5780091baaf Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 11 Aug 2022 10:13:16 +0200 Subject: [PATCH 53/72] macho: update __DWARF sections before and after writing out __LINKEDIT --- src/link/Dwarf.zig | 2 +- src/link/MachO/DebugSymbols.zig | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 2cd00f5a87430c5bbcbe5327b3a7ebb88183d5a3..73891a81f5642bcc367050cbf2d72688b00d9eae 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1259,7 +1259,7 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co debug_info_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff; } debug_info_sect.size = needed_size; - d_sym.debug_line_header_dirty = true; + d_sym.debug_info_header_dirty = true; } const file_pos = debug_info_sect.offset + atom.off; try pwriteDbgInfoNops( diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 3bfe334302a1a7f929d4baa5f72472a315dd717b..c2aa562db5fdcf446e920099a63df8b1a689a595 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -63,17 +63,16 @@ pub const Reloc = struct { pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void { if (self.linkedit_segment_cmd_index == null) { self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len); - log.debug("found __LINKEDIT segment free space 0x{x} to 0x{x}", .{ - self.base.page_size, - self.base.page_size * 2, - }); + const fileoff = @intCast(u64, self.base.page_size); + const needed_size = @intCast(u64, self.base.page_size) * 2; + log.debug("found __LINKEDIT segment free space 0x{x} to 0x{x}", .{ fileoff, needed_size }); // TODO this needs reworking try self.segments.append(allocator, .{ .segname = makeStaticString("__LINKEDIT"), - .vmaddr = self.base.page_size, - .vmsize = self.base.page_size, - .fileoff = self.base.page_size, - .filesize = self.base.page_size, + .vmaddr = fileoff, + .vmsize = needed_size, + .fileoff = fileoff, + .filesize = needed_size, .maxprot = macho.PROT.READ, .initprot = macho.PROT.READ, .cmdsize = @sizeOf(macho.segment_command_64), @@ -284,6 +283,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti const lc_writer = lc_buffer.writer(); var ncmds: u32 = 0; + self.updateDwarfSegment(); try self.writeLinkeditSegmentData(&ncmds, lc_writer); self.updateDwarfSegment(); -- 2.54.0 From aeaffd42f6d1c8fc92fa7669a579a8e6c46eb641 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 12 Aug 2022 11:55:38 +0200 Subject: [PATCH 54/72] x86: fix generating debug info for variables Add handling for these additional `MCValue`s: * `.immediate` - lower to `DW.OP.consts` or `DW.OP.constu` depending on signedness followed by popping off the DWARF stack with `DW.OP.stack_value` * `.undef` - lower to `DW.OP.implicit_value` * `.none` - lower to `DW.OP.lit0` followed by popping off the DWARF stack with `DW.OP.stack_value` For any remaining unhandled case, we generate `DW.OP.nop` in order not to mess up remaining DWARF info. --- src/arch/x86_64/CodeGen.zig | 48 ++++++++++++++++++++++++++++++++++++- src/link/Dwarf.zig | 11 +++++---- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7f7473bc666484c8b6e3f7fce6995a17df75a1ee..e58163875c065172314773163c2b7425be1357f9 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -4370,6 +4370,7 @@ fn genVarDbgInfo( .dwarf => |dw| { const dbg_info = &dw.dbg_info; try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.variable)); + const endian = self.target.cpu.arch.endian(); switch (mcv) { .register => |reg| { @@ -4390,7 +4391,6 @@ fn genVarDbgInfo( dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); }, .memory, .got_load, .direct_load => { - const endian = self.target.cpu.arch.endian(); const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)); const is_ptr = switch (tag) { .dbg_var_ptr => true, @@ -4425,7 +4425,53 @@ fn genVarDbgInfo( else => {}, } }, + .immediate => |x| { + const signedness: std.builtin.Signedness = blk: { + if (ty.zigTypeTag() != .Int) break :blk .unsigned; + break :blk ty.intInfo(self.target.*).signedness; + }; + try dbg_info.ensureUnusedCapacity(2); + const fixup = dbg_info.items.len; + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, + switch (signedness) { + .signed => DW.OP.consts, + .unsigned => DW.OP.constu, + }, + }); + switch (signedness) { + .signed => try leb128.writeILEB128(dbg_info.writer(), @bitCast(i64, x)), + .unsigned => try leb128.writeULEB128(dbg_info.writer(), x), + } + try dbg_info.append(DW.OP.stack_value); + dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); + }, + .undef => { + // DW.AT.location, DW.FORM.exprloc + // uleb128(exprloc_len) + // DW.OP.implicit_value uleb128(len_of_bytes) bytes + const abi_size = @intCast(u32, ty.abiSize(self.target.*)); + var implicit_value_len = std.ArrayList(u8).init(self.gpa); + defer implicit_value_len.deinit(); + try leb128.writeULEB128(implicit_value_len.writer(), abi_size); + const total_exprloc_len = 1 + implicit_value_len.items.len + abi_size; + try leb128.writeULEB128(dbg_info.writer(), total_exprloc_len); + try dbg_info.ensureUnusedCapacity(total_exprloc_len); + dbg_info.appendAssumeCapacity(DW.OP.implicit_value); + dbg_info.appendSliceAssumeCapacity(implicit_value_len.items); + dbg_info.appendNTimesAssumeCapacity(0xaa, abi_size); + }, + .none => { + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendSliceAssumeCapacity(&[3]u8{ // DW.AT.location, DW.FORM.exprloc + 2, DW.OP.lit0, DW.OP.stack_value, + }); + }, else => { + try dbg_info.ensureUnusedCapacity(2); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, DW.OP.nop, + }); log.debug("TODO generate debug info for {}", .{mcv}); }, } diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 73891a81f5642bcc367050cbf2d72688b00d9eae..3ae151491f82716e5e3a1d1164aec6ff380a0efa 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -102,7 +102,7 @@ pub const DeclState = struct { } pub fn addExprlocReloc(self: *DeclState, target: u32, offset: u32, is_ptr: bool) !void { - log.debug("{x}: target sym @{d}, via GOT {}", .{ offset, target, is_ptr }); + log.debug("{x}: target sym %{d}, via GOT {}", .{ offset, target, is_ptr }); try self.exprloc_relocs.append(self.gpa, .{ .@"type" = if (is_ptr) .got_load else .direct_load, .target = target, @@ -135,7 +135,7 @@ pub const DeclState = struct { .@"type" = ty, .offset = undefined, }); - log.debug("@{d}: {}", .{ sym_index, ty.fmtDebug() }); + log.debug("%{d}: {}", .{ sym_index, ty.fmtDebug() }); try self.abbrev_resolver.putNoClobberContext(self.gpa, ty, sym_index, .{ .mod = self.mod, }); @@ -143,7 +143,7 @@ pub const DeclState = struct { .mod = self.mod, }).?; }; - log.debug("{x}: @{d} + 0", .{ offset, resolv }); + log.debug("{x}: %{d} + 0", .{ offset, resolv }); try self.abbrev_relocs.append(self.gpa, .{ .target = resolv, .atom = atom, @@ -1056,6 +1056,7 @@ pub fn commitDeclState( break :blk false; }; if (deferred) { + log.debug("resolving %{d} deferred until flush", .{target}); try self.global_abbrev_relocs.append(gpa, .{ .target = null, .offset = reloc.offset, @@ -1063,10 +1064,12 @@ pub fn commitDeclState( .addend = reloc.addend, }); } else { + const value = symbol.atom.off + symbol.offset + reloc.addend; + log.debug("{x}: [() => {x}] (%{d}, '{}')", .{ reloc.offset, value, target, ty.fmtDebug() }); mem.writeInt( u32, dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)], - symbol.atom.off + symbol.offset + reloc.addend, + value, target_endian, ); } -- 2.54.0 From 7e07f3d4f95f9e7a0d95ee7b9335798eccb470c7 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Fri, 12 Aug 2022 14:47:54 -0700 Subject: [PATCH 55/72] stage2 astgen: Use `rl` semantics for `@Type` Resolves #12430. --- src/AstGen.zig | 3 ++- test/behavior.zig | 1 + test/behavior/bugs/12430.zig | 11 +++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/behavior/bugs/12430.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index af77cdacc466df99613f90e561aa4fd52e2fa60d..1151ed60daafecd81da92c24d26ab016a164bf7a 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7676,7 +7676,8 @@ fn builtinCall( } }, }); gz.instructions.appendAssumeCapacity(new_index); - return indexToRef(new_index); + const result = indexToRef(new_index); + return rvalue(gz, rl, result, node); }, .panic => { try emitDbgNode(gz, node); diff --git a/test/behavior.zig b/test/behavior.zig index e9c4ec779bb5e6bc4dca8ea89eccddb61be1ab5a..40f8ca0fb3800fafe4d21156fc8c1c3c470c01c7 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -84,6 +84,7 @@ test { _ = @import("behavior/bugs/11213.zig"); _ = @import("behavior/bugs/12003.zig"); _ = @import("behavior/bugs/12033.zig"); + _ = @import("behavior/bugs/12430.zig"); _ = @import("behavior/byteswap.zig"); _ = @import("behavior/byval_arg_var.zig"); _ = @import("behavior/call.zig"); diff --git a/test/behavior/bugs/12430.zig b/test/behavior/bugs/12430.zig new file mode 100644 index 0000000000000000000000000000000000000000..cbf1658b1ccf195e54af47ac29342234247a4e16 --- /dev/null +++ b/test/behavior/bugs/12430.zig @@ -0,0 +1,11 @@ +const std = @import("std"); + +test { + const T = comptime b: { + break :b @Type(.{ .Int = .{ + .signedness = .unsigned, + .bits = 8, + } }); + }; + try std.testing.expect(T == u8); +} -- 2.54.0 From 4c5156544b1b96a4860f98ecac5c7abe47de5465 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 6 Aug 2022 21:23:21 +0200 Subject: [PATCH 56/72] stage2 ARM: pass stack arguments in opposite order Earlier arguments have a smaller address (i.e. towards the bottom of the stack) --- src/arch/arm/CodeGen.zig | 28 +++++++++++--------- src/arch/arm/Emit.zig | 57 ++++++++++++++-------------------------- 2 files changed, 34 insertions(+), 51 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index be2891ef6fc0116e15af4099387ebd2d1eafddb6..0ac1344221ee0712192a29147f5580b8f88fd2ed 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -332,7 +332,7 @@ pub fn generate( }; for (function.dbg_arg_relocs.items) |reloc| { - try function.genArgDbgInfo(reloc.inst, reloc.index, call_info.stack_byte_count); + try function.genArgDbgInfo(reloc.inst, reloc.index); } var mir = Mir{ @@ -351,7 +351,8 @@ pub fn generate( .prev_di_pc = 0, .prev_di_line = module_fn.lbrace_line, .prev_di_column = module_fn.lbrace_column, - .prologue_stack_space = call_info.stack_byte_count + function.saved_regs_stack_space, + .stack_size = function.max_end_stack, + .saved_regs_stack_space = function.saved_regs_stack_space, }; defer emit.deinit(); @@ -464,6 +465,7 @@ fn gen(self: *Self) !void { const total_stack_size = self.max_end_stack + self.saved_regs_stack_space; const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align); const stack_size = aligned_total_stack_end - self.saved_regs_stack_space; + self.max_end_stack = stack_size; if (Instruction.Operand.fromU32(stack_size)) |op| { self.mir_instructions.set(sub_reloc, .{ .tag = .sub, @@ -1812,7 +1814,7 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV switch (error_union_mcv) { .register => return self.fail("TODO errUnionErr for registers", .{}), .stack_argument_offset => |off| { - return MCValue{ .stack_argument_offset = off - err_offset }; + return MCValue{ .stack_argument_offset = off + err_offset }; }, .stack_offset => |off| { return MCValue{ .stack_offset = off - err_offset }; @@ -1849,7 +1851,7 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) switch (error_union_mcv) { .register => return self.fail("TODO errUnionPayload for registers", .{}), .stack_argument_offset => |off| { - return MCValue{ .stack_argument_offset = off - payload_offset }; + return MCValue{ .stack_argument_offset = off + payload_offset }; }, .stack_offset => |off| { return MCValue{ .stack_offset = off - payload_offset }; @@ -1983,7 +1985,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { .dead, .unreach => unreachable, .register => unreachable, // a slice doesn't fit in one register .stack_argument_offset => |off| { - break :result MCValue{ .stack_argument_offset = off - 4 }; + break :result MCValue{ .stack_argument_offset = off + 4 }; }, .stack_offset => |off| { break :result MCValue{ .stack_offset = off - 4 }; @@ -2507,7 +2509,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { switch (mcv) { .dead, .unreach => unreachable, .stack_argument_offset => |off| { - break :result MCValue{ .stack_argument_offset = off - struct_field_offset }; + break :result MCValue{ .stack_argument_offset = off + struct_field_offset }; }, .stack_offset => |off| { break :result MCValue{ .stack_offset = off - struct_field_offset }; @@ -3369,9 +3371,7 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) error{OutOfMemory}!void { } } -fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, arg_index: u32, stack_byte_count: u32) error{OutOfMemory}!void { - const prologue_stack_space = stack_byte_count + self.saved_regs_stack_space; - +fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, arg_index: u32) error{OutOfMemory}!void { const mcv = self.args[arg_index]; const ty = self.air.instructions.items(.data)[inst].ty; const name = self.mod_fn.getParamName(self.bin_file.options.module.?, arg_index); @@ -3404,7 +3404,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, arg_index: u32, stack_byte_c // const abi_size = @intCast(u32, ty.abiSize(self.target.*)); const adjusted_stack_offset = switch (mcv) { .stack_offset => |offset| -@intCast(i32, offset), - .stack_argument_offset => |offset| @intCast(i32, prologue_stack_space - offset), + .stack_argument_offset => |offset| @intCast(i32, self.saved_regs_stack_space + offset), else => unreachable, }; @@ -3559,7 +3559,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .stack_offset => unreachable, .stack_argument_offset => |offset| try self.genSetStackArgument( arg_ty, - info.stack_byte_count - offset, + offset, arg_mcv, ), else => unreachable, @@ -5653,8 +5653,8 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { if (ty.abiAlignment(self.target.*) == 8) nsaa = std.mem.alignForwardGeneric(u32, nsaa, 8); - nsaa += param_size; result.args[i] = .{ .stack_argument_offset = nsaa }; + nsaa += param_size; } } @@ -5687,9 +5687,11 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { for (param_types) |ty, i| { if (ty.abiSize(self.target.*) > 0) { const param_size = @intCast(u32, ty.abiSize(self.target.*)); + const param_alignment = ty.abiAlignment(self.target.*); - stack_offset = std.mem.alignForwardGeneric(u32, stack_offset, ty.abiAlignment(self.target.*)) + param_size; + stack_offset = std.mem.alignForwardGeneric(u32, stack_offset, param_alignment); result.args[i] = .{ .stack_argument_offset = stack_offset }; + stack_offset += param_size; } else { result.args[i] = .{ .none = {} }; } diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 47d508b34aa5c315da3417e790c8a17dbcd4fd33..cf749792f0e81fa06a7cb5e3371c81da9d5afa49 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -33,9 +33,13 @@ prev_di_column: u32, /// Relative to the beginning of `code`. prev_di_pc: usize, -/// The amount of stack space consumed by all stack arguments as well -/// as the saved callee-saved registers -prologue_stack_space: u32, +/// The amount of stack space consumed by the saved callee-saved +/// registers in bytes +saved_regs_stack_space: u32, + +/// The final stack frame size of the function (already aligned to the +/// respective stack alignment). Does not include prologue stack space. +stack_size: u32, /// The branch type of every branch branch_types: std.AutoHashMapUnmanaged(Mir.Inst.Index, BranchType) = .{}, @@ -500,14 +504,15 @@ fn mirLoadStackArgument(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const cond = emit.mir.instructions.items(.cond)[inst]; const r_stack_offset = emit.mir.instructions.items(.data)[inst].r_stack_offset; + const rt = r_stack_offset.rt; - const raw_offset = emit.prologue_stack_space - r_stack_offset.stack_offset; + const raw_offset = emit.stack_size + emit.saved_regs_stack_space + r_stack_offset.stack_offset; switch (tag) { .ldr_ptr_stack_argument => { const operand = Instruction.Operand.fromU32(raw_offset) orelse return emit.fail("TODO mirLoadStack larger offsets", .{}); - try emit.writeInstruction(Instruction.add(cond, r_stack_offset.rt, .fp, operand)); + try emit.writeInstruction(Instruction.add(cond, rt, .sp, operand)); }, .ldr_stack_argument, .ldrb_stack_argument, @@ -516,23 +521,11 @@ fn mirLoadStackArgument(emit: *Emit, inst: Mir.Inst.Index) !void { break :blk Instruction.Offset.imm(@intCast(u12, raw_offset)); } else return emit.fail("TODO mirLoadStack larger offsets", .{}); - const ldr = switch (tag) { - .ldr_stack_argument => &Instruction.ldr, - .ldrb_stack_argument => &Instruction.ldrb, + switch (tag) { + .ldr_stack_argument => try emit.writeInstruction(Instruction.ldr(cond, rt, .sp, .{ .offset = offset })), + .ldrb_stack_argument => try emit.writeInstruction(Instruction.ldrb(cond, rt, .sp, .{ .offset = offset })), else => unreachable, - }; - - const ldr_workaround = switch (builtin.zig_backend) { - .stage1 => ldr.*, - else => ldr, - }; - - try emit.writeInstruction(ldr_workaround( - cond, - r_stack_offset.rt, - .fp, - .{ .offset = offset }, - )); + } }, .ldrh_stack_argument, .ldrsb_stack_argument, @@ -542,24 +535,12 @@ fn mirLoadStackArgument(emit: *Emit, inst: Mir.Inst.Index) !void { break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, raw_offset)); } else return emit.fail("TODO mirLoadStack larger offsets", .{}); - const ldr = switch (tag) { - .ldrh_stack_argument => &Instruction.ldrh, - .ldrsb_stack_argument => &Instruction.ldrsb, - .ldrsh_stack_argument => &Instruction.ldrsh, + switch (tag) { + .ldrh_stack_argument => try emit.writeInstruction(Instruction.ldrh(cond, rt, .sp, .{ .offset = offset })), + .ldrsb_stack_argument => try emit.writeInstruction(Instruction.ldrsb(cond, rt, .sp, .{ .offset = offset })), + .ldrsh_stack_argument => try emit.writeInstruction(Instruction.ldrsh(cond, rt, .sp, .{ .offset = offset })), else => unreachable, - }; - - const ldr_workaround = switch (builtin.zig_backend) { - .stage1 => ldr.*, - else => ldr, - }; - - try emit.writeInstruction(ldr_workaround( - cond, - r_stack_offset.rt, - .fp, - .{ .offset = offset }, - )); + } }, else => unreachable, } -- 2.54.0 From 91969ad908ebb0362eac994ba10cb23bce0a7a4c Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 12 Aug 2022 20:29:48 +0200 Subject: [PATCH 57/72] stage2 ARM: Fix tracking of function return values --- src/arch/arm/CodeGen.zig | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 0ac1344221ee0712192a29147f5580b8f88fd2ed..d78197d2a7159ae4c4af928ef9fc45d041583360 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -247,6 +247,31 @@ const BigTomb = struct { log.debug("%{d} => {}", .{ bt.inst, result }); const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); + + switch (result) { + .register => |reg| { + // In some cases (such as bitcast), an operand + // may be the same MCValue as the result. If + // that operand died and was a register, it + // was freed by processDeath. We have to + // "re-allocate" the register. + if (bt.function.register_manager.isRegFree(reg)) { + bt.function.register_manager.getRegAssumeFree(reg, bt.inst); + } + }, + .register_c_flag, + .register_v_flag, + => |reg| { + if (bt.function.register_manager.isRegFree(reg)) { + bt.function.register_manager.getRegAssumeFree(reg, bt.inst); + } + bt.function.cpsr_flags_inst = bt.inst; + }, + .cpsr_flags => { + bt.function.cpsr_flags_inst = bt.inst; + }, + else => {}, + } } bt.function.finishAirBookkeeping(); } @@ -3524,7 +3549,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.register_manager.getReg(reg, null); } - if (info.return_value == .stack_offset) { + // If returning by reference, r0 will contain the address of where + // to put the result into. In that case, make sure that r0 remains + // untouched by the parameter passing code + const r0_lock: ?RegisterLock = if (info.return_value == .stack_offset) blk: { log.debug("airCall: return by reference", .{}); const ret_ty = fn_ty.fnReturnType(); const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); @@ -3540,7 +3568,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset }); info.return_value = .{ .stack_offset = stack_offset }; - } + + break :blk self.register_manager.lockRegAssumeUnused(.r0); + } else null; + defer if (r0_lock) |reg| self.register_manager.unlockReg(reg); // Make space for the arguments passed via the stack self.max_end_stack += info.stack_byte_count; -- 2.54.0 From c9d9fd53a61cbc656f543a3fa778ac4c72a58701 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 13 Aug 2022 15:51:49 +0200 Subject: [PATCH 58/72] stage2 ARM: add inline memcpy to genSetStack --- src/arch/arm/CodeGen.zig | 131 ++++++++++++++++++++++++++++++++++----- 1 file changed, 116 insertions(+), 15 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index d78197d2a7159ae4c4af928ef9fc45d041583360..5b772cc4d0675b59dcf59cdcb727770034ab631b 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -2286,16 +2286,17 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .register_c_flag, .register_v_flag, => unreachable, // cannot hold an address - .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), - .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), + .immediate => |imm| { + try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }); + }, + .ptr_stack_offset => |off| { + try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }); + }, .register => |reg| { const reg_lock = self.register_manager.lockReg(reg); defer if (reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); switch (dst_mcv) { - .dead => unreachable, - .undef => unreachable, - .cpsr_flags => unreachable, .register => |dst_reg| { try self.genLdrRegister(dst_reg, reg, elem_ty); }, @@ -2331,7 +2332,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); } }, - else => return self.fail("TODO load from register into {}", .{dst_mcv}), + else => unreachable, // attempting to load into non-register or non-stack MCValue } }, .memory, @@ -2428,7 +2429,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type // sub src_reg, fp, #off try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); }, - .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @intCast(u32, addr) }), + .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(u32, addr) }), .stack_argument_offset => |off| { _ = try self.addInst(.{ .tag = .ldr_ptr_stack_argument, @@ -3374,6 +3375,102 @@ fn genInlineMemcpy( // end: } +fn genInlineMemset( + self: *Self, + dst: MCValue, + val: MCValue, + len: MCValue, +) !void { + const dst_reg = switch (dst) { + .register => |r| r, + else => try self.copyToTmpRegister(Type.initTag(.manyptr_u8), dst), + }; + const dst_reg_lock = self.register_manager.lockReg(dst_reg); + defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); + + const val_reg = switch (val) { + .register => |r| r, + else => try self.copyToTmpRegister(Type.initTag(.u8), val), + }; + const val_reg_lock = self.register_manager.lockReg(val_reg); + defer if (val_reg_lock) |lock| self.register_manager.unlockReg(lock); + + const len_reg = switch (len) { + .register => |r| r, + else => try self.copyToTmpRegister(Type.usize, len), + }; + const len_reg_lock = self.register_manager.lockReg(len_reg); + defer if (len_reg_lock) |lock| self.register_manager.unlockReg(lock); + + const count_reg = try self.register_manager.allocReg(null, gp); + + try self.genInlineMemsetCode(dst_reg, val_reg, len_reg, count_reg); +} + +fn genInlineMemsetCode( + self: *Self, + dst: Register, + val: Register, + len: Register, + count: Register, +) !void { + // mov count, #0 + _ = try self.addInst(.{ + .tag = .mov, + .data = .{ .rr_op = .{ + .rd = count, + .rn = .r0, + .op = Instruction.Operand.imm(0, 0), + } }, + }); + + // loop: + // cmp count, len + _ = try self.addInst(.{ + .tag = .cmp, + .data = .{ .rr_op = .{ + .rd = .r0, + .rn = count, + .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none), + } }, + }); + + // bge end + _ = try self.addInst(.{ + .tag = .b, + .cond = .ge, + .data = .{ .inst = @intCast(u32, self.mir_instructions.len + 4) }, + }); + + // strb val, [src, count] + _ = try self.addInst(.{ + .tag = .strb, + .data = .{ .rr_offset = .{ + .rt = val, + .rn = dst, + .offset = .{ .offset = Instruction.Offset.reg(count, .none) }, + } }, + }); + + // add count, count, #1 + _ = try self.addInst(.{ + .tag = .add, + .data = .{ .rr_op = .{ + .rd = count, + .rn = count, + .op = Instruction.Operand.imm(1, 0), + } }, + }); + + // b loop + _ = try self.addInst(.{ + .tag = .b, + .data = .{ .inst = @intCast(u32, self.mir_instructions.len - 4) }, + }); + + // end: +} + /// Adds a Type to the .debug_info at the current position. The bytes will be populated later, /// after codegen for this symbol is done. fn addDbgInfoTypeReloc(self: *Self, ty: Type) error{OutOfMemory}!void { @@ -4652,11 +4749,15 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro if (!self.wantSafety()) return; // The already existing value will do just fine. // TODO Upgrade this to a memset call when we have that available. - switch (ty.abiSize(self.target.*)) { - 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }), - 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }), - 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), - else => return self.fail("TODO implement memset", .{}), + switch (abi_size) { + 1 => try self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }), + 2 => try self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }), + 4 => try self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), + else => try self.genInlineMemset( + .{ .ptr_stack_offset = stack_offset }, + .{ .immediate = 0xaa }, + .{ .immediate = abi_size }, + ), } }, .cpsr_flags, @@ -5068,9 +5169,9 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I return; // The already existing value will do just fine. // TODO Upgrade this to a memset call when we have that available. switch (abi_size) { - 1 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaa }), - 2 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaa }), - 4 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), + 1 => try self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaa }), + 2 => try self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaa }), + 4 => try self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), else => return self.fail("TODO implement memset", .{}), } }, -- 2.54.0 From dc5174440ca547d787b85d407b6836a77688610b Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sun, 14 Aug 2022 11:28:12 +0200 Subject: [PATCH 59/72] feat: better No Results Found page This adds some helpful links to the page and makes it look nicer by using etc. --- lib/docs/index.html | 8 +++++++- lib/docs/main.js | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/docs/index.html b/lib/docs/index.html index f9c2954a3a658526e8cb58fc52e90d06da570313..3d4e663dbcfd54dab07ba425e7a001e4e03ed5e2 100644 --- a/lib/docs/index.html +++ b/lib/docs/index.html @@ -654,7 +654,13 @@ -- 2.54.0 From 8f6f4bc7caf09a4399a5f2b1fdf5c966b89bab15 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sun, 14 Aug 2022 11:49:44 +0200 Subject: [PATCH 62/72] feat: make modal more restrictive and more Esc freedom This makes it so that you can no longer interact with the search bar or the results or anything while the modal is open. That's why it's a "modal" and not a "dialog". It also makes it so that you can now always press Esc to return to the results or the main page. Previously this was only possible when the search field was active. --- lib/docs/main.js | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/lib/docs/main.js b/lib/docs/main.js index 3072918b32bcd9ff6741f5bef466e0e3ccb8a1f4..57cdd3c5130167660ec784a3a60a3119e2ef1ce1 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -3148,6 +3148,22 @@ var zigAnalysis; domSearch.blur(); } + function onEscape(ev) { + if (!domHelpModal.classList.contains("hidden")) { + domHelpModal.classList.add("hidden"); + ev.preventDefault(); + ev.stopPropagation(); + } else { + domSearch.value = ""; + domSearch.blur(); + domSearchPlaceholder.classList.remove("hidden"); + curSearchIndex = -1; + ev.preventDefault(); + ev.stopPropagation(); + startSearch(); + } + } + function onSearchKeyDown(ev) { switch (getKeyString(ev)) { case "Enter": @@ -3164,13 +3180,8 @@ var zigAnalysis; ev.stopPropagation(); return; case "Esc": - domSearch.value = ""; - domSearch.blur(); - curSearchIndex = -1; - ev.preventDefault(); - ev.stopPropagation(); - startSearch(); - return; + onEscape(ev); + return case "Up": moveSearchCursor(-1); ev.preventDefault(); @@ -3245,19 +3256,18 @@ var zigAnalysis; function onWindowKeyDown(ev) { switch (getKeyString(ev)) { case "Esc": - if (!domHelpModal.classList.contains("hidden")) { - domHelpModal.classList.add("hidden"); + onEscape(ev); + break; + case "s": + if (domHelpModal.classList.contains("hidden")) { + // TODO: scroll the page to the very top + domSearch.focus(); + domSearch.select(); ev.preventDefault(); ev.stopPropagation(); + startAsyncSearch(); } break; - case "s": - domSearch.focus(); - domSearch.select(); - ev.preventDefault(); - ev.stopPropagation(); - startAsyncSearch(); - break; case "?": ev.preventDefault(); ev.stopPropagation(); @@ -3273,6 +3283,7 @@ var zigAnalysis; domHelpModal.style.top = window.innerHeight / 2 - domHelpModal.clientHeight / 2 + "px"; domHelpModal.focus(); + domSearch.blur(); } function clearAsyncSearch() { -- 2.54.0 From 2723d8e6e2fea6229a8f9082bc991cb7bef39054 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sun, 14 Aug 2022 11:51:43 +0200 Subject: [PATCH 63/72] style: format Some formatting, apparently. --- lib/docs/main.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/docs/main.js b/lib/docs/main.js index 57cdd3c5130167660ec784a3a60a3119e2ef1ce1..b6a6e8332b488cc298ad96a1eec435aff46ac291 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -117,10 +117,10 @@ var zigAnalysis; }); domSectSearchAllResultsLink.addEventListener('click', onClickSearchShowAllResults, false); function onClickSearchShowAllResults(ev) { - ev.preventDefault(); - ev.stopPropagation(); - searchTrimResults = false; - onHashChange(); + ev.preventDefault(); + ev.stopPropagation(); + searchTrimResults = false; + onHashChange(); } domPrivDeclsBox.addEventListener( @@ -3309,7 +3309,7 @@ var zigAnalysis; list.sort(); return list; } - + function renderSearch() { let matchedItems = []; let ignoreCase = curNavSearch.toLowerCase() === curNavSearch; @@ -3398,13 +3398,13 @@ var zigAnalysis; const text = lastPkgName + "." + match.path.declNames.join("."); const href = navLink(match.path.pkgNames, match.path.declNames); - matchedItemsHTML += "
  • "+ text + "
  • "; + matchedItemsHTML += "
  • " + text + "
  • "; } // Replace the search results using our newly constructed HTML string domListSearchResults.innerHTML = matchedItemsHTML; if (searchTrimmed) { - domSectSearchAllResultsLink.classList.remove("hidden"); + domSectSearchAllResultsLink.classList.remove("hidden"); } renderSearchCursor(); -- 2.54.0 From e5379e5027d33f5954fc7daa00b835a374f57272 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sun, 14 Aug 2022 12:09:17 +0200 Subject: [PATCH 64/72] docs: add comment --- lib/docs/main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/docs/main.js b/lib/docs/main.js index b6a6e8332b488cc298ad96a1eec435aff46ac291..44a7c88572a6097565c776b0fe83594566f39ebf 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -3148,6 +3148,7 @@ var zigAnalysis; domSearch.blur(); } + // hide the modal if it's visible or return to the previous result page and unfocus the search function onEscape(ev) { if (!domHelpModal.classList.contains("hidden")) { domHelpModal.classList.add("hidden"); -- 2.54.0 From af20dfae60bb5c1c1ae0db25339e1f4ad5f8a2e7 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sun, 14 Aug 2022 12:20:26 +0200 Subject: [PATCH 65/72] bug: add TODO --- lib/docs/main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/docs/main.js b/lib/docs/main.js index 44a7c88572a6097565c776b0fe83594566f39ebf..680fc1d962afbbb89a1db7f267a6fd6a11bcde8e 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -3189,6 +3189,7 @@ var zigAnalysis; ev.stopPropagation(); return; case "Down": + // TODO: make the page scroll down if the search cursor is out of the screen moveSearchCursor(1); ev.preventDefault(); ev.stopPropagation(); -- 2.54.0 From 78bb29d1dc45acedf7ec8990e1ca2c47978b5087 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sun, 14 Aug 2022 19:17:20 +0200 Subject: [PATCH 66/72] fix: scroll page to very top when S is pressed Credits go to @rudedogg --- lib/docs/index.html | 2 +- lib/docs/main.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/docs/index.html b/lib/docs/index.html index 37025c4e72a93bbe67939bbedaccc1a8178fea90..236923c1d89952bd3d34dd0391c3f7047b05be58 100644 --- a/lib/docs/index.html +++ b/lib/docs/index.html @@ -612,7 +612,7 @@ -
    +
    diff --git a/lib/docs/main.js b/lib/docs/main.js index 680fc1d962afbbb89a1db7f267a6fd6a11bcde8e..e2b5162beb326b7148d610750994ab118a3e5f5a 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -41,7 +41,7 @@ var zigAnalysis; const domSearch = document.getElementById("search"); const domSectSearchResults = document.getElementById("sectSearchResults"); const domSectSearchAllResultsLink = document.getElementById("sectSearchAllResultsLink"); - + const domDocs = document.getElementById("docs"); const domListSearchResults = document.getElementById("listSearchResults"); const domSectSearchNoResults = document.getElementById("sectSearchNoResults"); const domSectInfo = document.getElementById("sectInfo"); @@ -3262,9 +3262,9 @@ var zigAnalysis; break; case "s": if (domHelpModal.classList.contains("hidden")) { - // TODO: scroll the page to the very top domSearch.focus(); domSearch.select(); + domDocs.scrollTo(0, 0); ev.preventDefault(); ev.stopPropagation(); startAsyncSearch(); -- 2.54.0 From 2279f27e0f97e66aedc87ec5c85f05b185a3631d Mon Sep 17 00:00:00 2001 From: Yujiri Date: Fri, 12 Aug 2022 11:56:00 +0000 Subject: [PATCH 67/72] Fix #12423: auto_hash not hashing arrays of slices uniquely --- lib/std/hash/auto_hash.zig | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/std/hash/auto_hash.zig b/lib/std/hash/auto_hash.zig index 951c0148d9d03f1f3a1835de2583ec9c5555488b..32f4f4378f68b590b5b277721a546feeff0b51a3 100644 --- a/lib/std/hash/auto_hash.zig +++ b/lib/std/hash/auto_hash.zig @@ -30,13 +30,15 @@ pub fn hashPointer(hasher: anytype, key: anytype, comptime strat: HashStrategy) .DeepRecursive => hash(hasher, key.*, .DeepRecursive), }, - .Slice => switch (strat) { - .Shallow => { - hashPointer(hasher, key.ptr, .Shallow); - hash(hasher, key.len, .Shallow); - }, - .Deep => hashArray(hasher, key, .Shallow), - .DeepRecursive => hashArray(hasher, key, .DeepRecursive), + .Slice => { + switch (strat) { + .Shallow => { + hashPointer(hasher, key.ptr, .Shallow); + }, + .Deep => hashArray(hasher, key, .Shallow), + .DeepRecursive => hashArray(hasher, key, .DeepRecursive), + } + hash(hasher, key.len, .Shallow); }, .Many, @@ -53,17 +55,8 @@ pub fn hashPointer(hasher: anytype, key: anytype, comptime strat: HashStrategy) /// Helper function to hash a set of contiguous objects, from an array or slice. pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { - switch (strat) { - .Shallow => { - for (key) |element| { - hash(hasher, element, .Shallow); - } - }, - else => { - for (key) |element| { - hash(hasher, element, strat); - } - }, + for (key) |element| { + hash(hasher, element, strat); } } @@ -359,6 +352,12 @@ test "testHash array" { try testing.expectEqual(h, hasher.final()); } +test "testHash multi-dimensional array" { + const a = [_][]const u32{ &.{ 1, 2, 3 }, &.{ 4, 5 } }; + const b = [_][]const u32{ &.{ 1, 2 }, &.{ 3, 4, 5 } }; + try testing.expect(testHash(a) != testHash(b)); +} + test "testHash struct" { const Foo = struct { a: u32 = 1, -- 2.54.0 From 764cf4e53ffc29bbd39c636020019ae419135d82 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Sat, 13 Aug 2022 22:08:40 -0700 Subject: [PATCH 68/72] std.fs: Fix `WalkerEntry.dir` not always being the containing dir Before this commit, the modified test would fail with `FileNotFound` because the `entry.dir` would be for the entry itself rather than the containing dir of the entry. That is, if you were walking a tree of `a/b`, then (previously) the entry for `b` would incorrectly have an `entry.dir` for `b` rather than `a`. --- lib/std/fs.zig | 6 ++++-- lib/std/fs/test.zig | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 0968e168124f0784b4084373e973a006451e886b..b1e88d2e0109c96dc98fcda9213f88c1ee50ab6b 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -877,8 +877,9 @@ pub const IterableDir = struct { /// a reference to the path. pub fn next(self: *Walker) !?WalkerEntry { while (self.stack.items.len != 0) { - // `top` becomes invalid after appending to `self.stack` + // `top` and `containing` become invalid after appending to `self.stack` var top = &self.stack.items[self.stack.items.len - 1]; + var containing = top; var dirname_len = top.dirname_len; if (try top.iter.next()) |base| { self.name_buffer.shrinkRetainingCapacity(dirname_len); @@ -899,10 +900,11 @@ pub const IterableDir = struct { .dirname_len = self.name_buffer.items.len, }); top = &self.stack.items[self.stack.items.len - 1]; + containing = &self.stack.items[self.stack.items.len - 2]; } } return WalkerEntry{ - .dir = top.iter.dir, + .dir = containing.iter.dir, .basename = self.name_buffer.items[dirname_len..], .path = self.name_buffer.items, .kind = base.kind, diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 538ce1bf5ed18f3df908cb1bad4a3d839c20c0ac..a7686080c1500c09bfe6ce6d148d481860c25f68 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1058,6 +1058,9 @@ test "walker" { std.debug.print("found unexpected path: {s}\n", .{std.fmt.fmtSliceEscapeLower(entry.path)}); return err; }; + // make sure that the entry.dir is the containing dir + var entry_dir = try entry.dir.openDir(entry.basename, .{}); + defer entry_dir.close(); num_walked += 1; } try testing.expectEqual(expected_paths.kvs.len, num_walked); -- 2.54.0 From cb901e578cbc321003d5e4327d51d349d91f6dd6 Mon Sep 17 00:00:00 2001 From: LeRoyce Pearson Date: Mon, 15 Aug 2022 02:28:42 -0600 Subject: [PATCH 69/72] stage2: add compile errors for comptime `@shrExact` and `@divExact` failures --- src/Sema.zig | 12 +++++++++--- test/cases/compile_errors/exact division failure.zig | 10 ++++++++++ .../compile_errors/float exact division failure.zig | 10 ++++++++++ .../{stage1/obj => }/shrExact_shifts_out_1_bits.zig | 4 ++-- 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 test/cases/compile_errors/exact division failure.zig create mode 100644 test/cases/compile_errors/float exact division failure.zig rename test/cases/compile_errors/{stage1/obj => }/shrExact_shifts_out_1_bits.zig (58%) diff --git a/src/Sema.zig b/src/Sema.zig index d697ab0a9958fea7647be7e20d9e1385ce063be8..12851cc9add831e94fc3fea67618a266c251bac6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10441,7 +10441,7 @@ fn zirShr( // Detect if any ones would be shifted out. const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, target); if (!(try truncated.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) { - return sema.addConstUndef(lhs_ty); + return sema.fail(block, src, "exact shift shifted out 1 bits", .{}); } } const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, target); @@ -11346,13 +11346,19 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (maybe_lhs_val) |lhs_val| { if (maybe_rhs_val) |rhs_val| { if (is_int) { - // TODO: emit compile error if there is a remainder + const modulus_val = try lhs_val.intMod(rhs_val, resolved_type, sema.arena, target); + if (modulus_val.compareWithZero(.neq)) { + return sema.fail(block, src, "exact division produced remainder", .{}); + } return sema.addConstant( resolved_type, try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target), ); } else { - // TODO: emit compile error if there is a remainder + const modulus_val = try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, target); + if (modulus_val.compareWithZero(.neq)) { + return sema.fail(block, src, "exact division produced remainder", .{}); + } return sema.addConstant( resolved_type, try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target), diff --git a/test/cases/compile_errors/exact division failure.zig b/test/cases/compile_errors/exact division failure.zig new file mode 100644 index 0000000000000000000000000000000000000000..d134e0e2fbabd3fe38886a1e97c5a8835903fd1e --- /dev/null +++ b/test/cases/compile_errors/exact division failure.zig @@ -0,0 +1,10 @@ +comptime { + const x = @divExact(10, 3); + _ = x; +} + +// error +// backend=llvm +// target=native +// +// :2:15: error: exact division produced remainder diff --git a/test/cases/compile_errors/float exact division failure.zig b/test/cases/compile_errors/float exact division failure.zig new file mode 100644 index 0000000000000000000000000000000000000000..c09defc56ebf72c04ed24248fcc45a5143534087 --- /dev/null +++ b/test/cases/compile_errors/float exact division failure.zig @@ -0,0 +1,10 @@ +comptime { + const x = @divExact(10.0, 3.0); + _ = x; +} + +// error +// backend=llvm +// target=native +// +// :2:15: error: exact division produced remainder diff --git a/test/cases/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig b/test/cases/compile_errors/shrExact_shifts_out_1_bits.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig rename to test/cases/compile_errors/shrExact_shifts_out_1_bits.zig index 223db7663042f98a32ff416c5e513d806e6e155a..dd23c4bcb3f68258fc52fe6ad53924900cfe1a1e 100644 --- a/test/cases/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig +++ b/test/cases/compile_errors/shrExact_shifts_out_1_bits.zig @@ -4,7 +4,7 @@ comptime { } // error -// backend=stage1 +// backend=llvm // target=native // -// tmp.zig:2:15: error: exact shift shifted out 1 bits +// :2:15: error: exact shift shifted out 1 bits -- 2.54.0 From a9c4dc84f487c4764c5787439ea87962f698d511 Mon Sep 17 00:00:00 2001 From: sin-ack Date: Fri, 12 Aug 2022 21:51:07 +0000 Subject: [PATCH 70/72] Sema: Revert sema.err to null if the Decl already has an error Previously we would assign the error message to Sema and then never clear it even when destroying the error message, which caused memory corruption. Closes #12437 --- src/Sema.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index ed8204b3467c0003cb6a33e4b7097a8196fe5bad..879ecb4e2f4161f838f78fe18bf68e45af0fee20 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1906,8 +1906,6 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { } const mod = sema.mod; - sema.err = err_msg; - { errdefer err_msg.destroy(mod.gpa); if (err_msg.src_loc.lazy == .unneeded) { @@ -1925,8 +1923,10 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError { const gop = mod.failed_decls.getOrPutAssumeCapacity(sema.owner_decl_index); if (gop.found_existing) { // If there are multiple errors for the same Decl, prefer the first one added. + sema.err = null; err_msg.destroy(mod.gpa); } else { + sema.err = err_msg; gop.value_ptr.* = err_msg; } return error.AnalysisFail; -- 2.54.0 From 95573dbeebf7a9d3617c1944ab0a74691b7e9d51 Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Mon, 15 Aug 2022 20:44:30 +0200 Subject: [PATCH 71/72] ci: add gzip compression to stdlib docs & langref --- ci/srht/update_download_page | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/ci/srht/update_download_page b/ci/srht/update_download_page index 5c007b6986d4d94c8cfb5e892d5e7de233ef3fd3..5277da7d2bb361c2fa3dd3406940ffd6d078d291 100755 --- a/ci/srht/update_download_page +++ b/ci/srht/update_download_page @@ -103,11 +103,19 @@ cd "$HOME" # Upload new stdlib autodocs mkdir -p docs_to_upload/documentation/master/std/ -cp "$ZIGDIR/docs/std/index.html" docs_to_upload/documentation/master/std/index.html -cp "$ZIGDIR/docs/std/data.js" docs_to_upload/documentation/master/std/data.js -cp "$ZIGDIR/docs/std/main.js" docs_to_upload/documentation/master/std/main.js -cp "$LANGREF" docs_to_upload/documentation/master/index.html -$S3CMD put -P --no-mime-magic --recursive --add-header="Cache-Control: max-age=0, must-revalidate" "docs_to_upload/" s3://ziglang.org/ + +gzip -c -9 "$ZIGDIR/docs/std/index.html" > docs_to_upload/documentation/master/std/index.html +gzip -c -9 "$ZIGDIR/docs/std/data.js" > docs_to_upload/documentation/master/std/data.js +gzip -c -9 "$ZIGDIR/docs/std/main.js" > docs_to_upload/documentation/master/std/main.js +gzip -c -9 "$LANGREF" > docs_to_upload/documentation/master/index.html +$S3CMD put -P --no-mime-magic --recursive --add-header="Content-Encoding:gzip" --add-header="Cache-Control: max-age=0, must-revalidate" "docs_to_upload/" s3://ziglang.org/ + +## Copy without compression: +# cp "$ZIGDIR/docs/std/index.html" docs_to_upload/documentation/master/std/index.html +# cp "$ZIGDIR/docs/std/data.js" docs_to_upload/documentation/master/std/data.js +# cp "$ZIGDIR/docs/std/main.js" docs_to_upload/documentation/master/std/main.js +# cp "$LANGREF" docs_to_upload/documentation/master/index.html +# $S3CMD put -P --no-mime-magic --recursive --add-header="Cache-Control: max-age=0, must-revalidate" "docs_to_upload/" s3://ziglang.org/ git clone --depth 1 git@github.com:ziglang/www.ziglang.org.git cd www.ziglang.org -- 2.54.0 From b3922289be1ffaf194b55face332892280981356 Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Mon, 15 Aug 2022 21:56:59 +0200 Subject: [PATCH 72/72] Zir: add missing support for packed ints in declIterator --- src/Zir.zig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Zir.zig b/src/Zir.zig index 538ef6aaf8c35b12a3e4586e47103283d2b29cd6..7a1db54ea28c0c7b4e8416699cd2ff33d7173a2b 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -3608,6 +3608,16 @@ pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator { break :decls_len decls_len; } else 0; + if (small.has_backing_int) { + const backing_int_body_len = zir.extra[extra_index]; + extra_index += 1; // backing_int_body_len + if (backing_int_body_len == 0) { + extra_index += 1; // backing_int_ref + } else { + extra_index += backing_int_body_len; // backing_int_body_inst + } + } + return declIteratorInner(zir, extra_index, decls_len); }, .enum_decl => { -- 2.54.0