diff --git a/regression/ansi-c/gcc_attributes16/main.c b/regression/ansi-c/gcc_attributes16/main.c new file mode 100644 index 00000000000..252467c14df --- /dev/null +++ b/regression/ansi-c/gcc_attributes16/main.c @@ -0,0 +1,193 @@ +#include + +struct S1 +{ + uint8_t c; + int32_t i; +} __attribute__((packed)); +struct S1 s1; + +struct S1a +{ + uint8_t c; + int32_t i; +} __attribute__((packed)) __attribute__((aligned(16))); +struct S1a s1a; + +struct __attribute__((packed)) S2 +{ + uint8_t c; + int32_t i; +}; +struct S2 s2; + +struct __attribute__((packed)) __attribute__((aligned(16))) S2a +{ + uint8_t c; + int32_t i; +}; +struct S2a s2a; + +// "packed" will be ignored +__attribute__((packed)) struct S3 +{ + uint8_t c; + int32_t i; +}; +struct S3 s3; + +// both "packed" and "aligned" +__attribute__((packed)) __attribute__((aligned(16))) struct S3a +{ + uint8_t c; + int32_t i; +}; +struct S3a s3a; + +struct S4 +{ + uint8_t c; + int32_t i; +} __attribute__((packed)) s4; + +struct S4a +{ + uint8_t c; + int32_t i; +} __attribute__((packed)) __attribute__((aligned(16))) s4a; + +struct __attribute__((packed)) S5 +{ + uint8_t c; + int32_t i; +} s5; + +struct __attribute__((packed)) __attribute__((aligned(16))) S5a +{ + uint8_t c; + int32_t i; +} s5a; + +typedef struct __attribute__((packed)) S6 +{ + uint8_t c; + int32_t i; +} s6; + +typedef struct __attribute__((packed)) __attribute__((aligned(16))) S6a +{ + uint8_t c; + int32_t i; +} s6a; + +// "packed" will be ignored in the following by both GCC and Clang +struct S7 +{ + uint8_t c; + int32_t i; +} s7 __attribute__((packed)); + +// "packed" will be ignored in the following by both GCC and Clang +struct S7a +{ + uint8_t c; + int32_t i; +} s7a __attribute__((packed)) __attribute__((aligned(16))); + +typedef struct T1 +{ + uint8_t c; + int32_t i; +} __attribute__((packed)) T1_t; + +typedef struct T1a +{ + uint8_t c; + int32_t i; +} __attribute__((packed)) __attribute__((aligned(16))) T1a_t; + +typedef struct __attribute__((packed)) T2 +{ + uint8_t c; + int32_t i; +} T2_t; + +typedef struct __attribute__((packed)) __attribute__((aligned(16))) T2a +{ + uint8_t c; + int32_t i; +} T2a_t; + +struct U +{ + uint8_t c; + int32_t i; +}; + +struct S +{ + uint8_t c; + // attribute ignored by GCC + struct S1 __attribute((packed)) i1; + struct U __attribute((packed)) i2; + uint8_t c2; + // alignment has to be a power of 2 + // struct S2 __attribute((aligned(5))) i2_5; + struct S2a __attribute((aligned(8))) i3; + uint8_t c3; + struct S3a __attribute((aligned(8))) i4; + uint8_t c4; + T1a_t __attribute((aligned(8))) i5; + T1_t __attribute((aligned(8))) i6; +}; + +int32_t main() +{ + _Static_assert(sizeof(struct S1) == 5, ""); + _Static_assert(sizeof(s1) == 5, ""); + _Static_assert(sizeof(struct S1a) == 16, ""); + _Static_assert(sizeof(s1a) == 16, ""); + + _Static_assert(sizeof(struct S2) == 5, ""); + _Static_assert(sizeof(s2) == 5, ""); + _Static_assert(sizeof(struct S2a) == 16, ""); + _Static_assert(sizeof(s2a) == 16, ""); + + _Static_assert(sizeof(struct S3) == 8, ""); + _Static_assert(sizeof(s3) == 8, ""); + _Static_assert(sizeof(struct S3a) == 8, ""); + _Static_assert(sizeof(s3a) == 8, ""); + + _Static_assert(sizeof(struct S4) == 5, ""); + _Static_assert(sizeof(s4) == 5, ""); + _Static_assert(sizeof(struct S4a) == 16, ""); + _Static_assert(sizeof(s4a) == 16, ""); + + _Static_assert(sizeof(struct S5) == 5, ""); + _Static_assert(sizeof(s5) == 5, ""); + _Static_assert(sizeof(struct S5a) == 16, ""); + _Static_assert(sizeof(s5a) == 16, ""); + + _Static_assert(sizeof(struct S6) == 5, ""); + _Static_assert(sizeof(s6) == 5, ""); + _Static_assert(sizeof(struct S6a) == 16, ""); + _Static_assert(sizeof(s6a) == 16, ""); + + _Static_assert(sizeof(struct S7) == 8, ""); + _Static_assert(sizeof(s7) == 8, ""); + _Static_assert(sizeof(struct S7a) == 8, ""); + _Static_assert(sizeof(s7a) == 8, ""); + + _Static_assert(sizeof(struct T1) == 5, ""); + _Static_assert(sizeof(T1_t) == 5, ""); + _Static_assert(sizeof(struct T1a) == 16, ""); + _Static_assert(sizeof(T1a_t) == 16, ""); + + _Static_assert(sizeof(struct T2) == 5, ""); + _Static_assert(sizeof(T2_t) == 5, ""); + _Static_assert(sizeof(struct T2a) == 16, ""); + _Static_assert(sizeof(T2a_t) == 16, ""); + + _Static_assert(sizeof(struct S) == 96, ""); + return 0; +} diff --git a/regression/ansi-c/gcc_attributes16/test.desc b/regression/ansi-c/gcc_attributes16/test.desc new file mode 100644 index 00000000000..0e1ed863bc1 --- /dev/null +++ b/regression/ansi-c/gcc_attributes16/test.desc @@ -0,0 +1,8 @@ +CORE gcc-only +main.c + +^EXIT=0$ +^SIGNAL=0$ +-- +^warning: ignoring +^CONVERSION ERROR$ diff --git a/src/ansi-c/c_typecheck_type.cpp b/src/ansi-c/c_typecheck_type.cpp index 02e50709308..0fb28fd798a 100644 --- a/src/ansi-c/c_typecheck_type.cpp +++ b/src/ansi-c/c_typecheck_type.cpp @@ -72,6 +72,14 @@ void c_typecheck_baset::typecheck_type(typet &type) { typecheck_expr(alignment); make_constant(alignment); + const auto align_int = numeric_cast(alignment); + if( + !align_int.has_value() || + power(2, align_int->floorPow2()) != *align_int) + { + throw errort().with_location(type.source_location()) + << "alignment is not a positive power of 2"; + } } } @@ -726,6 +734,29 @@ void c_typecheck_baset::typecheck_vector_type(typet &type) type = new_type.with_source_location(source_location); } +/// Adjust \p alignment to be the least common multiple with \p other_alignment, +/// if both of them are non-nil. If exactly one of them is non-nil, set +/// \p alignment to that value. +static void align_to(exprt &alignment, const exprt &other_alignment) +{ + if(other_alignment.is_nil()) + return; + else if(alignment.is_nil()) + alignment = other_alignment; + else + { + const auto a = numeric_cast(alignment); + CHECK_RETURN(a.has_value()); + const auto other = numeric_cast(other_alignment); + CHECK_RETURN(other.has_value()); + + // alignments are powers of two, so taking the maximum is sufficient for it + // will be equal to the least common multiple + if(a < other) + alignment = other_alignment; + } +} + void c_typecheck_baset::typecheck_compound_type(struct_union_typet &type) { // These get replaced by symbol types later. @@ -744,7 +775,7 @@ void c_typecheck_baset::typecheck_compound_type(struct_union_typet &type) remove_qualifiers.write(type); bool is_packed = type.get_bool(ID_C_packed); - irept alignment = type.find(ID_C_alignment); + exprt alignment = static_cast(type.find(ID_C_alignment)); if(type.find(ID_tag).is_nil()) { @@ -835,6 +866,10 @@ void c_typecheck_baset::typecheck_compound_type(struct_union_typet &type) throw errort().with_location(type.source_location()) << "redefinition of body of '" << s_it->second.pretty_name << "'"; } + + align_to( + alignment, + static_cast(s_it->second.type.find(ID_C_alignment))); } } @@ -1606,7 +1641,7 @@ void c_typecheck_baset::typecheck_typedef_type(typet &type) c_qualifierst c_qualifiers(type); bool is_packed = type.get_bool(ID_C_packed); - irept alignment = type.find(ID_C_alignment); + exprt alignment = static_cast(type.find(ID_C_alignment)); c_qualifiers += c_qualifierst(symbol.type); type = symbol.type; @@ -1614,6 +1649,11 @@ void c_typecheck_baset::typecheck_typedef_type(typet &type) if(is_packed) type.set(ID_C_packed, true); + else + type.remove(ID_C_packed); + + align_to( + alignment, static_cast(symbol.type.find(ID_C_alignment))); if(alignment.is_not_nil()) type.set(ID_C_alignment, alignment); diff --git a/src/ansi-c/parser.y b/src/ansi-c/parser.y index 59e320f7112..857aa72fb69 100644 --- a/src/ansi-c/parser.y +++ b/src/ansi-c/parser.y @@ -1243,7 +1243,37 @@ basic_type_specifier: sue_declaration_specifier: declaration_qualifier_list elaborated_type_name { - $$=merge($1, $2); + // ignore packed or aligned attributes in this context (Clang warns + // that they will be ignored, GCC just silently ignores them) + if(parser_stack($1).id() == ID_packed || + parser_stack($1).id() == ID_aligned) + { + $$=$2; + } + else if(parser_stack($1).id() == ID_merged_type) + { + auto &sub = parser_stack($1).get_sub(); + irept::subt filtered_sub; + filtered_sub.reserve(sub.size()); + for(const auto &s : sub) + { + if(s.id() != ID_packed && s.id() != ID_aligned) + filtered_sub.push_back(s); + } + if(filtered_sub.empty()) + $$=$2; + else + { + if(filtered_sub.size() == 1) + parser_stack($1).swap(filtered_sub.front()); + else + sub.swap(filtered_sub); + + $$=merge($1, $2); + } + } + else + $$=merge($1, $2); } | sue_type_specifier storage_class gcc_type_attribute_opt {