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