@nopadding and @compact attributes (#1235)

Add `@nopadding` attribute. `@compact`
This commit is contained in:
Christian Buttner
2024-07-12 18:25:09 +02:00
committed by GitHub
parent e7d8f64a49
commit 1976a11154
4 changed files with 74 additions and 1 deletions

View File

@@ -427,8 +427,9 @@ typedef struct
typedef struct
{
TypeSize size;
Decl **members;
MemberIndex union_rep;
Decl **members;
Decl *padded_decl;
AlignSize padding : 16;
} StructDecl;
@@ -676,6 +677,8 @@ typedef struct Decl_
bool is_cond : 1;
bool has_link : 1;
bool is_if : 1;
bool attr_nopadding : 1;
bool attr_compact : 1;
OperatorOverload operator : 4;
union
{

View File

@@ -804,6 +804,7 @@ typedef enum
ATTRIBUTE_BIGENDIAN,
ATTRIBUTE_BUILTIN,
ATTRIBUTE_CALLCONV,
ATTRIBUTE_COMPACT,
ATTRIBUTE_DEPRECATED,
ATTRIBUTE_DYNAMIC,
ATTRIBUTE_EXPORT,
@@ -820,6 +821,7 @@ typedef enum
ATTRIBUTE_NODISCARD,
ATTRIBUTE_NOINIT,
ATTRIBUTE_NOINLINE,
ATTRIBUTE_NOPADDING,
ATTRIBUTE_NORETURN,
ATTRIBUTE_NOSTRIP,
ATTRIBUTE_OBFUSCATE,

View File

@@ -36,6 +36,7 @@ static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl, bool *erase
static bool sema_analyse_union_members(SemaContext *context, Decl *decl);
static bool sema_analyse_struct_members(SemaContext *context, Decl *decl);
static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent, Decl *decl, bool *erase_decl);
static inline bool sema_check_struct_holes(SemaContext *context, Decl *decl, Decl *member, Type *member_type);
static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *parent, Decl *member, unsigned index, bool allow_overlap, bool *erase_decl);
static inline bool sema_analyse_doc_header(SemaContext *context, AstId doc, Decl **params, Decl **extra_params, bool *pure_ref);
@@ -260,6 +261,9 @@ static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent
}
case DECL_STRUCT:
case DECL_UNION:
// Extend the nopadding attributes to substructs.
if (parent->attr_nopadding) decl->attr_nopadding = true;
if (parent->attr_compact) decl->attr_compact = true;
case DECL_BITSTRUCT:
decl->is_export = is_export;
if (!sema_analyse_decl(context, decl)) return false;
@@ -269,6 +273,24 @@ static inline bool sema_analyse_struct_member(SemaContext *context, Decl *parent
}
}
static inline bool sema_check_struct_holes(SemaContext *context, Decl *decl, Decl *member, Type *member_type)
{
if (member_type->type_kind == TYPE_STRUCT || member_type->type_kind == TYPE_UNION)
{
if (member_type->decl->strukt.padded_decl)
{
if (!decl->strukt.padded_decl) decl->strukt.padded_decl = member_type->decl->strukt.padded_decl;
if (decl->attr_compact)
{
SEMA_ERROR(member, "This member has holes.");
SEMA_NOTE(member_type->decl->strukt.padded_decl, "Padding would be added for this type.");
return false;
}
}
}
return true;
}
/**
* Analyse union members, calculating alignment.
*/
@@ -316,6 +338,10 @@ static bool sema_analyse_union_members(SemaContext *context, Decl *decl)
}
AlignSize member_alignment;
if (!sema_set_abi_alignment(context, member->type, &member_alignment)) return false;
Type *member_type = type_flatten(member->type);
if (!sema_check_struct_holes(context, decl, member, member_type)) return false;
ByteSize member_size = type_size(member->type);
assert(member_size <= MAX_TYPE_SIZE);
// Update max alignment
@@ -564,6 +590,17 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl)
member->alignment = member_alignment;
if (align_offset - offset != 0)
{
if (!decl->strukt.padded_decl) decl->strukt.padded_decl = member;
if (decl->attr_nopadding || member->attr_nopadding)
{
RETURN_SEMA_ERROR(member, "%d bytes of padding would be added to align this member.", align_offset - offset);
}
}
if (!sema_check_struct_holes(context, decl, member, member_type)) return false;
offset = align_offset;
member->offset = offset;
offset += type_size(member->type);
@@ -599,6 +636,26 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl)
assert(!decl->strukt.padding);
decl->strukt.padding = (AlignSize)(size - offset);
}
if (decl->attr_nopadding && type_is_substruct(decl->type))
{
Decl *first_member = struct_members[0];
Type *type = type_flatten(first_member->type);
if (type->type_kind == TYPE_STRUCT && !type->decl->attr_nopadding)
{
RETURN_SEMA_ERROR(first_member, "Inlined struct requires @nopadding attribute.");
}
}
if (size != offset)
{
if (!decl->strukt.padded_decl) decl->strukt.padded_decl = decl;
if (decl->attr_nopadding)
{
RETURN_SEMA_ERROR(decl, "%d bytes of padding would be added to the end this struct.", size - offset);
}
}
decl->is_packed = is_unaligned;
decl->strukt.size = size;
return true;
@@ -2279,6 +2336,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr,
[ATTRIBUTE_BIGENDIAN] = ATTR_BITSTRUCT,
[ATTRIBUTE_BUILTIN] = ATTR_MACRO | ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST,
[ATTRIBUTE_CALLCONV] = ATTR_FUNC | ATTR_INTERFACE_METHOD,
[ATTRIBUTE_COMPACT] = ATTR_STRUCT | ATTR_UNION,
[ATTRIBUTE_DEPRECATED] = USER_DEFINED_TYPES | CALLABLE_TYPE | ATTR_CONST | ATTR_GLOBAL | ATTR_MEMBER | ATTR_BITSTRUCT_MEMBER | ATTR_INTERFACE,
[ATTRIBUTE_DYNAMIC] = ATTR_FUNC,
[ATTRIBUTE_EXPORT] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES | ATTR_DEF,
@@ -2295,6 +2353,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr,
[ATTRIBUTE_NODISCARD] = CALLABLE_TYPE,
[ATTRIBUTE_NOINIT] = ATTR_GLOBAL | ATTR_LOCAL,
[ATTRIBUTE_NOINLINE] = ATTR_FUNC | ATTR_CALL,
[ATTRIBUTE_NOPADDING] = ATTR_STRUCT | ATTR_UNION | ATTR_MEMBER,
[ATTRIBUTE_NORETURN] = CALLABLE_TYPE,
[ATTRIBUTE_NOSTRIP] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | EXPORTED_USER_DEFINED_TYPES,
[ATTRIBUTE_OBFUSCATE] = ATTR_ENUM | ATTR_FAULT,
@@ -2536,6 +2595,13 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr,
decl->func_decl.attr_noinline = true;
decl->func_decl.attr_inline = false;
break;
case ATTRIBUTE_NOPADDING:
decl->attr_nopadding = true;
break;
case ATTRIBUTE_COMPACT:
decl->attr_nopadding = true;
decl->attr_compact = true;
break;
case ATTRIBUTE_NOINIT:
decl->var.no_init = true;
break;

View File

@@ -309,6 +309,7 @@ void symtab_init(uint32_t capacity)
attribute_list[ATTRIBUTE_BIGENDIAN] = KW_DEF("@bigendian");
attribute_list[ATTRIBUTE_BUILTIN] = KW_DEF("@builtin");
attribute_list[ATTRIBUTE_CALLCONV] = KW_DEF("@callconv");
attribute_list[ATTRIBUTE_COMPACT] = KW_DEF("@compact");
attribute_list[ATTRIBUTE_DEPRECATED] = KW_DEF("@deprecated");
attribute_list[ATTRIBUTE_DYNAMIC] = KW_DEF("@dynamic");
attribute_list[ATTRIBUTE_EXPORT] = KW_DEF("@export");
@@ -325,6 +326,7 @@ void symtab_init(uint32_t capacity)
attribute_list[ATTRIBUTE_NODISCARD] = KW_DEF("@nodiscard");
attribute_list[ATTRIBUTE_NOINIT] = KW_DEF("@noinit");
attribute_list[ATTRIBUTE_NOINLINE] = KW_DEF("@noinline");
attribute_list[ATTRIBUTE_NOPADDING] = KW_DEF("@nopadding");
attribute_list[ATTRIBUTE_NORETURN] = KW_DEF("@noreturn");
attribute_list[ATTRIBUTE_NOSTRIP] = KW_DEF("@nostrip");
attribute_list[ATTRIBUTE_OBFUSCATE] = KW_DEF("@obfuscate");