mirror of
https://github.com/ziglang/zig.git
synced 2025-12-24 15:13:08 +00:00
AstGen: implement inline asm output
This commit is contained in:
parent
a136c093bf
commit
4630e3891c
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
24
src/Sema.zig
24
src/Sema.zig
@ -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(
|
||||
|
||||
56
src/Zir.zig
56
src/Zir.zig
@ -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;
|
||||
|
||||
@ -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});
|
||||
}
|
||||
|
||||
@ -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(": ");
|
||||
|
||||
@ -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,
|
||||
|
||||
31
src/main.zig
31
src/main.zig
@ -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()) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user