zig/deps/aro/record_layout.zig
Veikka Tuominen 5792570197 add Aro sources as a dependency
ref: 5688dbccfb58216468267a0f46b96bed7013715a
2023-10-01 23:51:54 +03:00

670 lines
30 KiB
Zig
Vendored

//! Record layout code adapted from https://github.com/mahkoh/repr-c
//! Licensed under MIT license: https://github.com/mahkoh/repr-c/tree/master/repc/facade
const std = @import("std");
const Type = @import("Type.zig");
const Attribute = @import("Attribute.zig");
const Compilation = @import("Compilation.zig");
const Parser = @import("Parser.zig");
const Record = Type.Record;
const Field = Record.Field;
const TypeLayout = Type.TypeLayout;
const FieldLayout = Type.FieldLayout;
const target_util = @import("target.zig");
const BITS_PER_BYTE = 8;
const OngoingBitfield = struct {
size_bits: u64,
unused_size_bits: u64,
};
const SysVContext = struct {
/// Does the record have an __attribute__((packed)) annotation.
attr_packed: bool,
/// The value of #pragma pack(N) at the type level if any.
max_field_align_bits: ?u64,
/// The alignment of this record.
aligned_bits: u32,
is_union: bool,
/// The size of the record. This might not be a multiple of 8 if the record contains bit-fields.
/// For structs, this is also the offset of the first bit after the last field.
size_bits: u64,
/// non-null if the previous field was a non-zero-sized bit-field. Only used by MinGW.
ongoing_bitfield: ?OngoingBitfield,
comp: *const Compilation,
fn init(ty: Type, comp: *const Compilation, pragma_pack: ?u8) SysVContext {
var pack_value: ?u64 = null;
if (pragma_pack) |pak| {
pack_value = pak * BITS_PER_BYTE;
}
var req_align: u29 = BITS_PER_BYTE;
if (ty.requestedAlignment(comp)) |aln| {
req_align = aln * BITS_PER_BYTE;
}
return SysVContext{
.attr_packed = ty.hasAttribute(.@"packed"),
.max_field_align_bits = pack_value,
.aligned_bits = req_align,
.is_union = ty.is(.@"union"),
.size_bits = 0,
.comp = comp,
.ongoing_bitfield = null,
};
}
fn layoutFields(self: *SysVContext, rec: *const Record) void {
for (rec.fields, 0..) |*fld, fld_indx| {
const type_layout = computeLayout(fld.ty, self.comp);
var field_attrs: ?[]const Attribute = null;
if (rec.field_attributes) |attrs| {
field_attrs = attrs[fld_indx];
}
if (self.comp.target.isMinGW()) {
fld.layout = self.layoutMinGWField(fld, field_attrs, type_layout);
} else {
if (fld.isRegularField()) {
fld.layout = self.layoutRegularField(field_attrs, type_layout);
} else {
fld.layout = self.layoutBitField(field_attrs, type_layout, fld.isNamed(), fld.specifiedBitWidth());
}
}
}
}
/// On MinGW the alignment of the field is calculated in the usual way except that the alignment of
/// the underlying type is ignored in three cases
/// - the field is packed
/// - the field is a bit-field and the previous field was a non-zero-sized bit-field with the same type size
/// - the field is a zero-sized bit-field and the previous field was not a non-zero-sized bit-field
/// See test case 0068.
fn ignoreTypeAlignment(is_attr_packed: bool, bit_width: ?u32, ongoing_bitfield: ?OngoingBitfield, fld_layout: TypeLayout) bool {
if (is_attr_packed) return true;
if (bit_width) |width| {
if (ongoing_bitfield) |ongoing| {
if (ongoing.size_bits == fld_layout.size_bits) return true;
} else {
if (width == 0) return true;
}
}
return false;
}
fn layoutMinGWField(
self: *SysVContext,
field: *const Field,
field_attrs: ?[]const Attribute,
field_layout: TypeLayout,
) FieldLayout {
const annotation_alignment_bits = BITS_PER_BYTE * (Type.annotationAlignment(self.comp, field_attrs) orelse 1);
const is_attr_packed = self.attr_packed or isPacked(field_attrs);
const ignore_type_alignment = ignoreTypeAlignment(is_attr_packed, field.bit_width, self.ongoing_bitfield, field_layout);
var field_alignment_bits: u64 = field_layout.field_alignment_bits;
if (ignore_type_alignment) {
field_alignment_bits = BITS_PER_BYTE;
}
field_alignment_bits = @max(field_alignment_bits, annotation_alignment_bits);
if (self.max_field_align_bits) |bits| {
field_alignment_bits = @min(field_alignment_bits, bits);
}
// The field affects the record alignment in one of three cases
// - the field is a regular field
// - the field is a zero-width bit-field following a non-zero-width bit-field
// - the field is a non-zero-width bit-field and not packed.
// See test case 0069.
const update_record_alignment =
field.isRegularField() or
(field.specifiedBitWidth() == 0 and self.ongoing_bitfield != null) or
(field.specifiedBitWidth() != 0 and !is_attr_packed);
// If a field affects the alignment of a record, the alignment is calculated in the
// usual way except that __attribute__((packed)) is ignored on a zero-width bit-field.
// See test case 0068.
if (update_record_alignment) {
var ty_alignment_bits = field_layout.field_alignment_bits;
if (is_attr_packed and (field.isRegularField() or field.specifiedBitWidth() != 0)) {
ty_alignment_bits = BITS_PER_BYTE;
}
ty_alignment_bits = @max(ty_alignment_bits, annotation_alignment_bits);
if (self.max_field_align_bits) |bits| {
ty_alignment_bits = @intCast(@min(ty_alignment_bits, bits));
}
self.aligned_bits = @max(self.aligned_bits, ty_alignment_bits);
}
// NOTE: ty_alignment_bits and field_alignment_bits are different in the following case:
// Y = { size: 64, alignment: 64 }struct {
// { offset: 0, size: 1 }c { size: 8, alignment: 8 }char:1,
// @attr_packed _ { size: 64, alignment: 64 }long long:0,
// { offset: 8, size: 8 }d { size: 8, alignment: 8 }char,
// }
if (field.isRegularField()) {
return self.layoutRegularFieldMinGW(field_layout.size_bits, field_alignment_bits);
} else {
return self.layoutBitFieldMinGW(field_layout.size_bits, field_alignment_bits, field.isNamed(), field.specifiedBitWidth());
}
}
fn layoutBitFieldMinGW(
self: *SysVContext,
ty_size_bits: u64,
field_alignment_bits: u64,
is_named: bool,
width: u64,
) FieldLayout {
std.debug.assert(width <= ty_size_bits); // validated in parser
// In a union, the size of the underlying type does not affect the size of the union.
// See test case 0070.
if (self.is_union) {
self.size_bits = @max(self.size_bits, width);
if (!is_named) return .{};
return .{
.offset_bits = 0,
.size_bits = width,
};
}
if (width == 0) {
self.ongoing_bitfield = null;
} else {
// If there is an ongoing bit-field in a struct whose underlying type has the same size and
// if there is enough space left to place this bit-field, then this bit-field is placed in
// the ongoing bit-field and the size of the struct is not affected by this
// bit-field. See test case 0037.
if (self.ongoing_bitfield) |*ongoing| {
if (ongoing.size_bits == ty_size_bits and ongoing.unused_size_bits >= width) {
const offset_bits = self.size_bits - ongoing.unused_size_bits;
ongoing.unused_size_bits -= width;
if (!is_named) return .{};
return .{
.offset_bits = offset_bits,
.size_bits = width,
};
}
}
// Otherwise this field is part of a new ongoing bit-field.
self.ongoing_bitfield = .{
.size_bits = ty_size_bits,
.unused_size_bits = ty_size_bits - width,
};
}
const offset_bits = std.mem.alignForward(u64, self.size_bits, field_alignment_bits);
self.size_bits = if (width == 0) offset_bits else offset_bits + ty_size_bits;
if (!is_named) return .{};
return .{
.offset_bits = offset_bits,
.size_bits = width,
};
}
fn layoutRegularFieldMinGW(
self: *SysVContext,
ty_size_bits: u64,
field_alignment_bits: u64,
) FieldLayout {
self.ongoing_bitfield = null;
// A struct field starts at the next offset in the struct that is properly
// aligned with respect to the start of the struct. See test case 0033.
// A union field always starts at offset 0.
const offset_bits = if (self.is_union) 0 else std.mem.alignForward(u64, self.size_bits, field_alignment_bits);
// Set the size of the record to the maximum of the current size and the end of
// the field. See test case 0034.
self.size_bits = @max(self.size_bits, offset_bits + ty_size_bits);
return .{
.offset_bits = offset_bits,
.size_bits = ty_size_bits,
};
}
fn layoutRegularField(
self: *SysVContext,
fld_attrs: ?[]const Attribute,
fld_layout: TypeLayout,
) FieldLayout {
var fld_align_bits = fld_layout.field_alignment_bits;
// If the struct or the field is packed, then the alignment of the underlying type is
// ignored. See test case 0084.
if (self.attr_packed or isPacked(fld_attrs)) {
fld_align_bits = BITS_PER_BYTE;
}
// The field alignment can be increased by __attribute__((aligned)) annotations on the
// field. See test case 0085.
if (Type.annotationAlignment(self.comp, fld_attrs)) |anno| {
fld_align_bits = @max(fld_align_bits, anno * BITS_PER_BYTE);
}
// #pragma pack takes precedence over all other attributes. See test cases 0084 and
// 0085.
if (self.max_field_align_bits) |req_bits| {
fld_align_bits = @intCast(@min(fld_align_bits, req_bits));
}
// A struct field starts at the next offset in the struct that is properly
// aligned with respect to the start of the struct.
const offset_bits = if (self.is_union) 0 else std.mem.alignForward(u64, self.size_bits, fld_align_bits);
const size_bits = fld_layout.size_bits;
// The alignment of a record is the maximum of its field alignments. See test cases
// 0084, 0085, 0086.
self.size_bits = @max(self.size_bits, offset_bits + size_bits);
self.aligned_bits = @max(self.aligned_bits, fld_align_bits);
return .{
.offset_bits = offset_bits,
.size_bits = size_bits,
};
}
fn layoutBitField(
self: *SysVContext,
fld_attrs: ?[]const Attribute,
fld_layout: TypeLayout,
is_named: bool,
bit_width: u64,
) FieldLayout {
const ty_size_bits = fld_layout.size_bits;
var ty_fld_algn_bits: u32 = fld_layout.field_alignment_bits;
if (bit_width > 0) {
std.debug.assert(bit_width <= ty_size_bits); // Checked in parser
// Some targets ignore the alignment of the underlying type when laying out
// non-zero-sized bit-fields. See test case 0072. On such targets, bit-fields never
// cross a storage boundary. See test case 0081.
if (target_util.ignoreNonZeroSizedBitfieldTypeAlignment(self.comp.target)) {
ty_fld_algn_bits = 1;
}
} else {
// Some targets ignore the alignment of the underlying type when laying out
// zero-sized bit-fields. See test case 0073.
if (target_util.ignoreZeroSizedBitfieldTypeAlignment(self.comp.target)) {
ty_fld_algn_bits = 1;
}
// Some targets have a minimum alignment of zero-sized bit-fields. See test case
// 0074.
if (target_util.minZeroWidthBitfieldAlignment(self.comp.target)) |target_align| {
ty_fld_algn_bits = @max(ty_fld_algn_bits, target_align);
}
}
// __attribute__((packed)) on the record is identical to __attribute__((packed)) on each
// field. See test case 0067.
const attr_packed = self.attr_packed or isPacked(fld_attrs);
const has_packing_annotation = attr_packed or self.max_field_align_bits != null;
const annotation_alignment: u32 = if (Type.annotationAlignment(self.comp, fld_attrs)) |anno| anno * BITS_PER_BYTE else 1;
const first_unused_bit: u64 = if (self.is_union) 0 else self.size_bits;
var field_align_bits: u64 = 1;
if (bit_width == 0) {
field_align_bits = @max(ty_fld_algn_bits, annotation_alignment);
} else if (self.comp.langopts.emulate == .gcc) {
// On GCC, the field alignment is at least the alignment requested by annotations
// except as restricted by #pragma pack. See test case 0083.
field_align_bits = annotation_alignment;
if (self.max_field_align_bits) |max_bits| {
field_align_bits = @min(annotation_alignment, max_bits);
}
// On GCC, if there are no packing annotations and
// - the field would otherwise start at an offset such that it would cross a
// storage boundary or
// - the alignment of the type is larger than its size,
// then it is aligned to the type's field alignment. See test case 0083.
if (!has_packing_annotation) {
const start_bit = std.mem.alignForward(u64, first_unused_bit, field_align_bits);
const does_field_cross_boundary = start_bit % ty_fld_algn_bits + bit_width > ty_size_bits;
if (ty_fld_algn_bits > ty_size_bits or does_field_cross_boundary) {
field_align_bits = @max(field_align_bits, ty_fld_algn_bits);
}
}
} else {
std.debug.assert(self.comp.langopts.emulate == .clang);
// On Clang, the alignment requested by annotations is not respected if it is
// larger than the value of #pragma pack. See test case 0083.
if (annotation_alignment <= self.max_field_align_bits orelse std.math.maxInt(u29)) {
field_align_bits = @max(field_align_bits, annotation_alignment);
}
// On Clang, if there are no packing annotations and the field would cross a
// storage boundary if it were positioned at the first unused bit in the record,
// it is aligned to the type's field alignment. See test case 0083.
if (!has_packing_annotation) {
const does_field_cross_boundary = first_unused_bit % ty_fld_algn_bits + bit_width > ty_size_bits;
if (does_field_cross_boundary)
field_align_bits = @max(field_align_bits, ty_fld_algn_bits);
}
}
const offset_bits = std.mem.alignForward(u64, first_unused_bit, field_align_bits);
self.size_bits = @max(self.size_bits, offset_bits + bit_width);
// Unnamed fields do not contribute to the record alignment except on a few targets.
// See test case 0079.
if (is_named or target_util.unnamedFieldAffectsAlignment(self.comp.target)) {
var inherited_align_bits: u32 = undefined;
if (bit_width == 0) {
// If the width is 0, #pragma pack and __attribute__((packed)) are ignored.
// See test case 0075.
inherited_align_bits = @max(ty_fld_algn_bits, annotation_alignment);
} else if (self.max_field_align_bits) |max_align_bits| {
// Otherwise, if a #pragma pack is in effect, __attribute__((packed)) on the field or
// record is ignored. See test case 0076.
inherited_align_bits = @max(ty_fld_algn_bits, annotation_alignment);
inherited_align_bits = @intCast(@min(inherited_align_bits, max_align_bits));
} else if (attr_packed) {
// Otherwise, if the field or the record is packed, the field alignment is 1 bit unless
// it is explicitly increased with __attribute__((aligned)). See test case 0077.
inherited_align_bits = annotation_alignment;
} else {
// Otherwise, the field alignment is the field alignment of the underlying type unless
// it is explicitly increased with __attribute__((aligned)). See test case 0078.
inherited_align_bits = @max(ty_fld_algn_bits, annotation_alignment);
}
self.aligned_bits = @max(self.aligned_bits, inherited_align_bits);
}
if (!is_named) return .{};
return .{
.size_bits = bit_width,
.offset_bits = offset_bits,
};
}
};
const MsvcContext = struct {
req_align_bits: u32,
max_field_align_bits: ?u32,
/// The alignment of pointers that point to an object of this type. This is greater than or equal
/// to the required alignment. Once all fields have been laid out, the size of the record will be
/// rounded up to this value.
pointer_align_bits: u32,
/// The alignment of this type when it is used as a record field. This is greater than or equal to
/// the pointer alignment.
field_align_bits: u32,
size_bits: u64,
ongoing_bitfield: ?OngoingBitfield,
contains_non_bitfield: bool,
is_union: bool,
comp: *const Compilation,
fn init(ty: Type, comp: *const Compilation, pragma_pack: ?u8) MsvcContext {
var pack_value: ?u32 = null;
if (ty.hasAttribute(.@"packed")) {
// __attribute__((packed)) behaves like #pragma pack(1) in clang. See test case 0056.
pack_value = BITS_PER_BYTE;
}
if (pack_value == null) {
if (pragma_pack) |pack| {
pack_value = pack * BITS_PER_BYTE;
}
}
if (pack_value) |pack| {
pack_value = msvcPragmaPack(comp, pack);
}
// The required alignment can be increased by adding a __declspec(align)
// annotation. See test case 0023.
var must_align: u29 = BITS_PER_BYTE;
if (ty.requestedAlignment(comp)) |req_align| {
must_align = req_align * BITS_PER_BYTE;
}
return MsvcContext{
.req_align_bits = must_align,
.pointer_align_bits = must_align,
.field_align_bits = must_align,
.size_bits = 0,
.max_field_align_bits = pack_value,
.ongoing_bitfield = null,
.contains_non_bitfield = false,
.is_union = ty.is(.@"union"),
.comp = comp,
};
}
fn layoutField(self: *MsvcContext, fld: *const Field, fld_attrs: ?[]const Attribute) FieldLayout {
const type_layout = computeLayout(fld.ty, self.comp);
// The required alignment of the field is the maximum of the required alignment of the
// underlying type and the __declspec(align) annotation on the field itself.
// See test case 0028.
var req_align = type_layout.required_alignment_bits;
if (Type.annotationAlignment(self.comp, fld_attrs)) |anno| {
req_align = @max(anno * BITS_PER_BYTE, req_align);
}
// The required alignment of a record is the maximum of the required alignments of its
// fields except that the required alignment of bitfields is ignored.
// See test case 0029.
if (fld.isRegularField()) {
self.req_align_bits = @max(self.req_align_bits, req_align);
}
// The offset of the field is based on the field alignment of the underlying type.
// See test case 0027.
var fld_align_bits = type_layout.field_alignment_bits;
if (self.max_field_align_bits) |max_align| {
fld_align_bits = @min(fld_align_bits, max_align);
}
// check the requested alignment of the field type.
if (fld.ty.requestedAlignment(self.comp)) |type_req_align| {
fld_align_bits = @max(fld_align_bits, type_req_align * 8);
}
if (isPacked(fld_attrs)) {
// __attribute__((packed)) on a field is a clang extension. It behaves as if #pragma
// pack(1) had been applied only to this field. See test case 0057.
fld_align_bits = BITS_PER_BYTE;
}
// __attribute__((packed)) on a field is a clang extension. It behaves as if #pragma
// pack(1) had been applied only to this field. See test case 0057.
fld_align_bits = @max(fld_align_bits, req_align);
if (fld.isRegularField()) {
return self.layoutRegularField(type_layout.size_bits, fld_align_bits);
} else {
return self.layoutBitField(type_layout.size_bits, fld_align_bits, fld.specifiedBitWidth());
}
}
fn layoutBitField(self: *MsvcContext, ty_size_bits: u64, field_align: u32, bit_width: u32) FieldLayout {
if (bit_width == 0) {
// A zero-sized bit-field that does not follow a non-zero-sized bit-field does not affect
// the overall layout of the record. Even in a union where the order would otherwise
// not matter. See test case 0035.
if (self.ongoing_bitfield) |_| {
self.ongoing_bitfield = null;
} else {
// this field takes 0 space.
return .{ .offset_bits = self.size_bits, .size_bits = bit_width };
}
} else {
std.debug.assert(bit_width <= ty_size_bits);
// If there is an ongoing bit-field in a struct whose underlying type has the same size and
// if there is enough space left to place this bit-field, then this bit-field is placed in
// the ongoing bit-field and the overall layout of the struct is not affected by this
// bit-field. See test case 0037.
if (!self.is_union) {
if (self.ongoing_bitfield) |*p| {
if (p.size_bits == ty_size_bits and p.unused_size_bits >= bit_width) {
const offset_bits = self.size_bits - p.unused_size_bits;
p.unused_size_bits -= bit_width;
return .{ .offset_bits = offset_bits, .size_bits = bit_width };
}
}
}
// Otherwise this field is part of a new ongoing bit-field.
self.ongoing_bitfield = .{ .size_bits = ty_size_bits, .unused_size_bits = ty_size_bits - bit_width };
}
const offset_bits = if (!self.is_union) bits: {
// This is the one place in the layout of a record where the pointer alignment might
// get assigned a smaller value than the field alignment. This can only happen if
// the field or the type of the field has a required alignment. Otherwise the value
// of field_alignment_bits is already bound by max_field_alignment_bits.
// See test case 0038.
const p_align = if (self.max_field_align_bits) |max_fld_align|
@min(max_fld_align, field_align)
else
field_align;
self.pointer_align_bits = @max(self.pointer_align_bits, p_align);
self.field_align_bits = @max(self.field_align_bits, field_align);
const offset_bits = std.mem.alignForward(u64, self.size_bits, field_align);
self.size_bits = if (bit_width == 0) offset_bits else offset_bits + ty_size_bits;
break :bits offset_bits;
} else bits: {
// Bit-fields do not affect the alignment of a union. See test case 0041.
self.size_bits = @max(self.size_bits, ty_size_bits);
break :bits 0;
};
return .{ .offset_bits = offset_bits, .size_bits = bit_width };
}
fn layoutRegularField(self: *MsvcContext, size_bits: u64, field_align: u32) FieldLayout {
self.contains_non_bitfield = true;
self.ongoing_bitfield = null;
// The alignment of the field affects both the pointer alignment and the field
// alignment of the record. See test case 0032.
self.pointer_align_bits = @max(self.pointer_align_bits, field_align);
self.field_align_bits = @max(self.field_align_bits, field_align);
const offset_bits = switch (self.is_union) {
true => 0,
false => std.mem.alignForward(u64, self.size_bits, field_align),
};
self.size_bits = @max(self.size_bits, offset_bits + size_bits);
return .{ .offset_bits = offset_bits, .size_bits = size_bits };
}
fn handleZeroSizedRecord(self: *MsvcContext) void {
if (self.is_union) {
// MSVC does not allow unions without fields.
// If all fields in a union have size 0, the size of the union is set to
// - its field alignment if it contains at least one non-bitfield
// - 4 bytes if it contains only bitfields
// See test case 0025.
if (self.contains_non_bitfield) {
self.size_bits = self.field_align_bits;
} else {
self.size_bits = 4 * BITS_PER_BYTE;
}
} else {
// If all fields in a struct have size 0, its size is set to its required alignment
// but at least to 4 bytes. See test case 0026.
self.size_bits = @max(self.req_align_bits, 4 * BITS_PER_BYTE);
self.pointer_align_bits = @intCast(@min(self.pointer_align_bits, self.size_bits));
}
}
};
pub fn compute(rec: *Type.Record, ty: Type, comp: *const Compilation, pragma_pack: ?u8) void {
switch (comp.langopts.emulate) {
.gcc, .clang => {
var context = SysVContext.init(ty, comp, pragma_pack);
context.layoutFields(rec);
context.size_bits = std.mem.alignForward(u64, context.size_bits, context.aligned_bits);
rec.type_layout = .{
.size_bits = context.size_bits,
.field_alignment_bits = context.aligned_bits,
.pointer_alignment_bits = context.aligned_bits,
.required_alignment_bits = BITS_PER_BYTE,
};
},
.msvc => {
var context = MsvcContext.init(ty, comp, pragma_pack);
for (rec.fields, 0..) |*fld, fld_indx| {
var field_attrs: ?[]const Attribute = null;
if (rec.field_attributes) |attrs| {
field_attrs = attrs[fld_indx];
}
fld.layout = context.layoutField(fld, field_attrs);
}
if (context.size_bits == 0) {
// As an extension, MSVC allows records that only contain zero-sized bitfields and empty
// arrays. Such records would be zero-sized but this case is handled here separately to
// ensure that there are no zero-sized records.
context.handleZeroSizedRecord();
}
context.size_bits = std.mem.alignForward(u64, context.size_bits, context.pointer_align_bits);
rec.type_layout = .{
.size_bits = context.size_bits,
.field_alignment_bits = context.field_align_bits,
.pointer_alignment_bits = context.pointer_align_bits,
.required_alignment_bits = context.req_align_bits,
};
},
}
}
fn computeLayout(ty: Type, comp: *const Compilation) TypeLayout {
if (ty.getRecord()) |rec| {
const requested = BITS_PER_BYTE * (ty.requestedAlignment(comp) orelse 0);
return .{
.size_bits = rec.type_layout.size_bits,
.pointer_alignment_bits = @max(requested, rec.type_layout.pointer_alignment_bits),
.field_alignment_bits = @max(requested, rec.type_layout.field_alignment_bits),
.required_alignment_bits = rec.type_layout.required_alignment_bits,
};
} else {
const type_align = ty.alignof(comp) * BITS_PER_BYTE;
return .{
.size_bits = ty.bitSizeof(comp) orelse 0,
.pointer_alignment_bits = type_align,
.field_alignment_bits = type_align,
.required_alignment_bits = BITS_PER_BYTE,
};
}
}
fn isPacked(attrs: ?[]const Attribute) bool {
const a = attrs orelse return false;
for (a) |attribute| {
if (attribute.tag != .@"packed") continue;
return true;
}
return false;
}
// The effect of #pragma pack(N) depends on the target.
//
// x86: By default, there is no maximum field alignment. N={1,2,4} set the maximum field
// alignment to that value. All other N activate the default.
// x64: By default, there is no maximum field alignment. N={1,2,4,8} set the maximum field
// alignment to that value. All other N activate the default.
// arm: By default, the maximum field alignment is 8. N={1,2,4,8,16} set the maximum field
// alignment to that value. All other N activate the default.
// arm64: By default, the maximum field alignment is 8. N={1,2,4,8} set the maximum field
// alignment to that value. N=16 disables the maximum field alignment. All other N
// activate the default.
//
// See test case 0020.
pub fn msvcPragmaPack(comp: *const Compilation, pack: u32) ?u32 {
return switch (pack) {
8, 16, 32 => pack,
64 => if (comp.target.cpu.arch == .x86) null else pack,
128 => if (comp.target.cpu.arch == .thumb) pack else null,
else => {
return switch (comp.target.cpu.arch) {
.thumb, .aarch64 => 64,
else => null,
};
},
};
}