Skip to content

Commit 228562d

Browse files
committed
C front-end: support constructor/destructor attribute with priority argument
GCC supports priorities to prescribe the order in which constructors (and destructors) are to be run: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html. Fixes: #8688
1 parent 4fe3ade commit 228562d

File tree

8 files changed

+177
-6
lines changed

8 files changed

+177
-6
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include <assert.h>
2+
3+
#ifdef __GNUC__
4+
int x = 0, y = 0, w = 0;
5+
6+
// Constructor with priority argument; name chosen to make sure lexicographic
7+
// ordering does not incidentally produce the correct sequence.
8+
__attribute__((constructor(101))) void z_init_priority(void)
9+
{
10+
x = 1;
11+
}
12+
13+
// Constructor with priority argument
14+
__attribute__((constructor(102))) void y_init_priority2(void)
15+
{
16+
assert(x == 1);
17+
x = 2;
18+
}
19+
20+
// Destructor with priority argument
21+
__attribute__((destructor(201))) void fini_priority(void)
22+
{
23+
assert(w == 4);
24+
y = 2;
25+
}
26+
27+
// Constructor without priority (should still work, have lowest priority)
28+
__attribute__((constructor)) void x_init_no_priority(void)
29+
{
30+
assert(x == 2);
31+
x = 3;
32+
}
33+
34+
// Destructor without priority (should still work, has highest priority)
35+
__attribute__((destructor)) void fini_no_priority(void)
36+
{
37+
w = 4;
38+
}
39+
#endif
40+
41+
int main()
42+
{
43+
#ifdef __GNUC__
44+
// All constructors should have run before main
45+
assert(x == 3);
46+
// Destructors haven't run yet
47+
assert(y == 0);
48+
assert(w == 0);
49+
#endif
50+
return 0;
51+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

src/ansi-c/ansi_c_convert_type.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,27 @@ void ansi_c_convert_typet::read_rec(const typet &type)
228228
else if(type.id()==ID_noreturn)
229229
c_qualifiers.is_noreturn=true;
230230
else if(type.id()==ID_constructor)
231+
{
231232
constructor=true;
233+
234+
// may come with priority
235+
const irep_idt &priority_opt = type.find(ID_constructor_priority);
236+
if(priority_opt.is_nil())
237+
constructor_priority = exprt{ID_default};
238+
else
239+
constructor_priority = static_cast<const exprt &>(priority_opt);
240+
}
232241
else if(type.id()==ID_destructor)
242+
{
233243
destructor=true;
244+
245+
// may come with priority
246+
const irep_idt &priority_opt = type.find(ID_destructor_priority);
247+
if(priority_opt.is_nil())
248+
destructor_priority = exprt{ID_default};
249+
else
250+
destructor_priority = static_cast<const exprt &>(priority_opt);
251+
}
234252
else if(
235253
type.id() == ID_alias && type.has_subtype() &&
236254
to_type_with_subtype(type).subtype().id() == ID_string_constant)
@@ -363,7 +381,16 @@ void ansi_c_convert_typet::write(typet &type)
363381
throw 0;
364382
}
365383

366-
type_p->id(constructor ? ID_constructor : ID_destructor);
384+
if(constructor)
385+
{
386+
type_p->id(ID_constructor);
387+
type_p->add(ID_constructor_priority, constructor_priority);
388+
}
389+
else
390+
{
391+
type_p->id(ID_destructor);
392+
type_p->add(ID_destructor_priority, destructor_priority);
393+
}
367394
}
368395
}
369396
else if(constructor || destructor)

src/ansi-c/ansi_c_convert_type.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class ansi_c_convert_typet
4444
exprt vector_size, alignment, bv_width, fraction_width;
4545
exprt msc_based; // this is Visual Studio
4646
bool constructor, destructor;
47+
exprt constructor_priority, destructor_priority;
4748

4849
// contracts
4950
exprt::operandst c_assigns, c_frees, c_ensures, c_requires;

src/ansi-c/ansi_c_entry_point.cpp

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,10 @@ bool generate_ansi_c_start_function(
511511

512512
record_function_outputs(symbol, init_code, symbol_table);
513513

514-
// now call destructor functions (a GCC extension)
514+
// now call destructor functions (a GCC extension); compute required order
515+
// first
516+
std::map<mp_integer, std::list<std::reference_wrapper<const symbolt>>>
517+
destructors_by_priority;
515518

516519
for(const auto &symbol_table_entry : symbol_table.symbols)
517520
{
@@ -525,8 +528,39 @@ bool generate_ansi_c_start_function(
525528
code_type.return_type().id() == ID_destructor &&
526529
code_type.parameters().empty())
527530
{
528-
code_function_callt destructor_call(symbol.symbol_expr());
529-
destructor_call.add_source_location() = symbol.location;
531+
const exprt &priorty = static_cast<const exprt &>(
532+
code_type.return_type().find(ID_destructor_priority));
533+
if(priorty.id() == ID_default)
534+
destructors_by_priority[1].push_back(symbol);
535+
else
536+
{
537+
std::optional<mp_integer> priority = numeric_cast<mp_integer>(priority);
538+
DATA_INVARIANT(
539+
priority.has_value() && *priority >= 0,
540+
"destructor priority expected to be non-negative integer");
541+
destructors_by_priority[-*priority].push_back(symbol);
542+
}
543+
}
544+
}
545+
546+
// append all destructors with priority "default" to the otherwise
547+
// lowest-priority destructor list
548+
if(
549+
destructors_by_priority.size() > 1 &&
550+
std::prev(destructors_by_priority.end())->first == 1)
551+
{
552+
auto &dest_list = destructors_by_priority.begin()->second;
553+
dest_list.splice(
554+
dest_list.begin(), std::prev(destructors_by_priority.end())->second);
555+
}
556+
557+
for(const auto &priority_pair : destructors_by_priority)
558+
{
559+
for(const auto &sym_ref : priority_pair.second)
560+
{
561+
const code_typet &code_type = to_code_type(sym_ref.get().type);
562+
code_function_callt destructor_call{sym_ref.get().symbol_expr()};
563+
destructor_call.add_source_location() = sym_ref.get().location;
530564
init_code.add(std::move(destructor_call));
531565
}
532566
}

src/ansi-c/parser.y

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,8 +1699,20 @@ gcc_type_attribute:
16991699
{ $$=$1; set($$, ID_noreturn); }
17001700
| TOK_GCC_ATTRIBUTE_CONSTRUCTOR
17011701
{ $$=$1; set($$, ID_constructor); }
1702+
| TOK_GCC_ATTRIBUTE_CONSTRUCTOR '(' TOK_INTEGER ')'
1703+
{
1704+
$$=$1;
1705+
set($$, ID_constructor);
1706+
parser_stack($$).set(ID_constructor_priority, parser_stack($3));
1707+
}
17021708
| TOK_GCC_ATTRIBUTE_DESTRUCTOR
17031709
{ $$=$1; set($$, ID_destructor); }
1710+
| TOK_GCC_ATTRIBUTE_DESTRUCTOR '(' TOK_INTEGER ')'
1711+
{
1712+
$$=$1;
1713+
set($$, ID_destructor);
1714+
parser_stack($$).set(ID_destructor_priority, parser_stack($3));
1715+
}
17041716
| TOK_GCC_ATTRIBUTE_USED
17051717
{ $$=$1; set($$, ID_used); }
17061718
;

src/linking/static_lifetime_init.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,10 @@ void static_lifetime_init(
142142
dest.add(std::move(*code));
143143
}
144144

145-
// now call designated "initialization" functions
145+
// now call designated "initialization" functions; compute required order
146+
// first
147+
std::map<mp_integer, std::list<std::reference_wrapper<const symbolt>>>
148+
constructors_by_priority;
146149

147150
for(const std::string &id : symbols)
148151
{
@@ -156,8 +159,41 @@ void static_lifetime_init(
156159
code_type.return_type().id() == ID_constructor &&
157160
code_type.parameters().empty())
158161
{
162+
const exprt &priorty = static_cast<const exprt &>(
163+
code_type.return_type().find(ID_constructor_priority));
164+
if(priorty.id() == ID_default)
165+
constructors_by_priority[-1].push_back(symbol);
166+
else
167+
{
168+
std::optional<mp_integer> priority = numeric_cast<mp_integer>(priority);
169+
DATA_INVARIANT(
170+
priority.has_value() && *priority >= 0,
171+
"constructor priority expected to be non-negative integer");
172+
constructors_by_priority[*priority].push_back(symbol);
173+
}
174+
}
175+
}
176+
177+
// append all constructors with priority "default" to the otherwise
178+
// lowest-priority constructor list
179+
if(
180+
constructors_by_priority.size() > 1 &&
181+
constructors_by_priority.begin()->first == -1)
182+
{
183+
auto &dest_list = std::prev(constructors_by_priority.end())->second;
184+
dest_list.splice(dest_list.end(), constructors_by_priority.begin()->second);
185+
}
186+
187+
for(const auto &priority_pair : constructors_by_priority)
188+
{
189+
for(const auto &sym_ref : priority_pair.second)
190+
{
191+
const code_typet &code_type = to_code_type(sym_ref.get().type);
159192
dest.add(code_expressiont{side_effect_expr_function_callt{
160-
symbol.symbol_expr(), {}, code_type.return_type(), source_location}});
193+
sym_ref.get().symbol_expr(),
194+
{},
195+
code_type.return_type(),
196+
source_location}});
161197
}
162198
}
163199
}

src/util/irep_ids.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,8 @@ IREP_ID_ONE(methods)
422422
IREP_ID_ONE(static_members)
423423
IREP_ID_ONE(constructor)
424424
IREP_ID_ONE(destructor)
425+
IREP_ID_ONE(constructor_priority)
426+
IREP_ID_ONE(destructor_priority)
425427
IREP_ID_ONE(bases)
426428
IREP_ID_ONE(base)
427429
IREP_ID_ONE(from_base)

0 commit comments

Comments
 (0)