stage2: implement asm with multiple outputs

This commit is contained in:
Veikka Tuominen 2022-06-07 19:13:50 +03:00
parent e4c0b848a4
commit fbd7e4506f
3 changed files with 41 additions and 39 deletions

View File

@ -6876,6 +6876,9 @@ fn asmExpr(
const constraint = (try astgen.strLitAsString(constraint_token)).index;
const has_arrow = token_tags[symbolic_name + 4] == .arrow;
if (has_arrow) {
if (output_type_bits != 0) {
return astgen.failNode(output_node, "inline assembly allows up to one output value", .{});
}
output_type_bits |= @as(u32, 1) << @intCast(u5, i);
const out_type_node = node_datas[output_node].lhs;
const out_type_inst = try typeExpr(gz, scope, out_type_node);
@ -6892,7 +6895,7 @@ fn asmExpr(
outputs[i] = .{
.name = name,
.constraint = constraint,
.operand = try localVarRef(gz, scope, rl, node, ident_token),
.operand = try localVarRef(gz, scope, .ref, node, ident_token),
};
}
}

View File

@ -11334,43 +11334,40 @@ fn zirAsm(
try sema.requireRuntimeBlock(block, src);
}
if (outputs_len > 1) {
return sema.fail(block, src, "TODO implement Sema for asm with more than 1 output", .{});
}
var extra_i = extra.end;
var output_type_bits = extra.data.output_type_bits;
var needed_capacity: usize = @typeInfo(Air.Asm).Struct.fields.len + outputs_len + inputs_len;
const Output = struct {
constraint: []const u8,
name: []const u8,
ty: Type,
};
const output: ?Output = if (outputs_len == 0) null else blk: {
const ConstraintName = struct { c: []const u8, n: []const u8 };
const out_args = try sema.arena.alloc(Air.Inst.Ref, outputs_len);
const outputs = try sema.arena.alloc(ConstraintName, outputs_len);
var expr_ty = Air.Inst.Ref.void_type;
for (out_args) |*arg, out_i| {
const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i);
extra_i = output.end;
const is_type = @truncate(u1, output_type_bits) != 0;
output_type_bits >>= 1;
if (!is_type) {
return sema.fail(block, src, "TODO implement Sema for asm with non `->` output", .{});
if (is_type) {
// Indicate the output is the asm instruction return value.
arg.* = .none;
const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand);
expr_ty = try sema.addType(out_ty);
} else {
arg.* = try sema.resolveInst(output.data.operand);
}
const constraint = sema.code.nullTerminatedString(output.data.constraint);
const name = sema.code.nullTerminatedString(output.data.name);
needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
break :blk Output{
.constraint = constraint,
.name = name,
.ty = try sema.resolveType(block, ret_ty_src, output.data.operand),
};
};
outputs[out_i] = .{ .c = constraint, .n = name };
}
const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len);
const inputs = try sema.arena.alloc(struct { c: []const u8, n: []const u8 }, inputs_len);
const inputs = try sema.arena.alloc(ConstraintName, inputs_len);
for (args) |*arg, arg_i| {
const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i);
@ -11405,7 +11402,7 @@ fn zirAsm(
const asm_air = try block.addInst(.{
.tag = .assembly,
.data = .{ .ty_pl = .{
.ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type,
.ty = expr_ty,
.payload = sema.addExtraAssumeCapacity(Air.Asm{
.source_len = @intCast(u32, asm_source.len),
.outputs_len = outputs_len,
@ -11414,18 +11411,15 @@ fn zirAsm(
}),
} },
});
if (output != null) {
// Indicate the output is the asm instruction return value.
sema.air_extra.appendAssumeCapacity(@enumToInt(Air.Inst.Ref.none));
}
sema.appendRefsAssumeCapacity(out_args);
sema.appendRefsAssumeCapacity(args);
if (output) |o| {
for (outputs) |o| {
const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
mem.copy(u8, buffer, o.constraint);
buffer[o.constraint.len] = 0;
mem.copy(u8, buffer[o.constraint.len + 1 ..], o.name);
buffer[o.constraint.len + 1 + o.name.len] = 0;
sema.air_extra.items.len += (o.constraint.len + o.name.len + (2 + 3)) / 4;
mem.copy(u8, buffer, o.c);
buffer[o.c.len] = 0;
mem.copy(u8, buffer[o.c.len + 1 ..], o.n);
buffer[o.c.len + 1 + o.n.len] = 0;
sema.air_extra.items.len += (o.c.len + o.n.len + (2 + 3)) / 4;
}
for (inputs) |input| {
const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());

View File

@ -5435,10 +5435,6 @@ pub const FuncGen = struct {
const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
extra_i += inputs.len;
if (outputs.len > 1) {
return self.todo("implement llvm codegen for asm with more than 1 output", .{});
}
var llvm_constraints: std.ArrayListUnmanaged(u8) = .{};
defer llvm_constraints.deinit(self.gpa);
@ -5446,7 +5442,10 @@ pub const FuncGen = struct {
defer arena_allocator.deinit();
const arena = arena_allocator.allocator();
const llvm_params_len = inputs.len;
const return_count: u8 = for (outputs) |output| {
if (output == .none) break 1;
} else 0;
const llvm_params_len = inputs.len + outputs.len - return_count;
const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len);
const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len);
var llvm_param_i: usize = 0;
@ -5456,9 +5455,6 @@ pub const FuncGen = struct {
try name_map.ensureUnusedCapacity(arena, outputs.len + inputs.len);
for (outputs) |output| {
if (output != .none) {
return self.todo("implement inline asm with non-returned output", .{});
}
const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
@ -5471,6 +5467,15 @@ pub const FuncGen = struct {
llvm_constraints.appendAssumeCapacity(',');
}
llvm_constraints.appendAssumeCapacity('=');
if (output != .none) {
try llvm_constraints.ensureUnusedCapacity(self.gpa, llvm_constraints.capacity + 1);
llvm_constraints.appendAssumeCapacity('*');
const output_inst = try self.resolveInst(output);
llvm_param_values[llvm_param_i] = output_inst;
llvm_param_types[llvm_param_i] = output_inst.typeOf();
llvm_param_i += 1;
}
llvm_constraints.appendSliceAssumeCapacity(constraint[1..]);
name_map.putAssumeCapacityNoClobber(name, {});