diff options
author | Luc Van Oostenryck <luc.vanoostenryck@gmail.com> | 2021-01-01 22:21:46 +0100 |
---|---|---|
committer | Luc Van Oostenryck <luc.vanoostenryck@gmail.com> | 2021-01-01 22:21:46 +0100 |
commit | fe455dd6b9fb47743271f342691c4b68532f1260 (patch) | |
tree | 44e21912adf82caa03e11214fc682bbe460e1b16 | |
parent | 10283ac0f48df99e02a5cb16a423972ae42de00d (diff) | |
parent | 2cf493d1f63f075e6fe0cccbcb7463b1aa773a74 (diff) | |
download | sparse-fe455dd6b9fb47743271f342691c4b68532f1260.tar.gz |
Merge branch 'packed'
* packed: add support for __packed struct
-rw-r--r-- | Documentation/TODO.md | 3 | ||||
-rw-r--r-- | linearize.c | 13 | ||||
-rw-r--r-- | parse.c | 78 | ||||
-rw-r--r-- | symbol.c | 12 | ||||
-rw-r--r-- | symbol.h | 2 | ||||
-rw-r--r-- | validation/enum-type-dubious.c | 18 | ||||
-rw-r--r-- | validation/enum-type-exotic.c | 28 | ||||
-rw-r--r-- | validation/packed-bitfield0.c | 58 | ||||
-rw-r--r-- | validation/packed-bitfield1.c | 27 | ||||
-rw-r--r-- | validation/packed-bitfield2.c | 15 | ||||
-rw-r--r-- | validation/packed-bitfield3.c | 28 | ||||
-rw-r--r-- | validation/packed-bitfield4.c | 18 | ||||
-rw-r--r-- | validation/packed-bitfield5.c | 20 | ||||
-rw-r--r-- | validation/packed-deref0.c | 23 | ||||
-rw-r--r-- | validation/packed-struct.c | 32 | ||||
-rw-r--r-- | validation/parsing/enum-attr.c | 29 | ||||
-rw-r--r-- | validation/type-attribute-align.c | 19 | ||||
-rw-r--r-- | validation/type-attribute-as.c | 33 | ||||
-rw-r--r-- | validation/type-attribute-mod.c | 21 | ||||
-rw-r--r-- | validation/type-attribute-qual.c | 15 |
20 files changed, 447 insertions, 45 deletions
diff --git a/Documentation/TODO.md b/Documentation/TODO.md index 4dc9e63a..3f00bb11 100644 --- a/Documentation/TODO.md +++ b/Documentation/TODO.md @@ -4,9 +4,6 @@ TODO Essential --------- * SSA is broken by simplify_loads() & branches rewriting/simplification -* attributes of struct, union & enums are ignored (and maybe others too). - This requires correct support for __packed which itself needs partial - and unaligned loads & stores (wip) * add support for bitwise enums (wip) Documentation diff --git a/linearize.c b/linearize.c index 0250c6bb..e80715ab 100644 --- a/linearize.c +++ b/linearize.c @@ -977,8 +977,17 @@ static struct symbol *bitfield_base_type(struct symbol *sym) if (sym) { if (sym->type == SYM_NODE) base = base->ctype.base_type; - if (base->type == SYM_BITFIELD) - return base->ctype.base_type; + if (base->type == SYM_BITFIELD) { + base = base->ctype.base_type; + if (sym->packed) { + int size = bits_to_bytes(sym->bit_offset + sym->bit_size); + sym = __alloc_symbol(0); + *sym = *base; + sym->bit_size = bytes_to_bits(size); + return sym; + } + return base; + } } return sym; } @@ -669,6 +669,8 @@ struct statement *alloc_statement(struct position pos, int type) static struct token *struct_declaration_list(struct token *token, struct symbol_list **list); +static void apply_ctype(struct position pos, struct ctype *dst, struct ctype *src); + static void apply_modifiers(struct position pos, struct decl_state *ctx) { struct symbol *ctype; @@ -717,10 +719,11 @@ static struct token *struct_union_enum_specifier(enum type type, struct token *token, struct decl_state *ctx, struct token *(*parse)(struct token *, struct symbol *)) { + struct decl_state attr = { }; struct symbol *sym; struct position *repos; - token = handle_attributes(token, ctx); + token = handle_attributes(token, &attr); if (token_type(token) == TOKEN_IDENT) { sym = lookup_symbol(token->ident, NS_STRUCT); if (!sym || @@ -736,35 +739,36 @@ static struct token *struct_union_enum_specifier(enum type type, ctx->ctype.base_type = sym; repos = &token->pos; token = token->next; - if (match_op(token, '{')) { - // The following test is actually wrong for empty - // structs, but (1) they are not C99, (2) gcc does - // the same thing, and (3) it's easier. - if (sym->symbol_list) - error_die(token->pos, "redefinition of %s", show_typename (sym)); - sym->pos = *repos; - token = parse(token->next, sym); - token = expect(token, '}', "at end of struct-union-enum-specifier"); - - // Mark the structure as needing re-examination - sym->examined = 0; - sym->endpos = token->pos; - } - return token; - } + if (!match_op(token, '{')) + return token; - // private struct/union/enum type - if (!match_op(token, '{')) { + // The following test is actually wrong for empty + // structs, but (1) they are not C99, (2) gcc does + // the same thing, and (3) it's easier. + if (sym->symbol_list) + error_die(token->pos, "redefinition of %s", show_typename (sym)); + sym->pos = *repos; + + // Mark the structure as needing re-examination + sym->examined = 0; + } else if (match_op(token, '{')) { + // private struct/union/enum type + sym = alloc_symbol(token->pos, type); + set_current_scope(sym); // used by dissect + ctx->ctype.base_type = sym; + } else { sparse_error(token->pos, "expected declaration"); ctx->ctype.base_type = &bad_ctype; return token; } - sym = alloc_symbol(token->pos, type); - set_current_scope(sym); // used by dissect token = parse(token->next, sym); - ctx->ctype.base_type = sym; - token = expect(token, '}', "at end of specifier"); + token = expect(token, '}', "at end of specifier"); + attr.ctype.base_type = sym; + token = handle_attributes(token, &attr); + apply_ctype(token->pos, &sym->ctype, &attr.ctype); + sym->packed = attr.packed; + sym->endpos = token->pos; return token; @@ -1043,8 +1047,6 @@ static struct token *enum_specifier(struct token *token, struct symbol *sym, str return ret; } -static void apply_ctype(struct position pos, struct ctype *thistype, struct ctype *ctype); - static struct token *typeof_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx) { @@ -1056,7 +1058,7 @@ static struct token *typeof_specifier(struct token *token, struct symbol *sym, s struct symbol *sym; token = typename(token->next, &sym, NULL); ctx->ctype.base_type = sym->ctype.base_type; - apply_ctype(token->pos, &sym->ctype, &ctx->ctype); + apply_ctype(token->pos, &ctx->ctype, &sym->ctype); } else { struct symbol *typeof_sym = alloc_symbol(token->pos, SYM_TYPEOF); token = parse_expression(token->next, &typeof_sym->initializer); @@ -1088,8 +1090,10 @@ static struct token *ignore_attribute(struct token *token, struct symbol *attr, static struct token *attribute_packed(struct token *token, struct symbol *attr, struct decl_state *ctx) { - if (!ctx->ctype.alignment) + if (!ctx->ctype.alignment) { ctx->ctype.alignment = 1; + ctx->packed = 1; + } return token; } @@ -1427,24 +1431,24 @@ static struct token *generic_qualifier(struct token *next, struct symbol *sym, s return next; } -static void apply_ctype(struct position pos, struct ctype *thistype, struct ctype *ctype) +static void apply_ctype(struct position pos, struct ctype *dst, struct ctype *src) { - unsigned long mod = thistype->modifiers; + unsigned long mod = src->modifiers; if (mod) - apply_qualifier(&pos, ctype, mod); + apply_qualifier(&pos, dst, mod); /* Context */ - concat_ptr_list((struct ptr_list *)thistype->contexts, - (struct ptr_list **)&ctype->contexts); + concat_ptr_list((struct ptr_list *)src->contexts, + (struct ptr_list **)&dst->contexts); /* Alignment */ - if (thistype->alignment > ctype->alignment) - ctype->alignment = thistype->alignment; + if (src->alignment > dst->alignment) + dst->alignment = src->alignment; /* Address space */ - if (thistype->as) - ctype->as = thistype->as; + if (src->as) + dst->as = src->as; } static void specifier_conflict(struct position pos, int what, struct ident *new) @@ -1529,7 +1533,7 @@ static struct token *declaration_specifiers(struct token *token, struct decl_sta break; seen |= Set_S | Set_T; ctx->ctype.base_type = s->ctype.base_type; - apply_ctype(token->pos, &s->ctype, &ctx->ctype); + apply_ctype(token->pos, &ctx->ctype, &s->ctype); token = token->next; continue; } @@ -88,6 +88,7 @@ struct struct_union_info { unsigned long bit_size; int align_size; char has_flex_array; + bool packed; struct symbol *flex_array; }; @@ -120,6 +121,7 @@ static int bitfield_base_size(struct symbol *sym) static void lay_out_struct(struct symbol *sym, struct struct_union_info *info) { unsigned long bit_size, align_bit_mask; + unsigned long alignment; int base_size; bit_size = info->bit_size; @@ -136,7 +138,8 @@ static void lay_out_struct(struct symbol *sym, struct struct_union_info *info) info->flex_array = sym; } - align_bit_mask = bytes_to_bits(sym->ctype.alignment) - 1; + alignment = info->packed ? 1 : sym->ctype.alignment; + align_bit_mask = bytes_to_bits(alignment) - 1; /* * Bitfields have some very special rules.. @@ -147,7 +150,7 @@ static void lay_out_struct(struct symbol *sym, struct struct_union_info *info) // Zero-width fields just fill up the unit. int width = base_size ? : (bit_offset ? room : 0); - if (width > room) { + if (width > room && !info->packed) { bit_size = (bit_size + align_bit_mask) & ~align_bit_mask; bit_offset = 0; } @@ -157,6 +160,8 @@ static void lay_out_struct(struct symbol *sym, struct struct_union_info *info) info->bit_size = bit_size + width; // warning (sym->pos, "bitfield: offset=%d:%d size=:%d", sym->offset, sym->bit_offset, width); + if (info->packed && sym->type == SYM_NODE) + sym->packed = 1; return; } @@ -173,6 +178,7 @@ static void lay_out_struct(struct symbol *sym, struct struct_union_info *info) static struct symbol * examine_struct_union_type(struct symbol *sym, int advance) { struct struct_union_info info = { + .packed = sym->packed, .max_align = 1, .bit_size = 0, .align_size = 1 @@ -191,7 +197,7 @@ static struct symbol * examine_struct_union_type(struct symbol *sym, int advance sparse_error(info.flex_array->pos, "flexible array member '%s' is not last", show_ident(info.flex_array->ident)); examine_symbol_type(member); - if (member->ctype.alignment > info.max_align) { + if (member->ctype.alignment > info.max_align && !sym->packed) { // Unnamed bitfields do not affect alignment. if (member->ident || !is_bitfield_type(member)) info.max_align = member->ctype.alignment; @@ -112,6 +112,7 @@ struct decl_state { unsigned char prefer_abstract; unsigned char autotype; unsigned char forced; + unsigned char packed; }; struct pseudo; @@ -192,6 +193,7 @@ struct symbol { accessed:1, builtin:1, torename:1, + packed:1, transparent_union:1; int rank:3; // arithmetic's rank struct expression *array_size; diff --git a/validation/enum-type-dubious.c b/validation/enum-type-dubious.c new file mode 100644 index 00000000..f2cb39fc --- /dev/null +++ b/validation/enum-type-dubious.c @@ -0,0 +1,18 @@ +enum foobar { + FOO = (void*)0, + BAR = (void*)1, + BAZ = (int*)0, + QUX = (int*)123, +}; + +/* + * check-name: enum-type-dubious + * check-known-to-fail + * + * check-error-start +validation/enum-type-dubious.c:2:8: error: enumerator value for 'FOO' is not an integer constant +validation/enum-type-dubious.c:3:8: error: enumerator value for 'BAR' is not an integer constant +validation/enum-type-dubious.c:4:8: error: enumerator value for 'BAZ' is not an integer constant +validation/enum-type-dubious.c:5:8: error: enumerator value for 'QUX' is not an integer constant + * check-error-end + */ diff --git a/validation/enum-type-exotic.c b/validation/enum-type-exotic.c new file mode 100644 index 00000000..a17ca0ad --- /dev/null +++ b/validation/enum-type-exotic.c @@ -0,0 +1,28 @@ +enum foobar { + C = (unsigned char)0, + L = 1L, +}; + +unsigned int foo(void); +unsigned int foo(void) +{ +#ifdef __CHECKER__ + _Static_assert([typeof(C)] == [enum foobar], "enum type"); + _Static_assert([typeof(C)] != [unsigned char], "char type"); +#endif + + typeof(C) v = ~0; + return v; +} + +/* + * check-name: enum-type-exotic + * check-description: + * GCC type's for C is 'int' or maybe 'unsigned int' + * but certainly not 'unsigned char' like here. + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-excludes: ret\\.32 *\\$255 + */ diff --git a/validation/packed-bitfield0.c b/validation/packed-bitfield0.c new file mode 100644 index 00000000..2e209161 --- /dev/null +++ b/validation/packed-bitfield0.c @@ -0,0 +1,58 @@ +#define alignof(X) __alignof__(X) +#define __packed __attribute__((packed)) + +struct sa { + int a:7; + int c:10; + int b:2; +} __packed; +_Static_assert(alignof(struct sa) == 1, "alignof(struct sa)"); +_Static_assert( sizeof(struct sa) == 3, "sizeof(struct sa)"); + + +static int get_size(void) +{ + return sizeof(struct sa); +} + +static void chk_align(struct sa sa, struct sa *p) +{ + _Static_assert(alignof(sa) == 1, "alignof(sa)"); + _Static_assert(alignof(*p) == 1, "alignof(*p)"); +} + +static int fp0(struct sa *sa) +{ + return sa->c; +} + +static int fpx(struct sa *sa, int idx) +{ + return sa[idx].c; +} + +static int fglobal(void) +{ + extern struct sa g; + return g.c; +} + +static struct sa l; +static int flocal(void) +{ + return l.c; +} + + +int main(void) +{ + extern void fun(struct sa *); + struct sa sa = { 0 }; + + fun(&sa); + return 0; +} + +/* + * check-name: packed-bitfield0 + */ diff --git a/validation/packed-bitfield1.c b/validation/packed-bitfield1.c new file mode 100644 index 00000000..b7b575ce --- /dev/null +++ b/validation/packed-bitfield1.c @@ -0,0 +1,27 @@ +#define __packed __attribute__((packed)) + +struct s { + unsigned int f0:1; + unsigned int f1:1; + unsigned int pad:6; +} __packed; +_Static_assert(sizeof(struct s) == 1, "sizeof(struct s)"); + +extern struct s g; + +static int foo(struct s *ptr) +{ + int f = 0; + + f += g.f0; + f += g.f1; + + f += ptr->f0; + f += ptr->f1; + + return f; +} + +/* + * check-name: packed-bitfield1 + */ diff --git a/validation/packed-bitfield2.c b/validation/packed-bitfield2.c new file mode 100644 index 00000000..244204c2 --- /dev/null +++ b/validation/packed-bitfield2.c @@ -0,0 +1,15 @@ +struct bf2 { + unsigned p1:2; + unsigned i1:32; + unsigned p2:2; + unsigned s9:9; + unsigned s9:9; + unsigned s9:9; + unsigned b1:1; +} __attribute__((packed)); + +_Static_assert(sizeof(struct bf2) == 8); + +/* + * check-name: packed-bitfield2 + */ diff --git a/validation/packed-bitfield3.c b/validation/packed-bitfield3.c new file mode 100644 index 00000000..13368c84 --- /dev/null +++ b/validation/packed-bitfield3.c @@ -0,0 +1,28 @@ +#define __packed __attribute__((packed)) + +typedef unsigned char u8; +typedef __UINT16_TYPE__ u16; +typedef __UINT32_TYPE__ u32; +typedef __UINT64_TYPE__ u64; + +struct b { + u32 a:1; + u32 b:2; + u32 c:4; + u32 d:8; + u32 e:16; +} __packed; +_Static_assert(__alignof(struct b) == 1); +_Static_assert( sizeof(struct b) == 4); + +struct c { + u8 a; + u8 b; + u64 c:48; +} __packed; +_Static_assert(__alignof(struct c) == 1); +_Static_assert( sizeof(struct c) == 8); + +/* + * check-name: packed-bitfield3 + */ diff --git a/validation/packed-bitfield4.c b/validation/packed-bitfield4.c new file mode 100644 index 00000000..1a956344 --- /dev/null +++ b/validation/packed-bitfield4.c @@ -0,0 +1,18 @@ +#define __packed __attribute__((packed)) + +typedef __UINT32_TYPE__ u32; + +struct s { + u32 f:24; +} __packed; +_Static_assert(sizeof(struct s) == 3); + +static int ld(struct s *s) +{ + return s->f; +} + +/* + * check-name: packed-bitfield4 + * check-description: Is check_access() OK with short packed bitfields? + */ diff --git a/validation/packed-bitfield5.c b/validation/packed-bitfield5.c new file mode 100644 index 00000000..87dbf9c2 --- /dev/null +++ b/validation/packed-bitfield5.c @@ -0,0 +1,20 @@ +#define __packed __attribute__((packed)) + +typedef __UINT32_TYPE__ u32; + +struct s { + u32 a:5; + u32 f:30; + u32 z:5; +} __packed; +_Static_assert(sizeof(struct s) == 5); + +static int ld(struct s *s) +{ + return s->f; +} + +/* + * check-name: packed-bitfield5 + * check-description: is check_access() OK with 'overlapping' packed bitfields? + */ diff --git a/validation/packed-deref0.c b/validation/packed-deref0.c new file mode 100644 index 00000000..d48ad1ac --- /dev/null +++ b/validation/packed-deref0.c @@ -0,0 +1,23 @@ +#define __packed __attribute__((packed)) + +typedef struct { + __INT8_TYPE__ a; + __INT16_TYPE__ b; + __INT32_TYPE__ c; +} __packed obj_t; + +_Static_assert(sizeof(obj_t) == 7, "sizeof packed struct"); + +static void foo(obj_t *ptr, int val) +{ + ptr->c = val; +} + +static void bar(obj_t o) +{ + foo(&o, 0); +} + +/* + * check-name: packed-deref0 + */ diff --git a/validation/packed-struct.c b/validation/packed-struct.c new file mode 100644 index 00000000..dad22791 --- /dev/null +++ b/validation/packed-struct.c @@ -0,0 +1,32 @@ +#define __packed __attribute__((packed)) + +typedef unsigned char u8; +typedef __UINT16_TYPE__ u16; +typedef __UINT32_TYPE__ u32; +typedef __UINT64_TYPE__ u64; + +struct a { + u8 a; + u8 b; + u16 c; +} __packed; +_Static_assert(__alignof(struct a) == 1, "align struct"); +_Static_assert( sizeof(struct a) == 4, " size struct"); + +struct b { + u32 a; + u32 b; +} __packed; +_Static_assert(__alignof(struct b) == 1, "align struct"); +_Static_assert( sizeof(struct b) == 8, "size struct"); + +struct c { + u16 a; + u32 b; +} __packed; +_Static_assert(__alignof(struct c) == 1, "align struct"); +_Static_assert( sizeof(struct c) == 6, "size struct"); + +/* + * check-name: packed-struct + */ diff --git a/validation/parsing/enum-attr.c b/validation/parsing/enum-attr.c new file mode 100644 index 00000000..8d851a16 --- /dev/null +++ b/validation/parsing/enum-attr.c @@ -0,0 +1,29 @@ +#define __attr __attribute__((deprecated)) + +enum { + old __attr, + cur __attr = 42, + new, +}; + +enum odd { + odd = __attr 33, +}; + +enum bad { + bad = 43 __attr, +}; + +/* + * check-name: enum-attr + * + * check-error-start +parsing/enum-attr.c:10:15: error: typename in expression +parsing/enum-attr.c:10:15: error: undefined identifier '__attribute__' +parsing/enum-attr.c:10:15: error: bad constant expression type +parsing/enum-attr.c:10:22: error: Expected } at end of specifier +parsing/enum-attr.c:10:22: error: got 33 +parsing/enum-attr.c:14:18: error: Expected } at end of specifier +parsing/enum-attr.c:14:18: error: got __attribute__ + * check-error-end + */ diff --git a/validation/type-attribute-align.c b/validation/type-attribute-align.c new file mode 100644 index 00000000..5f3db14a --- /dev/null +++ b/validation/type-attribute-align.c @@ -0,0 +1,19 @@ +#define __aligned(N) __attribute__((aligned(N))) +#define alignof(X) __alignof__(X) + +struct s { + short a, b, c; +} __aligned(2*sizeof(short)); + +static int fs(void) { return sizeof(struct s); } +static int fa(void) { return alignof(struct s); } + +void main(void) +{ + _Static_assert( sizeof(struct s) == 4 * sizeof(short), "size"); + _Static_assert(alignof(struct s) == 2 * sizeof(short), "alignment"); +} + +/* + * check-name: type-attribute-align + */ diff --git a/validation/type-attribute-as.c b/validation/type-attribute-as.c new file mode 100644 index 00000000..38f06b37 --- /dev/null +++ b/validation/type-attribute-as.c @@ -0,0 +1,33 @@ +#define __as __attribute__((address_space(__as))) + +struct s { + int i; +} __as; + + +extern void use0(void *); +extern void use1(void __as *); + +void main(void) +{ + struct s s; + int i; + + use0(&s); // KO + use0(&i); // OK + use1(&s); // OK + use1(&i); // KO +} + +/* + * check-name: type-attribute-as + * + * check-error-start +type-attribute-as.c:16:15: warning: incorrect type in argument 1 (different address spaces) +type-attribute-as.c:16:15: expected void * +type-attribute-as.c:16:15: got struct s __as * +type-attribute-as.c:19:15: warning: incorrect type in argument 1 (different address spaces) +type-attribute-as.c:19:15: expected void __as * +type-attribute-as.c:19:15: got int * + * check-error-end + */ diff --git a/validation/type-attribute-mod.c b/validation/type-attribute-mod.c new file mode 100644 index 00000000..d55011df --- /dev/null +++ b/validation/type-attribute-mod.c @@ -0,0 +1,21 @@ +#define __noderef __attribute__((noderef)) + +struct s { + int i; +} __noderef; + + +void main(void) +{ + struct s s; + + s.i = 0; +} + +/* + * check-name: type-attribute-mod + * + * check-error-start +type-attribute-mod.c:12:9: warning: dereference of noderef expression + * check-error-end + */ diff --git a/validation/type-attribute-qual.c b/validation/type-attribute-qual.c new file mode 100644 index 00000000..62d8b98e --- /dev/null +++ b/validation/type-attribute-qual.c @@ -0,0 +1,15 @@ +static const struct s { + int x; +} map[2]; + +static void foo(struct s *p, int v) +{ + p->x += v; +} + +/* + * check-name: type-attribute-qual + * check-description: When declaring a type and a variable in the same + * declaration, ensure that type qualifiers apply to the variable + * and not to the type. + */ |