AstGen: implement inline asm output

This commit is contained in:
Andrew Kelley 2021-04-19 18:44:59 -07:00
parent a136c093bf
commit 4630e3891c
8 changed files with 126 additions and 49 deletions

View File

@ -711,3 +711,8 @@ fn astgenAndSemaVarDecl(
const decl_index = try mod.declareDeclDependency(astgen.decl, new_decl);
const result = try gz.addDecl(.decl_val, decl_index, node);
return rvalue(gz, scope, rl, result, node);
// when implementing this be sure to add test coverage for the asm return type
// not resolving into a type (the node_offset_asm_ret_ty field of LazySrcLoc)

View File

@ -4875,39 +4875,53 @@ fn asmExpr(
const main_tokens = tree.nodes.items(.main_token);
const node_datas = tree.nodes.items(.data);
const asm_source = try expr(gz, scope, .{ .ty = .const_slice_u8_type }, full.ast.template);
const asm_source = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, full.ast.template);
if (full.outputs.len != 0) {
// when implementing this be sure to add test coverage for the asm return type
// not resolving into a type (the node_offset_asm_ret_ty field of LazySrcLoc)
return astgen.failTok(full.ast.asm_token, "TODO implement asm with an output", .{});
// See https://github.com/ziglang/zig/issues/215 and related issues discussing
// possible inline assembly improvements. Until this is settled, I am avoiding
// potentially wasting time implementing status quo assembly that is not used by
// any of the standard library.
if (full.outputs.len > 1) {
return astgen.failNode(node, "TODO more than 1 asm output", .{});
}
const output: struct {
ty: Zir.Inst.Ref = .none,
constraint: u32 = 0,
} = if (full.outputs.len == 0) .{} else blk: {
const output_node = full.outputs[0];
const out_type_node = node_datas[output_node].lhs;
if (out_type_node == 0) {
return astgen.failNode(out_type_node, "TODO asm with non -> output", .{});
}
const constraint_token = main_tokens[output_node] + 2;
break :blk .{
.ty = try typeExpr(gz, scope, out_type_node),
.constraint = (try gz.strLitAsString(constraint_token)).index,
};
};
const constraints = try arena.alloc(u32, full.inputs.len);
const args = try arena.alloc(Zir.Inst.Ref, full.inputs.len);
for (full.inputs) |input, i| {
const constraint_token = main_tokens[input] + 2;
const string_bytes = &astgen.string_bytes;
constraints[i] = @intCast(u32, string_bytes.items.len);
const token_bytes = tree.tokenSlice(constraint_token);
try astgen.parseStrLit(constraint_token, string_bytes, token_bytes, 0);
try string_bytes.append(astgen.gpa, 0);
constraints[i] = (try gz.strLitAsString(constraint_token)).index;
args[i] = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[input].lhs);
}
const tag: Zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm";
const result = try gz.addPlNode(tag, node, Zir.Inst.Asm{
.asm_source = asm_source,
.return_type = .void_type,
.output = .none,
.output_type = output.ty,
.args_len = @intCast(u32, full.inputs.len),
.clobbers_len = 0, // TODO implement asm clobbers
});
try astgen.extra.ensureCapacity(astgen.gpa, astgen.extra.items.len +
args.len + constraints.len);
args.len + constraints.len + @boolToInt(output.ty != .none));
if (output.ty != .none) {
astgen.extra.appendAssumeCapacity(output.constraint);
}
astgen.appendRefsAssumeCapacity(args);
astgen.extra.appendSliceAssumeCapacity(constraints);

View File

@ -4337,17 +4337,16 @@ fn zirAsm(
const asm_source_src: LazySrcLoc = .{ .node_offset_asm_source = inst_data.src_node };
const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Asm, inst_data.payload_index);
const return_type = try sema.resolveType(block, ret_ty_src, extra.data.return_type);
const asm_source = try sema.resolveConstString(block, asm_source_src, extra.data.asm_source);
var extra_i = extra.end;
const Output = struct { name: []const u8, inst: *Inst };
const output: ?Output = if (extra.data.output != .none) blk: {
const name = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
const Output = struct { constraint: []const u8, ty: Type };
const output: ?Output = if (extra.data.output_type != .none) blk: {
const constraint = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
extra_i += 1;
break :blk Output{
.name = name,
.inst = try sema.resolveInst(extra.data.output),
.constraint = constraint,
.ty = try sema.resolveType(block, ret_ty_src, extra.data.output_type),
};
} else null;
@ -4369,23 +4368,22 @@ fn zirAsm(
}
try sema.requireRuntimeBlock(block, src);
const asm_tzir = try sema.arena.create(Inst.Assembly);
asm_tzir.* = .{
const asm_air = try sema.arena.create(Inst.Assembly);
asm_air.* = .{
.base = .{
.tag = .assembly,
.ty = return_type,
.ty = if (output) |o| o.ty else Type.initTag(.void),
.src = src,
},
.asm_source = asm_source,
.is_volatile = is_volatile,
.output = if (output) |o| o.inst else null,
.output_name = if (output) |o| o.name else null,
.output_constraint = if (output) |o| o.constraint else null,
.inputs = inputs,
.clobbers = clobbers,
.args = args,
};
try block.instructions.append(sema.gpa, &asm_tzir.base);
return &asm_tzir.base;
try block.instructions.append(sema.gpa, &asm_air.base);
return &asm_air.base;
}
fn zirCmp(

View File

@ -1760,15 +1760,14 @@ pub const Inst = struct {
};
/// Stored in extra. Trailing is:
/// * output_name: u32 // index into string_bytes (null terminated) if output is present
/// * output_constraint: u32 // index into string_bytes (null terminated) if output is present
/// * arg: Ref // for every args_len.
/// * constraint: u32 // index into string_bytes (null terminated) for every args_len.
/// * clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.
pub const Asm = struct {
asm_source: Ref,
return_type: Ref,
/// May be omitted.
output: Ref,
output_type: Ref,
args_len: u32,
clobbers_len: u32,
};
@ -2308,8 +2307,6 @@ const Writer = struct {
.break_inline,
=> try self.writeBreak(stream, inst),
.@"asm",
.asm_volatile,
.elem_ptr_node,
.elem_val_node,
.field_ptr_named,
@ -2337,6 +2334,10 @@ const Writer = struct {
.builtin_async_call,
=> try self.writePlNode(stream, inst),
.@"asm",
.asm_volatile,
=> try self.writePlNodeAsm(stream, inst),
.error_set_decl => try self.writePlNodeErrorSetDecl(stream, inst),
.add_with_overflow,
@ -2642,6 +2643,51 @@ const Writer = struct {
try self.writeSrc(stream, inst_data.src());
}
fn writePlNodeAsm(self: *Writer, stream: anytype, inst: Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
const extra = self.code.extraData(Inst.Asm, inst_data.payload_index);
var extra_i: usize = extra.end;
if (extra.data.output_type != .none) {
const constraint_str_index = self.code.extra[extra_i];
extra_i += 1;
const constraint = self.code.nullTerminatedString(constraint_str_index);
try stream.print("\"{}\"->", .{std.zig.fmtEscapes(constraint)});
try self.writeInstRef(stream, extra.data.output_type);
try stream.writeAll(", ");
}
{
var i: usize = 0;
while (i < extra.data.args_len) : (i += 1) {
const arg = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_i]);
extra_i += 1;
try self.writeInstRef(stream, arg);
try stream.writeAll(", ");
}
}
{
var i: usize = 0;
while (i < extra.data.args_len) : (i += 1) {
const str_index = self.code.extra[extra_i];
extra_i += 1;
const constraint = self.code.nullTerminatedString(str_index);
try stream.print("\"{}\", ", .{std.zig.fmtEscapes(constraint)});
}
}
{
var i: usize = 0;
while (i < extra.data.clobbers_len) : (i += 1) {
const str_index = self.code.extra[extra_i];
extra_i += 1;
const clobber = self.code.nullTerminatedString(str_index);
try stream.print("{}, ", .{std.zig.fmtId(clobber)});
}
}
try self.writeInstRef(stream, extra.data.asm_source);
try stream.writeAll(") ");
try self.writeSrc(stream, inst_data.src());
}
fn writePlNodeOverflowArithmetic(self: *Writer, stream: anytype, inst: Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
const extra = self.code.extraData(Inst.OverflowArithmetic, inst_data.payload_index).data;

View File

@ -2754,7 +2754,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});
}
if (inst.output_name) |output| {
if (inst.output_constraint) |output| {
if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
}
@ -2789,7 +2789,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{});
}
if (inst.output_name) |output| {
if (inst.output_constraint) |output| {
if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
}
@ -2822,7 +2822,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
}
if (inst.output_name) |output| {
if (inst.output_constraint) |output| {
if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
}
@ -2855,7 +2855,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
}
if (inst.output_name) |output| {
if (inst.output_constraint) |output| {
if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
}

View File

@ -1036,11 +1036,11 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
}
const volatile_string: []const u8 = if (as.is_volatile) "volatile " else "";
try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source });
if (as.output) |_| {
return o.dg.fail(.{ .node_offset = 0 }, "TODO inline asm output", .{});
if (as.output_constraint) |_| {
return o.dg.fail(.{ .node_offset = 0 }, "TODO: CBE inline asm output", .{});
}
if (as.inputs.len > 0) {
if (as.output == null) {
if (as.output_constraint == null) {
try writer.writeAll(" :");
}
try writer.writeAll(": ");

View File

@ -372,8 +372,7 @@ pub const Inst = struct {
base: Inst,
asm_source: []const u8,
is_volatile: bool,
output: ?*Inst,
output_name: ?[]const u8,
output_constraint: ?[]const u8,
inputs: []const []const u8,
clobbers: []const []const u8,
args: []const *Inst,

View File

@ -3561,26 +3561,41 @@ pub fn cmdAstgen(
defer file.zir.deinit(gpa);
{
const token_bytes = @sizeOf(std.zig.ast.TokenList) +
file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(std.zig.ast.ByteOffset));
const tree_bytes = @sizeOf(std.zig.ast.Tree) + file.tree.nodes.len *
(@sizeOf(std.zig.ast.Node.Tag) +
@sizeOf(std.zig.ast.Node.Data) +
@sizeOf(std.zig.ast.TokenIndex));
const instruction_bytes = file.zir.instructions.len *
(@sizeOf(Zir.Inst.Tag) + @sizeOf(Zir.Inst.Data));
// Here we don't use @sizeOf(Zir.Inst.Data) because it would include
// the debug safety tag but we want to measure release size.
(@sizeOf(Zir.Inst.Tag) + 8);
const extra_bytes = file.zir.extra.len * @sizeOf(u32);
const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
file.zir.string_bytes.len * @sizeOf(u8);
const stdout = io.getStdOut();
const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
// zig fmt: off
try stdout.writer().print(
\\# Total bytes: {}
\\# Source bytes: {}
\\# Tokens: {} ({})
\\# AST Nodes: {} ({})
\\# Total ZIR bytes: {}
\\# Instructions: {d} ({})
\\# String Table Bytes: {}
\\# Extra Data Items: {d} ({})
\\
, .{
std.fmt.fmtIntSizeBin(total_bytes),
file.zir.instructions.len,
std.fmt.fmtIntSizeBin(instruction_bytes),
std.fmt.fmtIntSizeBin(file.zir.string_bytes.len),
file.zir.extra.len,
std.fmt.fmtIntSizeBin(extra_bytes),
fmtIntSizeBin(source.len),
file.tree.tokens.len, fmtIntSizeBin(token_bytes),
file.tree.nodes.len, fmtIntSizeBin(tree_bytes),
fmtIntSizeBin(total_bytes),
file.zir.instructions.len, fmtIntSizeBin(instruction_bytes),
fmtIntSizeBin(file.zir.string_bytes.len),
file.zir.extra.len, fmtIntSizeBin(extra_bytes),
});
// zig fmt: on
}
if (file.zir.hasCompileErrors()) {