Commit dfecf89d06

Robin Voetter <robin@voetter.nl>
2023-04-10 18:34:30
spirv: fix OpFunctionCall parameters interleaving with insts
resolve() is now able to emit instructions. If usage of this function is interleaved with calls to emitRaw() and writeOperand(), then an instruction may get inserted between operands, causing an invalid module. The solution here is to just perform a temporary allocation.
1 parent 5d896a6
Changed files (1)
src
codegen
src/codegen/spirv.zig
@@ -2717,19 +2717,29 @@ pub const DeclGen = struct {
         const result_id = self.spv.allocId();
         const callee_id = try self.resolve(pl_op.operand);
 
-        try self.func.body.emitRaw(self.spv.gpa, .OpFunctionCall, 3 + args.len);
-        self.func.body.writeOperand(spec.IdResultType, result_type_id);
-        self.func.body.writeOperand(spec.IdResult, result_id);
-        self.func.body.writeOperand(spec.IdRef, callee_id);
+        const params = try self.gpa.alloc(spec.IdRef, args.len);
+        defer self.gpa.free(params);
 
+        var n_params: usize = 0;
         for (args) |arg| {
+            // Note: resolve() might emit instructions, so we need to call it
+            // before starting to emit OpFunctionCall instructions. Hence the
+            // temporary params buffer.
             const arg_id = try self.resolve(arg);
             const arg_ty = self.air.typeOf(arg);
             if (!arg_ty.hasRuntimeBitsIgnoreComptime()) continue;
 
-            self.func.body.writeOperand(spec.IdRef, arg_id);
+            params[n_params] = arg_id;
+            n_params += 1;
         }
 
+        try self.func.body.emit(self.spv.gpa, .OpFunctionCall, .{
+            .id_result_type = result_type_id,
+            .id_result = result_id,
+            .function = callee_id,
+            .id_ref_3 = params[0..n_params],
+        });
+
         if (return_type.isNoReturn()) {
             try self.func.body.emit(self.spv.gpa, .OpUnreachable, {});
         }