Commit fbd7e4506f

Veikka Tuominen <git@vexu.eu>
2022-06-07 18:13:50
stage2: implement asm with multiple outputs
1 parent e4c0b84
Changed files (3)
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, {});
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),
             };
         }
     }
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());