| ... | @@ -246,6 +246,8 @@ pub fn defineComplete( | ... | @@ -246,6 +246,8 @@ pub fn defineComplete( |
| 246 | ptr_cty.fmtDeclaratorPrefix(zcu), | 246 | ptr_cty.fmtDeclaratorPrefix(zcu), |
| 247 | ptr_cty.fmtDeclaratorSuffix(zcu), | 247 | ptr_cty.fmtDeclaratorSuffix(zcu), |
| 248 | }); | 248 | }); |
| | 249 | // Don't bother with `writeStaticAssertLayout`---there's not really any way we could mess |
| | 250 | // slices up, and they're all obviously the same layout. |
| 249 | }, | 251 | }, |
| 250 | .optional => switch (CType.classifyOptional(ty, zcu)) { | 252 | .optional => switch (CType.classifyOptional(ty, zcu)) { |
| 251 | .error_set, | 253 | .error_set, |
| ... | @@ -260,6 +262,7 @@ pub fn defineComplete( | ... | @@ -260,6 +262,7 @@ pub fn defineComplete( |
| 260 | name_cty.fmtTypeName(zcu), | 262 | name_cty.fmtTypeName(zcu), |
| 261 | ty.fmt(pt), | 263 | ty.fmt(pt), |
| 262 | }); | 264 | }); |
| | 265 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 263 | }, | 266 | }, |
| 264 | | 267 | |
| 265 | .@"struct" => { | 268 | .@"struct" => { |
| ... | @@ -277,6 +280,7 @@ pub fn defineComplete( | ... | @@ -277,6 +280,7 @@ pub fn defineComplete( |
| 277 | payload_cty.fmtDeclaratorPrefix(zcu), | 280 | payload_cty.fmtDeclaratorPrefix(zcu), |
| 278 | payload_cty.fmtDeclaratorSuffix(zcu), | 281 | payload_cty.fmtDeclaratorSuffix(zcu), |
| 279 | }); | 282 | }); |
| | 283 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 280 | }, | 284 | }, |
| 281 | }, | 285 | }, |
| 282 | .array => if (ty.hasRuntimeBits(zcu)) { | 286 | .array => if (ty.hasRuntimeBits(zcu)) { |
| ... | @@ -297,6 +301,7 @@ pub fn defineComplete( | ... | @@ -297,6 +301,7 @@ pub fn defineComplete( |
| 297 | array_cty.fmtDeclaratorSuffix(zcu), | 301 | array_cty.fmtDeclaratorSuffix(zcu), |
| 298 | ty.fmt(pt), | 302 | ty.fmt(pt), |
| 299 | }); | 303 | }); |
| | 304 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 300 | }, | 305 | }, |
| 301 | .vector => if (ty.hasRuntimeBits(zcu)) { | 306 | .vector => if (ty.hasRuntimeBits(zcu)) { |
| 302 | const name_cty: CType = .{ .vec = ty }; | 307 | const name_cty: CType = .{ .vec = ty }; |
| ... | @@ -312,6 +317,7 @@ pub fn defineComplete( | ... | @@ -312,6 +317,7 @@ pub fn defineComplete( |
| 312 | array_cty.fmtDeclaratorSuffix(zcu), | 317 | array_cty.fmtDeclaratorSuffix(zcu), |
| 313 | ty.fmt(pt), | 318 | ty.fmt(pt), |
| 314 | }); | 319 | }); |
| | 320 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 315 | }, | 321 | }, |
| 316 | else => {}, | 322 | else => {}, |
| 317 | } | 323 | } |
| ... | @@ -374,18 +380,21 @@ fn defineTuple( | ... | @@ -374,18 +380,21 @@ fn defineTuple( |
| 374 | zig_offset = field_align.forward(zig_offset); | 380 | zig_offset = field_align.forward(zig_offset); |
| 375 | if (!field_ty.hasRuntimeBits(zcu)) continue; | 381 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 376 | c_offset = field_align.forward(c_offset); | 382 | c_offset = field_align.forward(c_offset); |
| | 383 | try w.writeByte(' '); |
| 377 | if (zig_offset == 0 and overalign) { | 384 | if (zig_offset == 0 and overalign) { |
| 378 | // This is the first field; specify its alignment to align the tuple. | 385 | // This is the first field; specify its alignment to align the tuple. |
| 379 | try w.print(" zig_align({d})", .{tuple_align.toByteUnits().?}); | 386 | try writeFieldAlign(field_ty, tuple_align, w, zcu); |
| 380 | } else if (zig_offset > c_offset) { | 387 | } else if (zig_offset > c_offset) { |
| 381 | // This field needs to be overaligned compared to what its offset would otherwise be. | 388 | // This field needs to be overaligned compared to what its offset would otherwise be. |
| 382 | const need_align: Alignment = .fromLog2Units(@ctz(zig_offset)); | 389 | const need_align: Alignment = .minStrict( |
| 383 | try w.print(" zig_align({d})", .{need_align.toByteUnits().?}); | 390 | tuple_align, // don't make the struct more aligned than it should be |
| | 391 | .fromLog2Units(@ctz(zig_offset)), |
| | 392 | ); |
| | 393 | try writeFieldAlign(field_ty, need_align, w, zcu); |
| 384 | c_offset = need_align.forward(c_offset); | 394 | c_offset = need_align.forward(c_offset); |
| 385 | assert(c_offset == zig_offset); | | |
| 386 | } | 395 | } |
| 387 | const field_cty: CType = try .lower(field_ty, deps, arena, zcu); | 396 | const field_cty: CType = try .lower(field_ty, deps, arena, zcu); |
| 388 | try w.print(" {f}f{d}{f};\n", .{ | 397 | try w.print("{f}f{d}{f};\n", .{ |
| 389 | field_cty.fmtDeclaratorPrefix(zcu), | 398 | field_cty.fmtDeclaratorPrefix(zcu), |
| 390 | field_index, | 399 | field_index, |
| 391 | field_cty.fmtDeclaratorSuffix(zcu), | 400 | field_cty.fmtDeclaratorSuffix(zcu), |
| ... | @@ -395,6 +404,8 @@ fn defineTuple( | ... | @@ -395,6 +404,8 @@ fn defineTuple( |
| 395 | c_offset += field_size; | 404 | c_offset += field_size; |
| 396 | } | 405 | } |
| 397 | try w.writeAll("};\n"); | 406 | try w.writeAll("};\n"); |
| | 407 | |
| | 408 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 398 | } | 409 | } |
| 399 | fn defineStruct( | 410 | fn defineStruct( |
| 400 | ty: Type, | 411 | ty: Type, |
| ... | @@ -420,6 +431,8 @@ fn defineStruct( | ... | @@ -420,6 +431,8 @@ fn defineStruct( |
| 420 | const natural_offset = natural_align.forward(offset); | 431 | const natural_offset = natural_align.forward(offset); |
| 421 | const actual_offset = struct_type.field_offsets.get(ip)[field_index]; | 432 | const actual_offset = struct_type.field_offsets.get(ip)[field_index]; |
| 422 | if (actual_offset < natural_offset) break :pack true; | 433 | if (actual_offset < natural_offset) break :pack true; |
| | 434 | // Also pack if any field is more aligned than the struct should be. |
| | 435 | if (natural_align.compareStrict(.gt, struct_type.alignment)) break :pack true; |
| 423 | offset = actual_offset + field_ty.abiSize(zcu); | 436 | offset = actual_offset + field_ty.abiSize(zcu); |
| 424 | } | 437 | } |
| 425 | break :pack false; | 438 | break :pack false; |
| ... | @@ -459,22 +472,22 @@ fn defineStruct( | ... | @@ -459,22 +472,22 @@ fn defineStruct( |
| 459 | false => natural_align.forward(offset), | 472 | false => natural_align.forward(offset), |
| 460 | }; | 473 | }; |
| 461 | const actual_offset = struct_type.field_offsets.get(ip)[field_index]; | 474 | const actual_offset = struct_type.field_offsets.get(ip)[field_index]; |
| | 475 | try w.writeByte(' '); |
| 462 | if (actual_offset == 0 and overalign) { | 476 | if (actual_offset == 0 and overalign) { |
| 463 | // This is the first field; specify its alignment to align the struct. | 477 | // This is the first field; specify its alignment to align the struct. |
| 464 | try w.print(" zig_align({d})", .{struct_type.alignment.toByteUnits().?}); | 478 | try writeFieldAlign(field_ty, struct_type.alignment, w, zcu); |
| 465 | } else if (actual_offset > natural_offset) { | 479 | } else if (actual_offset > natural_offset) { |
| 466 | // This field needs to be underaligned or overaligned compared to what its | 480 | // This field needs to be underaligned or overaligned compared to what its |
| 467 | // offset would otherwise be. | 481 | // offset would otherwise be. |
| 468 | const need_align: Alignment = .fromLog2Units(@ctz(actual_offset)); | 482 | const need_align: Alignment = .minStrict( |
| 469 | if (need_align.compareStrict(.lt, natural_align)) { | 483 | struct_type.alignment, // don't make the struct more aligned than it should be |
| 470 | try w.print(" zig_under_align({d})", .{need_align.toByteUnits().?}); | 484 | .fromLog2Units(@ctz(actual_offset)), |
| 471 | } else { | 485 | ); |
| 472 | try w.print(" zig_align({d})", .{need_align.toByteUnits().?}); | 486 | try writeFieldAlign(field_ty, need_align, w, zcu); |
| 473 | } | | |
| 474 | } | 487 | } |
| 475 | const field_cty: CType = try .lower(field_ty, deps, arena, zcu); | 488 | const field_cty: CType = try .lower(field_ty, deps, arena, zcu); |
| 476 | const field_name = struct_type.field_names.get(ip)[field_index].toSlice(ip); | 489 | const field_name = struct_type.field_names.get(ip)[field_index].toSlice(ip); |
| 477 | try w.print(" {f}{f}{f};\n", .{ | 490 | try w.print("{f}{f}{f};\n", .{ |
| 478 | field_cty.fmtDeclaratorPrefix(zcu), | 491 | field_cty.fmtDeclaratorPrefix(zcu), |
| 479 | fmtIdentSolo(field_name), | 492 | fmtIdentSolo(field_name), |
| 480 | field_cty.fmtDeclaratorSuffix(zcu), | 493 | field_cty.fmtDeclaratorSuffix(zcu), |
| ... | @@ -485,6 +498,8 @@ fn defineStruct( | ... | @@ -485,6 +498,8 @@ fn defineStruct( |
| 485 | try w.writeByte('}'); | 498 | try w.writeByte('}'); |
| 486 | if (pack) try w.writeByte(')'); | 499 | if (pack) try w.writeByte(')'); |
| 487 | try w.writeAll(";\n"); | 500 | try w.writeAll(";\n"); |
| | 501 | |
| | 502 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 488 | } | 503 | } |
| 489 | fn defineUnionAuto( | 504 | fn defineUnionAuto( |
| 490 | ty: Type, | 505 | ty: Type, |
| ... | @@ -500,12 +515,20 @@ fn defineUnionAuto( | ... | @@ -500,12 +515,20 @@ fn defineUnionAuto( |
| 500 | const union_type = ip.loadUnionType(ty.toIntern()); | 515 | const union_type = ip.loadUnionType(ty.toIntern()); |
| 501 | const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type); | 516 | const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type); |
| 502 | | 517 | |
| | 518 | const layout = Type.getUnionLayout(union_type, zcu); |
| | 519 | |
| 503 | // If there are any underaligned fields, we need to byte-pack the union. | 520 | // If there are any underaligned fields, we need to byte-pack the union. |
| 504 | const pack: bool = for (union_type.field_types.get(ip)) |field_ty_ip| { | 521 | const pack: bool = for (union_type.field_types.get(ip)) |field_ty_ip| { |
| 505 | const field_ty: Type = .fromInterned(field_ty_ip); | 522 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 506 | if (!field_ty.hasRuntimeBits(zcu)) continue; | 523 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 507 | const natural_align = field_ty.abiAlignment(zcu); | 524 | const natural_align = field_ty.abiAlignment(zcu); |
| 508 | if (natural_align.compareStrict(.gt, union_type.alignment)) break true; | 525 | if (natural_align.compareStrict(.gt, union_type.alignment)) break true; |
| | 526 | // The tag will immediately follow the payload. This layout may put the tag in what would |
| | 527 | // otherwise be padding on the payload union, because if the most-aligned union field is not |
| | 528 | // the largest one, a larger field may make the payload "underaligned" overall. As such, we |
| | 529 | // need to check whether this field is okay with the payload size, and if not then we must |
| | 530 | // byte-pack. |
| | 531 | if (!natural_align.check(layout.payload_size)) break true; |
| 509 | } else false; | 532 | } else false; |
| 510 | | 533 | |
| 511 | // If the alignment of other fields would not give the union sufficient alignment, we | 534 | // If the alignment of other fields would not give the union sufficient alignment, we |
| ... | @@ -536,6 +559,10 @@ fn defineUnionAuto( | ... | @@ -536,6 +559,10 @@ fn defineUnionAuto( |
| 536 | }); | 559 | }); |
| 537 | if (payload_has_bits) { | 560 | if (payload_has_bits) { |
| 538 | try w.writeByte(' '); | 561 | try w.writeByte(' '); |
| | 562 | if (overalign) { |
| | 563 | // Specify the alignment of `union { ... } payload;` to align the union's `struct`. |
| | 564 | try w.print("zig_align({d}) ", .{union_type.alignment.toByteUnits().?}); |
| | 565 | } |
| 539 | if (pack) try w.writeAll("zig_packed("); | 566 | if (pack) try w.writeAll("zig_packed("); |
| 540 | try w.writeAll("union {\n"); | 567 | try w.writeAll("union {\n"); |
| 541 | for (0..enum_tag_ty.enumFieldCount(zcu)) |field_index| { | 568 | for (0..enum_tag_ty.enumFieldCount(zcu)) |field_index| { |
| ... | @@ -543,12 +570,7 @@ fn defineUnionAuto( | ... | @@ -543,12 +570,7 @@ fn defineUnionAuto( |
| 543 | if (!field_ty.hasRuntimeBits(zcu)) continue; | 570 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 544 | const field_name = enum_tag_ty.enumFieldName(field_index, zcu).toSlice(ip); | 571 | const field_name = enum_tag_ty.enumFieldName(field_index, zcu).toSlice(ip); |
| 545 | const field_cty: CType = try .lower(field_ty, deps, arena, zcu); | 572 | const field_cty: CType = try .lower(field_ty, deps, arena, zcu); |
| 546 | try w.writeAll(" "); | 573 | try w.print(" {f}{f}{f};\n", .{ |
| 547 | if (overalign and field_index == 0) { | | |
| 548 | // This is the first field; specify its alignment to align the union. | | |
| 549 | try w.print("zig_align({d}) ", .{union_type.alignment.toByteUnits().?}); | | |
| 550 | } | | |
| 551 | try w.print("{f}{f}{f};\n", .{ | | |
| 552 | field_cty.fmtDeclaratorPrefix(zcu), | 574 | field_cty.fmtDeclaratorPrefix(zcu), |
| 553 | fmtIdentSolo(field_name), | 575 | fmtIdentSolo(field_name), |
| 554 | field_cty.fmtDeclaratorSuffix(zcu), | 576 | field_cty.fmtDeclaratorSuffix(zcu), |
| ... | @@ -566,6 +588,8 @@ fn defineUnionAuto( | ... | @@ -566,6 +588,8 @@ fn defineUnionAuto( |
| 566 | }); | 588 | }); |
| 567 | } | 589 | } |
| 568 | try w.writeAll("};\n"); | 590 | try w.writeAll("};\n"); |
| | 591 | |
| | 592 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 569 | } | 593 | } |
| 570 | fn defineUnionExtern( | 594 | fn defineUnionExtern( |
| 571 | ty: Type, | 595 | ty: Type, |
| ... | @@ -622,11 +646,12 @@ fn defineUnionExtern( | ... | @@ -622,11 +646,12 @@ fn defineUnionExtern( |
| 622 | if (!field_ty.hasRuntimeBits(zcu)) continue; | 646 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 623 | const field_name = enum_tag_ty.enumFieldName(field_index, zcu).toSlice(ip); | 647 | const field_name = enum_tag_ty.enumFieldName(field_index, zcu).toSlice(ip); |
| 624 | const field_cty: CType = try .lower(field_ty, deps, arena, zcu); | 648 | const field_cty: CType = try .lower(field_ty, deps, arena, zcu); |
| | 649 | try w.writeByte(' '); |
| 625 | if (overalign and field_index == 0) { | 650 | if (overalign and field_index == 0) { |
| 626 | // This is the first field; specify its alignment to align the union. | 651 | // This is the first field; specify its alignment to align the union. |
| 627 | try w.print(" zig_align({d})", .{union_type.alignment.toByteUnits().?}); | 652 | try writeFieldAlign(field_ty, union_type.alignment, w, zcu); |
| 628 | } | 653 | } |
| 629 | try w.print(" {f}{f}{f};\n", .{ | 654 | try w.print("{f}{f}{f};\n", .{ |
| 630 | field_cty.fmtDeclaratorPrefix(zcu), | 655 | field_cty.fmtDeclaratorPrefix(zcu), |
| 631 | fmtIdentSolo(field_name), | 656 | fmtIdentSolo(field_name), |
| 632 | field_cty.fmtDeclaratorSuffix(zcu), | 657 | field_cty.fmtDeclaratorSuffix(zcu), |
| ... | @@ -635,6 +660,40 @@ fn defineUnionExtern( | ... | @@ -635,6 +660,40 @@ fn defineUnionExtern( |
| 635 | try w.writeByte('}'); | 660 | try w.writeByte('}'); |
| 636 | if (pack) try w.writeByte(')'); | 661 | if (pack) try w.writeByte(')'); |
| 637 | try w.writeAll(";\n"); | 662 | try w.writeAll(";\n"); |
| | 663 | |
| | 664 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| | 665 | } |
| | 666 | |
| | 667 | /// Writes an annotation which, placed before a struct/union field declaration with field type `ty`, |
| | 668 | /// will specify that field as having the given alignment. |
| | 669 | fn writeFieldAlign( |
| | 670 | ty: Type, |
| | 671 | alignment: Alignment, |
| | 672 | w: *Writer, |
| | 673 | zcu: *const Zcu, |
| | 674 | ) Writer.Error!void { |
| | 675 | if (alignment.compareStrict(.lt, ty.abiAlignment(zcu))) { |
| | 676 | try w.print("zig_under_align({d}) ", .{alignment.toByteUnits().?}); |
| | 677 | } else { |
| | 678 | try w.print("zig_align({d}) ", .{alignment.toByteUnits().?}); |
| | 679 | } |
| | 680 | } |
| | 681 | |
| | 682 | /// Emits static assertions that the size and alignment of `cty` match those of the Zig type `ty`. |
| | 683 | fn writeStaticAssertLayout( |
| | 684 | ty: Type, |
| | 685 | cty: CType, |
| | 686 | w: *Writer, |
| | 687 | zcu: *const Zcu, |
| | 688 | ) Writer.Error!void { |
| | 689 | try w.print( |
| | 690 | \\zig_static_assert(sizeof ({f}) == {d}, "incorrect size"); |
| | 691 | \\zig_static_assert(_Alignof ({f}) == {d}, "incorrect alignment"); |
| | 692 | \\ |
| | 693 | , .{ |
| | 694 | cty.fmtTypeName(zcu), ty.abiSize(zcu), |
| | 695 | cty.fmtTypeName(zcu), ty.abiAlignment(zcu).toByteUnits().?, |
| | 696 | }); |
| 638 | } | 697 | } |
| 639 | | 698 | |
| 640 | const std = @import("std"); | 699 | const std = @import("std"); |