authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2022-12-11 14:40:18+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-04-09 01:51:53+02:00
log75abe3b43bf28dc1cd1ab06cc9403d4fa93e7002
treea9c386790fd00061ba65d4d88dee11f71ea81640
parent0c53fea42cea8543733612c7c63a6982ca6e59e6
signaturelock-open Commit is signed but in an unrecognized format.

spirv: optional constants

Implements lowering optional constants in the SPIR-V backend.

1 files changed, 60 insertions(+), 19 deletions(-)

src/codegen/spirv.zig+60-19
...@@ -238,7 +238,7 @@ pub const DeclGen = struct {...@@ -238,7 +238,7 @@ pub const DeclGen = struct {
238 return try self.resolveDecl(fn_decl_index);238 return try self.resolveDecl(fn_decl_index);
239 }239 }
240240
241 return try self.genConstant(ty, val, .direct);241 return try self.constant(ty, val, .direct);
242 }242 }
243 const index = Air.refToIndex(inst).?;243 const index = Air.refToIndex(inst).?;
244 return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage.244 return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage.
...@@ -394,15 +394,15 @@ pub const DeclGen = struct {...@@ -394,15 +394,15 @@ pub const DeclGen = struct {
394 return result_id;394 return result_id;
395 }395 }
396396
397 fn genConstant(self: *DeclGen, ty: Type, val: Value, repr: Repr) Error!IdRef {397 fn constant(self: *DeclGen, ty: Type, val: Value, repr: Repr) Error!IdRef {
398 const result_id = self.spv.allocId();398 const result_id = self.spv.allocId();
399 try self.genConstantForId(result_id, ty, val, repr);399 try self.genConstant(result_id, ty, val, repr);
400 return result_id;400 return result_id;
401 }401 }
402402
403 /// Generate a constant representing `val`.403 /// Generate a constant representing `val`.
404 /// TODO: Deduplication?404 /// TODO: Deduplication?
405 fn genConstantForId(self: *DeclGen, result_id: IdRef, ty: Type, val: Value, repr: Repr) Error!void {405 fn genConstant(self: *DeclGen, result_id: IdRef, ty: Type, val: Value, repr: Repr) Error!void {
406 const target = self.getTarget();406 const target = self.getTarget();
407 const section = &self.spv.sections.types_globals_constants;407 const section = &self.spv.sections.types_globals_constants;
408 const result_ty_ref = try self.resolveType(ty, repr);408 const result_ty_ref = try self.resolveType(ty, repr);
...@@ -453,7 +453,7 @@ pub const DeclGen = struct {...@@ -453,7 +453,7 @@ pub const DeclGen = struct {
453 const constituents = try self.spv.gpa.alloc(IdRef, len);453 const constituents = try self.spv.gpa.alloc(IdRef, len);
454 defer self.spv.gpa.free(constituents);454 defer self.spv.gpa.free(constituents);
455 for (elem_vals[0..len], 0..) |elem_val, i| {455 for (elem_vals[0..len], 0..) |elem_val, i| {
456 constituents[i] = try self.genConstant(elem_ty, elem_val, repr);456 constituents[i] = try self.constant(elem_ty, elem_val, repr);
457 }457 }
458 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{458 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{
459 .id_result_type = result_ty_id,459 .id_result_type = result_ty_id,
...@@ -469,12 +469,12 @@ pub const DeclGen = struct {...@@ -469,12 +469,12 @@ pub const DeclGen = struct {
469 const constituents = try self.spv.gpa.alloc(IdRef, total_len);469 const constituents = try self.spv.gpa.alloc(IdRef, total_len);
470 defer self.spv.gpa.free(constituents);470 defer self.spv.gpa.free(constituents);
471471
472 const elem_val_id = try self.genConstant(elem_ty, elem_val, repr);472 const elem_val_id = try self.constant(elem_ty, elem_val, repr);
473 for (constituents[0..len]) |*elem| {473 for (constituents[0..len]) |*elem| {
474 elem.* = elem_val_id;474 elem.* = elem_val_id;
475 }475 }
476 if (ty.sentinel()) |sentinel| {476 if (ty.sentinel()) |sentinel| {
477 constituents[len] = try self.genConstant(elem_ty, sentinel, repr);477 constituents[len] = try self.constant(elem_ty, sentinel, repr);
478 }478 }
479 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{479 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{
480 .id_result_type = result_ty_id,480 .id_result_type = result_ty_id,
...@@ -492,7 +492,7 @@ pub const DeclGen = struct {...@@ -492,7 +492,7 @@ pub const DeclGen = struct {
492 const total_len = @intCast(u32, ty.arrayLenIncludingSentinel());492 const total_len = @intCast(u32, ty.arrayLenIncludingSentinel());
493 const constituents = try self.spv.gpa.alloc(IdRef, total_len);493 const constituents = try self.spv.gpa.alloc(IdRef, total_len);
494 defer self.spv.gpa.free(constituents);494 defer self.spv.gpa.free(constituents);
495 for (bytes) |byte, i| {495 for (bytes, 0..) |byte, i| {
496 constituents[i] = self.spv.allocId();496 constituents[i] = self.spv.allocId();
497 try self.spv.emitConstant(elem_ty_id, constituents[i], .{ .uint32 = byte });497 try self.spv.emitConstant(elem_ty_id, constituents[i], .{ .uint32 = byte });
498 }498 }
...@@ -518,7 +518,7 @@ pub const DeclGen = struct {...@@ -518,7 +518,7 @@ pub const DeclGen = struct {
518 const elem_refs = try self.gpa.alloc(IdRef, vector_len);518 const elem_refs = try self.gpa.alloc(IdRef, vector_len);
519 defer self.gpa.free(elem_refs);519 defer self.gpa.free(elem_refs);
520 for (elem_refs, 0..) |*elem, i| {520 for (elem_refs, 0..) |*elem, i| {
521 elem.* = try self.genConstant(elem_ty, elem_vals[i], repr);521 elem.* = try self.constant(elem_ty, elem_vals[i], repr);
522 }522 }
523 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{523 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{
524 .id_result_type = result_ty_id,524 .id_result_type = result_ty_id,
...@@ -543,7 +543,7 @@ pub const DeclGen = struct {...@@ -543,7 +543,7 @@ pub const DeclGen = struct {
543 for (tuple.types, 0..) |field_ty, i| {543 for (tuple.types, 0..) |field_ty, i| {
544 const field_val = tuple.values[i];544 const field_val = tuple.values[i];
545 if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue;545 if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue;
546 constituents[member_i] = try self.genConstant(field_ty, field_val, repr);546 constituents[member_i] = try self.constant(field_ty, field_val, repr);
547 member_i += 1;547 member_i += 1;
548 }548 }
549549
...@@ -561,7 +561,7 @@ pub const DeclGen = struct {...@@ -561,7 +561,7 @@ pub const DeclGen = struct {
561 var member_i: usize = 0;561 var member_i: usize = 0;
562 for (struct_ty.fields.values(), 0..) |field, i| {562 for (struct_ty.fields.values(), 0..) |field, i| {
563 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;563 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
564 constituents[member_i] = try self.genConstant(field.ty, field_vals[i], repr);564 constituents[member_i] = try self.constant(field.ty, field_vals[i], repr);
565 member_i += 1;565 member_i += 1;
566 }566 }
567567
...@@ -582,8 +582,8 @@ pub const DeclGen = struct {...@@ -582,8 +582,8 @@ pub const DeclGen = struct {
582 const slice = val.castTag(.slice).?.data;582 const slice = val.castTag(.slice).?.data;
583 var buf: Type.SlicePtrFieldTypeBuffer = undefined;583 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
584584
585 const ptr_id = try self.genConstant(ty.slicePtrFieldType(&buf), slice.ptr, .indirect);585 const ptr_id = try self.constant(ty.slicePtrFieldType(&buf), slice.ptr, .indirect);
586 const len_id = try self.genConstant(Type.usize, slice.len, .indirect);586 const len_id = try self.constant(Type.usize, slice.len, .indirect);
587587
588 const constituents = [_]IdRef{ ptr_id, len_id };588 const constituents = [_]IdRef{ ptr_id, len_id };
589 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{589 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{
...@@ -594,6 +594,45 @@ pub const DeclGen = struct {...@@ -594,6 +594,45 @@ pub const DeclGen = struct {
594 },594 },
595 else => return self.todo("pointer of value type {s}", .{@tagName(val.tag())}),595 else => return self.todo("pointer of value type {s}", .{@tagName(val.tag())}),
596 },596 },
597 .Optional => {
598 var buf: Type.Payload.ElemType = undefined;
599 const payload_ty = ty.optionalChild(&buf);
600
601 const has_payload = !val.isNull();
602
603 // Note: keep in sync with the resolveType implementation for optionals.
604 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
605 // Just a bool. Note: always in indirect representation.
606 try self.genConstInt(result_ty_ref, result_id, @boolToInt(has_payload));
607 } else if (ty.optionalReprIsPayload()) {
608 // A nullable pointer.
609 if (val.castTag(.opt_payload)) |payload| {
610 try self.genConstant(result_id, payload_ty, payload.data, repr);
611 } else if (has_payload) {
612 try self.genConstant(result_id, payload_ty, val, repr);
613 } else {
614 try section.emit(self.spv.gpa, .OpConstantNull, .{
615 .id_result_type = result_ty_id,
616 .id_result = result_id,
617 });
618 }
619 return;
620 }
621
622 // Struct-and-field pair.
623 // Note: If this optional has no payload, we initialize the the data member with OpUndef.
624 const bool_ty_ref = try self.resolveType(Type.bool, .indirect);
625 const valid_id = try self.constInt(bool_ty_ref, @boolToInt(has_payload));
626 const payload_val = if (val.castTag(.opt_payload)) |pl| pl.data else Value.undef;
627 const payload_id = try self.constant(payload_ty, payload_val, .indirect);
628
629 const constituents = [_]IdRef{ payload_id, valid_id };
630 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{
631 .id_result_type = result_ty_id,
632 .id_result = result_id,
633 .constituents = &constituents,
634 });
635 },
597 .Fn => switch (repr) {636 .Fn => switch (repr) {
598 .direct => unreachable,637 .direct => unreachable,
599 .indirect => return self.todo("function pointers", .{}),638 .indirect => return self.todo("function pointers", .{}),
...@@ -606,7 +645,7 @@ pub const DeclGen = struct {...@@ -606,7 +645,7 @@ pub const DeclGen = struct {
606 fn genDeclRef(self: *DeclGen, result_ty_ref: SpvType.Ref, result_id: IdRef, decl_index: Decl.Index) Error!void {645 fn genDeclRef(self: *DeclGen, result_ty_ref: SpvType.Ref, result_id: IdRef, decl_index: Decl.Index) Error!void {
607 const decl = self.module.declPtr(decl_index);646 const decl = self.module.declPtr(decl_index);
608 self.module.markDeclAlive(decl);647 self.module.markDeclAlive(decl);
609 const decl_id = try self.genConstant(decl.ty, decl.val, .indirect);648 const decl_id = try self.constant(decl.ty, decl.val, .indirect);
610 try self.variable(.global, result_id, result_ty_ref, decl_id);649 try self.variable(.global, result_id, result_ty_ref, decl_id);
611 }650 }
612651
...@@ -814,7 +853,9 @@ pub const DeclGen = struct {...@@ -814,7 +853,9 @@ pub const DeclGen = struct {
814 const payload_ty = ty.optionalChild(&buf);853 const payload_ty = ty.optionalChild(&buf);
815 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {854 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
816 // Just use a bool.855 // Just use a bool.
817 return try self.resolveType(Type.initTag(.bool), repr);856 // Note: Always generate the bool with indirect format, to save on some sanity
857 // Perform the converison to a direct bool when the field is extracted.
858 return try self.resolveType(Type.bool, .indirect);
818 }859 }
819860
820 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);861 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);
...@@ -823,7 +864,7 @@ pub const DeclGen = struct {...@@ -823,7 +864,7 @@ pub const DeclGen = struct {
823 return payload_ty_ref;864 return payload_ty_ref;
824 }865 }
825866
826 const bool_ty_ref = try self.resolveType(Type.initTag(.bool), .indirect);867 const bool_ty_ref = try self.resolveType(Type.bool, .indirect);
827868
828 // its an actual optional869 // its an actual optional
829 return try self.simpleStructType(&.{870 return try self.simpleStructType(&.{
...@@ -906,7 +947,7 @@ pub const DeclGen = struct {...@@ -906,7 +947,7 @@ pub const DeclGen = struct {
906 .name = fqn,947 .name = fqn,
907 });948 });
908 } else {949 } else {
909 try self.genConstantForId(result_id, decl.ty, decl.val, .direct);950 try self.genConstant(result_id, decl.ty, decl.val, .direct);
910 }951 }
911 }952 }
912953
...@@ -1256,7 +1297,7 @@ pub const DeclGen = struct {...@@ -1256,7 +1297,7 @@ pub const DeclGen = struct {
1256 var lhs_id = try self.resolve(bin_op.lhs);1297 var lhs_id = try self.resolve(bin_op.lhs);
1257 var rhs_id = try self.resolve(bin_op.rhs);1298 var rhs_id = try self.resolve(bin_op.rhs);
1258 const result_id = self.spv.allocId();1299 const result_id = self.spv.allocId();
1259 const result_type_id = try self.resolveTypeId(Type.initTag(.bool));1300 const result_type_id = try self.resolveTypeId(Type.bool);
1260 const op_ty = self.air.typeOf(bin_op.lhs);1301 const op_ty = self.air.typeOf(bin_op.lhs);
1261 assert(op_ty.eql(self.air.typeOf(bin_op.rhs), self.module));1302 assert(op_ty.eql(self.air.typeOf(bin_op.rhs), self.module));
12621303
...@@ -1350,7 +1391,7 @@ pub const DeclGen = struct {...@@ -1350,7 +1391,7 @@ pub const DeclGen = struct {
1350 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1391 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1351 const operand_id = try self.resolve(ty_op.operand);1392 const operand_id = try self.resolve(ty_op.operand);
1352 const result_id = self.spv.allocId();1393 const result_id = self.spv.allocId();
1353 const result_type_id = try self.resolveTypeId(Type.initTag(.bool));1394 const result_type_id = try self.resolveTypeId(Type.bool);
1354 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, .{1395 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, .{
1355 .id_result_type = result_type_id,1396 .id_result_type = result_type_id,
1356 .id_result = result_id,1397 .id_result = result_id,