diff --git a/src/AstGen.zig b/src/AstGen.zig index 36944c1a8a..831071c479 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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), }; } } diff --git a/src/Sema.zig b/src/Sema.zig index 550190654b..5159d6f5d3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -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()); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index f7a1d732b4..a56dd8cf6f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -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, {});