From 1976a111542b9c69aec68077dbd31dcdb1fd67e2 Mon Sep 17 00:00:00 2001 From: Christian Buttner Date: Fri, 12 Jul 2024 18:25:09 +0200 Subject: [PATCH] `@nopadding` and `@compact` attributes (#1235) Add `@nopadding` attribute. `@compact` --- src/compiler/compiler_internal.h | 5 ++- src/compiler/enums.h | 2 + src/compiler/sema_decls.c | 66 ++++++++++++++++++++++++++++++++ src/compiler/symtab.c | 2 + 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 6438c0527..9366c28cb 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -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 { diff --git a/src/compiler/enums.h b/src/compiler/enums.h index ffb33792a..b41608255 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -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, diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index bab55b8d6..d7cad96bf 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -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; diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 75090c846..14ea92f4d 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -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");