authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2022-12-10 01:25:28+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-04-09 01:51:52+02:00
log3c7f93aa69e495820448a17869bd4663ed396ff2
tree960fe85215ded196739493e83dc903905102ff61
parentfbe5f0c3459484babcf3d4ba6fe4901612a409bb
signaturelock-open Commit is signed but in an unrecognized format.

spirv: generic global pointers

Similar to function locals, taking the address of a global that does not have an explicit address space assigned to it should result in a generic pointer, not a global pointer. Also similar to function locals, they cannot be generated into the generic storage class, and so are generated into the global storage class and then cast to a generic pointer, using OpSpecConstantOp. Note that using OpSpecConstantOp results is only allowed by a hand full of other OpSpecConstant instructions - which is why we generate constant structs using OpSpecConstantComposite: These may use OpVariable and OpSpecConstantOp results, while OpConstantComposite may not.

2 files changed, 69 insertions(+), 41 deletions(-)

src/codegen/spirv.zig+61-40
...@@ -452,7 +452,7 @@ pub const DeclGen = struct {...@@ -452,7 +452,7 @@ pub const DeclGen = struct {
452 constituents[i] = self.spv.allocId();452 constituents[i] = self.spv.allocId();
453 try self.genConstant(constituents[i], elem_ty, elem_val, repr);453 try self.genConstant(constituents[i], elem_ty, elem_val, repr);
454 }454 }
455 try section.emit(self.spv.gpa, .OpConstantComposite, .{455 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{
456 .id_result_type = result_ty_id,456 .id_result_type = result_ty_id,
457 .id_result = result_id,457 .id_result = result_id,
458 .constituents = constituents,458 .constituents = constituents,
...@@ -474,7 +474,7 @@ pub const DeclGen = struct {...@@ -474,7 +474,7 @@ pub const DeclGen = struct {
474 constituents[len] = self.spv.allocId();474 constituents[len] = self.spv.allocId();
475 try self.genConstant(constituents[len], elem_ty, sentinel, repr);475 try self.genConstant(constituents[len], elem_ty, sentinel, repr);
476 }476 }
477 try section.emit(self.spv.gpa, .OpConstantComposite, .{477 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{
478 .id_result_type = result_ty_id,478 .id_result_type = result_ty_id,
479 .id_result = result_id,479 .id_result = result_id,
480 .constituents = constituents,480 .constituents = constituents,
...@@ -494,7 +494,7 @@ pub const DeclGen = struct {...@@ -494,7 +494,7 @@ pub const DeclGen = struct {
494 elem.* = self.spv.allocId();494 elem.* = self.spv.allocId();
495 try self.genConstant(elem.*, elem_ty, elem_vals[i], repr);495 try self.genConstant(elem.*, elem_ty, elem_vals[i], repr);
496 }496 }
497 try section.emit(self.spv.gpa, .OpConstantComposite, .{497 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{
498 .id_result_type = result_ty_id,498 .id_result_type = result_ty_id,
499 .id_result = result_id,499 .id_result = result_id,
500 .constituents = elem_refs,500 .constituents = elem_refs,
...@@ -547,7 +547,7 @@ pub const DeclGen = struct {...@@ -547,7 +547,7 @@ pub const DeclGen = struct {
547 };547 };
548 defer self.spv.gpa.free(constituents);548 defer self.spv.gpa.free(constituents);
549549
550 try section.emit(self.spv.gpa, .OpConstantComposite, .{550 try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{
551 .id_result_type = result_ty_id,551 .id_result_type = result_ty_id,
552 .id_result = result_id,552 .id_result = result_id,
553 .constituents = constituents,553 .constituents = constituents,
...@@ -558,11 +558,7 @@ pub const DeclGen = struct {...@@ -558,11 +558,7 @@ pub const DeclGen = struct {
558 const decl_index = val.castTag(.decl_ref).?.data;558 const decl_index = val.castTag(.decl_ref).?.data;
559 const decl_result_id = self.spv.allocId();559 const decl_result_id = self.spv.allocId();
560 try self.genDeclRef(decl_result_id, decl_index);560 try self.genDeclRef(decl_result_id, decl_index);
561 try section.emit(self.spv.gpa, .OpVariable, .{561 try self.variable(.global, result_id, result_ty_ref, decl_result_id);
562 .id_result_type = result_ty_id,
563 .id_result = result_id,
564 .storage_class = spirvStorageClass(ty.ptrAddressSpace()),
565 });
566 },562 },
567 else => return self.todo("constant pointer of value type {s}", .{@tagName(val.tag())}),563 else => return self.todo("constant pointer of value type {s}", .{@tagName(val.tag())}),
568 },564 },
...@@ -1490,48 +1486,73 @@ pub const DeclGen = struct {...@@ -1490,48 +1486,73 @@ pub const DeclGen = struct {
1490 return try self.structFieldPtr(result_ptr_ty, struct_ptr_ty, struct_ptr, field_index);1486 return try self.structFieldPtr(result_ptr_ty, struct_ptr_ty, struct_ptr, field_index);
1491 }1487 }
14921488
1493 fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {1489 fn variable(
1494 if (self.liveness.isUnused(inst)) return null;1490 self: *DeclGen,
1495 const ty = self.air.typeOfIndex(inst);1491 comptime context: enum { function, global },
1496 const result_ty_ref = try self.resolveType(ty, .direct);1492 result_id: IdRef,
1497 const result_ty_id = self.typeId(result_ty_ref);1493 ptr_ty_ref: SpvType.Ref,
1498 const result_id = self.spv.allocId();1494 initializer: ?IdRef,
14991495 ) !void {
1500 const storage_class = spirvStorageClass(ty.ptrAddressSpace());1496 const storage_class = self.spv.typeRefType(ptr_ty_ref).payload(.pointer).storage_class;
1501
1502 const ptr_ty_id = switch (storage_class) {
1503 .Generic => blk: {
1504 const payload = try self.spv.arena.create(SpvType.Payload.Pointer);
1505 payload.* = self.spv.typeRefType(result_ty_ref).payload(.pointer).*;
1506 payload.storage_class = .Function;
1507 break :blk try self.spv.resolveTypeId(SpvType.initPayload(&payload.base));
1508 },
1509 else => result_ty_id,
1510 };
1511 const actual_storage_class = switch (storage_class) {1497 const actual_storage_class = switch (storage_class) {
1512 .Generic, .Function => .Function,1498 .Generic => switch (context) {
1499 .function => .Function,
1500 .global => .CrossWorkgroup,
1501 },
1513 else => storage_class,1502 else => storage_class,
1514 };1503 };
1515 const section = switch (storage_class) {1504 const actual_ptr_ty_ref = switch (storage_class) {
1505 .Generic => try self.spv.changePtrStorageClass(ptr_ty_ref, actual_storage_class),
1506 else => ptr_ty_ref,
1507 };
1508 const alloc_result_id = switch (storage_class) {
1509 .Generic => self.spv.allocId(),
1510 else => result_id,
1511 };
1512
1513 const section = switch (actual_storage_class) {
1514 .Generic => unreachable,
1516 // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to1515 // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to
1517 // directly generate them into func.prologue instead of the body.1516 // directly generate them into func.prologue instead of the body.
1518 .Generic, .Function => &self.func.prologue,1517 .Function => &self.func.prologue,
1519 else => &self.spv.sections.types_globals_constants,1518 else => &self.spv.sections.types_globals_constants,
1520 };1519 };
1521 try section.emit(self.spv.gpa, .OpVariable, .{1520 try section.emit(self.spv.gpa, .OpVariable, .{
1522 .id_result_type = ptr_ty_id,1521 .id_result_type = self.typeId(actual_ptr_ty_ref),
1523 .id_result = result_id,1522 .id_result = alloc_result_id,
1524 .storage_class = actual_storage_class,1523 .storage_class = actual_storage_class,
1524 .initializer = initializer,
1525 });1525 });
1526 if (storage_class == .Generic) {1526
1527 const casted_result_id = self.spv.allocId();1527 if (storage_class != .Generic) {
1528 try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{1528 return;
1529 .id_result_type = result_ty_id,1529 }
1530 .id_result = casted_result_id,1530
1531 .pointer = result_id,1531 // Now we need to convert the pointer.
1532 });1532 // If this is a function local, we need to perform the conversion at runtime. Otherwise, we can do
1533 return casted_result_id;1533 // it ahead of time using OpSpecConstantOp.
1534 switch (actual_storage_class) {
1535 .Function => try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{
1536 .id_result_type = self.typeId(ptr_ty_ref),
1537 .id_result = result_id,
1538 .pointer = alloc_result_id,
1539 }),
1540 else => {
1541 try section.emitRaw(self.spv.gpa, .OpSpecConstantOp, 3 + 1);
1542 section.writeOperand(IdRef, self.typeId(ptr_ty_ref));
1543 section.writeOperand(IdRef, result_id);
1544 section.writeOperand(Opcode, .OpPtrCastToGeneric);
1545 section.writeOperand(IdRef, alloc_result_id);
1546 },
1534 }1547 }
1548 }
1549
1550 fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
1551 if (self.liveness.isUnused(inst)) return null;
1552 const ty = self.air.typeOfIndex(inst);
1553 const result_ty_ref = try self.resolveType(ty, .direct);
1554 const result_id = self.spv.allocId();
1555 try self.variable(.function, result_id, result_ty_ref, null);
1535 return result_id;1556 return result_id;
1536 }1557 }
15371558
src/codegen/spirv/Module.zig+8-1
...@@ -556,9 +556,16 @@ fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct...@@ -556,9 +556,16 @@ fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct
556 }556 }
557}557}
558558
559pub fn changePtrStorageClass(self: *Module, ptr_ty_ref: Type.Ref, new_storage_class: spec.StorageClass) !Type.Ref {
560 const payload = try self.arena.create(Type.Payload.Pointer);
561 payload.* = self.typeRefType(ptr_ty_ref).payload(.pointer).*;
562 payload.storage_class = new_storage_class;
563 return try self.resolveType(Type.initPayload(&payload.base));
564}
565
559pub fn emitConstant(566pub fn emitConstant(
560 self: *Module,567 self: *Module,
561 ty_id: spec.IdRef,568 ty_id: IdRef,
562 result_id: IdRef,569 result_id: IdRef,
563 value: spec.LiteralContextDependentNumber,570 value: spec.LiteralContextDependentNumber,
564) !void {571) !void {