| ... | @@ -284,8 +284,9 @@ pub fn defineComplete( | ... | @@ -284,8 +284,9 @@ pub fn defineComplete( |
| 284 | }, | 284 | }, |
| 285 | }, | 285 | }, |
| 286 | .array => if (ty.hasRuntimeBits(zcu)) { | 286 | .array => if (ty.hasRuntimeBits(zcu)) { |
| | 287 | const elem_ty = ty.childType(zcu); |
| 287 | const name_cty: CType = .{ .arr = ty }; | 288 | const name_cty: CType = .{ .arr = ty }; |
| 288 | const elem_cty: CType = try .lower(ty.childType(zcu), deps, arena, zcu); | 289 | const elem_cty: CType = try .lower(elem_ty, deps, arena, zcu); |
| 289 | const array_cty: CType = .{ .array = .{ | 290 | const array_cty: CType = .{ .array = .{ |
| 290 | .len = ty.arrayLenIncludingSentinel(zcu), | 291 | .len = ty.arrayLenIncludingSentinel(zcu), |
| 291 | .elem_ty = &elem_cty, | 292 | .elem_ty = &elem_cty, |
| ... | @@ -295,17 +296,28 @@ pub fn defineComplete( | ... | @@ -295,17 +296,28 @@ pub fn defineComplete( |
| 295 | break :nonstring Value.compareHetero(s, .neq, .zero_comptime_int, zcu); | 296 | break :nonstring Value.compareHetero(s, .neq, .zero_comptime_int, zcu); |
| 296 | }, | 297 | }, |
| 297 | } }; | 298 | } }; |
| 298 | try w.print("{f} {{ {f}array{f}; }}; /* {f} */\n", .{ | 299 | if (elem_ty.defaultStructFieldAlignment(.auto, zcu) == elem_ty.abiAlignment(zcu)) { |
| 299 | name_cty.fmtTypeName(zcu), | 300 | try w.print("{f} {{ {f}array{f}; }}; /* {f} */\n", .{ |
| 300 | array_cty.fmtDeclaratorPrefix(zcu), | 301 | name_cty.fmtTypeName(zcu), |
| 301 | array_cty.fmtDeclaratorSuffix(zcu), | 302 | array_cty.fmtDeclaratorPrefix(zcu), |
| 302 | ty.fmt(pt), | 303 | array_cty.fmtDeclaratorSuffix(zcu), |
| 303 | }); | 304 | ty.fmt(pt), |
| | 305 | }); |
| | 306 | } else { |
| | 307 | try w.print("zig_packed({f} {{ zig_under_align({d}) {f}array{f}; }}); /* {f} */\n", .{ |
| | 308 | name_cty.fmtTypeName(zcu), |
| | 309 | elem_ty.abiAlignment(zcu).toByteUnits().?, |
| | 310 | array_cty.fmtDeclaratorPrefix(zcu), |
| | 311 | array_cty.fmtDeclaratorSuffix(zcu), |
| | 312 | ty.fmt(pt), |
| | 313 | }); |
| | 314 | } |
| 304 | try writeStaticAssertLayout(ty, name_cty, w, zcu); | 315 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 305 | }, | 316 | }, |
| 306 | .vector => if (ty.hasRuntimeBits(zcu)) { | 317 | .vector => if (ty.hasRuntimeBits(zcu)) { |
| | 318 | const elem_ty = ty.childType(zcu); |
| 307 | const name_cty: CType = .{ .vec = ty }; | 319 | const name_cty: CType = .{ .vec = ty }; |
| 308 | const elem_cty: CType = try .lower(ty.childType(zcu), deps, arena, zcu); | 320 | const elem_cty: CType = try .lower(elem_ty, deps, arena, zcu); |
| 309 | const array_cty: CType = .{ .array = .{ | 321 | const array_cty: CType = .{ .array = .{ |
| 310 | .len = ty.arrayLenIncludingSentinel(zcu), | 322 | .len = ty.arrayLenIncludingSentinel(zcu), |
| 311 | .elem_ty = &elem_cty, | 323 | .elem_ty = &elem_cty, |
| ... | @@ -351,21 +363,39 @@ fn defineTuple( | ... | @@ -351,21 +363,39 @@ fn defineTuple( |
| 351 | const ip = &zcu.intern_pool; | 363 | const ip = &zcu.intern_pool; |
| 352 | const tuple = ip.indexToKey(ty.toIntern()).tuple_type; | 364 | const tuple = ip.indexToKey(ty.toIntern()).tuple_type; |
| 353 | | 365 | |
| 354 | // Fields cannot be underaligned, because tuple fields cannot have specified alignments. | | |
| 355 | // However, overaligned fields are possible thanks to intermediate zero-bit fields. | | |
| 356 | | | |
| 357 | const tuple_align = ty.abiAlignment(zcu); | 366 | const tuple_align = ty.abiAlignment(zcu); |
| 358 | | 367 | |
| | 368 | // If there are any underaligned fields, we need to byte-pack the tuple. |
| | 369 | const pack: bool = pack: { |
| | 370 | var offset: u64 = 0; |
| | 371 | for (tuple.types.get(ip)) |field_ty_ip| { |
| | 372 | const field_ty: Type = .fromInterned(field_ty_ip); |
| | 373 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| | 374 | const natural_align = field_ty.defaultStructFieldAlignment(.auto, zcu); |
| | 375 | const natural_offset = natural_align.forward(offset); |
| | 376 | offset = field_ty.abiAlignment(zcu).forward(offset); |
| | 377 | if (offset < natural_offset) break :pack true; |
| | 378 | // Also pack if any field is more aligned than the tuple should be. |
| | 379 | if (natural_align.compareStrict(.gt, tuple_align)) break :pack true; |
| | 380 | offset += field_ty.abiSize(zcu); |
| | 381 | } |
| | 382 | break :pack false; |
| | 383 | }; |
| | 384 | |
| 359 | // If the alignment of other fields would not give the tuple sufficient alignment, we | 385 | // If the alignment of other fields would not give the tuple sufficient alignment, we |
| 360 | // need to align the first field (which does not affect its offset, because 0 is always | 386 | // need to align the first field (which does not affect its offset, because 0 is always |
| 361 | // well-aligned) to indirectly specify the tuple alignment. | 387 | // well-aligned) to indirectly specify the tuple alignment. |
| 362 | const overalign: bool = for (tuple.types.get(ip)) |field_ty_ip| { | 388 | const overalign: bool = switch (pack) { |
| 363 | const field_ty: Type = .fromInterned(field_ty_ip); | 389 | true => tuple_align.compareStrict(.gt, .@"1"), |
| 364 | if (!field_ty.hasRuntimeBits(zcu)) continue; | 390 | false => for (tuple.types.get(ip)) |field_ty_ip| { |
| 365 | const natural_align = field_ty.defaultStructFieldAlignment(.auto, zcu); | 391 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 366 | if (natural_align.compareStrict(.gte, tuple_align)) break false; | 392 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 367 | } else true; | 393 | const natural_align = field_ty.defaultStructFieldAlignment(.auto, zcu); |
| | 394 | if (natural_align.compareStrict(.gte, tuple_align)) break false; |
| | 395 | } else true, |
| | 396 | }; |
| 368 | | 397 | |
| | 398 | if (pack) try w.writeAll("zig_packed("); |
| 369 | const name_cty: CType = .{ .@"struct" = ty }; | 399 | const name_cty: CType = .{ .@"struct" = ty }; |
| 370 | try w.print("{f} {{ /* {f} */\n", .{ | 400 | try w.print("{f} {{ /* {f} */\n", .{ |
| 371 | name_cty.fmtTypeName(zcu), | 401 | name_cty.fmtTypeName(zcu), |
| ... | @@ -376,18 +406,18 @@ fn defineTuple( | ... | @@ -376,18 +406,18 @@ fn defineTuple( |
| 376 | for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty_ip, field_val_ip, field_index| { | 406 | for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty_ip, field_val_ip, field_index| { |
| 377 | if (field_val_ip != .none) continue; // `comptime` field | 407 | if (field_val_ip != .none) continue; // `comptime` field |
| 378 | const field_ty: Type = .fromInterned(field_ty_ip); | 408 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 379 | const field_align = field_ty.abiAlignment(zcu); | 409 | zig_offset = field_ty.abiAlignment(zcu).forward(zig_offset); |
| 380 | zig_offset = field_align.forward(zig_offset); | | |
| 381 | if (!field_ty.hasRuntimeBits(zcu)) continue; | 410 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 382 | c_offset = field_align.forward(c_offset); | 411 | if (!pack) c_offset = field_ty.defaultStructFieldAlignment(.auto, zcu).forward(c_offset); |
| 383 | try w.writeByte(' '); | 412 | try w.writeByte(' '); |
| 384 | if (zig_offset == 0 and overalign) { | 413 | if (zig_offset == 0 and overalign) { |
| 385 | // This is the first field; specify its alignment to align the tuple. | 414 | // This is the first field; specify its alignment to align the tuple. |
| 386 | try writeFieldAlign(field_ty, tuple_align, w, zcu); | 415 | try writeFieldAlign(field_ty, tuple_align, w, zcu); |
| 387 | } else if (zig_offset > c_offset) { | 416 | } else if (zig_offset > c_offset) { |
| 388 | // This field needs to be overaligned compared to what its offset would otherwise be. | 417 | // This field needs to be underaligned or overaligned compared to what its |
| | 418 | // offset would otherwise be. |
| 389 | const need_align: Alignment = .minStrict( | 419 | const need_align: Alignment = .minStrict( |
| 390 | tuple_align, // don't make the struct more aligned than it should be | 420 | tuple_align, // don't make the tuple more aligned than it should be |
| 391 | .fromLog2Units(@ctz(zig_offset)), | 421 | .fromLog2Units(@ctz(zig_offset)), |
| 392 | ); | 422 | ); |
| 393 | try writeFieldAlign(field_ty, need_align, w, zcu); | 423 | try writeFieldAlign(field_ty, need_align, w, zcu); |
| ... | @@ -403,7 +433,9 @@ fn defineTuple( | ... | @@ -403,7 +433,9 @@ fn defineTuple( |
| 403 | zig_offset += field_size; | 433 | zig_offset += field_size; |
| 404 | c_offset += field_size; | 434 | c_offset += field_size; |
| 405 | } | 435 | } |
| 406 | try w.writeAll("};\n"); | 436 | try w.writeByte('}'); |
| | 437 | if (pack) try w.writeByte(')'); |
| | 438 | try w.writeAll(";\n"); |
| 407 | | 439 | |
| 408 | try writeStaticAssertLayout(ty, name_cty, w, zcu); | 440 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 409 | } | 441 | } |
| ... | @@ -521,7 +553,7 @@ fn defineUnionAuto( | ... | @@ -521,7 +553,7 @@ fn defineUnionAuto( |
| 521 | const pack: bool = for (union_type.field_types.get(ip)) |field_ty_ip| { | 553 | const pack: bool = for (union_type.field_types.get(ip)) |field_ty_ip| { |
| 522 | const field_ty: Type = .fromInterned(field_ty_ip); | 554 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 523 | if (!field_ty.hasRuntimeBits(zcu)) continue; | 555 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 524 | const natural_align = field_ty.abiAlignment(zcu); | 556 | const natural_align = field_ty.defaultStructFieldAlignment(.auto, zcu); |
| 525 | if (natural_align.compareStrict(.gt, union_type.alignment)) break true; | 557 | 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 | 558 | // 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 | 559 | // otherwise be padding on the payload union, because if the most-aligned union field is not |
| ... | @@ -539,7 +571,7 @@ fn defineUnionAuto( | ... | @@ -539,7 +571,7 @@ fn defineUnionAuto( |
| 539 | false => for (union_type.field_types.get(ip)) |field_ty_ip| { | 571 | false => for (union_type.field_types.get(ip)) |field_ty_ip| { |
| 540 | const field_ty: Type = .fromInterned(field_ty_ip); | 572 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 541 | if (!field_ty.hasRuntimeBits(zcu)) continue; | 573 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 542 | const natural_align = field_ty.abiAlignment(zcu); | 574 | const natural_align = field_ty.defaultStructFieldAlignment(.auto, zcu); |
| 543 | if (natural_align.compareStrict(.gte, union_type.alignment)) break false; | 575 | if (natural_align.compareStrict(.gte, union_type.alignment)) break false; |
| 544 | } else overalign: { | 576 | } else overalign: { |
| 545 | if (union_type.has_runtime_tag) { | 577 | if (union_type.has_runtime_tag) { |
| ... | @@ -610,7 +642,7 @@ fn defineUnionExtern( | ... | @@ -610,7 +642,7 @@ fn defineUnionExtern( |
| 610 | const pack: bool = for (union_type.field_types.get(ip)) |field_ty_ip| { | 642 | const pack: bool = for (union_type.field_types.get(ip)) |field_ty_ip| { |
| 611 | const field_ty: Type = .fromInterned(field_ty_ip); | 643 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 612 | if (!field_ty.hasRuntimeBits(zcu)) continue; | 644 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 613 | const natural_align = field_ty.abiAlignment(zcu); | 645 | const natural_align = field_ty.defaultStructFieldAlignment(.@"extern", zcu); |
| 614 | if (natural_align.compareStrict(.gt, union_type.alignment)) break true; | 646 | if (natural_align.compareStrict(.gt, union_type.alignment)) break true; |
| 615 | } else false; | 647 | } else false; |
| 616 | | 648 | |
| ... | @@ -622,7 +654,7 @@ fn defineUnionExtern( | ... | @@ -622,7 +654,7 @@ fn defineUnionExtern( |
| 622 | false => for (union_type.field_types.get(ip)) |field_ty_ip| { | 654 | false => for (union_type.field_types.get(ip)) |field_ty_ip| { |
| 623 | const field_ty: Type = .fromInterned(field_ty_ip); | 655 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 624 | if (!field_ty.hasRuntimeBits(zcu)) continue; | 656 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 625 | const natural_align = field_ty.abiAlignment(zcu); | 657 | const natural_align = field_ty.defaultStructFieldAlignment(.@"extern", zcu); |
| 626 | if (natural_align.compareStrict(.gte, union_type.alignment)) break false; | 658 | if (natural_align.compareStrict(.gte, union_type.alignment)) break false; |
| 627 | } else overalign: { | 659 | } else overalign: { |
| 628 | if (union_type.has_runtime_tag) { | 660 | if (union_type.has_runtime_tag) { |
| ... | @@ -672,7 +704,7 @@ fn writeFieldAlign( | ... | @@ -672,7 +704,7 @@ fn writeFieldAlign( |
| 672 | w: *Writer, | 704 | w: *Writer, |
| 673 | zcu: *const Zcu, | 705 | zcu: *const Zcu, |
| 674 | ) Writer.Error!void { | 706 | ) Writer.Error!void { |
| 675 | if (alignment.compareStrict(.lt, ty.abiAlignment(zcu))) { | 707 | if (alignment.compareStrict(.lt, ty.defaultStructFieldAlignment(.auto, zcu))) { |
| 676 | try w.print("zig_under_align({d}) ", .{alignment.toByteUnits().?}); | 708 | try w.print("zig_under_align({d}) ", .{alignment.toByteUnits().?}); |
| 677 | } else { | 709 | } else { |
| 678 | try w.print("zig_align({d}) ", .{alignment.toByteUnits().?}); | 710 | try w.print("zig_align({d}) ", .{alignment.toByteUnits().?}); |