| ... | ... | @@ -246,6 +246,8 @@ pub fn defineComplete( |
| 246 | 246 | ptr_cty.fmtDeclaratorPrefix(zcu), |
| 247 | 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 | 252 | .optional => switch (CType.classifyOptional(ty, zcu)) { |
| 251 | 253 | .error_set, |
| ... | ... | @@ -260,6 +262,7 @@ pub fn defineComplete( |
| 260 | 262 | name_cty.fmtTypeName(zcu), |
| 261 | 263 | ty.fmt(pt), |
| 262 | 264 | }); |
| 265 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 263 | 266 | }, |
| 264 | 267 | |
| 265 | 268 | .@"struct" => { |
| ... | ... | @@ -277,6 +280,7 @@ pub fn defineComplete( |
| 277 | 280 | payload_cty.fmtDeclaratorPrefix(zcu), |
| 278 | 281 | payload_cty.fmtDeclaratorSuffix(zcu), |
| 279 | 282 | }); |
| 283 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 280 | 284 | }, |
| 281 | 285 | }, |
| 282 | 286 | .array => if (ty.hasRuntimeBits(zcu)) { |
| ... | ... | @@ -297,6 +301,7 @@ pub fn defineComplete( |
| 297 | 301 | array_cty.fmtDeclaratorSuffix(zcu), |
| 298 | 302 | ty.fmt(pt), |
| 299 | 303 | }); |
| 304 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 300 | 305 | }, |
| 301 | 306 | .vector => if (ty.hasRuntimeBits(zcu)) { |
| 302 | 307 | const name_cty: CType = .{ .vec = ty }; |
| ... | ... | @@ -312,6 +317,7 @@ pub fn defineComplete( |
| 312 | 317 | array_cty.fmtDeclaratorSuffix(zcu), |
| 313 | 318 | ty.fmt(pt), |
| 314 | 319 | }); |
| 320 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 315 | 321 | }, |
| 316 | 322 | else => {}, |
| 317 | 323 | } |
| ... | ... | @@ -374,18 +380,21 @@ fn defineTuple( |
| 374 | 380 | zig_offset = field_align.forward(zig_offset); |
| 375 | 381 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 376 | 382 | c_offset = field_align.forward(c_offset); |
| 383 | try w.writeByte(' '); |
| 377 | 384 | if (zig_offset == 0 and overalign) { |
| 378 | 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 | 387 | } else if (zig_offset > c_offset) { |
| 381 | 388 | // This field needs to be overaligned compared to what its offset would otherwise be. |
| 382 | | const need_align: Alignment = .fromLog2Units(@ctz(zig_offset)); |
| 383 | | try w.print(" zig_align({d})", .{need_align.toByteUnits().?}); |
| 389 | const need_align: Alignment = .minStrict( |
| 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 | 394 | c_offset = need_align.forward(c_offset); |
| 385 | | assert(c_offset == zig_offset); |
| 386 | 395 | } |
| 387 | 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 | 398 | field_cty.fmtDeclaratorPrefix(zcu), |
| 390 | 399 | field_index, |
| 391 | 400 | field_cty.fmtDeclaratorSuffix(zcu), |
| ... | ... | @@ -395,6 +404,8 @@ fn defineTuple( |
| 395 | 404 | c_offset += field_size; |
| 396 | 405 | } |
| 397 | 406 | try w.writeAll("};\n"); |
| 407 | |
| 408 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 398 | 409 | } |
| 399 | 410 | fn defineStruct( |
| 400 | 411 | ty: Type, |
| ... | ... | @@ -420,6 +431,8 @@ fn defineStruct( |
| 420 | 431 | const natural_offset = natural_align.forward(offset); |
| 421 | 432 | const actual_offset = struct_type.field_offsets.get(ip)[field_index]; |
| 422 | 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 | 436 | offset = actual_offset + field_ty.abiSize(zcu); |
| 424 | 437 | } |
| 425 | 438 | break :pack false; |
| ... | ... | @@ -459,22 +472,22 @@ fn defineStruct( |
| 459 | 472 | false => natural_align.forward(offset), |
| 460 | 473 | }; |
| 461 | 474 | const actual_offset = struct_type.field_offsets.get(ip)[field_index]; |
| 475 | try w.writeByte(' '); |
| 462 | 476 | if (actual_offset == 0 and overalign) { |
| 463 | 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 | 479 | } else if (actual_offset > natural_offset) { |
| 466 | 480 | // This field needs to be underaligned or overaligned compared to what its |
| 467 | 481 | // offset would otherwise be. |
| 468 | | const need_align: Alignment = .fromLog2Units(@ctz(actual_offset)); |
| 469 | | if (need_align.compareStrict(.lt, natural_align)) { |
| 470 | | try w.print(" zig_under_align({d})", .{need_align.toByteUnits().?}); |
| 471 | | } else { |
| 472 | | try w.print(" zig_align({d})", .{need_align.toByteUnits().?}); |
| 473 | | } |
| 482 | const need_align: Alignment = .minStrict( |
| 483 | struct_type.alignment, // don't make the struct more aligned than it should be |
| 484 | .fromLog2Units(@ctz(actual_offset)), |
| 485 | ); |
| 486 | try writeFieldAlign(field_ty, need_align, w, zcu); |
| 474 | 487 | } |
| 475 | 488 | const field_cty: CType = try .lower(field_ty, deps, arena, zcu); |
| 476 | 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 | 491 | field_cty.fmtDeclaratorPrefix(zcu), |
| 479 | 492 | fmtIdentSolo(field_name), |
| 480 | 493 | field_cty.fmtDeclaratorSuffix(zcu), |
| ... | ... | @@ -485,6 +498,8 @@ fn defineStruct( |
| 485 | 498 | try w.writeByte('}'); |
| 486 | 499 | if (pack) try w.writeByte(')'); |
| 487 | 500 | try w.writeAll(";\n"); |
| 501 | |
| 502 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 488 | 503 | } |
| 489 | 504 | fn defineUnionAuto( |
| 490 | 505 | ty: Type, |
| ... | ... | @@ -500,12 +515,20 @@ fn defineUnionAuto( |
| 500 | 515 | const union_type = ip.loadUnionType(ty.toIntern()); |
| 501 | 516 | const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type); |
| 502 | 517 | |
| 518 | const layout = Type.getUnionLayout(union_type, zcu); |
| 519 | |
| 503 | 520 | // If there are any underaligned fields, we need to byte-pack the union. |
| 504 | 521 | const pack: bool = for (union_type.field_types.get(ip)) |field_ty_ip| { |
| 505 | 522 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 506 | 523 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 507 | 524 | const natural_align = field_ty.abiAlignment(zcu); |
| 508 | 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 | 532 | } else false; |
| 510 | 533 | |
| 511 | 534 | // If the alignment of other fields would not give the union sufficient alignment, we |
| ... | ... | @@ -536,6 +559,10 @@ fn defineUnionAuto( |
| 536 | 559 | }); |
| 537 | 560 | if (payload_has_bits) { |
| 538 | 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 | 566 | if (pack) try w.writeAll("zig_packed("); |
| 540 | 567 | try w.writeAll("union {\n"); |
| 541 | 568 | for (0..enum_tag_ty.enumFieldCount(zcu)) |field_index| { |
| ... | ... | @@ -543,12 +570,7 @@ fn defineUnionAuto( |
| 543 | 570 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 544 | 571 | const field_name = enum_tag_ty.enumFieldName(field_index, zcu).toSlice(ip); |
| 545 | 572 | const field_cty: CType = try .lower(field_ty, deps, arena, zcu); |
| 546 | | try w.writeAll(" "); |
| 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", .{ |
| 573 | try w.print(" {f}{f}{f};\n", .{ |
| 552 | 574 | field_cty.fmtDeclaratorPrefix(zcu), |
| 553 | 575 | fmtIdentSolo(field_name), |
| 554 | 576 | field_cty.fmtDeclaratorSuffix(zcu), |
| ... | ... | @@ -566,6 +588,8 @@ fn defineUnionAuto( |
| 566 | 588 | }); |
| 567 | 589 | } |
| 568 | 590 | try w.writeAll("};\n"); |
| 591 | |
| 592 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 569 | 593 | } |
| 570 | 594 | fn defineUnionExtern( |
| 571 | 595 | ty: Type, |
| ... | ... | @@ -622,11 +646,12 @@ fn defineUnionExtern( |
| 622 | 646 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 623 | 647 | const field_name = enum_tag_ty.enumFieldName(field_index, zcu).toSlice(ip); |
| 624 | 648 | const field_cty: CType = try .lower(field_ty, deps, arena, zcu); |
| 649 | try w.writeByte(' '); |
| 625 | 650 | if (overalign and field_index == 0) { |
| 626 | 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 | 655 | field_cty.fmtDeclaratorPrefix(zcu), |
| 631 | 656 | fmtIdentSolo(field_name), |
| 632 | 657 | field_cty.fmtDeclaratorSuffix(zcu), |
| ... | ... | @@ -635,6 +660,40 @@ fn defineUnionExtern( |
| 635 | 660 | try w.writeByte('}'); |
| 636 | 661 | if (pack) try w.writeByte(')'); |
| 637 | 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 | 699 | const std = @import("std"); |