authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2022-11-26 16:51:53+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-04-09 01:51:49+02:00
log3f92eaceb61796254d0465ba5689378f15155791
tree6ac76e438e7ef8823aa40b06cd7f793ddecee4ff
parentdae8b4c11f6a59dc6ccd3e4fe327c43eb73c44cb
signaturelock-open Commit is signed but in an unrecognized format.

spirv: array, structs, bitcast, call

Implements type lowering for arrays and structs, and implements instruction lowering for bitcast and call. Bitcast currently naively maps to the OpBitcast instruction - this is only valid for some primitive types, and should be improved to work with composites.

4 files changed, 146 insertions(+), 28 deletions(-)

src/codegen/spirv.zig+104-1
......@@ -492,6 +492,21 @@ pub const DeclGen = struct {
492492
493493 return try self.spv.resolveType(SpvType.float(bits));
494494 },
495 .Array => {
496 const elem_ty = ty.childType();
497 const total_len_u64 = ty.arrayLen() + @boolToInt(ty.sentinel() != null);
498 const total_len = std.math.cast(u32, total_len_u64) orelse {
499 return self.fail("array type of {} elements is too large", .{total_len_u64});
500 };
501
502 const payload = try self.spv.arena.create(SpvType.Payload.Array);
503 payload.* = .{
504 .element_type = try self.resolveType(elem_ty),
505 .length = total_len,
506 .array_stride = @intCast(u32, ty.abiSize(target)),
507 };
508 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
509 },
495510 .Fn => {
496511 // TODO: Put this somewhere in Sema.zig
497512 if (ty.fnIsVarArgs())
......@@ -537,7 +552,37 @@ pub const DeclGen = struct {
537552 };
538553 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
539554 },
555 .Struct => {
556 if (ty.isSimpleTupleOrAnonStruct()) {
557 return self.todo("implement tuple struct type", .{});
558 }
559
560 const struct_ty = ty.castTag(.@"struct").?.data;
561
562 if (struct_ty.layout == .Packed) {
563 return try self.resolveType(struct_ty.backing_int_ty);
564 }
565
566 const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count());
567 var member_index: usize = 0;
568 for (struct_ty.fields.values()) |field| {
569 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
570
571 members[member_index] = .{
572 .ty = try self.resolveType(field.ty),
573 .offset = field.offset,
574 .decorations = .{},
575 };
576 }
540577
578 const payload = try self.spv.arena.create(SpvType.Payload.Struct);
579 payload.* = .{
580 .members = members[0..member_index],
581 .decorations = .{},
582 .member_decoration_extra = &.{},
583 };
584 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
585 },
541586 .Null,
542587 .Undefined,
543588 .EnumLiteral,
......@@ -632,7 +677,7 @@ pub const DeclGen = struct {
632677 .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd),
633678 .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr),
634679
635 .not => try self.airNot(inst),
680 .not => try self.airNot(inst),
636681
637682 .cmp_eq => try self.airCmp(inst, .OpFOrdEqual, .OpLogicalEqual, .OpIEqual),
638683 .cmp_neq => try self.airCmp(inst, .OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual),
......@@ -646,6 +691,7 @@ pub const DeclGen = struct {
646691 .block => (try self.airBlock(inst)) orelse return,
647692 .load => try self.airLoad(inst),
648693
694 .bitcast => try self.airBitcast(inst),
649695 .br => return self.airBr(inst),
650696 .breakpoint => return,
651697 .cond_br => return self.airCondBr(inst),
......@@ -657,6 +703,11 @@ pub const DeclGen = struct {
657703 .unreach => return self.airUnreach(),
658704 .assembly => (try self.airAssembly(inst)) orelse return,
659705
706 .call => (try self.airCall(inst, .auto)) orelse return,
707 .call_always_tail => (try self.airCall(inst, .always_tail)) orelse return,
708 .call_never_tail => (try self.airCall(inst, .never_tail)) orelse return,
709 .call_never_inline => (try self.airCall(inst, .never_inline)) orelse return,
710
660711 .dbg_var_ptr => return,
661712 .dbg_var_val => return,
662713 .dbg_block_begin => return,
......@@ -911,6 +962,19 @@ pub const DeclGen = struct {
911962 return result_id.toRef();
912963 }
913964
965 fn airBitcast(self: *DeclGen, inst: Air.Inst.Index) !IdRef {
966 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
967 const operand_id = try self.resolve(ty_op.operand);
968 const result_id = self.spv.allocId();
969 const result_type_id = try self.resolveTypeId(Type.initTag(.bool));
970 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
971 .id_result_type = result_type_id,
972 .id_result = result_id,
973 .operand = operand_id,
974 });
975 return result_id.toRef();
976 }
977
914978 fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void {
915979 const br = self.air.instructions.items(.data)[inst].br;
916980 const block = self.blocks.get(br.block_inst).?;
......@@ -1158,4 +1222,43 @@ pub const DeclGen = struct {
11581222
11591223 return null;
11601224 }
1225
1226 fn airCall(self: *DeclGen, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !?IdRef {
1227 _ = modifier;
1228
1229 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1230 const extra = self.air.extraData(Air.Call, pl_op.payload);
1231 const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]);
1232 const callee_ty = self.air.typeOf(pl_op.operand);
1233 const zig_fn_ty = switch (callee_ty.zigTypeTag()) {
1234 .Fn => callee_ty,
1235 .Pointer => return self.fail("cannot call function pointers", .{}),
1236 else => unreachable,
1237 };
1238 const fn_info = zig_fn_ty.fnInfo();
1239 const return_type = fn_info.return_type;
1240
1241 const result_type_id = try self.resolveTypeId(return_type);
1242 const result_id = self.spv.allocId();
1243 const callee_id = try self.resolve(pl_op.operand);
1244
1245 try self.func.body.emitRaw(self.spv.gpa, .OpFunctionCall, 3 + args.len);
1246 self.func.body.writeOperand(spec.IdResultType, result_type_id);
1247 self.func.body.writeOperand(spec.IdResult, result_id);
1248 self.func.body.writeOperand(spec.IdRef, callee_id);
1249
1250 for (args) |arg| {
1251 const arg_id = try self.resolve(arg);
1252 const arg_ty = self.air.typeOf(arg);
1253 if (!arg_ty.hasRuntimeBitsIgnoreComptime()) continue;
1254
1255 self.func.body.writeOperand(spec.IdRef, arg_id);
1256 }
1257
1258 if (return_type.isNoReturn()) {
1259 try self.func.body.emit(self.spv.gpa, .OpUnreachable, {});
1260 }
1261
1262 return result_id.toRef();
1263 }
11611264};
src/codegen/spirv/Module.zig+14-3
......@@ -222,7 +222,7 @@ pub fn resolveType(self: *Module, ty: Type) !Type.Ref {
222222 return @intToEnum(Type.Ref, result.index);
223223}
224224
225pub fn resolveTypeId(self: *Module, ty: Type) !IdRef {
225pub fn resolveTypeId(self: *Module, ty: Type) !IdResultType {
226226 const type_ref = try self.resolveType(ty);
227227 return self.typeResultId(type_ref);
228228}
......@@ -243,7 +243,7 @@ pub fn typeRefId(self: Module, type_ref: Type.Ref) IdRef {
243243/// Note: This function does not attempt to perform any validation on the type.
244244/// The type is emitted in a shallow fashion; any child types should already
245245/// be emitted at this point.
246pub fn emitType(self: *Module, ty: Type) !IdResultType {
246pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {
247247 const result_id = self.allocId();
248248 const ref_id = result_id.toRef();
249249 const types = &self.sections.types_globals_constants;
......@@ -347,10 +347,21 @@ pub fn emitType(self: *Module, ty: Type) !IdResultType {
347347 .array => {
348348 const info = ty.payload(.array);
349349 assert(info.length != 0);
350
351 const size_type = Type.initTag(.u32);
352 const size_type_id = try self.resolveTypeId(size_type);
353
354 const length_id = self.allocId();
355 try types.emit(self.gpa, .OpConstant, .{
356 .id_result_type = size_type_id,
357 .id_result = length_id,
358 .value = .{ .uint32 = info.length },
359 });
360
350361 try types.emit(self.gpa, .OpTypeArray, .{
351362 .id_result = result_id,
352363 .element_type = self.typeResultId(ty.childType()).toRef(),
353 .length = .{ .id = 0 }, // TODO: info.length must be emitted as constant!
364 .length = length_id.toRef(),
354365 });
355366 if (info.array_stride != 0) {
356367 try annotations.decorate(self.gpa, ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } });
src/codegen/spirv/type.zig+26-24
......@@ -421,7 +421,7 @@ pub const Type = extern union {
421421 length: u32,
422422 /// Type has the 'ArrayStride' decoration.
423423 /// If zero, no stride is present.
424 array_stride: u32,
424 array_stride: u32 = 0,
425425 };
426426
427427 pub const RuntimeArray = struct {
......@@ -434,6 +434,7 @@ pub const Type = extern union {
434434
435435 pub const Struct = struct {
436436 base: Payload = .{ .tag = .@"struct" },
437 // TODO: name
437438 members: []Member,
438439 decorations: StructDecorations,
439440
......@@ -444,20 +445,21 @@ pub const Type = extern union {
444445 pub const Member = struct {
445446 ty: Ref,
446447 offset: u32,
448 // TODO: name
447449 decorations: MemberDecorations,
448450 };
449451
450452 pub const StructDecorations = packed struct {
451453 /// Type has the 'Block' decoration.
452 block: bool,
454 block: bool = false,
453455 /// Type has the 'BufferBlock' decoration.
454 buffer_block: bool,
456 buffer_block: bool = false,
455457 /// Type has the 'GLSLShared' decoration.
456 glsl_shared: bool,
458 glsl_shared: bool = false,
457459 /// Type has the 'GLSLPacked' decoration.
458 glsl_packed: bool,
460 glsl_packed: bool = false,
459461 /// Type has the 'CPacked' decoration.
460 c_packed: bool,
462 c_packed: bool = false,
461463 };
462464
463465 pub const MemberDecorations = packed struct {
......@@ -473,31 +475,31 @@ pub const Type = extern union {
473475 col_major,
474476 /// Member is not a matrix or array of matrices.
475477 none,
476 },
478 } = .none,
477479
478480 // Regular decorations, these do not imply extra fields.
479481
480482 /// Member has the 'NoPerspective' decoration.
481 no_perspective: bool,
483 no_perspective: bool = false,
482484 /// Member has the 'Flat' decoration.
483 flat: bool,
485 flat: bool = false,
484486 /// Member has the 'Patch' decoration.
485 patch: bool,
487 patch: bool = false,
486488 /// Member has the 'Centroid' decoration.
487 centroid: bool,
489 centroid: bool = false,
488490 /// Member has the 'Sample' decoration.
489 sample: bool,
491 sample: bool = false,
490492 /// Member has the 'Invariant' decoration.
491493 /// Note: requires parent struct to have 'Block'.
492 invariant: bool,
494 invariant: bool = false,
493495 /// Member has the 'Volatile' decoration.
494 @"volatile": bool,
496 @"volatile": bool = false,
495497 /// Member has the 'Coherent' decoration.
496 coherent: bool,
498 coherent: bool = false,
497499 /// Member has the 'NonWritable' decoration.
498 non_writable: bool,
500 non_writable: bool = false,
499501 /// Member has the 'NonReadable' decoration.
500 non_readable: bool,
502 non_readable: bool = false,
501503
502504 // The following decorations all imply extra field(s).
503505
......@@ -506,27 +508,27 @@ pub const Type = extern union {
506508 /// Note: If any member of a struct has the BuiltIn decoration, all members must have one.
507509 /// Note: Each builtin may only be reachable once for a particular entry point.
508510 /// Note: The member type may be constrained by a particular built-in, defined in the client API specification.
509 builtin: bool,
511 builtin: bool = false,
510512 /// Member has the 'Stream' decoration.
511513 /// This member has an extra field of type `u32`.
512 stream: bool,
514 stream: bool = false,
513515 /// Member has the 'Location' decoration.
514516 /// This member has an extra field of type `u32`.
515 location: bool,
517 location: bool = false,
516518 /// Member has the 'Component' decoration.
517519 /// This member has an extra field of type `u32`.
518 component: bool,
520 component: bool = false,
519521 /// Member has the 'XfbBuffer' decoration.
520522 /// This member has an extra field of type `u32`.
521 xfb_buffer: bool,
523 xfb_buffer: bool = false,
522524 /// Member has the 'XfbStride' decoration.
523525 /// This member has an extra field of type `u32`.
524 xfb_stride: bool,
526 xfb_stride: bool = false,
525527 /// Member has the 'UserSemantic' decoration.
526528 /// This member has an extra field of type `[]u8`, which is encoded
527529 /// by an `u32` containing the number of chars exactly, and then the string padded to
528530 /// a multiple of 4 bytes with zeroes.
529 user_semantic: bool,
531 user_semantic: bool = false,
530532 };
531533 };
532534
src/link/SpirV.zig+2
......@@ -226,6 +226,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No
226226 const air = entry.value_ptr.air;
227227 const liveness = entry.value_ptr.liveness;
228228
229 log.debug("generating code for {s}", .{decl.name});
230
229231 // Note, if `decl` is not a function, air/liveness may be undefined.
230232 if (try decl_gen.gen(decl_index, air, liveness)) |msg| {
231233 try module.failed_decls.put(module.gpa, decl_index, msg);