authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2022-11-30 22:28:35+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-04-09 01:51:51+02:00
log2a8e784989b9053ce609a38a9d384a77ce5badaa
tree1cf8dff5bb4e4049c18e86d97ca91d89158aeb64
parent8a00ec162c76ef28cbbca59a7124d7d175a77e97
signaturelock-open Commit is signed but in an unrecognized format.

spirv: introduce type/value representations

There are two main ways in which a value can be stored: "Direct", as it will be operated on as an immediate value, and "indirect", as it is stored in memory. Some types need a different representation here: Bools, for example, are opaque in SPIR-V, and so these need to have a different representation in memory. The bool operations are not easily interchangable with integer operations, though, so they need to be OpTypeBool as immediate value.

3 files changed, 132 insertions(+), 140 deletions(-)

src/codegen/spirv.zig+114-121
...@@ -133,6 +133,16 @@ pub const DeclGen = struct {...@@ -133,6 +133,16 @@ pub const DeclGen = struct {
133 class: Class,133 class: Class,
134 };134 };
135135
136 /// Data can be lowered into in two basic representations: indirect, which is when
137 /// a type is stored in memory, and direct, which is how a type is stored when its
138 /// a direct SPIR-V value.
139 const Repr = enum {
140 /// A SPIR-V value as it would be used in operations.
141 direct,
142 /// A SPIR-V value as it is stored in memory.
143 indirect,
144 };
145
136 /// Initialize the common resources of a DeclGen. Some fields are left uninitialized,146 /// Initialize the common resources of a DeclGen. Some fields are left uninitialized,
137 /// only set when `gen` is called.147 /// only set when `gen` is called.
138 pub fn init(148 pub fn init(
...@@ -215,7 +225,7 @@ pub const DeclGen = struct {...@@ -215,7 +225,7 @@ pub const DeclGen = struct {
215 /// Fetch the result-id for a previously generated instruction or constant.225 /// Fetch the result-id for a previously generated instruction or constant.
216 fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !IdRef {226 fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !IdRef {
217 if (self.air.value(inst)) |val| {227 if (self.air.value(inst)) |val| {
218 return self.genConstant(self.air.typeOf(inst), val);228 return self.genConstant(self.air.typeOf(inst), val, .direct);
219 }229 }
220 const index = Air.refToIndex(inst).?;230 const index = Air.refToIndex(inst).?;
221 return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage.231 return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage.
...@@ -329,9 +339,29 @@ pub const DeclGen = struct {...@@ -329,9 +339,29 @@ pub const DeclGen = struct {
329 };339 };
330 }340 }
331341
342 fn constInt(self: *DeclGen, ty_ref: SpvType.Ref, value: anytype) !IdRef {
343 const ty = self.spv.typeRefType(ty_ref);
344 const ty_id = self.typeId(ty_ref);
345
346 const literal: spec.LiteralContextDependentNumber = switch (ty.intSignedness()) {
347 .signed => switch (ty.intFloatBits()) {
348 1...32 => .{ .int32 = @intCast(i32, value) },
349 33...64 => .{ .int64 = @intCast(i64, value) },
350 else => unreachable, // TODO: composite integer literals
351 },
352 .unsigned => switch (ty.intFloatBits()) {
353 1...32 => .{ .uint32 = @intCast(u32, value) },
354 33...64 => .{ .uint64 = @intCast(u64, value) },
355 else => unreachable,
356 },
357 };
358
359 return try self.spv.emitConstant(ty_id, literal);
360 }
361
332 /// Generate a constant representing `val`.362 /// Generate a constant representing `val`.
333 /// TODO: Deduplication?363 /// TODO: Deduplication?
334 fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!IdRef {364 fn genConstant(self: *DeclGen, ty: Type, val: Value, repr: Repr) Error!IdRef {
335 if (ty.zigTypeTag() == .Fn) {365 if (ty.zigTypeTag() == .Fn) {
336 const fn_decl_index = switch (val.tag()) {366 const fn_decl_index = switch (val.tag()) {
337 .extern_fn => val.castTag(.extern_fn).?.data.owner_decl,367 .extern_fn => val.castTag(.extern_fn).?.data.owner_decl,
...@@ -345,56 +375,37 @@ pub const DeclGen = struct {...@@ -345,56 +375,37 @@ pub const DeclGen = struct {
345375
346 const target = self.getTarget();376 const target = self.getTarget();
347 const section = &self.spv.sections.types_globals_constants;377 const section = &self.spv.sections.types_globals_constants;
348 const result_id = self.spv.allocId();378 const result_ty_ref = try self.resolveType(ty, repr);
349 const result_type_id = try self.resolveTypeId(ty);379 const result_ty_id = self.typeId(result_ty_ref);
350380
351 if (val.isUndef()) {381 if (val.isUndef()) {
352 try section.emit(self.spv.gpa, .OpUndef, .{ .id_result_type = result_type_id, .id_result = result_id });382 const result_id = self.spv.allocId();
383 try section.emit(self.spv.gpa, .OpUndef, .{ .id_result_type = result_ty_id, .id_result = result_id });
353 return result_id;384 return result_id;
354 }385 }
355386
356 switch (ty.zigTypeTag()) {387 switch (ty.zigTypeTag()) {
357 .Int => {388 .Int => {
358 const int_info = ty.intInfo(target);389 const int_bits = if (ty.isSignedInt()) @bitCast(u64, val.toSignedInt(target)) else val.toUnsignedInt(target);
359 const backing_bits = self.backingIntBits(int_info.bits) orelse {390 return self.constInt(result_ty_ref, int_bits);
360 // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits.
361 return self.todo("implement composite int constants for {}", .{ty.fmtDebug()});
362 };
363
364 // We can just use toSignedInt/toUnsignedInt here as it returns u64 - a type large enough to hold any
365 // SPIR-V native type (up to i/u64 with Int64). If SPIR-V ever supports native ints of a larger size, this
366 // might need to be updated.
367 assert(self.largestSupportedIntBits() <= @bitSizeOf(u64));
368
369 // Note, value is required to be sign-extended, so we don't need to mask off the upper bits.
370 // See https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#Literal
371 var int_bits = if (ty.isSignedInt()) @bitCast(u64, val.toSignedInt(target)) else val.toUnsignedInt(target);
372
373 const value: spec.LiteralContextDependentNumber = switch (backing_bits) {
374 1...32 => .{ .uint32 = @truncate(u32, int_bits) },
375 33...64 => .{ .uint64 = int_bits },
376 else => unreachable,
377 };
378
379 try section.emit(self.spv.gpa, .OpConstant, .{
380 .id_result_type = result_type_id,
381 .id_result = result_id,
382 .value = value,
383 });
384 },391 },
385 .Bool => {392 .Bool => switch (repr) {
386 const operands = .{ .id_result_type = result_type_id, .id_result = result_id };393 .direct => {
387 if (val.toBool()) {394 const result_id = self.spv.allocId();
388 try section.emit(self.spv.gpa, .OpConstantTrue, operands);395 const operands = .{ .id_result_type = result_ty_id, .id_result = result_id };
389 } else {396 if (val.toBool()) {
390 try section.emit(self.spv.gpa, .OpConstantFalse, operands);397 try section.emit(self.spv.gpa, .OpConstantTrue, operands);
391 }398 } else {
399 try section.emit(self.spv.gpa, .OpConstantFalse, operands);
400 }
401 return result_id;
402 },
403 .indirect => return try self.constInt(result_ty_ref, @boolToInt(val.toBool())),
392 },404 },
393 .Float => {405 .Float => {
394 // At this point we are guaranteed that the target floating point type is supported, otherwise the function406 // At this point we are guaranteed that the target floating point type is supported, otherwise the function
395 // would have exited at resolveTypeId(ty).407 // would have exited at resolveTypeId(ty).
396408 const literal: spec.LiteralContextDependentNumber = switch (ty.floatBits(target)) {
397 const value: spec.LiteralContextDependentNumber = switch (ty.floatBits(target)) {
398 // Prevent upcasting to f32 by bitcasting and writing as a uint32.409 // Prevent upcasting to f32 by bitcasting and writing as a uint32.
399 16 => .{ .uint32 = @bitCast(u16, val.toFloat(f16)) },410 16 => .{ .uint32 = @bitCast(u16, val.toFloat(f16)) },
400 32 => .{ .float32 = val.toFloat(f32) },411 32 => .{ .float32 = val.toFloat(f32) },
...@@ -404,11 +415,7 @@ pub const DeclGen = struct {...@@ -404,11 +415,7 @@ pub const DeclGen = struct {
404 else => unreachable,415 else => unreachable,
405 };416 };
406417
407 try section.emit(self.spv.gpa, .OpConstant, .{418 return try self.spv.emitConstant(result_ty_id, literal);
408 .id_result_type = result_type_id,
409 .id_result = result_id,
410 .value = value,
411 });
412 },419 },
413 .Array => switch (val.tag()) {420 .Array => switch (val.tag()) {
414 .aggregate => { // todo: combine with Vector421 .aggregate => { // todo: combine with Vector
...@@ -417,14 +424,16 @@ pub const DeclGen = struct {...@@ -417,14 +424,16 @@ pub const DeclGen = struct {
417 const len = @intCast(u32, ty.arrayLenIncludingSentinel()); // TODO: limit spir-v to 32 bit arrays in a more elegant way.424 const len = @intCast(u32, ty.arrayLenIncludingSentinel()); // TODO: limit spir-v to 32 bit arrays in a more elegant way.
418 const constituents = try self.spv.gpa.alloc(IdRef, len);425 const constituents = try self.spv.gpa.alloc(IdRef, len);
419 defer self.spv.gpa.free(constituents);426 defer self.spv.gpa.free(constituents);
420 for (elem_vals[0..len]) |elem_val, i| {427 for (elem_vals[0..len], 0..) |elem_val, i| {
421 constituents[i] = try self.genConstant(elem_ty, elem_val);428 constituents[i] = try self.genConstant(elem_ty, elem_val, repr);
422 }429 }
430 const result_id = self.spv.allocId();
423 try section.emit(self.spv.gpa, .OpConstantComposite, .{431 try section.emit(self.spv.gpa, .OpConstantComposite, .{
424 .id_result_type = result_type_id,432 .id_result_type = result_ty_id,
425 .id_result = result_id,433 .id_result = result_id,
426 .constituents = constituents,434 .constituents = constituents,
427 });435 });
436 return result_id;
428 },437 },
429 .repeated => {438 .repeated => {
430 const elem_val = val.castTag(.repeated).?.data;439 const elem_val = val.castTag(.repeated).?.data;
...@@ -433,18 +442,20 @@ pub const DeclGen = struct {...@@ -433,18 +442,20 @@ pub const DeclGen = struct {
433 const constituents = try self.spv.gpa.alloc(IdRef, len);442 const constituents = try self.spv.gpa.alloc(IdRef, len);
434 defer self.spv.gpa.free(constituents);443 defer self.spv.gpa.free(constituents);
435444
436 const elem_val_id = try self.genConstant(elem_ty, elem_val);445 const elem_val_id = try self.genConstant(elem_ty, elem_val, repr);
437 for (constituents[0..len]) |*elem| {446 for (constituents[0..len]) |*elem| {
438 elem.* = elem_val_id;447 elem.* = elem_val_id;
439 }448 }
440 if (ty.sentinel()) |sentinel| {449 if (ty.sentinel()) |sentinel| {
441 constituents[len] = try self.genConstant(elem_ty, sentinel);450 constituents[len] = try self.genConstant(elem_ty, sentinel, repr);
442 }451 }
452 const result_id = self.spv.allocId();
443 try section.emit(self.spv.gpa, .OpConstantComposite, .{453 try section.emit(self.spv.gpa, .OpConstantComposite, .{
444 .id_result_type = result_type_id,454 .id_result_type = result_ty_id,
445 .id_result = result_id,455 .id_result = result_id,
446 .constituents = constituents,456 .constituents = constituents,
447 });457 });
458 return result_id;
448 },459 },
449 else => return self.todo("array constant with tag {s}", .{@tagName(val.tag())}),460 else => return self.todo("array constant with tag {s}", .{@tagName(val.tag())}),
450 },461 },
...@@ -457,39 +468,22 @@ pub const DeclGen = struct {...@@ -457,39 +468,22 @@ pub const DeclGen = struct {
457 const elem_refs = try self.gpa.alloc(IdRef, vector_len);468 const elem_refs = try self.gpa.alloc(IdRef, vector_len);
458 defer self.gpa.free(elem_refs);469 defer self.gpa.free(elem_refs);
459 for (elem_refs, 0..) |*elem, i| {470 for (elem_refs, 0..) |*elem, i| {
460 elem.* = try self.genConstant(elem_ty, elem_vals[i]);471 elem.* = try self.genConstant(elem_ty, elem_vals[i], repr);
461 }472 }
473 const result_id = self.spv.allocId();
462 try section.emit(self.spv.gpa, .OpConstantComposite, .{474 try section.emit(self.spv.gpa, .OpConstantComposite, .{
463 .id_result_type = result_type_id,475 .id_result_type = result_ty_id,
464 .id_result = result_id,476 .id_result = result_id,
465 .constituents = elem_refs,477 .constituents = elem_refs,
466 });478 });
479 return result_id;
467 },480 },
468 else => return self.todo("vector constant with tag {s}", .{@tagName(val.tag())}),481 else => return self.todo("vector constant with tag {s}", .{@tagName(val.tag())}),
469 },482 },
470 .Enum => {483 .Enum => {
471 var ty_buffer: Type.Payload.Bits = undefined;
472 const int_ty = ty.intTagType(&ty_buffer);
473 const int_info = int_ty.intInfo(target);
474
475 const backing_bits = self.backingIntBits(int_info.bits) orelse {
476 return self.todo("implement composite int constants for {}", .{int_ty.fmtDebug()});
477 };
478
479 var int_buffer: Value.Payload.U64 = undefined;484 var int_buffer: Value.Payload.U64 = undefined;
480 const int_val = val.enumToInt(ty, &int_buffer).toUnsignedInt(target); // TODO: composite integer constants485 const int_val = val.enumToInt(ty, &int_buffer).toUnsignedInt(target); // TODO: composite integer constants
481486 return self.constInt(result_ty_ref, int_val);
482 const value: spec.LiteralContextDependentNumber = switch (backing_bits) {
483 1...32 => .{ .uint32 = @truncate(u32, int_val) },
484 33...64 => .{ .uint64 = int_val },
485 else => unreachable,
486 };
487
488 try section.emit(self.spv.gpa, .OpConstant, .{
489 .id_result_type = result_type_id,
490 .id_result = result_id,
491 .value = value,
492 });
493 },487 },
494 .Struct => {488 .Struct => {
495 const constituents = if (ty.isSimpleTupleOrAnonStruct()) blk: {489 const constituents = if (ty.isSimpleTupleOrAnonStruct()) blk: {
...@@ -498,10 +492,10 @@ pub const DeclGen = struct {...@@ -498,10 +492,10 @@ pub const DeclGen = struct {
498 errdefer self.spv.gpa.free(constituents);492 errdefer self.spv.gpa.free(constituents);
499493
500 var member_index: usize = 0;494 var member_index: usize = 0;
501 for (tuple.types) |field_ty, i| {495 for (tuple.types, 0..) |field_ty, i| {
502 const field_val = tuple.values[i];496 const field_val = tuple.values[i];
503 if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue;497 if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue;
504 constituents[member_index] = try self.genConstant(field_ty, field_val);498 constituents[member_index] = try self.genConstant(field_ty, field_val, repr);
505 member_index += 1;499 member_index += 1;
506 }500 }
507501
...@@ -517,9 +511,9 @@ pub const DeclGen = struct {...@@ -517,9 +511,9 @@ pub const DeclGen = struct {
517 const constituents = try self.spv.gpa.alloc(IdRef, struct_ty.fields.count());511 const constituents = try self.spv.gpa.alloc(IdRef, struct_ty.fields.count());
518 errdefer self.spv.gpa.free(constituents);512 errdefer self.spv.gpa.free(constituents);
519 var member_index: usize = 0;513 var member_index: usize = 0;
520 for (struct_ty.fields.values()) |field, i| {514 for (struct_ty.fields.values(), 0..) |field, i| {
521 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;515 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
522 constituents[member_index] = try self.genConstant(field.ty, field_vals[i]);516 constituents[member_index] = try self.genConstant(field.ty, field_vals[i], repr);
523 member_index += 1;517 member_index += 1;
524 }518 }
525519
...@@ -527,24 +521,28 @@ pub const DeclGen = struct {...@@ -527,24 +521,28 @@ pub const DeclGen = struct {
527 };521 };
528 defer self.spv.gpa.free(constituents);522 defer self.spv.gpa.free(constituents);
529523
524 const result_id = self.spv.allocId();
530 try section.emit(self.spv.gpa, .OpConstantComposite, .{525 try section.emit(self.spv.gpa, .OpConstantComposite, .{
531 .id_result_type = result_type_id,526 .id_result_type = result_ty_id,
532 .id_result = result_id,527 .id_result = result_id,
533 .constituents = constituents,528 .constituents = constituents,
534 });529 });
530 return result_id;
535 },531 },
536 .Void => unreachable,532 .Void => unreachable,
537 .Fn => unreachable,533 .Fn => unreachable,
538 else => return self.todo("constant generation of type {s}: {}", .{ @tagName(ty.zigTypeTag()), ty.fmtDebug() }),534 else => return self.todo("constant generation of type {s}: {}", .{ @tagName(ty.zigTypeTag()), ty.fmtDebug() }),
539 }535 }
540
541 return result_id;
542 }536 }
543537
544 /// Turn a Zig type into a SPIR-V Type, and return its type result-id.538 /// Turn a Zig type into a SPIR-V Type, and return its type result-id.
545 fn resolveTypeId(self: *DeclGen, ty: Type) !IdResultType {539 fn resolveTypeId(self: *DeclGen, ty: Type) !IdResultType {
546 const type_ref = try self.resolveType(ty);540 const type_ref = try self.resolveType(ty, .direct);
547 return self.spv.typeResultId(type_ref);541 return self.typeId(type_ref);
542 }
543
544 fn typeId(self: *DeclGen, ty_ref: SpvType.Ref) IdRef {
545 return self.spv.typeId(ty_ref);
548 }546 }
549547
550 /// Create an integer type suitable for storing at least 'bits' bits.548 /// Create an integer type suitable for storing at least 'bits' bits.
...@@ -576,19 +574,20 @@ pub const DeclGen = struct {...@@ -576,19 +574,20 @@ pub const DeclGen = struct {
576574
577 fn simpleStructTypeId(self: *DeclGen, members: []const SpvType.Payload.Struct.Member) !IdResultType {575 fn simpleStructTypeId(self: *DeclGen, members: []const SpvType.Payload.Struct.Member) !IdResultType {
578 const type_ref = try self.simpleStructType(members);576 const type_ref = try self.simpleStructType(members);
579 return self.spv.typeResultId(type_ref);577 return self.typeId(type_ref);
580 }578 }
581579
582 /// Turn a Zig type into a SPIR-V Type, and return a reference to it.580 /// Turn a Zig type into a SPIR-V Type, and return a reference to it.
583 fn resolveType(self: *DeclGen, ty: Type) Error!SpvType.Ref {581 fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!SpvType.Ref {
584 const target = self.getTarget();582 const target = self.getTarget();
585 switch (ty.zigTypeTag()) {583 switch (ty.zigTypeTag()) {
586 .Void, .NoReturn => return try self.spv.resolveType(SpvType.initTag(.void)),584 .Void, .NoReturn => return try self.spv.resolveType(SpvType.initTag(.void)),
587 .Bool => {585 .Bool => switch (repr) {
586 .direct => return try self.spv.resolveType(SpvType.initTag(.bool)),
588 // SPIR-V booleans are opaque, which is fine for operations, but they cant be stored.587 // SPIR-V booleans are opaque, which is fine for operations, but they cant be stored.
589 // This function returns the *stored* type, for values directly we convert this into a bool when588 // This function returns the *stored* type, for values directly we convert this into a bool when
590 // it is loaded, and convert it back to this type when stored.589 // it is loaded, and convert it back to this type when stored.
591 return try self.intType(.unsigned, 1);590 .indirect => return try self.intType(.unsigned, 1),
592 },591 },
593 .Int => {592 .Int => {
594 const int_info = ty.intInfo(target);593 const int_info = ty.intInfo(target);
...@@ -596,9 +595,8 @@ pub const DeclGen = struct {...@@ -596,9 +595,8 @@ pub const DeclGen = struct {
596 },595 },
597 .Enum => {596 .Enum => {
598 var buffer: Type.Payload.Bits = undefined;597 var buffer: Type.Payload.Bits = undefined;
599 const int_ty = ty.intTagType(&buffer);598 const tag_ty = ty.intTagType(&buffer);
600 const int_info = int_ty.intInfo(target);599 return self.resolveType(tag_ty, repr);
601 return try self.intType(.unsigned, int_info.bits);
602 },600 },
603 .Float => {601 .Float => {
604 // We can (and want) not really emulate floating points with other floating point types like with the integer types,602 // We can (and want) not really emulate floating points with other floating point types like with the integer types,
...@@ -626,7 +624,7 @@ pub const DeclGen = struct {...@@ -626,7 +624,7 @@ pub const DeclGen = struct {
626624
627 const payload = try self.spv.arena.create(SpvType.Payload.Array);625 const payload = try self.spv.arena.create(SpvType.Payload.Array);
628 payload.* = .{626 payload.* = .{
629 .element_type = try self.resolveType(elem_ty),627 .element_type = try self.resolveType(elem_ty, repr),
630 .length = total_len,628 .length = total_len,
631 };629 };
632 return try self.spv.resolveType(SpvType.initPayload(&payload.base));630 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
...@@ -636,12 +634,14 @@ pub const DeclGen = struct {...@@ -636,12 +634,14 @@ pub const DeclGen = struct {
636 if (ty.fnIsVarArgs())634 if (ty.fnIsVarArgs())
637 return self.fail("VarArgs functions are unsupported for SPIR-V", .{});635 return self.fail("VarArgs functions are unsupported for SPIR-V", .{});
638636
637 // TODO: Parameter passing convention etc.
638
639 const param_types = try self.spv.arena.alloc(SpvType.Ref, ty.fnParamLen());639 const param_types = try self.spv.arena.alloc(SpvType.Ref, ty.fnParamLen());
640 for (param_types, 0..) |*param, i| {640 for (param_types, 0..) |*param, i| {
641 param.* = try self.resolveType(ty.fnParamType(i));641 param.* = try self.resolveType(ty.fnParamType(i), .direct);
642 }642 }
643643
644 const return_type = try self.resolveType(ty.fnReturnType());644 const return_type = try self.resolveType(ty.fnReturnType(), .direct);
645645
646 const payload = try self.spv.arena.create(SpvType.Payload.Function);646 const payload = try self.spv.arena.create(SpvType.Payload.Function);
647 payload.* = .{ .return_type = return_type, .parameters = param_types };647 payload.* = .{ .return_type = return_type, .parameters = param_types };
...@@ -653,7 +653,7 @@ pub const DeclGen = struct {...@@ -653,7 +653,7 @@ pub const DeclGen = struct {
653 const ptr_payload = try self.spv.arena.create(SpvType.Payload.Pointer);653 const ptr_payload = try self.spv.arena.create(SpvType.Payload.Pointer);
654 ptr_payload.* = .{654 ptr_payload.* = .{
655 .storage_class = spirvStorageClass(ptr_info.@"addrspace"),655 .storage_class = spirvStorageClass(ptr_info.@"addrspace"),
656 .child_type = try self.resolveType(ptr_info.pointee_type),656 .child_type = try self.resolveType(ptr_info.pointee_type, .indirect),
657 // Note: only available in Kernels!657 // Note: only available in Kernels!
658 .alignment = ty.ptrAlignment(target) * 8,658 .alignment = ty.ptrAlignment(target) * 8,
659 };659 };
...@@ -680,7 +680,7 @@ pub const DeclGen = struct {...@@ -680,7 +680,7 @@ pub const DeclGen = struct {
680680
681 const payload = try self.spv.arena.create(SpvType.Payload.Vector);681 const payload = try self.spv.arena.create(SpvType.Payload.Vector);
682 payload.* = .{682 payload.* = .{
683 .component_type = try self.resolveType(ty.elemType()),683 .component_type = try self.resolveType(ty.elemType(), repr),
684 .component_count = @intCast(u32, ty.vectorLen()),684 .component_count = @intCast(u32, ty.vectorLen()),
685 };685 };
686 return try self.spv.resolveType(SpvType.initPayload(&payload.base));686 return try self.spv.resolveType(SpvType.initPayload(&payload.base));
...@@ -690,11 +690,11 @@ pub const DeclGen = struct {...@@ -690,11 +690,11 @@ pub const DeclGen = struct {
690 const tuple = ty.tupleFields();690 const tuple = ty.tupleFields();
691 const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, tuple.types.len);691 const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, tuple.types.len);
692 var member_index: u32 = 0;692 var member_index: u32 = 0;
693 for (tuple.types) |field_ty, i| {693 for (tuple.types, 0..) |field_ty, i| {
694 const field_val = tuple.values[i];694 const field_val = tuple.values[i];
695 if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBitsIgnoreComptime()) continue;695 if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBitsIgnoreComptime()) continue;
696 members[member_index] = .{696 members[member_index] = .{
697 .ty = try self.resolveType(field_ty),697 .ty = try self.resolveType(field_ty, repr),
698 };698 };
699 member_index += 1;699 member_index += 1;
700 }700 }
...@@ -708,16 +708,16 @@ pub const DeclGen = struct {...@@ -708,16 +708,16 @@ pub const DeclGen = struct {
708 const struct_ty = ty.castTag(.@"struct").?.data;708 const struct_ty = ty.castTag(.@"struct").?.data;
709709
710 if (struct_ty.layout == .Packed) {710 if (struct_ty.layout == .Packed) {
711 return try self.resolveType(struct_ty.backing_int_ty);711 return try self.resolveType(struct_ty.backing_int_ty, repr);
712 }712 }
713713
714 const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count());714 const members = try self.spv.arena.alloc(SpvType.Payload.Struct.Member, struct_ty.fields.count());
715 var member_index: usize = 0;715 var member_index: usize = 0;
716 for (struct_ty.fields.values()) |field, i| {716 for (struct_ty.fields.values(), 0..) |field, i| {
717 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;717 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
718718
719 members[member_index] = .{719 members[member_index] = .{
720 .ty = try self.resolveType(field.ty),720 .ty = try self.resolveType(field.ty, repr),
721 .name = struct_ty.fields.keys()[i],721 .name = struct_ty.fields.keys()[i],
722 };722 };
723 member_index += 1;723 member_index += 1;
...@@ -957,21 +957,14 @@ pub const DeclGen = struct {...@@ -957,21 +957,14 @@ pub const DeclGen = struct {
957 return result_id;957 return result_id;
958 }958 }
959959
960 fn maskStrangeInt(self: *DeclGen, ty_id: IdResultType, int_id: IdRef, bits: u16) !IdRef {960 fn maskStrangeInt(self: *DeclGen, ty_ref: SpvType.Ref, value_id: IdRef, bits: u16) !IdRef {
961 const backing_bits = self.backingIntBits(bits).?;
962 const mask_value = if (bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @intCast(u6, bits)) - 1;961 const mask_value = if (bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @intCast(u6, bits)) - 1;
963 const mask_lit: spec.LiteralContextDependentNumber = switch (backing_bits) {
964 1...32 => .{ .uint32 = @truncate(u32, mask_value) },
965 33...64 => .{ .uint64 = mask_value },
966 else => unreachable,
967 };
968 // TODO: We should probably optimize the amount of these constants a bit.
969 const mask_id = try self.spv.emitConstant(ty_id, mask_lit);
970 const result_id = self.spv.allocId();962 const result_id = self.spv.allocId();
963 const mask_id = try self.constInt(ty_ref, mask_value);
971 try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{964 try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{
972 .id_result_type = ty_id,965 .id_result_type = self.typeId(ty_ref),
973 .id_result = result_id,966 .id_result = result_id,
974 .operand_1 = int_id,967 .operand_1 = value_id,
975 .operand_2 = mask_id,968 .operand_2 = mask_id,
976 });969 });
977 return result_id;970 return result_id;
...@@ -994,8 +987,7 @@ pub const DeclGen = struct {...@@ -994,8 +987,7 @@ pub const DeclGen = struct {
994 var lhs_id = try self.resolve(bin_op.lhs);987 var lhs_id = try self.resolve(bin_op.lhs);
995 var rhs_id = try self.resolve(bin_op.rhs);988 var rhs_id = try self.resolve(bin_op.rhs);
996989
997 const result_id = self.spv.allocId();990 const result_ty_ref = try self.resolveType(ty, .direct);
998 const result_type_id = try self.resolveTypeId(ty);
999991
1000 assert(self.air.typeOf(bin_op.lhs).eql(ty, self.module));992 assert(self.air.typeOf(bin_op.lhs).eql(ty, self.module));
1001 assert(self.air.typeOf(bin_op.rhs).eql(ty, self.module));993 assert(self.air.typeOf(bin_op.rhs).eql(ty, self.module));
...@@ -1010,8 +1002,8 @@ pub const DeclGen = struct {...@@ -1010,8 +1002,8 @@ pub const DeclGen = struct {
1010 },1002 },
1011 .strange_integer => blk: {1003 .strange_integer => blk: {
1012 if (!modular) {1004 if (!modular) {
1013 lhs_id = try self.maskStrangeInt(result_type_id, lhs_id, info.bits);1005 lhs_id = try self.maskStrangeInt(result_ty_ref, lhs_id, info.bits);
1014 rhs_id = try self.maskStrangeInt(result_type_id, rhs_id, info.bits);1006 rhs_id = try self.maskStrangeInt(result_ty_ref, rhs_id, info.bits);
1015 }1007 }
1016 break :blk switch (info.signedness) {1008 break :blk switch (info.signedness) {
1017 .signed => @as(usize, 1),1009 .signed => @as(usize, 1),
...@@ -1026,8 +1018,9 @@ pub const DeclGen = struct {...@@ -1026,8 +1018,9 @@ pub const DeclGen = struct {
1026 .bool => unreachable,1018 .bool => unreachable,
1027 };1019 };
10281020
1021 const result_id = self.spv.allocId();
1029 const operands = .{1022 const operands = .{
1030 .id_result_type = result_type_id,1023 .id_result_type = self.typeId(result_ty_ref),
1031 .id_result = result_id,1024 .id_result = result_id,
1032 .operand_1 = lhs_id,1025 .operand_1 = lhs_id,
1033 .operand_2 = rhs_id,1026 .operand_2 = rhs_id,
...@@ -1068,7 +1061,7 @@ pub const DeclGen = struct {...@@ -1068,7 +1061,7 @@ pub const DeclGen = struct {
1068 const result_type_id = try self.resolveTypeId(result_ty);1061 const result_type_id = try self.resolveTypeId(result_ty);
10691062
1070 const overflow_member_ty = try self.intType(.unsigned, info.bits);1063 const overflow_member_ty = try self.intType(.unsigned, info.bits);
1071 const overflow_member_ty_id = self.spv.typeResultId(overflow_member_ty);1064 const overflow_member_ty_id = self.typeId(overflow_member_ty);
10721065
1073 const op_result_id = blk: {1066 const op_result_id = blk: {
1074 // Construct the SPIR-V result type.1067 // Construct the SPIR-V result type.
...@@ -1181,9 +1174,9 @@ pub const DeclGen = struct {...@@ -1181,9 +1174,9 @@ pub const DeclGen = struct {
1181 .float => 0,1174 .float => 0,
1182 .bool => 1,1175 .bool => 1,
1183 .strange_integer => blk: {1176 .strange_integer => blk: {
1184 const op_ty_id = try self.resolveTypeId(op_ty);1177 const op_ty_ref = try self.resolveType(op_ty, .direct);
1185 lhs_id = try self.maskStrangeInt(op_ty_id, lhs_id, info.bits);1178 lhs_id = try self.maskStrangeInt(op_ty_ref, lhs_id, info.bits);
1186 rhs_id = try self.maskStrangeInt(op_ty_id, rhs_id, info.bits);1179 rhs_id = try self.maskStrangeInt(op_ty_ref, rhs_id, info.bits);
1187 break :blk switch (info.signedness) {1180 break :blk switch (info.signedness) {
1188 .signed => @as(usize, 1),1181 .signed => @as(usize, 1),
1189 .unsigned => @as(usize, 2),1182 .unsigned => @as(usize, 2),
...@@ -1425,7 +1418,7 @@ pub const DeclGen = struct {...@@ -1425,7 +1418,7 @@ pub const DeclGen = struct {
1425 .Struct => switch (object_ty.containerLayout()) {1418 .Struct => switch (object_ty.containerLayout()) {
1426 .Packed => unreachable, // TODO1419 .Packed => unreachable, // TODO
1427 else => {1420 else => {
1428 const u32_ty_id = self.spv.typeResultId(try self.intType(.unsigned, 32));1421 const u32_ty_id = self.typeId(try self.intType(.unsigned, 32));
1429 const field_index_id = try self.spv.emitConstant(u32_ty_id, .{ .uint32 = field_index });1422 const field_index_id = try self.spv.emitConstant(u32_ty_id, .{ .uint32 = field_index });
1430 const result_id = self.spv.allocId();1423 const result_id = self.spv.allocId();
1431 const result_type_id = try self.resolveTypeId(result_ptr_ty);1424 const result_type_id = try self.resolveTypeId(result_ptr_ty);
...@@ -1740,7 +1733,7 @@ pub const DeclGen = struct {...@@ -1740,7 +1733,7 @@ pub const DeclGen = struct {
1740 return self.todo("switch on runtime value???", .{});1733 return self.todo("switch on runtime value???", .{});
1741 };1734 };
1742 const int_val = switch (cond_ty.zigTypeTag()) {1735 const int_val = switch (cond_ty.zigTypeTag()) {
1743 .Int => if (cond_ty.isSignedInt()) @bitCast(u64, value.toSignedInt()) else value.toUnsignedInt(target),1736 .Int => if (cond_ty.isSignedInt()) @bitCast(u64, value.toSignedInt(target)) else value.toUnsignedInt(target),
1744 .Enum => blk: {1737 .Enum => blk: {
1745 var int_buffer: Value.Payload.U64 = undefined;1738 var int_buffer: Value.Payload.U64 = undefined;
1746 // TODO: figure out of cond_ty is correct (something with enum literals)1739 // TODO: figure out of cond_ty is correct (something with enum literals)
src/codegen/spirv/Assembler.zig+1-1
...@@ -135,7 +135,7 @@ const AsmValue = union(enum) {...@@ -135,7 +135,7 @@ const AsmValue = union(enum) {
135 return switch (self) {135 return switch (self) {
136 .just_declared, .unresolved_forward_reference => unreachable,136 .just_declared, .unresolved_forward_reference => unreachable,
137 .value => |result| result,137 .value => |result| result,
138 .ty => |ref| spv.typeResultId(ref),138 .ty => |ref| spv.typeId(ref),
139 };139 };
140 }140 }
141};141};
src/codegen/spirv/Module.zig+17-18
...@@ -228,18 +228,17 @@ pub fn resolveType(self: *Module, ty: Type) !Type.Ref {...@@ -228,18 +228,17 @@ pub fn resolveType(self: *Module, ty: Type) !Type.Ref {
228}228}
229229
230pub fn resolveTypeId(self: *Module, ty: Type) !IdResultType {230pub fn resolveTypeId(self: *Module, ty: Type) !IdResultType {
231 const type_ref = try self.resolveType(ty);231 const ty_ref = try self.resolveType(ty);
232 return self.typeResultId(type_ref);232 return self.typeId(ty_ref);
233}233}
234234
235/// Get the result-id of a particular type, by reference. Asserts type_ref is valid.235pub fn typeRefType(self: Module, ty_ref: Type.Ref) Type {
236pub fn typeResultId(self: Module, type_ref: Type.Ref) IdResultType {236 return self.type_cache.keys()[@enumToInt(ty_ref)];
237 return self.type_cache.values()[@enumToInt(type_ref)];
238}237}
239238
240/// Get the result-id of a particular type as IdRef, by Type.Ref. Asserts type_ref is valid.239/// Get the result-id of a particular type, by reference. Asserts type_ref is valid.
241pub fn typeRefId(self: Module, type_ref: Type.Ref) IdRef {240pub fn typeId(self: Module, ty_ref: Type.Ref) IdResultType {
242 return self.type_cache.values()[@enumToInt(type_ref)];241 return self.type_cache.values()[@enumToInt(ty_ref)];
243}242}
244243
245/// Unconditionally emit a spir-v type into the appropriate section.244/// Unconditionally emit a spir-v type into the appropriate section.
...@@ -321,19 +320,19 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {...@@ -321,19 +320,19 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {
321 },320 },
322 .vector => try types.emit(self.gpa, .OpTypeVector, .{321 .vector => try types.emit(self.gpa, .OpTypeVector, .{
323 .id_result = result_id,322 .id_result = result_id,
324 .component_type = self.typeResultId(ty.childType()),323 .component_type = self.typeId(ty.childType()),
325 .component_count = ty.payload(.vector).component_count,324 .component_count = ty.payload(.vector).component_count,
326 }),325 }),
327 .matrix => try types.emit(self.gpa, .OpTypeMatrix, .{326 .matrix => try types.emit(self.gpa, .OpTypeMatrix, .{
328 .id_result = result_id,327 .id_result = result_id,
329 .column_type = self.typeResultId(ty.childType()),328 .column_type = self.typeId(ty.childType()),
330 .column_count = ty.payload(.matrix).column_count,329 .column_count = ty.payload(.matrix).column_count,
331 }),330 }),
332 .image => {331 .image => {
333 const info = ty.payload(.image);332 const info = ty.payload(.image);
334 try types.emit(self.gpa, .OpTypeImage, .{333 try types.emit(self.gpa, .OpTypeImage, .{
335 .id_result = result_id,334 .id_result = result_id,
336 .sampled_type = self.typeResultId(ty.childType()),335 .sampled_type = self.typeId(ty.childType()),
337 .dim = info.dim,336 .dim = info.dim,
338 .depth = @enumToInt(info.depth),337 .depth = @enumToInt(info.depth),
339 .arrayed = @boolToInt(info.arrayed),338 .arrayed = @boolToInt(info.arrayed),
...@@ -346,7 +345,7 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {...@@ -346,7 +345,7 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {
346 .sampler => try types.emit(self.gpa, .OpTypeSampler, result_id_operand),345 .sampler => try types.emit(self.gpa, .OpTypeSampler, result_id_operand),
347 .sampled_image => try types.emit(self.gpa, .OpTypeSampledImage, .{346 .sampled_image => try types.emit(self.gpa, .OpTypeSampledImage, .{
348 .id_result = result_id,347 .id_result = result_id,
349 .image_type = self.typeResultId(ty.childType()),348 .image_type = self.typeId(ty.childType()),
350 }),349 }),
351 .array => {350 .array => {
352 const info = ty.payload(.array);351 const info = ty.payload(.array);
...@@ -358,7 +357,7 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {...@@ -358,7 +357,7 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {
358357
359 try types.emit(self.gpa, .OpTypeArray, .{358 try types.emit(self.gpa, .OpTypeArray, .{
360 .id_result = result_id,359 .id_result = result_id,
361 .element_type = self.typeResultId(ty.childType()),360 .element_type = self.typeId(ty.childType()),
362 .length = length_id,361 .length = length_id,
363 });362 });
364 if (info.array_stride != 0) {363 if (info.array_stride != 0) {
...@@ -369,7 +368,7 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {...@@ -369,7 +368,7 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {
369 const info = ty.payload(.runtime_array);368 const info = ty.payload(.runtime_array);
370 try types.emit(self.gpa, .OpTypeRuntimeArray, .{369 try types.emit(self.gpa, .OpTypeRuntimeArray, .{
371 .id_result = result_id,370 .id_result = result_id,
372 .element_type = self.typeResultId(ty.childType()),371 .element_type = self.typeId(ty.childType()),
373 });372 });
374 if (info.array_stride != 0) {373 if (info.array_stride != 0) {
375 try self.decorate(ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } });374 try self.decorate(ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } });
...@@ -380,7 +379,7 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {...@@ -380,7 +379,7 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {
380 try types.emitRaw(self.gpa, .OpTypeStruct, 1 + info.members.len);379 try types.emitRaw(self.gpa, .OpTypeStruct, 1 + info.members.len);
381 types.writeOperand(IdResult, result_id);380 types.writeOperand(IdResult, result_id);
382 for (info.members) |member| {381 for (info.members) |member| {
383 types.writeOperand(IdRef, self.typeResultId(member.ty));382 types.writeOperand(IdRef, self.typeId(member.ty));
384 }383 }
385 try self.decorateStruct(ref_id, info);384 try self.decorateStruct(ref_id, info);
386 },385 },
...@@ -393,7 +392,7 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {...@@ -393,7 +392,7 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {
393 try types.emit(self.gpa, .OpTypePointer, .{392 try types.emit(self.gpa, .OpTypePointer, .{
394 .id_result = result_id,393 .id_result = result_id,
395 .storage_class = info.storage_class,394 .storage_class = info.storage_class,
396 .type = self.typeResultId(ty.childType()),395 .type = self.typeId(ty.childType()),
397 });396 });
398 if (info.array_stride != 0) {397 if (info.array_stride != 0) {
399 try self.decorate(ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } });398 try self.decorate(ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } });
...@@ -409,9 +408,9 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {...@@ -409,9 +408,9 @@ pub fn emitType(self: *Module, ty: Type) error{OutOfMemory}!IdResultType {
409 const info = ty.payload(.function);408 const info = ty.payload(.function);
410 try types.emitRaw(self.gpa, .OpTypeFunction, 2 + info.parameters.len);409 try types.emitRaw(self.gpa, .OpTypeFunction, 2 + info.parameters.len);
411 types.writeOperand(IdResult, result_id);410 types.writeOperand(IdResult, result_id);
412 types.writeOperand(IdRef, self.typeResultId(info.return_type));411 types.writeOperand(IdRef, self.typeId(info.return_type));
413 for (info.parameters) |parameter_type| {412 for (info.parameters) |parameter_type| {
414 types.writeOperand(IdRef, self.typeResultId(parameter_type));413 types.writeOperand(IdRef, self.typeId(parameter_type));
415 }414 }
416 },415 },
417 .event => try types.emit(self.gpa, .OpTypeEvent, result_id_operand),416 .event => try types.emit(self.gpa, .OpTypeEvent, result_id_operand),