| author | |
| committer | |
| log | 4630e3891c7c833d8a8f42e3755099b478dce3f3 |
| tree | 705e6542c481635149a8e1489964a074483a501f |
| parent | a136c093bf7a48398e36a73e91213f93a4efc503 |
8 files changed, 127 insertions(+), 50 deletions(-)
BRANCH_TODO+5| ... | ... | @@ -711,3 +711,8 @@ fn astgenAndSemaVarDecl( |
| 711 | 711 | const decl_index = try mod.declareDeclDependency(astgen.decl, new_decl); |
| 712 | 712 | const result = try gz.addDecl(.decl_val, decl_index, node); |
| 713 | 713 | return rvalue(gz, scope, rl, result, node); |
| 714 | ||
| 715 | ||
| 716 | ||
| 717 | // when implementing this be sure to add test coverage for the asm return type | |
| 718 | // not resolving into a type (the node_offset_asm_ret_ty field of LazySrcLoc) |
src/AstGen.zig+29-15| ... | ... | @@ -4875,39 +4875,53 @@ fn asmExpr( |
| 4875 | 4875 | const main_tokens = tree.nodes.items(.main_token); |
| 4876 | 4876 | const node_datas = tree.nodes.items(.data); |
| 4877 | 4877 | |
| 4878 | const asm_source = try expr(gz, scope, .{ .ty = .const_slice_u8_type }, full.ast.template); | |
| 4879 | ||
| 4880 | if (full.outputs.len != 0) { | |
| 4881 | // when implementing this be sure to add test coverage for the asm return type | |
| 4882 | // not resolving into a type (the node_offset_asm_ret_ty field of LazySrcLoc) | |
| 4883 | return astgen.failTok(full.ast.asm_token, "TODO implement asm with an output", .{}); | |
| 4878 | const asm_source = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, full.ast.template); | |
| 4879 | ||
| 4880 | // See https://github.com/ziglang/zig/issues/215 and related issues discussing | |
| 4881 | // possible inline assembly improvements. Until this is settled, I am avoiding | |
| 4882 | // potentially wasting time implementing status quo assembly that is not used by | |
| 4883 | // any of the standard library. | |
| 4884 | if (full.outputs.len > 1) { | |
| 4885 | return astgen.failNode(node, "TODO more than 1 asm output", .{}); | |
| 4884 | 4886 | } |
| 4887 | const output: struct { | |
| 4888 | ty: Zir.Inst.Ref = .none, | |
| 4889 | constraint: u32 = 0, | |
| 4890 | } = if (full.outputs.len == 0) .{} else blk: { | |
| 4891 | const output_node = full.outputs[0]; | |
| 4892 | const out_type_node = node_datas[output_node].lhs; | |
| 4893 | if (out_type_node == 0) { | |
| 4894 | return astgen.failNode(out_type_node, "TODO asm with non -> output", .{}); | |
| 4895 | } | |
| 4896 | const constraint_token = main_tokens[output_node] + 2; | |
| 4897 | break :blk .{ | |
| 4898 | .ty = try typeExpr(gz, scope, out_type_node), | |
| 4899 | .constraint = (try gz.strLitAsString(constraint_token)).index, | |
| 4900 | }; | |
| 4901 | }; | |
| 4885 | 4902 | |
| 4886 | 4903 | const constraints = try arena.alloc(u32, full.inputs.len); |
| 4887 | 4904 | const args = try arena.alloc(Zir.Inst.Ref, full.inputs.len); |
| 4888 | 4905 | |
| 4889 | 4906 | for (full.inputs) |input, i| { |
| 4890 | 4907 | const constraint_token = main_tokens[input] + 2; |
| 4891 | const string_bytes = &astgen.string_bytes; | |
| 4892 | constraints[i] = @intCast(u32, string_bytes.items.len); | |
| 4893 | const token_bytes = tree.tokenSlice(constraint_token); | |
| 4894 | try astgen.parseStrLit(constraint_token, string_bytes, token_bytes, 0); | |
| 4895 | try string_bytes.append(astgen.gpa, 0); | |
| 4896 | ||
| 4908 | constraints[i] = (try gz.strLitAsString(constraint_token)).index; | |
| 4897 | 4909 | args[i] = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[input].lhs); |
| 4898 | 4910 | } |
| 4899 | 4911 | |
| 4900 | 4912 | const tag: Zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm"; |
| 4901 | 4913 | const result = try gz.addPlNode(tag, node, Zir.Inst.Asm{ |
| 4902 | 4914 | .asm_source = asm_source, |
| 4903 | .return_type = .void_type, | |
| 4904 | .output = .none, | |
| 4915 | .output_type = output.ty, | |
| 4905 | 4916 | .args_len = @intCast(u32, full.inputs.len), |
| 4906 | 4917 | .clobbers_len = 0, // TODO implement asm clobbers |
| 4907 | 4918 | }); |
| 4908 | 4919 | |
| 4909 | 4920 | try astgen.extra.ensureCapacity(astgen.gpa, astgen.extra.items.len + |
| 4910 | args.len + constraints.len); | |
| 4921 | args.len + constraints.len + @boolToInt(output.ty != .none)); | |
| 4922 | if (output.ty != .none) { | |
| 4923 | astgen.extra.appendAssumeCapacity(output.constraint); | |
| 4924 | } | |
| 4911 | 4925 | astgen.appendRefsAssumeCapacity(args); |
| 4912 | 4926 | astgen.extra.appendSliceAssumeCapacity(constraints); |
| 4913 | 4927 |
src/Sema.zig+11-13| ... | ... | @@ -4337,17 +4337,16 @@ fn zirAsm( |
| 4337 | 4337 | const asm_source_src: LazySrcLoc = .{ .node_offset_asm_source = inst_data.src_node }; |
| 4338 | 4338 | const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = inst_data.src_node }; |
| 4339 | 4339 | const extra = sema.code.extraData(Zir.Inst.Asm, inst_data.payload_index); |
| 4340 | const return_type = try sema.resolveType(block, ret_ty_src, extra.data.return_type); | |
| 4341 | 4340 | const asm_source = try sema.resolveConstString(block, asm_source_src, extra.data.asm_source); |
| 4342 | 4341 | |
| 4343 | 4342 | var extra_i = extra.end; |
| 4344 | const Output = struct { name: []const u8, inst: *Inst }; | |
| 4345 | const output: ?Output = if (extra.data.output != .none) blk: { | |
| 4346 | const name = sema.code.nullTerminatedString(sema.code.extra[extra_i]); | |
| 4343 | const Output = struct { constraint: []const u8, ty: Type }; | |
| 4344 | const output: ?Output = if (extra.data.output_type != .none) blk: { | |
| 4345 | const constraint = sema.code.nullTerminatedString(sema.code.extra[extra_i]); | |
| 4347 | 4346 | extra_i += 1; |
| 4348 | 4347 | break :blk Output{ |
| 4349 | .name = name, | |
| 4350 | .inst = try sema.resolveInst(extra.data.output), | |
| 4348 | .constraint = constraint, | |
| 4349 | .ty = try sema.resolveType(block, ret_ty_src, extra.data.output_type), | |
| 4351 | 4350 | }; |
| 4352 | 4351 | } else null; |
| 4353 | 4352 | |
| ... | ... | @@ -4369,23 +4368,22 @@ fn zirAsm( |
| 4369 | 4368 | } |
| 4370 | 4369 | |
| 4371 | 4370 | try sema.requireRuntimeBlock(block, src); |
| 4372 | const asm_tzir = try sema.arena.create(Inst.Assembly); | |
| 4373 | asm_tzir.* = .{ | |
| 4371 | const asm_air = try sema.arena.create(Inst.Assembly); | |
| 4372 | asm_air.* = .{ | |
| 4374 | 4373 | .base = .{ |
| 4375 | 4374 | .tag = .assembly, |
| 4376 | .ty = return_type, | |
| 4375 | .ty = if (output) |o| o.ty else Type.initTag(.void), | |
| 4377 | 4376 | .src = src, |
| 4378 | 4377 | }, |
| 4379 | 4378 | .asm_source = asm_source, |
| 4380 | 4379 | .is_volatile = is_volatile, |
| 4381 | .output = if (output) |o| o.inst else null, | |
| 4382 | .output_name = if (output) |o| o.name else null, | |
| 4380 | .output_constraint = if (output) |o| o.constraint else null, | |
| 4383 | 4381 | .inputs = inputs, |
| 4384 | 4382 | .clobbers = clobbers, |
| 4385 | 4383 | .args = args, |
| 4386 | 4384 | }; |
| 4387 | try block.instructions.append(sema.gpa, &asm_tzir.base); | |
| 4388 | return &asm_tzir.base; | |
| 4385 | try block.instructions.append(sema.gpa, &asm_air.base); | |
| 4386 | return &asm_air.base; | |
| 4389 | 4387 | } |
| 4390 | 4388 | |
| 4391 | 4389 | fn zirCmp( |
src/Zir.zig+51-5| ... | ... | @@ -1760,15 +1760,14 @@ pub const Inst = struct { |
| 1760 | 1760 | }; |
| 1761 | 1761 | |
| 1762 | 1762 | /// Stored in extra. Trailing is: |
| 1763 | /// * output_name: u32 // index into string_bytes (null terminated) if output is present | |
| 1763 | /// * output_constraint: u32 // index into string_bytes (null terminated) if output is present | |
| 1764 | 1764 | /// * arg: Ref // for every args_len. |
| 1765 | 1765 | /// * constraint: u32 // index into string_bytes (null terminated) for every args_len. |
| 1766 | 1766 | /// * clobber: u32 // index into string_bytes (null terminated) for every clobbers_len. |
| 1767 | 1767 | pub const Asm = struct { |
| 1768 | 1768 | asm_source: Ref, |
| 1769 | return_type: Ref, | |
| 1770 | 1769 | /// May be omitted. |
| 1771 | output: Ref, | |
| 1770 | output_type: Ref, | |
| 1772 | 1771 | args_len: u32, |
| 1773 | 1772 | clobbers_len: u32, |
| 1774 | 1773 | }; |
| ... | ... | @@ -2308,8 +2307,6 @@ const Writer = struct { |
| 2308 | 2307 | .break_inline, |
| 2309 | 2308 | => try self.writeBreak(stream, inst), |
| 2310 | 2309 | |
| 2311 | .@"asm", | |
| 2312 | .asm_volatile, | |
| 2313 | 2310 | .elem_ptr_node, |
| 2314 | 2311 | .elem_val_node, |
| 2315 | 2312 | .field_ptr_named, |
| ... | ... | @@ -2337,6 +2334,10 @@ const Writer = struct { |
| 2337 | 2334 | .builtin_async_call, |
| 2338 | 2335 | => try self.writePlNode(stream, inst), |
| 2339 | 2336 | |
| 2337 | .@"asm", | |
| 2338 | .asm_volatile, | |
| 2339 | => try self.writePlNodeAsm(stream, inst), | |
| 2340 | ||
| 2340 | 2341 | .error_set_decl => try self.writePlNodeErrorSetDecl(stream, inst), |
| 2341 | 2342 | |
| 2342 | 2343 | .add_with_overflow, |
| ... | ... | @@ -2642,6 +2643,51 @@ const Writer = struct { |
| 2642 | 2643 | try self.writeSrc(stream, inst_data.src()); |
| 2643 | 2644 | } |
| 2644 | 2645 | |
| 2646 | fn writePlNodeAsm(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 2647 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 2648 | const extra = self.code.extraData(Inst.Asm, inst_data.payload_index); | |
| 2649 | var extra_i: usize = extra.end; | |
| 2650 | ||
| 2651 | if (extra.data.output_type != .none) { | |
| 2652 | const constraint_str_index = self.code.extra[extra_i]; | |
| 2653 | extra_i += 1; | |
| 2654 | const constraint = self.code.nullTerminatedString(constraint_str_index); | |
| 2655 | try stream.print("\"{}\"->", .{std.zig.fmtEscapes(constraint)}); | |
| 2656 | try self.writeInstRef(stream, extra.data.output_type); | |
| 2657 | try stream.writeAll(", "); | |
| 2658 | } | |
| 2659 | { | |
| 2660 | var i: usize = 0; | |
| 2661 | while (i < extra.data.args_len) : (i += 1) { | |
| 2662 | const arg = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_i]); | |
| 2663 | extra_i += 1; | |
| 2664 | try self.writeInstRef(stream, arg); | |
| 2665 | try stream.writeAll(", "); | |
| 2666 | } | |
| 2667 | } | |
| 2668 | { | |
| 2669 | var i: usize = 0; | |
| 2670 | while (i < extra.data.args_len) : (i += 1) { | |
| 2671 | const str_index = self.code.extra[extra_i]; | |
| 2672 | extra_i += 1; | |
| 2673 | const constraint = self.code.nullTerminatedString(str_index); | |
| 2674 | try stream.print("\"{}\", ", .{std.zig.fmtEscapes(constraint)}); | |
| 2675 | } | |
| 2676 | } | |
| 2677 | { | |
| 2678 | var i: usize = 0; | |
| 2679 | while (i < extra.data.clobbers_len) : (i += 1) { | |
| 2680 | const str_index = self.code.extra[extra_i]; | |
| 2681 | extra_i += 1; | |
| 2682 | const clobber = self.code.nullTerminatedString(str_index); | |
| 2683 | try stream.print("{}, ", .{std.zig.fmtId(clobber)}); | |
| 2684 | } | |
| 2685 | } | |
| 2686 | try self.writeInstRef(stream, extra.data.asm_source); | |
| 2687 | try stream.writeAll(") "); | |
| 2688 | try self.writeSrc(stream, inst_data.src()); | |
| 2689 | } | |
| 2690 | ||
| 2645 | 2691 | fn writePlNodeOverflowArithmetic(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 2646 | 2692 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 2647 | 2693 | const extra = self.code.extraData(Inst.OverflowArithmetic, inst_data.payload_index).data; |
src/codegen.zig+4-4| ... | ... | @@ -2754,7 +2754,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2754 | 2754 | return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{}); |
| 2755 | 2755 | } |
| 2756 | 2756 | |
| 2757 | if (inst.output_name) |output| { | |
| 2757 | if (inst.output_constraint) |output| { | |
| 2758 | 2758 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 2759 | 2759 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); |
| 2760 | 2760 | } |
| ... | ... | @@ -2789,7 +2789,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2789 | 2789 | return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{}); |
| 2790 | 2790 | } |
| 2791 | 2791 | |
| 2792 | if (inst.output_name) |output| { | |
| 2792 | if (inst.output_constraint) |output| { | |
| 2793 | 2793 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 2794 | 2794 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); |
| 2795 | 2795 | } |
| ... | ... | @@ -2822,7 +2822,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2822 | 2822 | return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{}); |
| 2823 | 2823 | } |
| 2824 | 2824 | |
| 2825 | if (inst.output_name) |output| { | |
| 2825 | if (inst.output_constraint) |output| { | |
| 2826 | 2826 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 2827 | 2827 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); |
| 2828 | 2828 | } |
| ... | ... | @@ -2855,7 +2855,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2855 | 2855 | return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{}); |
| 2856 | 2856 | } |
| 2857 | 2857 | |
| 2858 | if (inst.output_name) |output| { | |
| 2858 | if (inst.output_constraint) |output| { | |
| 2859 | 2859 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 2860 | 2860 | return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); |
| 2861 | 2861 | } |
src/codegen/c.zig+3-3| ... | ... | @@ -1036,11 +1036,11 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { |
| 1036 | 1036 | } |
| 1037 | 1037 | const volatile_string: []const u8 = if (as.is_volatile) "volatile " else ""; |
| 1038 | 1038 | try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source }); |
| 1039 | if (as.output) |_| { | |
| 1040 | return o.dg.fail(.{ .node_offset = 0 }, "TODO inline asm output", .{}); | |
| 1039 | if (as.output_constraint) |_| { | |
| 1040 | return o.dg.fail(.{ .node_offset = 0 }, "TODO: CBE inline asm output", .{}); | |
| 1041 | 1041 | } |
| 1042 | 1042 | if (as.inputs.len > 0) { |
| 1043 | if (as.output == null) { | |
| 1043 | if (as.output_constraint == null) { | |
| 1044 | 1044 | try writer.writeAll(" :"); |
| 1045 | 1045 | } |
| 1046 | 1046 | try writer.writeAll(": "); |
src/ir.zig+1-2| ... | ... | @@ -372,8 +372,7 @@ pub const Inst = struct { |
| 372 | 372 | base: Inst, |
| 373 | 373 | asm_source: []const u8, |
| 374 | 374 | is_volatile: bool, |
| 375 | output: ?*Inst, | |
| 376 | output_name: ?[]const u8, | |
| 375 | output_constraint: ?[]const u8, | |
| 377 | 376 | inputs: []const []const u8, |
| 378 | 377 | clobbers: []const []const u8, |
| 379 | 378 | args: []const *Inst, |
src/main.zig+23-8| ... | ... | @@ -3561,26 +3561,41 @@ pub fn cmdAstgen( |
| 3561 | 3561 | defer file.zir.deinit(gpa); |
| 3562 | 3562 | |
| 3563 | 3563 | { |
| 3564 | const token_bytes = @sizeOf(std.zig.ast.TokenList) + | |
| 3565 | file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(std.zig.ast.ByteOffset)); | |
| 3566 | const tree_bytes = @sizeOf(std.zig.ast.Tree) + file.tree.nodes.len * | |
| 3567 | (@sizeOf(std.zig.ast.Node.Tag) + | |
| 3568 | @sizeOf(std.zig.ast.Node.Data) + | |
| 3569 | @sizeOf(std.zig.ast.TokenIndex)); | |
| 3564 | 3570 | const instruction_bytes = file.zir.instructions.len * |
| 3565 | (@sizeOf(Zir.Inst.Tag) + @sizeOf(Zir.Inst.Data)); | |
| 3571 | // Here we don't use @sizeOf(Zir.Inst.Data) because it would include | |
| 3572 | // the debug safety tag but we want to measure release size. | |
| 3573 | (@sizeOf(Zir.Inst.Tag) + 8); | |
| 3566 | 3574 | const extra_bytes = file.zir.extra.len * @sizeOf(u32); |
| 3567 | 3575 | const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes + |
| 3568 | 3576 | file.zir.string_bytes.len * @sizeOf(u8); |
| 3569 | 3577 | const stdout = io.getStdOut(); |
| 3578 | const fmtIntSizeBin = std.fmt.fmtIntSizeBin; | |
| 3579 | // zig fmt: off | |
| 3570 | 3580 | try stdout.writer().print( |
| 3571 | \\# Total bytes: {} | |
| 3581 | \\# Source bytes: {} | |
| 3582 | \\# Tokens: {} ({}) | |
| 3583 | \\# AST Nodes: {} ({}) | |
| 3584 | \\# Total ZIR bytes: {} | |
| 3572 | 3585 | \\# Instructions: {d} ({}) |
| 3573 | 3586 | \\# String Table Bytes: {} |
| 3574 | 3587 | \\# Extra Data Items: {d} ({}) |
| 3575 | 3588 | \\ |
| 3576 | 3589 | , .{ |
| 3577 | std.fmt.fmtIntSizeBin(total_bytes), | |
| 3578 | file.zir.instructions.len, | |
| 3579 | std.fmt.fmtIntSizeBin(instruction_bytes), | |
| 3580 | std.fmt.fmtIntSizeBin(file.zir.string_bytes.len), | |
| 3581 | file.zir.extra.len, | |
| 3582 | std.fmt.fmtIntSizeBin(extra_bytes), | |
| 3590 | fmtIntSizeBin(source.len), | |
| 3591 | file.tree.tokens.len, fmtIntSizeBin(token_bytes), | |
| 3592 | file.tree.nodes.len, fmtIntSizeBin(tree_bytes), | |
| 3593 | fmtIntSizeBin(total_bytes), | |
| 3594 | file.zir.instructions.len, fmtIntSizeBin(instruction_bytes), | |
| 3595 | fmtIntSizeBin(file.zir.string_bytes.len), | |
| 3596 | file.zir.extra.len, fmtIntSizeBin(extra_bytes), | |
| 3583 | 3597 | }); |
| 3598 | // zig fmt: on | |
| 3584 | 3599 | } |
| 3585 | 3600 | |
| 3586 | 3601 | if (file.zir.hasCompileErrors()) { |