authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-19 18:44:59-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-19 18:44:59-07:00
log4630e3891c7c833d8a8f42e3755099b478dce3f3
tree705e6542c481635149a8e1489964a074483a501f
parenta136c093bf7a48398e36a73e91213f93a4efc503

AstGen: implement inline asm output


8 files changed, 127 insertions(+), 50 deletions(-)

BRANCH_TODO+5
...@@ -711,3 +711,8 @@ fn astgenAndSemaVarDecl(...@@ -711,3 +711,8 @@ fn astgenAndSemaVarDecl(
711 const decl_index = try mod.declareDeclDependency(astgen.decl, new_decl);711 const decl_index = try mod.declareDeclDependency(astgen.decl, new_decl);
712 const result = try gz.addDecl(.decl_val, decl_index, node);712 const result = try gz.addDecl(.decl_val, decl_index, node);
713 return rvalue(gz, scope, rl, result, node);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,39 +4875,53 @@ fn asmExpr(
4875 const main_tokens = tree.nodes.items(.main_token);4875 const main_tokens = tree.nodes.items(.main_token);
4876 const node_datas = tree.nodes.items(.data);4876 const node_datas = tree.nodes.items(.data);
48774877
4878 const asm_source = try expr(gz, scope, .{ .ty = .const_slice_u8_type }, full.ast.template);4878 const asm_source = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, full.ast.template);
48794879
4880 if (full.outputs.len != 0) {4880 // See https://github.com/ziglang/zig/issues/215 and related issues discussing
4881 // when implementing this be sure to add test coverage for the asm return type4881 // possible inline assembly improvements. Until this is settled, I am avoiding
4882 // not resolving into a type (the node_offset_asm_ret_ty field of LazySrcLoc)4882 // potentially wasting time implementing status quo assembly that is not used by
4883 return astgen.failTok(full.ast.asm_token, "TODO implement asm with an output", .{});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 };
48854902
4886 const constraints = try arena.alloc(u32, full.inputs.len);4903 const constraints = try arena.alloc(u32, full.inputs.len);
4887 const args = try arena.alloc(Zir.Inst.Ref, full.inputs.len);4904 const args = try arena.alloc(Zir.Inst.Ref, full.inputs.len);
48884905
4889 for (full.inputs) |input, i| {4906 for (full.inputs) |input, i| {
4890 const constraint_token = main_tokens[input] + 2;4907 const constraint_token = main_tokens[input] + 2;
4891 const string_bytes = &astgen.string_bytes;4908 constraints[i] = (try gz.strLitAsString(constraint_token)).index;
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
4897 args[i] = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[input].lhs);4909 args[i] = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[input].lhs);
4898 }4910 }
48994911
4900 const tag: Zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm";4912 const tag: Zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm";
4901 const result = try gz.addPlNode(tag, node, Zir.Inst.Asm{4913 const result = try gz.addPlNode(tag, node, Zir.Inst.Asm{
4902 .asm_source = asm_source,4914 .asm_source = asm_source,
4903 .return_type = .void_type,4915 .output_type = output.ty,
4904 .output = .none,
4905 .args_len = @intCast(u32, full.inputs.len),4916 .args_len = @intCast(u32, full.inputs.len),
4906 .clobbers_len = 0, // TODO implement asm clobbers4917 .clobbers_len = 0, // TODO implement asm clobbers
4907 });4918 });
49084919
4909 try astgen.extra.ensureCapacity(astgen.gpa, astgen.extra.items.len +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 astgen.appendRefsAssumeCapacity(args);4925 astgen.appendRefsAssumeCapacity(args);
4912 astgen.extra.appendSliceAssumeCapacity(constraints);4926 astgen.extra.appendSliceAssumeCapacity(constraints);
49134927
src/Sema.zig+11-13
...@@ -4337,17 +4337,16 @@ fn zirAsm(...@@ -4337,17 +4337,16 @@ fn zirAsm(
4337 const asm_source_src: LazySrcLoc = .{ .node_offset_asm_source = inst_data.src_node };4337 const asm_source_src: LazySrcLoc = .{ .node_offset_asm_source = inst_data.src_node };
4338 const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = inst_data.src_node };4338 const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = inst_data.src_node };
4339 const extra = sema.code.extraData(Zir.Inst.Asm, inst_data.payload_index);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 const asm_source = try sema.resolveConstString(block, asm_source_src, extra.data.asm_source);4340 const asm_source = try sema.resolveConstString(block, asm_source_src, extra.data.asm_source);
43424341
4343 var extra_i = extra.end;4342 var extra_i = extra.end;
4344 const Output = struct { name: []const u8, inst: *Inst };4343 const Output = struct { constraint: []const u8, ty: Type };
4345 const output: ?Output = if (extra.data.output != .none) blk: {4344 const output: ?Output = if (extra.data.output_type != .none) blk: {
4346 const name = sema.code.nullTerminatedString(sema.code.extra[extra_i]);4345 const constraint = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
4347 extra_i += 1;4346 extra_i += 1;
4348 break :blk Output{4347 break :blk Output{
4349 .name = name,4348 .constraint = constraint,
4350 .inst = try sema.resolveInst(extra.data.output),4349 .ty = try sema.resolveType(block, ret_ty_src, extra.data.output_type),
4351 };4350 };
4352 } else null;4351 } else null;
43534352
...@@ -4369,23 +4368,22 @@ fn zirAsm(...@@ -4369,23 +4368,22 @@ fn zirAsm(
4369 }4368 }
43704369
4371 try sema.requireRuntimeBlock(block, src);4370 try sema.requireRuntimeBlock(block, src);
4372 const asm_tzir = try sema.arena.create(Inst.Assembly);4371 const asm_air = try sema.arena.create(Inst.Assembly);
4373 asm_tzir.* = .{4372 asm_air.* = .{
4374 .base = .{4373 .base = .{
4375 .tag = .assembly,4374 .tag = .assembly,
4376 .ty = return_type,4375 .ty = if (output) |o| o.ty else Type.initTag(.void),
4377 .src = src,4376 .src = src,
4378 },4377 },
4379 .asm_source = asm_source,4378 .asm_source = asm_source,
4380 .is_volatile = is_volatile,4379 .is_volatile = is_volatile,
4381 .output = if (output) |o| o.inst else null,4380 .output_constraint = if (output) |o| o.constraint else null,
4382 .output_name = if (output) |o| o.name else null,
4383 .inputs = inputs,4381 .inputs = inputs,
4384 .clobbers = clobbers,4382 .clobbers = clobbers,
4385 .args = args,4383 .args = args,
4386 };4384 };
4387 try block.instructions.append(sema.gpa, &asm_tzir.base);4385 try block.instructions.append(sema.gpa, &asm_air.base);
4388 return &asm_tzir.base;4386 return &asm_air.base;
4389}4387}
43904388
4391fn zirCmp(4389fn zirCmp(
src/Zir.zig+51-5
...@@ -1760,15 +1760,14 @@ pub const Inst = struct {...@@ -1760,15 +1760,14 @@ pub const Inst = struct {
1760 };1760 };
17611761
1762 /// Stored in extra. Trailing is:1762 /// Stored in extra. Trailing is:
1763 /// * output_name: u32 // index into string_bytes (null terminated) if output is present1763 /// * output_constraint: u32 // index into string_bytes (null terminated) if output is present
1764 /// * arg: Ref // for every args_len.1764 /// * arg: Ref // for every args_len.
1765 /// * constraint: u32 // index into string_bytes (null terminated) for every args_len.1765 /// * constraint: u32 // index into string_bytes (null terminated) for every args_len.
1766 /// * clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.1766 /// * clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.
1767 pub const Asm = struct {1767 pub const Asm = struct {
1768 asm_source: Ref,1768 asm_source: Ref,
1769 return_type: Ref,
1770 /// May be omitted.1769 /// May be omitted.
1771 output: Ref,1770 output_type: Ref,
1772 args_len: u32,1771 args_len: u32,
1773 clobbers_len: u32,1772 clobbers_len: u32,
1774 };1773 };
...@@ -2308,8 +2307,6 @@ const Writer = struct {...@@ -2308,8 +2307,6 @@ const Writer = struct {
2308 .break_inline,2307 .break_inline,
2309 => try self.writeBreak(stream, inst),2308 => try self.writeBreak(stream, inst),
23102309
2311 .@"asm",
2312 .asm_volatile,
2313 .elem_ptr_node,2310 .elem_ptr_node,
2314 .elem_val_node,2311 .elem_val_node,
2315 .field_ptr_named,2312 .field_ptr_named,
...@@ -2337,6 +2334,10 @@ const Writer = struct {...@@ -2337,6 +2334,10 @@ const Writer = struct {
2337 .builtin_async_call,2334 .builtin_async_call,
2338 => try self.writePlNode(stream, inst),2335 => try self.writePlNode(stream, inst),
23392336
2337 .@"asm",
2338 .asm_volatile,
2339 => try self.writePlNodeAsm(stream, inst),
2340
2340 .error_set_decl => try self.writePlNodeErrorSetDecl(stream, inst),2341 .error_set_decl => try self.writePlNodeErrorSetDecl(stream, inst),
23412342
2342 .add_with_overflow,2343 .add_with_overflow,
...@@ -2642,6 +2643,51 @@ const Writer = struct {...@@ -2642,6 +2643,51 @@ const Writer = struct {
2642 try self.writeSrc(stream, inst_data.src());2643 try self.writeSrc(stream, inst_data.src());
2643 }2644 }
26442645
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 fn writePlNodeOverflowArithmetic(self: *Writer, stream: anytype, inst: Inst.Index) !void {2691 fn writePlNodeOverflowArithmetic(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2646 const inst_data = self.code.instructions.items(.data)[inst].pl_node;2692 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2647 const extra = self.code.extraData(Inst.OverflowArithmetic, inst_data.payload_index).data;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,7 +2754,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2754 return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});2754 return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});
2755 }2755 }
27562756
2757 if (inst.output_name) |output| {2757 if (inst.output_constraint) |output| {
2758 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {2758 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
2759 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});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,7 +2789,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2789 return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{});2789 return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{});
2790 }2790 }
27912791
2792 if (inst.output_name) |output| {2792 if (inst.output_constraint) |output| {
2793 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {2793 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
2794 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});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,7 +2822,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2822 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});2822 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
2823 }2823 }
28242824
2825 if (inst.output_name) |output| {2825 if (inst.output_constraint) |output| {
2826 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {2826 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
2827 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});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,7 +2855,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2855 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});2855 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
2856 }2856 }
28572857
2858 if (inst.output_name) |output| {2858 if (inst.output_constraint) |output| {
2859 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {2859 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
2860 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});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,11 +1036,11 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
1036 }1036 }
1037 const volatile_string: []const u8 = if (as.is_volatile) "volatile " else "";1037 const volatile_string: []const u8 = if (as.is_volatile) "volatile " else "";
1038 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source });1038 try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source });
1039 if (as.output) |_| {1039 if (as.output_constraint) |_| {
1040 return o.dg.fail(.{ .node_offset = 0 }, "TODO inline asm output", .{});1040 return o.dg.fail(.{ .node_offset = 0 }, "TODO: CBE inline asm output", .{});
1041 }1041 }
1042 if (as.inputs.len > 0) {1042 if (as.inputs.len > 0) {
1043 if (as.output == null) {1043 if (as.output_constraint == null) {
1044 try writer.writeAll(" :");1044 try writer.writeAll(" :");
1045 }1045 }
1046 try writer.writeAll(": ");1046 try writer.writeAll(": ");
src/ir.zig+1-2
...@@ -372,8 +372,7 @@ pub const Inst = struct {...@@ -372,8 +372,7 @@ pub const Inst = struct {
372 base: Inst,372 base: Inst,
373 asm_source: []const u8,373 asm_source: []const u8,
374 is_volatile: bool,374 is_volatile: bool,
375 output: ?*Inst,375 output_constraint: ?[]const u8,
376 output_name: ?[]const u8,
377 inputs: []const []const u8,376 inputs: []const []const u8,
378 clobbers: []const []const u8,377 clobbers: []const []const u8,
379 args: []const *Inst,378 args: []const *Inst,
src/main.zig+23-8
...@@ -3561,26 +3561,41 @@ pub fn cmdAstgen(...@@ -3561,26 +3561,41 @@ pub fn cmdAstgen(
3561 defer file.zir.deinit(gpa);3561 defer file.zir.deinit(gpa);
35623562
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 const instruction_bytes = file.zir.instructions.len *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 const extra_bytes = file.zir.extra.len * @sizeOf(u32);3574 const extra_bytes = file.zir.extra.len * @sizeOf(u32);
3567 const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +3575 const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
3568 file.zir.string_bytes.len * @sizeOf(u8);3576 file.zir.string_bytes.len * @sizeOf(u8);
3569 const stdout = io.getStdOut();3577 const stdout = io.getStdOut();
3578 const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
3579 // zig fmt: off
3570 try stdout.writer().print(3580 try stdout.writer().print(
3571 \\# Total bytes: {}3581 \\# Source bytes: {}
3582 \\# Tokens: {} ({})
3583 \\# AST Nodes: {} ({})
3584 \\# Total ZIR bytes: {}
3572 \\# Instructions: {d} ({})3585 \\# Instructions: {d} ({})
3573 \\# String Table Bytes: {}3586 \\# String Table Bytes: {}
3574 \\# Extra Data Items: {d} ({})3587 \\# Extra Data Items: {d} ({})
3575 \\3588 \\
3576 , .{3589 , .{
3577 std.fmt.fmtIntSizeBin(total_bytes),3590 fmtIntSizeBin(source.len),
3578 file.zir.instructions.len,3591 file.tree.tokens.len, fmtIntSizeBin(token_bytes),
3579 std.fmt.fmtIntSizeBin(instruction_bytes),3592 file.tree.nodes.len, fmtIntSizeBin(tree_bytes),
3580 std.fmt.fmtIntSizeBin(file.zir.string_bytes.len),3593 fmtIntSizeBin(total_bytes),
3581 file.zir.extra.len,3594 file.zir.instructions.len, fmtIntSizeBin(instruction_bytes),
3582 std.fmt.fmtIntSizeBin(extra_bytes),3595 fmtIntSizeBin(file.zir.string_bytes.len),
3596 file.zir.extra.len, fmtIntSizeBin(extra_bytes),
3583 });3597 });
3598 // zig fmt: on
3584 }3599 }
35853600
3586 if (file.zir.hasCompileErrors()) {3601 if (file.zir.hasCompileErrors()) {