| ... | ... | @@ -284,8 +284,9 @@ pub fn defineComplete( |
| 284 | 284 | }, |
| 285 | 285 | }, |
| 286 | 286 | .array => if (ty.hasRuntimeBits(zcu)) { |
| 287 | const elem_ty = ty.childType(zcu); |
| 287 | 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 | 290 | const array_cty: CType = .{ .array = .{ |
| 290 | 291 | .len = ty.arrayLenIncludingSentinel(zcu), |
| 291 | 292 | .elem_ty = &elem_cty, |
| ... | ... | @@ -295,17 +296,28 @@ pub fn defineComplete( |
| 295 | 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 | | name_cty.fmtTypeName(zcu), |
| 300 | | array_cty.fmtDeclaratorPrefix(zcu), |
| 301 | | array_cty.fmtDeclaratorSuffix(zcu), |
| 302 | | ty.fmt(pt), |
| 303 | | }); |
| 299 | if (elem_ty.defaultStructFieldAlignment(.auto, zcu) == elem_ty.abiAlignment(zcu)) { |
| 300 | try w.print("{f} {{ {f}array{f}; }}; /* {f} */\n", .{ |
| 301 | name_cty.fmtTypeName(zcu), |
| 302 | array_cty.fmtDeclaratorPrefix(zcu), |
| 303 | array_cty.fmtDeclaratorSuffix(zcu), |
| 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 | 315 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 305 | 316 | }, |
| 306 | 317 | .vector => if (ty.hasRuntimeBits(zcu)) { |
| 318 | const elem_ty = ty.childType(zcu); |
| 307 | 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 | 321 | const array_cty: CType = .{ .array = .{ |
| 310 | 322 | .len = ty.arrayLenIncludingSentinel(zcu), |
| 311 | 323 | .elem_ty = &elem_cty, |
| ... | ... | @@ -351,21 +363,39 @@ fn defineTuple( |
| 351 | 363 | const ip = &zcu.intern_pool; |
| 352 | 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 | 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 | 385 | // If the alignment of other fields would not give the tuple sufficient alignment, we |
| 360 | 386 | // need to align the first field (which does not affect its offset, because 0 is always |
| 361 | 387 | // well-aligned) to indirectly specify the tuple alignment. |
| 362 | | const overalign: bool = for (tuple.types.get(ip)) |field_ty_ip| { |
| 363 | | const field_ty: Type = .fromInterned(field_ty_ip); |
| 364 | | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 365 | | const natural_align = field_ty.defaultStructFieldAlignment(.auto, zcu); |
| 366 | | if (natural_align.compareStrict(.gte, tuple_align)) break false; |
| 367 | | } else true; |
| 388 | const overalign: bool = switch (pack) { |
| 389 | true => tuple_align.compareStrict(.gt, .@"1"), |
| 390 | false => for (tuple.types.get(ip)) |field_ty_ip| { |
| 391 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 392 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 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 | 399 | const name_cty: CType = .{ .@"struct" = ty }; |
| 370 | 400 | try w.print("{f} {{ /* {f} */\n", .{ |
| 371 | 401 | name_cty.fmtTypeName(zcu), |
| ... | ... | @@ -376,18 +406,18 @@ fn defineTuple( |
| 376 | 406 | for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty_ip, field_val_ip, field_index| { |
| 377 | 407 | if (field_val_ip != .none) continue; // `comptime` field |
| 378 | 408 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 379 | | const field_align = field_ty.abiAlignment(zcu); |
| 380 | | zig_offset = field_align.forward(zig_offset); |
| 409 | zig_offset = field_ty.abiAlignment(zcu).forward(zig_offset); |
| 381 | 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 | 412 | try w.writeByte(' '); |
| 384 | 413 | if (zig_offset == 0 and overalign) { |
| 385 | 414 | // This is the first field; specify its alignment to align the tuple. |
| 386 | 415 | try writeFieldAlign(field_ty, tuple_align, w, zcu); |
| 387 | 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 | 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 | 421 | .fromLog2Units(@ctz(zig_offset)), |
| 392 | 422 | ); |
| 393 | 423 | try writeFieldAlign(field_ty, need_align, w, zcu); |
| ... | ... | @@ -403,7 +433,9 @@ fn defineTuple( |
| 403 | 433 | zig_offset += field_size; |
| 404 | 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 | 440 | try writeStaticAssertLayout(ty, name_cty, w, zcu); |
| 409 | 441 | } |
| ... | ... | @@ -521,7 +553,7 @@ fn defineUnionAuto( |
| 521 | 553 | const pack: bool = for (union_type.field_types.get(ip)) |field_ty_ip| { |
| 522 | 554 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 523 | 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 | 557 | if (natural_align.compareStrict(.gt, union_type.alignment)) break true; |
| 526 | 558 | // The tag will immediately follow the payload. This layout may put the tag in what would |
| 527 | 559 | // otherwise be padding on the payload union, because if the most-aligned union field is not |
| ... | ... | @@ -539,7 +571,7 @@ fn defineUnionAuto( |
| 539 | 571 | false => for (union_type.field_types.get(ip)) |field_ty_ip| { |
| 540 | 572 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 541 | 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 | 575 | if (natural_align.compareStrict(.gte, union_type.alignment)) break false; |
| 544 | 576 | } else overalign: { |
| 545 | 577 | if (union_type.has_runtime_tag) { |
| ... | ... | @@ -610,7 +642,7 @@ fn defineUnionExtern( |
| 610 | 642 | const pack: bool = for (union_type.field_types.get(ip)) |field_ty_ip| { |
| 611 | 643 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 612 | 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 | 646 | if (natural_align.compareStrict(.gt, union_type.alignment)) break true; |
| 615 | 647 | } else false; |
| 616 | 648 | |
| ... | ... | @@ -622,7 +654,7 @@ fn defineUnionExtern( |
| 622 | 654 | false => for (union_type.field_types.get(ip)) |field_ty_ip| { |
| 623 | 655 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 624 | 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 | 658 | if (natural_align.compareStrict(.gte, union_type.alignment)) break false; |
| 627 | 659 | } else overalign: { |
| 628 | 660 | if (union_type.has_runtime_tag) { |
| ... | ... | @@ -672,7 +704,7 @@ fn writeFieldAlign( |
| 672 | 704 | w: *Writer, |
| 673 | 705 | zcu: *const Zcu, |
| 674 | 706 | ) Writer.Error!void { |
| 675 | | if (alignment.compareStrict(.lt, ty.abiAlignment(zcu))) { |
| 707 | if (alignment.compareStrict(.lt, ty.defaultStructFieldAlignment(.auto, zcu))) { |
| 676 | 708 | try w.print("zig_under_align({d}) ", .{alignment.toByteUnits().?}); |
| 677 | 709 | } else { |
| 678 | 710 | try w.print("zig_align({d}) ", .{alignment.toByteUnits().?}); |