diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 22045a282d..5b73a64837 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -492,6 +492,21 @@ pub const DeclGen = struct { return try self.spv.resolveType(SpvType.float(bits)); }, + .Array => { + const elem_ty = ty.childType(); + const total_len_u64 = ty.arrayLen() + @boolToInt(ty.sentinel() != null); + const total_len = std.math.cast(u32, total_len_u64) orelse { + return self.fail("array type of {} elements is too large", .{total_len_u64}); + }; + + const payload = try self.spv.arena.create(SpvType.Payload.Array); + payload.* = .{ + .element_type = try self.resolveType(elem_ty), + .length = total_len, + .array_stride = @intCast(u32, ty.abiSize(target)), + }; + return try self.spv.resolveType(SpvType.initPayload(&payload.base)); + }, .Fn => { // TODO: Put this somewhere in Sema.zig if (ty.fnIsVarArgs()) @@ -537,7 +552,37 @@ pub const DeclGen = struct { }; return try self.spv.resolveType(SpvType.initPayload(&payload.base)); }, + .Struct => { + if (ty.isSimpleTupleOrAnonStruct()) { + return self.todo("implement tuple struct type", .{}); + } + const struct_ty = ty.castTag(.@"struct").?.data; + + if (struct_ty.layout == .Packed) { + return try self.resolveType(struct_ty.backing_int_ty); + } + + const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count()); + var member_index: usize = 0; + for (struct_ty.fields.values()) |field| { + if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; + + members[member_index] = .{ + .ty = try self.resolveType(field.ty), + .offset = field.offset, + .decorations = .{}, + }; + } + + const payload = try self.spv.arena.create(SpvType.Payload.Struct); + payload.* = .{ + .members = members[0..member_index], + .decorations = .{}, + .member_decoration_extra = &.{}, + }; + return try self.spv.resolveType(SpvType.initPayload(&payload.base)); + }, .Null, .Undefined, .EnumLiteral, @@ -632,7 +677,7 @@ pub const DeclGen = struct { .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd), .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr), - .not => try self.airNot(inst), + .not => try self.airNot(inst), .cmp_eq => try self.airCmp(inst, .OpFOrdEqual, .OpLogicalEqual, .OpIEqual), .cmp_neq => try self.airCmp(inst, .OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual), @@ -646,6 +691,7 @@ pub const DeclGen = struct { .block => (try self.airBlock(inst)) orelse return, .load => try self.airLoad(inst), + .bitcast => try self.airBitcast(inst), .br => return self.airBr(inst), .breakpoint => return, .cond_br => return self.airCondBr(inst), @@ -657,6 +703,11 @@ pub const DeclGen = struct { .unreach => return self.airUnreach(), .assembly => (try self.airAssembly(inst)) orelse return, + .call => (try self.airCall(inst, .auto)) orelse return, + .call_always_tail => (try self.airCall(inst, .always_tail)) orelse return, + .call_never_tail => (try self.airCall(inst, .never_tail)) orelse return, + .call_never_inline => (try self.airCall(inst, .never_inline)) orelse return, + .dbg_var_ptr => return, .dbg_var_val => return, .dbg_block_begin => return, @@ -911,6 +962,19 @@ pub const DeclGen = struct { return result_id.toRef(); } + fn airBitcast(self: *DeclGen, inst: Air.Inst.Index) !IdRef { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const operand_id = try self.resolve(ty_op.operand); + const result_id = self.spv.allocId(); + const result_type_id = try self.resolveTypeId(Type.initTag(.bool)); + try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ + .id_result_type = result_type_id, + .id_result = result_id, + .operand = operand_id, + }); + return result_id.toRef(); + } + fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void { const br = self.air.instructions.items(.data)[inst].br; const block = self.blocks.get(br.block_inst).?; @@ -1158,4 +1222,43 @@ pub const DeclGen = struct { return null; } + + fn airCall(self: *DeclGen, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !?IdRef { + _ = modifier; + + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Call, pl_op.payload); + const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); + const callee_ty = self.air.typeOf(pl_op.operand); + const zig_fn_ty = switch (callee_ty.zigTypeTag()) { + .Fn => callee_ty, + .Pointer => return self.fail("cannot call function pointers", .{}), + else => unreachable, + }; + const fn_info = zig_fn_ty.fnInfo(); + const return_type = fn_info.return_type; + + const result_type_id = try self.resolveTypeId(return_type); + 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); + + for (args) |arg| { + 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); + } + + if (return_type.isNoReturn()) { + try self.func.body.emit(self.spv.gpa, .OpUnreachable, {}); + } + + return result_id.toRef(); + } }; diff --git a/src/codegen/spirv/Module.zig b/src/codegen/spirv/Module.zig index a6e0783bf4..f6c4cd735e 100644 --- a/src/codegen/spirv/Module.zig +++ b/src/codegen/spirv/Module.zig @@ -222,7 +222,7 @@ pub fn resolveType(self: *Module, ty: Type) !Type.Ref { return @intToEnum(Type.Ref, result.index); } -pub fn resolveTypeId(self: *Module, ty: Type) !IdRef { +pub fn resolveTypeId(self: *Module, ty: Type) !IdResultType { const type_ref = try self.resolveType(ty); return self.typeResultId(type_ref); } @@ -243,7 +243,7 @@ pub fn typeRefId(self: Module, type_ref: Type.Ref) IdRef { /// Note: This function does not attempt to perform any validation on the type. /// The type is emitted in a shallow fashion; any child types should already /// be emitted at this point. -pub fn emitType(self: *Module, ty: Type) !IdResultType { +pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType { const result_id = self.allocId(); const ref_id = result_id.toRef(); const types = &self.sections.types_globals_constants; @@ -347,10 +347,21 @@ pub fn emitType(self: *Module, ty: Type) !IdResultType { .array => { const info = ty.payload(.array); assert(info.length != 0); + + const size_type = Type.initTag(.u32); + const size_type_id = try self.resolveTypeId(size_type); + + const length_id = self.allocId(); + try types.emit(self.gpa, .OpConstant, .{ + .id_result_type = size_type_id, + .id_result = length_id, + .value = .{ .uint32 = info.length }, + }); + try types.emit(self.gpa, .OpTypeArray, .{ .id_result = result_id, .element_type = self.typeResultId(ty.childType()).toRef(), - .length = .{ .id = 0 }, // TODO: info.length must be emitted as constant! + .length = length_id.toRef(), }); if (info.array_stride != 0) { try annotations.decorate(self.gpa, ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } }); diff --git a/src/codegen/spirv/type.zig b/src/codegen/spirv/type.zig index a65ddca01e..2d9d6befe1 100644 --- a/src/codegen/spirv/type.zig +++ b/src/codegen/spirv/type.zig @@ -421,7 +421,7 @@ pub const Type = extern union { length: u32, /// Type has the 'ArrayStride' decoration. /// If zero, no stride is present. - array_stride: u32, + array_stride: u32 = 0, }; pub const RuntimeArray = struct { @@ -434,6 +434,7 @@ pub const Type = extern union { pub const Struct = struct { base: Payload = .{ .tag = .@"struct" }, + // TODO: name members: []Member, decorations: StructDecorations, @@ -444,20 +445,21 @@ pub const Type = extern union { pub const Member = struct { ty: Ref, offset: u32, + // TODO: name decorations: MemberDecorations, }; pub const StructDecorations = packed struct { /// Type has the 'Block' decoration. - block: bool, + block: bool = false, /// Type has the 'BufferBlock' decoration. - buffer_block: bool, + buffer_block: bool = false, /// Type has the 'GLSLShared' decoration. - glsl_shared: bool, + glsl_shared: bool = false, /// Type has the 'GLSLPacked' decoration. - glsl_packed: bool, + glsl_packed: bool = false, /// Type has the 'CPacked' decoration. - c_packed: bool, + c_packed: bool = false, }; pub const MemberDecorations = packed struct { @@ -473,31 +475,31 @@ pub const Type = extern union { col_major, /// Member is not a matrix or array of matrices. none, - }, + } = .none, // Regular decorations, these do not imply extra fields. /// Member has the 'NoPerspective' decoration. - no_perspective: bool, + no_perspective: bool = false, /// Member has the 'Flat' decoration. - flat: bool, + flat: bool = false, /// Member has the 'Patch' decoration. - patch: bool, + patch: bool = false, /// Member has the 'Centroid' decoration. - centroid: bool, + centroid: bool = false, /// Member has the 'Sample' decoration. - sample: bool, + sample: bool = false, /// Member has the 'Invariant' decoration. /// Note: requires parent struct to have 'Block'. - invariant: bool, + invariant: bool = false, /// Member has the 'Volatile' decoration. - @"volatile": bool, + @"volatile": bool = false, /// Member has the 'Coherent' decoration. - coherent: bool, + coherent: bool = false, /// Member has the 'NonWritable' decoration. - non_writable: bool, + non_writable: bool = false, /// Member has the 'NonReadable' decoration. - non_readable: bool, + non_readable: bool = false, // The following decorations all imply extra field(s). @@ -506,27 +508,27 @@ pub const Type = extern union { /// Note: If any member of a struct has the BuiltIn decoration, all members must have one. /// Note: Each builtin may only be reachable once for a particular entry point. /// Note: The member type may be constrained by a particular built-in, defined in the client API specification. - builtin: bool, + builtin: bool = false, /// Member has the 'Stream' decoration. /// This member has an extra field of type `u32`. - stream: bool, + stream: bool = false, /// Member has the 'Location' decoration. /// This member has an extra field of type `u32`. - location: bool, + location: bool = false, /// Member has the 'Component' decoration. /// This member has an extra field of type `u32`. - component: bool, + component: bool = false, /// Member has the 'XfbBuffer' decoration. /// This member has an extra field of type `u32`. - xfb_buffer: bool, + xfb_buffer: bool = false, /// Member has the 'XfbStride' decoration. /// This member has an extra field of type `u32`. - xfb_stride: bool, + xfb_stride: bool = false, /// Member has the 'UserSemantic' decoration. /// This member has an extra field of type `[]u8`, which is encoded /// by an `u32` containing the number of chars exactly, and then the string padded to /// a multiple of 4 bytes with zeroes. - user_semantic: bool, + user_semantic: bool = false, }; }; diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 284e25671d..2d5ca723b2 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -226,6 +226,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No const air = entry.value_ptr.air; const liveness = entry.value_ptr.liveness; + log.debug("generating code for {s}", .{decl.name}); + // Note, if `decl` is not a function, air/liveness may be undefined. if (try decl_gen.gen(decl_index, air, liveness)) |msg| { try module.failed_decls.put(module.gpa, decl_index, msg);