| ... | @@ -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 | } |
| 240 | | 240 | |
| 241 | return try self.constant(ty, val, .direct); | 241 | return try self.constant(ty, val); |
| 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. |
| ... | @@ -404,320 +404,493 @@ pub const DeclGen = struct { | ... | @@ -404,320 +404,493 @@ pub const DeclGen = struct { |
| 404 | return result_id; | 404 | return result_id; |
| 405 | } | 405 | } |
| 406 | | 406 | |
| 407 | fn constant(self: *DeclGen, ty: Type, val: Value, repr: Repr) Error!IdRef { | 407 | const IndirectConstantLowering = struct { |
| 408 | const result_id = self.spv.allocId(); | 408 | const undef = 0xAA; |
| 409 | try self.genConstant(result_id, ty, val, repr); | 409 | |
| 410 | return result_id; | 410 | dg: *DeclGen, |
| 411 | } | 411 | /// Cached reference of the u32 type. |
| | 412 | u32_ty_ref: SpvType.Ref, |
| | 413 | /// Cached type id of the u32 type. |
| | 414 | u32_ty_id: IdRef, |
| | 415 | /// The members of the resulting structure type |
| | 416 | members: std.ArrayList(SpvType.Payload.Struct.Member), |
| | 417 | /// The initializers of each of the members. |
| | 418 | initializers: std.ArrayList(IdRef), |
| | 419 | /// The current size of the structure. Includes |
| | 420 | /// the bytes in partial_word. |
| | 421 | size: u32 = 0, |
| | 422 | /// The partially filled last constant. |
| | 423 | /// If full, its flushed. |
| | 424 | partial_word: std.BoundedArray(u8, @sizeOf(Word)) = .{}, |
| | 425 | |
| | 426 | /// Flush the partial_word to the members. If the partial_word is not |
| | 427 | /// filled, this adds padding bytes (which are undefined). |
| | 428 | fn flush(self: *@This()) !void { |
| | 429 | if (self.partial_word.len == 0) { |
| | 430 | // No need to add it there. |
| | 431 | return; |
| | 432 | } |
| 412 | | 433 | |
| 413 | /// Generate a constant representing `val`. | 434 | for (self.partial_word.unusedCapacitySlice()) |*unused| { |
| 414 | /// TODO: Deduplication? | 435 | // TODO: Perhaps we should generate OpUndef for these bytes? |
| 415 | fn genConstant(self: *DeclGen, result_id: IdRef, ty: Type, val: Value, repr: Repr) Error!void { | 436 | unused.* = undef; |
| 416 | const target = self.getTarget(); | 437 | } |
| 417 | const section = &self.spv.sections.types_globals_constants; | | |
| 418 | const result_ty_ref = try self.resolveType(ty, repr); | | |
| 419 | const result_ty_id = self.typeId(result_ty_ref); | | |
| 420 | | 438 | |
| 421 | log.debug("genConstant: ty = {}, val = {}", .{ ty.fmtDebug(), val.fmtDebug() }); | 439 | const word = @bitCast(Word, self.partial_word.buffer); |
| | 440 | const result_id = self.dg.spv.allocId(); |
| | 441 | try self.dg.spv.emitConstant(self.u32_ty_id, result_id, .{ .uint32 = word }); |
| | 442 | try self.members.append(.{ .ty = self.u32_ty_ref }); |
| | 443 | try self.initializers.append(result_id); |
| 422 | | 444 | |
| 423 | if (val.isUndef()) { | 445 | self.partial_word.len = 0; |
| 424 | try section.emit(self.spv.gpa, .OpUndef, .{ .id_result_type = result_ty_id, .id_result = result_id }); | 446 | self.size = std.mem.alignForwardGeneric(u32, self.size, @sizeOf(Word)); |
| 425 | } | 447 | } |
| 426 | | 448 | |
| 427 | switch (ty.zigTypeTag()) { | 449 | /// Fill the buffer with undefined values until the size is aligned to `align`. |
| 428 | .Int => { | 450 | fn fillToAlign(self: *@This(), alignment: u32) !void { |
| 429 | const int_bits = if (ty.isSignedInt()) @bitCast(u64, val.toSignedInt(target)) else val.toUnsignedInt(target); | 451 | const target_size = std.mem.alignForwardGeneric(u32, self.size, alignment); |
| 430 | try self.genConstInt(result_ty_ref, result_id, int_bits); | 452 | try self.addUndef(target_size - self.size); |
| 431 | }, | 453 | } |
| 432 | .Bool => switch (repr) { | | |
| 433 | .direct => { | | |
| 434 | const operands = .{ .id_result_type = result_ty_id, .id_result = result_id }; | | |
| 435 | if (val.toBool()) { | | |
| 436 | try section.emit(self.spv.gpa, .OpConstantTrue, operands); | | |
| 437 | } else { | | |
| 438 | try section.emit(self.spv.gpa, .OpConstantFalse, operands); | | |
| 439 | } | | |
| 440 | }, | | |
| 441 | .indirect => try self.genConstInt(result_ty_ref, result_id, @boolToInt(val.toBool())), | | |
| 442 | }, | | |
| 443 | .Float => { | | |
| 444 | // At this point we are guaranteed that the target floating point type is supported, otherwise the function | | |
| 445 | // would have exited at resolveTypeId(ty). | | |
| 446 | const literal: spec.LiteralContextDependentNumber = switch (ty.floatBits(target)) { | | |
| 447 | // Prevent upcasting to f32 by bitcasting and writing as a uint32. | | |
| 448 | 16 => .{ .uint32 = @bitCast(u16, val.toFloat(f16)) }, | | |
| 449 | 32 => .{ .float32 = val.toFloat(f32) }, | | |
| 450 | 64 => .{ .float64 = val.toFloat(f64) }, | | |
| 451 | 128 => unreachable, // Filtered out in the call to resolveTypeId. | | |
| 452 | // TODO: Insert case for long double when the layout for that is determined? | | |
| 453 | else => unreachable, | | |
| 454 | }; | | |
| 455 | | 454 | |
| 456 | try self.spv.emitConstant(result_ty_id, result_id, literal); | 455 | fn addUndef(self: *@This(), amt: u64) !void { |
| 457 | }, | 456 | for (0..@intCast(usize, amt)) |_| { |
| 458 | .Array => switch (val.tag()) { | 457 | try self.addByte(undef); |
| 459 | .aggregate => { // todo: combine with Vector | 458 | } |
| 460 | const elem_vals = val.castTag(.aggregate).?.data; | 459 | } |
| 461 | const elem_ty = ty.elemType(); | 460 | |
| 462 | const len = @intCast(u32, ty.arrayLenIncludingSentinel()); // TODO: limit spir-v to 32 bit arrays in a more elegant way. | 461 | /// Add a single byte of data to the constant. |
| 463 | const constituents = try self.spv.gpa.alloc(IdRef, len); | 462 | fn addByte(self: *@This(), data: u8) !void { |
| 464 | defer self.spv.gpa.free(constituents); | 463 | self.partial_word.append(data) catch { |
| 465 | for (elem_vals[0..len], 0..) |elem_val, i| { | 464 | try self.flush(); |
| 466 | constituents[i] = try self.constant(elem_ty, elem_val, repr); | 465 | self.partial_word.append(data) catch unreachable; |
| 467 | } | 466 | }; |
| 468 | try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{ | 467 | self.size += 1; |
| 469 | .id_result_type = result_ty_id, | 468 | } |
| 470 | .id_result = result_id, | 469 | |
| 471 | .constituents = constituents, | 470 | /// Add many bytes of data to the constnat. |
| 472 | }); | 471 | fn addBytes(self: *@This(), data: []const u8) !void { |
| 473 | }, | 472 | // TODO: Improve performance by adding in bulk, or something? |
| 474 | .repeated => { | 473 | for (data) |byte| { |
| 475 | const elem_val = val.castTag(.repeated).?.data; | 474 | try self.addByte(byte); |
| 476 | const elem_ty = ty.elemType(); | 475 | } |
| 477 | const len = @intCast(u32, ty.arrayLen()); | 476 | } |
| 478 | const total_len = @intCast(u32, ty.arrayLenIncludingSentinel()); // TODO: limit spir-v to 32 bit arrays in a more elegant way. | 477 | |
| 479 | const constituents = try self.spv.gpa.alloc(IdRef, total_len); | 478 | fn addPtr(self: *@This(), ptr_ty_ref: SpvType.Ref, ptr_id: IdRef) !void { |
| 480 | defer self.spv.gpa.free(constituents); | 479 | // TODO: Double check pointer sizes here. |
| 481 | | 480 | // shared pointers might be u32... |
| 482 | const elem_val_id = try self.constant(elem_ty, elem_val, repr); | 481 | const target = self.dg.getTarget(); |
| 483 | for (constituents[0..len]) |*elem| { | 482 | const width = @divExact(target.cpu.arch.ptrBitWidth(), 8); |
| 484 | elem.* = elem_val_id; | 483 | if (self.size % width != 0) { |
| 485 | } | 484 | return self.dg.todo("misaligned pointer constants", .{}); |
| 486 | if (ty.sentinel()) |sentinel| { | 485 | } |
| 487 | constituents[len] = try self.constant(elem_ty, sentinel, repr); | 486 | try self.members.append(.{ .ty = ptr_ty_ref }); |
| 488 | } | 487 | try self.initializers.append(ptr_id); |
| 489 | try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{ | 488 | self.size += width; |
| 490 | .id_result_type = result_ty_id, | 489 | } |
| 491 | .id_result = result_id, | 490 | |
| 492 | .constituents = constituents, | 491 | fn addNullPtr(self: *@This(), ptr_ty_ref: SpvType.Ref) !void { |
| 493 | }); | 492 | const result_id = self.dg.spv.allocId(); |
| | 493 | try self.dg.spv.sections.types_globals_constants.emit(self.dg.spv.gpa, .OpConstantNull, .{ |
| | 494 | .id_result_type = self.dg.typeId(ptr_ty_ref), |
| | 495 | .id_result = result_id, |
| | 496 | }); |
| | 497 | try self.addPtr(ptr_ty_ref, result_id); |
| | 498 | } |
| | 499 | |
| | 500 | fn addConstInt(self: *@This(), comptime T: type, value: T) !void { |
| | 501 | if (@bitSizeOf(T) % 8 != 0) { |
| | 502 | @compileError("todo: non byte aligned int constants"); |
| | 503 | } |
| | 504 | |
| | 505 | // TODO: Swap endianness if the compiler is big endian. |
| | 506 | try self.addBytes(std.mem.asBytes(&value)); |
| | 507 | } |
| | 508 | |
| | 509 | fn addConstBool(self: *@This(), value: bool) !void { |
| | 510 | try self.addByte(@boolToInt(value)); // TODO: Keep in sync with something? |
| | 511 | } |
| | 512 | |
| | 513 | fn addInt(self: *@This(), ty: Type, val: Value) !void { |
| | 514 | const target = self.dg.getTarget(); |
| | 515 | const int_info = ty.intInfo(target); |
| | 516 | const int_bits = switch (int_info.signedness) { |
| | 517 | .signed => @bitCast(u64, val.toSignedInt(target)), |
| | 518 | .unsigned => val.toUnsignedInt(target), |
| | 519 | }; |
| | 520 | |
| | 521 | // TODO: Swap endianess if the compiler is big endian. |
| | 522 | const len = ty.abiSize(target); |
| | 523 | try self.addBytes(std.mem.asBytes(&int_bits)[0..@intCast(usize, len)]); |
| | 524 | } |
| | 525 | |
| | 526 | fn lower(self: *@This(), ty: Type, val: Value) !void { |
| | 527 | const target = self.dg.getTarget(); |
| | 528 | const dg = self.dg; |
| | 529 | |
| | 530 | switch (ty.zigTypeTag()) { |
| | 531 | .Int => try self.addInt(ty, val), |
| | 532 | .Bool => try self.addConstBool(val.toBool()), |
| | 533 | .Array => switch (val.tag()) { |
| | 534 | .aggregate => { |
| | 535 | const elem_vals = val.castTag(.aggregate).?.data; |
| | 536 | const elem_ty = ty.elemType(); |
| | 537 | const len = @intCast(u32, ty.arrayLenIncludingSentinel()); // TODO: limit spir-v to 32 bit arrays in a more elegant way. |
| | 538 | for (elem_vals[0..len]) |elem_val| { |
| | 539 | try self.lower(elem_ty, elem_val); |
| | 540 | } |
| | 541 | }, |
| | 542 | .repeated => { |
| | 543 | const elem_val = val.castTag(.repeated).?.data; |
| | 544 | const elem_ty = ty.elemType(); |
| | 545 | const len = @intCast(u32, ty.arrayLen()); |
| | 546 | for (0..len) |_| { |
| | 547 | try self.lower(elem_ty, elem_val); |
| | 548 | } |
| | 549 | if (ty.sentinel()) |sentinel| { |
| | 550 | try self.lower(elem_ty, sentinel); |
| | 551 | } |
| | 552 | }, |
| | 553 | .str_lit => { |
| | 554 | const str_lit = val.castTag(.str_lit).?.data; |
| | 555 | const bytes = dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; |
| | 556 | try self.addBytes(bytes); |
| | 557 | if (ty.sentinel()) |sentinel| { |
| | 558 | try self.addByte(@intCast(u8, sentinel.toUnsignedInt(target))); |
| | 559 | } |
| | 560 | }, |
| | 561 | else => |tag| return dg.todo("indirect array constant with tag {s}", .{@tagName(tag)}), |
| 494 | }, | 562 | }, |
| 495 | .str_lit => { | 563 | .Pointer => switch (val.tag()) { |
| 496 | // TODO: This is very efficient code generation, should probably implement constant caching for this. | 564 | .decl_ref_mut => { |
| 497 | const str_lit = val.castTag(.str_lit).?.data; | 565 | const ptr_ty_ref = try dg.resolveType(ty, .indirect); |
| 498 | const bytes = self.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; | 566 | const ptr_id = dg.spv.allocId(); |
| 499 | const elem_ty = ty.elemType(); | 567 | const decl_index = val.castTag(.decl_ref_mut).?.data.decl_index; |
| 500 | const elem_ty_id = try self.resolveTypeId(elem_ty); | 568 | try dg.genDeclRef(ptr_ty_ref, ptr_id, decl_index); |
| 501 | const len = @intCast(u32, ty.arrayLen()); | 569 | try self.addPtr(ptr_ty_ref, ptr_id); |
| 502 | const total_len = @intCast(u32, ty.arrayLenIncludingSentinel()); | 570 | }, |
| 503 | const constituents = try self.spv.gpa.alloc(IdRef, total_len); | 571 | .decl_ref => { |
| 504 | defer self.spv.gpa.free(constituents); | 572 | const ptr_ty_ref = try dg.resolveType(ty, .indirect); |
| 505 | for (bytes, 0..) |byte, i| { | 573 | const ptr_id = dg.spv.allocId(); |
| 506 | constituents[i] = self.spv.allocId(); | 574 | const decl_index = val.castTag(.decl_ref).?.data; |
| 507 | try self.spv.emitConstant(elem_ty_id, constituents[i], .{ .uint32 = byte }); | 575 | try dg.genDeclRef(ptr_ty_ref, ptr_id, decl_index); |
| 508 | } | 576 | try self.addPtr(ptr_ty_ref, ptr_id); |
| 509 | if (ty.sentinel()) |sentinel| { | 577 | }, |
| 510 | constituents[len] = self.spv.allocId(); | 578 | .slice => { |
| 511 | const byte = @intCast(u8, sentinel.toUnsignedInt(target)); | 579 | const slice = val.castTag(.slice).?.data; |
| 512 | try self.spv.emitConstant(elem_ty_id, constituents[len], .{ .uint32 = byte }); | 580 | |
| 513 | } | 581 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 514 | try section.emit(self.spv.gpa, .OpConstantComposite, .{ | 582 | const ptr_ty = ty.slicePtrFieldType(&buf); |
| 515 | .id_result_type = result_ty_id, | 583 | |
| 516 | .id_result = result_id, | 584 | try self.lower(ptr_ty, slice.ptr); |
| 517 | .constituents = constituents, | 585 | try self.addInt(Type.usize, slice.len); |
| 518 | }); | 586 | }, |
| | 587 | else => |tag| return dg.todo("pointer value of type {s}", .{@tagName(tag)}), |
| 519 | }, | 588 | }, |
| 520 | else => return self.todo("array constant with tag {s}", .{@tagName(val.tag())}), | 589 | .Struct => { |
| 521 | }, | 590 | if (ty.isSimpleTupleOrAnonStruct()) { |
| 522 | .Vector => switch (val.tag()) { | 591 | unreachable; // TODO |
| 523 | .aggregate => { | 592 | } else { |
| 524 | const elem_vals = val.castTag(.aggregate).?.data; | 593 | const struct_ty = ty.castTag(.@"struct").?.data; |
| 525 | const vector_len = @intCast(usize, ty.vectorLen()); | 594 | |
| 526 | const elem_ty = ty.elemType(); | 595 | if (struct_ty.layout == .Packed) { |
| 527 | | 596 | return dg.todo("packed struct constants", .{}); |
| 528 | const elem_refs = try self.gpa.alloc(IdRef, vector_len); | 597 | } |
| 529 | defer self.gpa.free(elem_refs); | 598 | |
| 530 | for (elem_refs, 0..) |*elem, i| { | 599 | const struct_begin = self.size; |
| 531 | elem.* = try self.constant(elem_ty, elem_vals[i], repr); | 600 | const field_vals = val.castTag(.aggregate).?.data; |
| | 601 | for (struct_ty.fields.values(), 0..) |field, i| { |
| | 602 | if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; |
| | 603 | try self.lower(field.ty, field_vals[i]); |
| | 604 | |
| | 605 | // Add padding if required. |
| | 606 | // TODO: Add to type generation as well? |
| | 607 | const unpadded_field_end = self.size - struct_begin; |
| | 608 | const padded_field_end = ty.structFieldOffset(i + 1, target); |
| | 609 | const padding = padded_field_end - unpadded_field_end; |
| | 610 | try self.addUndef(padding); |
| | 611 | } |
| 532 | } | 612 | } |
| 533 | try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{ | | |
| 534 | .id_result_type = result_ty_id, | | |
| 535 | .id_result = result_id, | | |
| 536 | .constituents = elem_refs, | | |
| 537 | }); | | |
| 538 | }, | 613 | }, |
| 539 | else => return self.todo("vector constant with tag {s}", .{@tagName(val.tag())}), | 614 | .Optional => { |
| 540 | }, | 615 | var opt_buf: Type.Payload.ElemType = undefined; |
| 541 | .Enum => { | 616 | const payload_ty = ty.optionalChild(&opt_buf); |
| 542 | var int_buffer: Value.Payload.U64 = undefined; | 617 | const has_payload = !val.isNull(); |
| 543 | const int_val = val.enumToInt(ty, &int_buffer).toUnsignedInt(target); // TODO: composite integer constants | 618 | const abi_size = ty.abiSize(target); |
| 544 | return self.genConstInt(result_ty_ref, result_id, int_val); | 619 | |
| 545 | }, | 620 | if (!payload_ty.hasRuntimeBits()) { |
| 546 | .Struct => { | 621 | try self.addConstBool(has_payload); |
| 547 | const constituents = if (ty.isSimpleTupleOrAnonStruct()) blk: { | 622 | return; |
| 548 | const tuple = ty.tupleFields(); | 623 | } else if (ty.optionalReprIsPayload()) { |
| 549 | const constituents = try self.spv.gpa.alloc(IdRef, tuple.types.len); | 624 | // Optional representation is a nullable pointer. |
| 550 | errdefer self.spv.gpa.free(constituents); | 625 | if (val.castTag(.opt_payload)) |payload| { |
| 551 | | 626 | try self.lower(payload_ty, payload.data); |
| 552 | var member_i: usize = 0; | 627 | } else if (has_payload) { |
| 553 | for (tuple.types, 0..) |field_ty, i| { | 628 | try self.lower(payload_ty, val); |
| 554 | const field_val = tuple.values[i]; | 629 | } else { |
| 555 | if (field_val.tag() != .unreachable_value or !field_ty.hasRuntimeBits()) continue; | 630 | const ptr_ty_ref = try dg.resolveType(ty, .indirect); |
| 556 | constituents[member_i] = try self.constant(field_ty, field_val, .indirect); | 631 | try self.addNullPtr(ptr_ty_ref); |
| 557 | member_i += 1; | 632 | } |
| | 633 | return; |
| 558 | } | 634 | } |
| 559 | | 635 | |
| 560 | break :blk constituents[0..member_i]; | 636 | // Optional representation is a structure. |
| 561 | } else blk: { | 637 | // { Payload, Bool } |
| 562 | const struct_ty = ty.castTag(.@"struct").?.data; | | |
| 563 | | 638 | |
| 564 | if (struct_ty.layout == .Packed) { | 639 | // Subtract 1 for @sizeOf(bool). |
| 565 | return self.todo("packed struct constants", .{}); | 640 | // TODO: Make this not hardcoded. |
| 566 | } | 641 | const payload_size = payload_ty.abiSize(target); |
| | 642 | const padding = abi_size - payload_size - 1; |
| 567 | | 643 | |
| 568 | const field_vals = val.castTag(.aggregate).?.data; | 644 | if (val.castTag(.opt_payload)) |payload| { |
| 569 | const constituents = try self.spv.gpa.alloc(IdRef, struct_ty.fields.count()); | 645 | try self.lower(payload_ty, payload.data); |
| 570 | errdefer self.spv.gpa.free(constituents); | 646 | } else { |
| 571 | var member_i: usize = 0; | 647 | try self.addUndef(payload_size); |
| 572 | for (struct_ty.fields.values(), 0..) |field, i| { | | |
| 573 | if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; | | |
| 574 | constituents[member_i] = try self.constant(field.ty, field_vals[i], .indirect); | | |
| 575 | member_i += 1; | | |
| 576 | } | 648 | } |
| | 649 | try self.addConstBool(has_payload); |
| | 650 | try self.addUndef(padding); |
| | 651 | }, |
| | 652 | .Enum => { |
| | 653 | var int_val_buffer: Value.Payload.U64 = undefined; |
| | 654 | const int_val = val.enumToInt(ty, &int_val_buffer); |
| 577 | | 655 | |
| 578 | break :blk constituents[0..member_i]; | 656 | var int_ty_buffer: Type.Payload.Bits = undefined; |
| 579 | }; | 657 | const int_ty = ty.intTagType(&int_ty_buffer); |
| 580 | defer self.spv.gpa.free(constituents); | | |
| 581 | | 658 | |
| 582 | try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{ | 659 | try self.lower(int_ty, int_val); |
| 583 | .id_result_type = result_ty_id, | | |
| 584 | .id_result = result_id, | | |
| 585 | .constituents = constituents, | | |
| 586 | }); | | |
| 587 | }, | | |
| 588 | .Pointer => switch (val.tag()) { | | |
| 589 | .decl_ref_mut => try self.genDeclRef(result_ty_ref, result_id, val.castTag(.decl_ref_mut).?.data.decl_index), | | |
| 590 | .decl_ref => try self.genDeclRef(result_ty_ref, result_id, val.castTag(.decl_ref).?.data), | | |
| 591 | .slice => { | | |
| 592 | const slice = val.castTag(.slice).?.data; | | |
| 593 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | | |
| 594 | | | |
| 595 | const ptr_id = try self.constant(ty.slicePtrFieldType(&buf), slice.ptr, .indirect); | | |
| 596 | const len_id = try self.constant(Type.usize, slice.len, .indirect); | | |
| 597 | | | |
| 598 | const constituents = [_]IdRef{ ptr_id, len_id }; | | |
| 599 | try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{ | | |
| 600 | .id_result_type = result_ty_id, | | |
| 601 | .id_result = result_id, | | |
| 602 | .constituents = &constituents, | | |
| 603 | }); | | |
| 604 | }, | 660 | }, |
| 605 | else => return self.todo("pointer of value type {s}", .{@tagName(val.tag())}), | 661 | .Union => { |
| 606 | }, | 662 | const tag_and_val = val.castTag(.@"union").?.data; |
| 607 | .Optional => { | 663 | const layout = ty.unionGetLayout(target); |
| 608 | var buf: Type.Payload.ElemType = undefined; | | |
| 609 | const payload_ty = ty.optionalChild(&buf); | | |
| 610 | | 664 | |
| 611 | const has_payload = !val.isNull(); | 665 | if (layout.payload_size == 0) { |
| | 666 | return try self.lower(ty.unionTagTypeSafety().?, tag_and_val.tag); |
| | 667 | } |
| 612 | | 668 | |
| 613 | // Note: keep in sync with the resolveType implementation for optionals. | 669 | const union_ty = ty.cast(Type.Payload.Union).?.data; |
| 614 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { | 670 | if (union_ty.layout == .Packed) { |
| 615 | // Just a bool. Note: always in indirect representation. | 671 | return dg.todo("packed union constants", .{}); |
| 616 | try self.genConstInt(result_ty_ref, result_id, @boolToInt(has_payload)); | | |
| 617 | } else if (ty.optionalReprIsPayload()) { | | |
| 618 | // A nullable pointer. | | |
| 619 | if (val.castTag(.opt_payload)) |payload| { | | |
| 620 | try self.genConstant(result_id, payload_ty, payload.data, repr); | | |
| 621 | } else if (has_payload) { | | |
| 622 | try self.genConstant(result_id, payload_ty, val, repr); | | |
| 623 | } else { | | |
| 624 | try section.emit(self.spv.gpa, .OpConstantNull, .{ | | |
| 625 | .id_result_type = result_ty_id, | | |
| 626 | .id_result = result_id, | | |
| 627 | }); | | |
| 628 | } | 672 | } |
| 629 | return; | | |
| 630 | } | | |
| 631 | | 673 | |
| 632 | // Struct-and-field pair. | 674 | const active_field = ty.unionTagFieldIndex(tag_and_val.tag, dg.module).?; |
| 633 | // Note: If this optional has no payload, we initialize the the data member with OpUndef. | 675 | const active_field_ty = union_ty.fields.values()[active_field].ty; |
| 634 | const bool_ty_ref = try self.resolveType(Type.bool, .indirect); | | |
| 635 | const valid_id = try self.constInt(bool_ty_ref, @boolToInt(has_payload)); | | |
| 636 | const payload_val = if (val.castTag(.opt_payload)) |pl| pl.data else Value.undef; | | |
| 637 | const payload_id = try self.constant(payload_ty, payload_val, .indirect); | | |
| 638 | | 676 | |
| 639 | const constituents = [_]IdRef{ payload_id, valid_id }; | 677 | const has_tag = layout.tag_size != 0; |
| 640 | try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{ | 678 | const tag_first = layout.tag_align >= layout.payload_align; |
| 641 | .id_result_type = result_ty_id, | | |
| 642 | .id_result = result_id, | | |
| 643 | .constituents = &constituents, | | |
| 644 | }); | | |
| 645 | }, | | |
| 646 | .Union => { | | |
| 647 | const tag_and_val = val.castTag(.@"union").?.data; | | |
| 648 | const layout = ty.unionGetLayout(target); | | |
| 649 | | 679 | |
| 650 | if (layout.payload_size == 0) { | 680 | if (has_tag and tag_first) { |
| 651 | return try self.genConstant(result_id, ty.unionTagTypeSafety().?, tag_and_val.tag, .indirect); | 681 | try self.lower(ty.unionTagTypeSafety().?, tag_and_val.tag); |
| 652 | } | 682 | } |
| 653 | | 683 | |
| 654 | const union_ty = ty.cast(Type.Payload.Union).?.data; | 684 | const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime()) blk: { |
| 655 | if (union_ty.layout == .Packed) { | 685 | try self.lower(active_field_ty, tag_and_val.val); |
| 656 | return self.todo("packed union constants", .{}); | 686 | break :blk active_field_ty.abiSize(target); |
| 657 | } | 687 | } else 0; |
| 658 | | 688 | |
| 659 | const active_field = ty.unionTagFieldIndex(tag_and_val.tag, self.module).?; | 689 | const payload_padding_len = layout.payload_size - active_field_size; |
| 660 | const union_ty_ref = try self.resolveUnionType(ty, active_field); | 690 | try self.addUndef(payload_padding_len); |
| 661 | const active_field_ty = union_ty.fields.values()[active_field].ty; | | |
| 662 | | 691 | |
| 663 | const tag_first = layout.tag_align >= layout.payload_align; | 692 | if (has_tag and !tag_first) { |
| 664 | const u8_ty_ref = try self.intType(.unsigned, 8); | 693 | try self.lower(ty.unionTagTypeSafety().?, tag_and_val.tag); |
| | 694 | } |
| 665 | | 695 | |
| 666 | const tag = if (layout.tag_size != 0) | 696 | try self.addUndef(layout.padding); |
| 667 | try self.constant(ty.unionTagTypeSafety().?, tag_and_val.tag, .indirect) | 697 | }, |
| 668 | else | 698 | else => |tag| return dg.todo("indirect constant of type {s}", .{@tagName(tag)}), |
| 669 | null; | 699 | } |
| | 700 | } |
| | 701 | }; |
| 670 | | 702 | |
| 671 | var members = std.BoundedArray(IdRef, 4){}; | 703 | /// Returns a pointer to `val`. The value is placed directly |
| | 704 | /// into the storage class `storage_class`, and this is also where the resulting |
| | 705 | /// pointer points to. Note: result is not necessarily an OpVariable instruction! |
| | 706 | fn lowerIndirectConstant( |
| | 707 | self: *DeclGen, |
| | 708 | result_id: IdRef, |
| | 709 | ty: Type, |
| | 710 | val: Value, |
| | 711 | storage_class: spec.StorageClass, |
| | 712 | alignment: u32, |
| | 713 | ) Error!void { |
| | 714 | // To simplify constant generation, we're going to generate constants as a word-array, and |
| | 715 | // pointer cast the result to the right type. |
| | 716 | // This means that the final constant will be generated as follows: |
| | 717 | // %T = OpTypeStruct %members... |
| | 718 | // %P = OpTypePointer %T |
| | 719 | // %U = OpTypePointer %ty |
| | 720 | // %1 = OpConstantComposite %T %initializers... |
| | 721 | // %2 = OpVariable %P %1 |
| | 722 | // %result_id = OpSpecConstantOp OpBitcast %U %2 |
| | 723 | // |
| | 724 | // The members consist of two options: |
| | 725 | // - Literal values: ints, strings, etc. These are generated as u32 words. |
| | 726 | // - Relocations, such as pointers: These are generated by embedding the pointer into the |
| | 727 | // to-be-generated structure. There are two options here, depending on the alignment of the |
| | 728 | // pointer value itself (not the alignment of the pointee). |
| | 729 | // - Natively or over-aligned values. These can just be generated directly. |
| | 730 | // - Underaligned pointers. These need to be packed into the word array by using a mixture of |
| | 731 | // OpSpecConstantOp instructions such as OpConvertPtrToU, OpBitcast, OpShift, etc. |
| | 732 | |
| | 733 | log.debug("lowerIndirectConstant: ty = {}, val = {}", .{ ty.fmtDebug(), val.fmtDebug() }); |
| | 734 | |
| | 735 | const constant_section = &self.spv.sections.types_globals_constants; |
| | 736 | |
| | 737 | const ty_ref = try self.resolveType(ty, .indirect); |
| | 738 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, alignment); |
| 672 | | 739 | |
| 673 | if (tag_first) { | 740 | if (val.isUndef()) { |
| 674 | if (tag) |id| members.appendAssumeCapacity(id); | 741 | // Special case: the entire value is undefined. In this case, we can just |
| 675 | } | 742 | // generate an OpVariable with no initializer. |
| | 743 | try constant_section.emit(self.spv.gpa, .OpVariable, .{ |
| | 744 | .id_result_type = self.typeId(ptr_ty_ref), |
| | 745 | .id_result = result_id, |
| | 746 | .storage_class = storage_class, |
| | 747 | }); |
| | 748 | return; |
| | 749 | } |
| 676 | | 750 | |
| 677 | const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime()) blk: { | 751 | const u32_ty_ref = try self.intType(.unsigned, 32); |
| 678 | const payload = try self.constant(active_field_ty, tag_and_val.val, .indirect); | 752 | var icl = IndirectConstantLowering{ |
| 679 | members.appendAssumeCapacity(payload); | 753 | .dg = self, |
| 680 | break :blk active_field_ty.abiSize(target); | 754 | .u32_ty_ref = u32_ty_ref, |
| 681 | } else 0; | 755 | .u32_ty_id = self.typeId(u32_ty_ref), |
| | 756 | .members = std.ArrayList(SpvType.Payload.Struct.Member).init(self.gpa), |
| | 757 | .initializers = std.ArrayList(IdRef).init(self.gpa), |
| | 758 | }; |
| 682 | | 759 | |
| 683 | const payload_padding_len = layout.payload_size - active_field_size; | 760 | try icl.lower(ty, val); |
| 684 | if (payload_padding_len != 0) { | 761 | try icl.flush(); |
| 685 | const payload_padding_ty_ref = try self.arrayType(@intCast(u32, payload_padding_len), u8_ty_ref); | | |
| 686 | members.appendAssumeCapacity(try self.genUndef(payload_padding_ty_ref)); | | |
| 687 | } | | |
| 688 | | 762 | |
| 689 | if (!tag_first) { | 763 | defer icl.members.deinit(); |
| 690 | if (tag) |id| members.appendAssumeCapacity(id); | 764 | defer icl.initializers.deinit(); |
| 691 | } | | |
| 692 | | 765 | |
| 693 | if (layout.padding != 0) { | 766 | const constant_struct_ty_ref = try self.spv.simpleStructType(icl.members.items); |
| 694 | const padding_ty_ref = try self.arrayType(layout.padding, u8_ty_ref); | 767 | const ptr_constant_struct_ty_ref = try self.spv.ptrType(constant_struct_ty_ref, storage_class, alignment); |
| 695 | members.appendAssumeCapacity(try self.genUndef(padding_ty_ref)); | 768 | |
| 696 | } | 769 | const constant_struct_id = self.spv.allocId(); |
| | 770 | try constant_section.emit(self.spv.gpa, .OpSpecConstantComposite, .{ |
| | 771 | .id_result_type = self.typeId(constant_struct_ty_ref), |
| | 772 | .id_result = constant_struct_id, |
| | 773 | .constituents = icl.initializers.items, |
| | 774 | }); |
| | 775 | |
| | 776 | const var_id = self.spv.allocId(); |
| | 777 | switch (storage_class) { |
| | 778 | .Generic => unreachable, |
| | 779 | .Function => { |
| | 780 | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| | 781 | .id_result_type = self.typeId(ptr_constant_struct_ty_ref), |
| | 782 | .id_result = var_id, |
| | 783 | .storage_class = storage_class, |
| | 784 | .initializer = constant_struct_id, |
| | 785 | }); |
| | 786 | // TODO: Set alignment of OpVariable. |
| 697 | | 787 | |
| 698 | try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{ | 788 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ |
| 699 | .id_result_type = self.typeId(union_ty_ref), | 789 | .id_result_type = self.typeId(ptr_ty_ref), |
| 700 | .id_result = result_id, | 790 | .id_result = result_id, |
| 701 | .constituents = members.slice(), | 791 | .operand = var_id, |
| 702 | }); | 792 | }); |
| | 793 | }, |
| | 794 | else => { |
| | 795 | try constant_section.emit(self.spv.gpa, .OpVariable, .{ |
| | 796 | .id_result_type = self.typeId(ptr_constant_struct_ty_ref), |
| | 797 | .id_result = var_id, |
| | 798 | .storage_class = storage_class, |
| | 799 | .initializer = constant_struct_id, |
| | 800 | }); |
| | 801 | // TODO: Set alignment of OpVariable. |
| 703 | | 802 | |
| 704 | // TODO: Cast to general union type? Required for pointers only or something? | 803 | try constant_section.emitSpecConstantOp(self.spv.gpa, .OpBitcast, .{ |
| | 804 | .id_result_type = self.typeId(ptr_ty_ref), |
| | 805 | .id_result = result_id, |
| | 806 | .operand = var_id, |
| | 807 | }); |
| 705 | }, | 808 | }, |
| | 809 | } |
| | 810 | } |
| | 811 | |
| | 812 | /// This function generates a load for a constant in direct (ie, non-memory) representation. |
| | 813 | /// When the constant is simple, it can be generated directly using OpConstant instructions. When |
| | 814 | /// the constant is more complicated however, it needs to be lowered to an indirect constant, which |
| | 815 | /// is then loaded using OpLoad. Such values are loaded into the Function address space by default. |
| | 816 | /// This function should only be called during function code generation. |
| | 817 | fn constant(self: *DeclGen, ty: Type, val: Value) !IdRef { |
| | 818 | const target = self.getTarget(); |
| | 819 | const section = &self.spv.sections.types_globals_constants; |
| | 820 | const result_ty_ref = try self.resolveType(ty, .direct); |
| | 821 | const result_ty_id = self.typeId(result_ty_ref); |
| | 822 | const result_id = self.spv.allocId(); |
| | 823 | |
| | 824 | if (val.isUndef()) { |
| | 825 | try section.emit(self.spv.gpa, .OpUndef, .{ |
| | 826 | .id_result_type = result_ty_id, |
| | 827 | .id_result = result_id, |
| | 828 | }); |
| | 829 | return result_id; |
| | 830 | } |
| 706 | | 831 | |
| 707 | .Fn => switch (repr) { | 832 | switch (ty.zigTypeTag()) { |
| 708 | .direct => unreachable, | 833 | .Int => { |
| 709 | .indirect => return self.todo("function pointers", .{}), | 834 | const int_bits = if (ty.isSignedInt()) |
| | 835 | @bitCast(u64, val.toSignedInt(target)) |
| | 836 | else |
| | 837 | val.toUnsignedInt(target); |
| | 838 | try self.genConstInt(result_ty_ref, result_id, int_bits); |
| | 839 | }, |
| | 840 | .Bool => { |
| | 841 | const operands = .{ .id_result_type = result_ty_id, .id_result = result_id }; |
| | 842 | if (val.toBool()) { |
| | 843 | try section.emit(self.spv.gpa, .OpConstantTrue, operands); |
| | 844 | } else { |
| | 845 | try section.emit(self.spv.gpa, .OpConstantFalse, operands); |
| | 846 | } |
| | 847 | }, |
| | 848 | else => { |
| | 849 | // The value cannot be generated directly, so generate it as an indirect function-local |
| | 850 | // constant, and then perform an OpLoad. |
| | 851 | const ptr_id = self.spv.allocId(); |
| | 852 | const alignment = ty.abiAlignment(target); |
| | 853 | try self.lowerIndirectConstant(ptr_id, ty, val, .Function, alignment); |
| | 854 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| | 855 | .id_result_type = result_ty_id, |
| | 856 | .id_result = result_id, |
| | 857 | .pointer = ptr_id, |
| | 858 | }); |
| | 859 | // TODO: Convert bools? This logic should hook into `load`. |
| 710 | }, | 860 | }, |
| 711 | .Void => unreachable, | | |
| 712 | else => return self.todo("constant generation of type {s}: {}", .{ @tagName(ty.zigTypeTag()), ty.fmtDebug() }), | | |
| 713 | } | 861 | } |
| | 862 | |
| | 863 | return result_id; |
| 714 | } | 864 | } |
| 715 | | 865 | |
| 716 | fn genDeclRef(self: *DeclGen, result_ty_ref: SpvType.Ref, result_id: IdRef, decl_index: Decl.Index) Error!void { | 866 | fn genDeclRef(self: *DeclGen, result_ty_ref: SpvType.Ref, result_id: IdRef, decl_index: Decl.Index) Error!void { |
| | 867 | // TODO: Clean up |
| 717 | const decl = self.module.declPtr(decl_index); | 868 | const decl = self.module.declPtr(decl_index); |
| 718 | self.module.markDeclAlive(decl); | 869 | self.module.markDeclAlive(decl); |
| 719 | const decl_id = try self.constant(decl.ty, decl.val, .indirect); | 870 | // _ = result_ty_ref; |
| 720 | try self.variable(.global, result_id, result_ty_ref, decl_id); | 871 | // const decl_id = try self.constant(decl.ty, decl.val, .indirect); |
| | 872 | // try self.variable(.global, result_id, result_ty_ref, decl_id); |
| | 873 | const result_storage_class = self.spv.typeRefType(result_ty_ref).payload(.pointer).storage_class; |
| | 874 | const indirect_result_id = if (result_storage_class != .CrossWorkgroup) |
| | 875 | self.spv.allocId() |
| | 876 | else |
| | 877 | result_id; |
| | 878 | |
| | 879 | try self.lowerIndirectConstant( |
| | 880 | indirect_result_id, |
| | 881 | decl.ty, |
| | 882 | decl.val, |
| | 883 | .CrossWorkgroup, // TODO: Make this .Function if required |
| | 884 | decl.@"align", |
| | 885 | ); |
| | 886 | const section = &self.spv.sections.types_globals_constants; |
| | 887 | if (result_storage_class != .CrossWorkgroup) { |
| | 888 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| | 889 | .id_result_type = self.typeId(result_ty_ref), |
| | 890 | .id_result = result_id, |
| | 891 | .pointer = indirect_result_id, |
| | 892 | }); |
| | 893 | } |
| 721 | } | 894 | } |
| 722 | | 895 | |
| 723 | /// Turn a Zig type into a SPIR-V Type, and return its type result-id. | 896 | /// Turn a Zig type into a SPIR-V Type, and return its type result-id. |
| ... | @@ -746,32 +919,6 @@ pub const DeclGen = struct { | ... | @@ -746,32 +919,6 @@ pub const DeclGen = struct { |
| 746 | return try self.intType(.unsigned, self.getTarget().cpu.arch.ptrBitWidth()); | 919 | return try self.intType(.unsigned, self.getTarget().cpu.arch.ptrBitWidth()); |
| 747 | } | 920 | } |
| 748 | | 921 | |
| 749 | /// Construct a simple struct type which consists of some members, and no decorations. | | |
| 750 | /// `members` lifetime only needs to last for this function as it is copied. | | |
| 751 | fn simpleStructType(self: *DeclGen, members: []const SpvType.Payload.Struct.Member) !SpvType.Ref { | | |
| 752 | const payload = try self.spv.arena.create(SpvType.Payload.Struct); | | |
| 753 | payload.* = .{ | | |
| 754 | .members = try self.spv.arena.dupe(SpvType.Payload.Struct.Member, members), | | |
| 755 | .decorations = .{}, | | |
| 756 | }; | | |
| 757 | return try self.spv.resolveType(SpvType.initPayload(&payload.base)); | | |
| 758 | } | | |
| 759 | | | |
| 760 | fn simpleStructTypeId(self: *DeclGen, members: []const SpvType.Payload.Struct.Member) !IdResultType { | | |
| 761 | const type_ref = try self.simpleStructType(members); | | |
| 762 | return self.typeId(type_ref); | | |
| 763 | } | | |
| 764 | | | |
| 765 | /// Construct an array type which has 'len' elements of 'type' | | |
| 766 | fn arrayType(self: *DeclGen, len: u32, ty: SpvType.Ref) !SpvType.Ref { | | |
| 767 | const payload = try self.spv.arena.create(SpvType.Payload.Array); | | |
| 768 | payload.* = .{ | | |
| 769 | .element_type = ty, | | |
| 770 | .length = len, | | |
| 771 | }; | | |
| 772 | return try self.spv.resolveType(SpvType.initPayload(&payload.base)); | | |
| 773 | } | | |
| 774 | | | |
| 775 | /// Generate a union type, optionally with a known field. If the tag alignment is greater | 922 | /// Generate a union type, optionally with a known field. If the tag alignment is greater |
| 776 | /// than that of the payload, a regular union (non-packed, with both tag and payload), will | 923 | /// than that of the payload, a regular union (non-packed, with both tag and payload), will |
| 777 | /// be generated as follows: | 924 | /// be generated as follows: |
| ... | @@ -831,7 +978,7 @@ pub const DeclGen = struct { | ... | @@ -831,7 +978,7 @@ pub const DeclGen = struct { |
| 831 | | 978 | |
| 832 | const payload_padding_len = layout.payload_size - active_field_size; | 979 | const payload_padding_len = layout.payload_size - active_field_size; |
| 833 | if (payload_padding_len != 0) { | 980 | if (payload_padding_len != 0) { |
| 834 | const payload_padding_ty_ref = try self.arrayType(@intCast(u32, payload_padding_len), u8_ty_ref); | 981 | const payload_padding_ty_ref = try self.spv.arrayType(@intCast(u32, payload_padding_len), u8_ty_ref); |
| 835 | members.appendAssumeCapacity(.{ .name = "padding_payload", .ty = payload_padding_ty_ref }); | 982 | members.appendAssumeCapacity(.{ .name = "padding_payload", .ty = payload_padding_ty_ref }); |
| 836 | } | 983 | } |
| 837 | | 984 | |
| ... | @@ -840,11 +987,11 @@ pub const DeclGen = struct { | ... | @@ -840,11 +987,11 @@ pub const DeclGen = struct { |
| 840 | } | 987 | } |
| 841 | | 988 | |
| 842 | if (layout.padding != 0) { | 989 | if (layout.padding != 0) { |
| 843 | const padding_ty_ref = try self.arrayType(layout.padding, u8_ty_ref); | 990 | const padding_ty_ref = try self.spv.arrayType(layout.padding, u8_ty_ref); |
| 844 | members.appendAssumeCapacity(.{ .name = "padding", .ty = padding_ty_ref }); | 991 | members.appendAssumeCapacity(.{ .name = "padding", .ty = padding_ty_ref }); |
| 845 | } | 992 | } |
| 846 | | 993 | |
| 847 | return try self.simpleStructType(members.slice()); | 994 | return try self.spv.simpleStructType(members.slice()); |
| 848 | } | 995 | } |
| 849 | | 996 | |
| 850 | /// Turn a Zig type into a SPIR-V Type, and return a reference to it. | 997 | /// Turn a Zig type into a SPIR-V Type, and return a reference to it. |
| ... | @@ -893,7 +1040,7 @@ pub const DeclGen = struct { | ... | @@ -893,7 +1040,7 @@ pub const DeclGen = struct { |
| 893 | const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel()) orelse { | 1040 | const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel()) orelse { |
| 894 | return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel()}); | 1041 | return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel()}); |
| 895 | }; | 1042 | }; |
| 896 | return try self.arrayType(total_len, elem_ty_ref); | 1043 | return try self.spv.arrayType(total_len, elem_ty_ref); |
| 897 | }, | 1044 | }, |
| 898 | .Fn => { | 1045 | .Fn => { |
| 899 | // TODO: Put this somewhere in Sema.zig | 1046 | // TODO: Put this somewhere in Sema.zig |
| ... | @@ -918,7 +1065,7 @@ pub const DeclGen = struct { | ... | @@ -918,7 +1065,7 @@ pub const DeclGen = struct { |
| 918 | | 1065 | |
| 919 | const ptr_payload = try self.spv.arena.create(SpvType.Payload.Pointer); | 1066 | const ptr_payload = try self.spv.arena.create(SpvType.Payload.Pointer); |
| 920 | ptr_payload.* = .{ | 1067 | ptr_payload.* = .{ |
| 921 | .storage_class = spirvStorageClass(ptr_info.@"addrspace"), | 1068 | .storage_class = spvStorageClass(ptr_info.@"addrspace"), |
| 922 | .child_type = try self.resolveType(ptr_info.pointee_type, .indirect), | 1069 | .child_type = try self.resolveType(ptr_info.pointee_type, .indirect), |
| 923 | // Note: only available in Kernels! | 1070 | // Note: only available in Kernels! |
| 924 | .alignment = ty.ptrAlignment(target) * 8, | 1071 | .alignment = ty.ptrAlignment(target) * 8, |
| ... | @@ -929,7 +1076,7 @@ pub const DeclGen = struct { | ... | @@ -929,7 +1076,7 @@ pub const DeclGen = struct { |
| 929 | return ptr_ty_id; | 1076 | return ptr_ty_id; |
| 930 | } | 1077 | } |
| 931 | | 1078 | |
| 932 | return try self.simpleStructType(&.{ | 1079 | return try self.spv.simpleStructType(&.{ |
| 933 | .{ .ty = ptr_ty_id, .name = "ptr" }, | 1080 | .{ .ty = ptr_ty_id, .name = "ptr" }, |
| 934 | .{ .ty = try self.sizeType(), .name = "len" }, | 1081 | .{ .ty = try self.sizeType(), .name = "len" }, |
| 935 | }); | 1082 | }); |
| ... | @@ -1018,7 +1165,7 @@ pub const DeclGen = struct { | ... | @@ -1018,7 +1165,7 @@ pub const DeclGen = struct { |
| 1018 | const bool_ty_ref = try self.resolveType(Type.bool, .indirect); | 1165 | const bool_ty_ref = try self.resolveType(Type.bool, .indirect); |
| 1019 | | 1166 | |
| 1020 | // its an actual optional | 1167 | // its an actual optional |
| 1021 | return try self.simpleStructType(&.{ | 1168 | return try self.spv.simpleStructType(&.{ |
| 1022 | .{ .ty = payload_ty_ref, .name = "payload" }, | 1169 | .{ .ty = payload_ty_ref, .name = "payload" }, |
| 1023 | .{ .ty = bool_ty_ref, .name = "valid" }, | 1170 | .{ .ty = bool_ty_ref, .name = "valid" }, |
| 1024 | }); | 1171 | }); |
| ... | @@ -1037,7 +1184,7 @@ pub const DeclGen = struct { | ... | @@ -1037,7 +1184,7 @@ pub const DeclGen = struct { |
| 1037 | } | 1184 | } |
| 1038 | } | 1185 | } |
| 1039 | | 1186 | |
| 1040 | fn spirvStorageClass(as: std.builtin.AddressSpace) spec.StorageClass { | 1187 | fn spvStorageClass(as: std.builtin.AddressSpace) spec.StorageClass { |
| 1041 | return switch (as) { | 1188 | return switch (as) { |
| 1042 | .generic => .Generic, // TODO: Disallow? | 1189 | .generic => .Generic, // TODO: Disallow? |
| 1043 | .gs, .fs, .ss => unreachable, | 1190 | .gs, .fs, .ss => unreachable, |
| ... | @@ -1100,7 +1247,46 @@ pub const DeclGen = struct { | ... | @@ -1100,7 +1247,46 @@ pub const DeclGen = struct { |
| 1100 | .name = fqn, | 1247 | .name = fqn, |
| 1101 | }); | 1248 | }); |
| 1102 | } else { | 1249 | } else { |
| 1103 | try self.genConstant(result_id, decl.ty, decl.val, .direct); | 1250 | const init_val = if (decl.val.castTag(.variable)) |payload| |
| | 1251 | payload.data.init |
| | 1252 | else |
| | 1253 | decl.val; |
| | 1254 | |
| | 1255 | if (init_val.tag() == .unreachable_value) { |
| | 1256 | return self.todo("importing extern variables", .{}); |
| | 1257 | } |
| | 1258 | |
| | 1259 | // TODO: integrate with variable(). |
| | 1260 | |
| | 1261 | const storage_class = spvStorageClass(decl.@"addrspace"); |
| | 1262 | const actual_storage_class = switch (storage_class) { |
| | 1263 | .Generic => .CrossWorkgroup, |
| | 1264 | else => storage_class, |
| | 1265 | }; |
| | 1266 | |
| | 1267 | const var_result_id = switch (storage_class) { |
| | 1268 | .Generic => self.spv.allocId(), |
| | 1269 | else => result_id, |
| | 1270 | }; |
| | 1271 | |
| | 1272 | try self.lowerIndirectConstant( |
| | 1273 | var_result_id, |
| | 1274 | decl.ty, |
| | 1275 | init_val, |
| | 1276 | actual_storage_class, |
| | 1277 | decl.@"align", |
| | 1278 | ); |
| | 1279 | |
| | 1280 | if (storage_class == .Generic) { |
| | 1281 | const section = &self.spv.sections.types_globals_constants; |
| | 1282 | const ty_ref = try self.resolveType(decl.ty, .indirect); |
| | 1283 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, decl.@"align"); |
| | 1284 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| | 1285 | .id_result_type = self.typeId(ptr_ty_ref), |
| | 1286 | .id_result = result_id, |
| | 1287 | .pointer = var_result_id, |
| | 1288 | }); |
| | 1289 | } |
| 1104 | } | 1290 | } |
| 1105 | } | 1291 | } |
| 1106 | | 1292 | |
| ... | @@ -1358,13 +1544,13 @@ pub const DeclGen = struct { | ... | @@ -1358,13 +1544,13 @@ pub const DeclGen = struct { |
| 1358 | // Construct the SPIR-V result type. | 1544 | // Construct the SPIR-V result type. |
| 1359 | // It is almost the same as the zig one, except that the fields must be the same type | 1545 | // It is almost the same as the zig one, except that the fields must be the same type |
| 1360 | // and they must be unsigned. | 1546 | // and they must be unsigned. |
| 1361 | const overflow_result_ty = try self.simpleStructTypeId(&.{ | 1547 | const overflow_result_ty_ref = try self.spv.simpleStructType(&.{ |
| 1362 | .{ .ty = overflow_member_ty, .name = "res" }, | 1548 | .{ .ty = overflow_member_ty, .name = "res" }, |
| 1363 | .{ .ty = overflow_member_ty, .name = "ov" }, | 1549 | .{ .ty = overflow_member_ty, .name = "ov" }, |
| 1364 | }); | 1550 | }); |
| 1365 | const result_id = self.spv.allocId(); | 1551 | const result_id = self.spv.allocId(); |
| 1366 | try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{ | 1552 | try self.func.body.emit(self.spv.gpa, .OpIAddCarry, .{ |
| 1367 | .id_result_type = overflow_result_ty, | 1553 | .id_result_type = self.typeId(overflow_result_ty_ref), |
| 1368 | .id_result = result_id, | 1554 | .id_result = result_id, |
| 1369 | .operand_1 = lhs, | 1555 | .operand_1 = lhs, |
| 1370 | .operand_2 = rhs, | 1556 | .operand_2 = rhs, |
| ... | @@ -1786,13 +1972,11 @@ pub const DeclGen = struct { | ... | @@ -1786,13 +1972,11 @@ pub const DeclGen = struct { |
| 1786 | .id_result = result_id, | 1972 | .id_result = result_id, |
| 1787 | .pointer = alloc_result_id, | 1973 | .pointer = alloc_result_id, |
| 1788 | }), | 1974 | }), |
| 1789 | else => { | 1975 | else => try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 1790 | try section.emitRaw(self.spv.gpa, .OpSpecConstantOp, 3 + 1); | 1976 | .id_result_type = self.typeId(ptr_ty_ref), |
| 1791 | section.writeOperand(IdRef, self.typeId(ptr_ty_ref)); | 1977 | .id_result = result_id, |
| 1792 | section.writeOperand(IdRef, result_id); | 1978 | .pointer = alloc_result_id, |
| 1793 | section.writeOperand(Opcode, .OpPtrCastToGeneric); | 1979 | }), |
| 1794 | section.writeOperand(IdRef, alloc_result_id); | | |
| 1795 | }, | | |
| 1796 | } | 1980 | } |
| 1797 | } | 1981 | } |
| 1798 | | 1982 | |