Skip to content

Commit 52a8afc

Browse files
author
Daniel Kroening
authored
Merge pull request diffblue#1898 from tautschnig/always-inline
Fully process always_inline (second attempt)
2 parents cbe691b + 688a0ab commit 52a8afc

14 files changed

+184
-4
lines changed
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// this is a GCC extension
2+
3+
#ifdef __GNUC__
4+
static inline __attribute__((always_inline)) _Bool
5+
__is_kfree_rcu_offset(unsigned long offset)
6+
{
7+
return offset < 4096;
8+
}
9+
10+
static inline __attribute__((always_inline)) void
11+
kfree_rcu(unsigned long offset)
12+
{
13+
// immediate use of a constant as argument to __builtin_constant_p
14+
((void)sizeof(char[1 - 2 * !!(!__builtin_constant_p(offset))]));
15+
// inlining required to turn the array size into a compile-time constant
16+
((void)sizeof(char[1 - 2 * !!(!__is_kfree_rcu_offset(offset))]));
17+
}
18+
19+
static inline __attribute__((always_inline)) void unused(unsigned long offset)
20+
{
21+
// this would not be constant as it's never used, the front-end needs to
22+
// discard it
23+
((void)sizeof(char[1 - 2 * !!(!__builtin_constant_p(offset))]));
24+
}
25+
26+
// unused, but no 'static'
27+
inline __attribute__((__always_inline__)) int also_unused(int _c)
28+
{
29+
return _c;
30+
}
31+
#endif
32+
33+
int main()
34+
{
35+
#ifdef __GNUC__
36+
kfree_rcu(42);
37+
#endif
38+
39+
return 0;
40+
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
^warning: ignoring
8+
^CONVERSION ERROR$
9+
--
10+
The static asserts (arrays that may have a negative size if the assertion fails)
11+
can only be evaluated if always_inline is correctly applied.

src/ansi-c/ansi_c_convert_type.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ void ansi_c_convert_typet::read_rec(const typet &type)
171171
c_storage_spec.is_weak=true;
172172
else if(type.id() == ID_used)
173173
c_storage_spec.is_used = true;
174+
else if(type.id() == ID_always_inline)
175+
c_storage_spec.is_always_inline = true;
174176
else if(type.id()==ID_auto)
175177
{
176178
// ignore

src/ansi-c/ansi_c_declaration.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ void ansi_c_declarationt::output(std::ostream &out) const
8181
out << " is_extern";
8282
if(get_is_static_assert())
8383
out << " is_static_assert";
84+
if(get_is_always_inline())
85+
out << " is_always_inline";
8486
out << "\n";
8587

8688
out << "Type: " << type().pretty() << "\n";
@@ -164,6 +166,9 @@ void ansi_c_declarationt::to_symbol(
164166
symbol.is_extern=false;
165167
else if(get_is_extern()) // traditional GCC
166168
symbol.is_file_local=true;
169+
170+
if(get_is_always_inline())
171+
symbol.is_macro = true;
167172
}
168173

169174
// GCC __attribute__((__used__)) - do not treat those as file-local

src/ansi-c/ansi_c_declaration.h

+10
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,16 @@ class ansi_c_declarationt:public exprt
205205
set(ID_is_used, is_used);
206206
}
207207

208+
bool get_is_always_inline() const
209+
{
210+
return get_bool(ID_is_always_inline);
211+
}
212+
213+
void set_is_always_inline(bool is_always_inline)
214+
{
215+
set(ID_is_always_inline, is_always_inline);
216+
}
217+
208218
void to_symbol(
209219
const ansi_c_declaratort &,
210220
symbolt &symbol) const;

src/ansi-c/c_storage_spec.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ void c_storage_spect::read(const typet &type)
3434
is_weak=true;
3535
else if(type.id() == ID_used)
3636
is_used = true;
37+
else if(type.id() == ID_always_inline)
38+
is_always_inline = true;
3739
else if(type.id()==ID_auto)
3840
{
3941
// ignore

src/ansi-c/c_storage_spec.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@ class c_storage_spect
3636
is_inline=false;
3737
is_weak=false;
3838
is_used = false;
39+
is_always_inline = false;
3940
alias.clear();
4041
asm_label.clear();
4142
section.clear();
4243
}
4344

44-
bool is_typedef, is_extern, is_static, is_register,
45-
is_inline, is_thread_local, is_weak, is_used;
45+
bool is_typedef, is_extern, is_static, is_register, is_inline,
46+
is_thread_local, is_weak, is_used, is_always_inline;
4647

4748
// __attribute__((alias("foo")))
4849
irep_idt alias;
@@ -53,6 +54,7 @@ class c_storage_spect
5354

5455
bool operator==(const c_storage_spect &other) const
5556
{
57+
// clang-format off
5658
return is_typedef==other.is_typedef &&
5759
is_extern==other.is_extern &&
5860
is_static==other.is_static &&
@@ -61,9 +63,11 @@ class c_storage_spect
6163
is_inline==other.is_inline &&
6264
is_weak==other.is_weak &&
6365
is_used == other.is_used &&
66+
is_always_inline == other.is_always_inline &&
6467
alias==other.alias &&
6568
asm_label==other.asm_label &&
6669
section==other.section;
70+
// clang-format on
6771
}
6872

6973
bool operator!=(const c_storage_spect &other) const
@@ -81,6 +85,7 @@ class c_storage_spect
8185
is_thread_local |=other.is_thread_local;
8286
is_weak |=other.is_weak;
8387
is_used |=other.is_used;
88+
is_always_inline |= other.is_always_inline;
8489
if(alias.empty())
8590
alias=other.alias;
8691
if(asm_label.empty())

src/ansi-c/c_typecheck_base.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ void c_typecheck_baset::typecheck_declaration(
689689
declaration.set_is_typedef(full_spec.is_typedef);
690690
declaration.set_is_weak(full_spec.is_weak);
691691
declaration.set_is_used(full_spec.is_used);
692+
declaration.set_is_always_inline(full_spec.is_always_inline);
692693

693694
symbolt symbol;
694695
declaration.to_symbol(*d_it, symbol);

src/ansi-c/c_typecheck_expr.cpp

+87-1
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ Author: Daniel Kroening, [email protected]
1818
#include <util/c_types.h>
1919
#include <util/config.h>
2020
#include <util/cprover_prefix.h>
21+
#include <util/expr_util.h>
2122
#include <util/ieee_float.h>
2223
#include <util/pointer_offset_size.h>
2324
#include <util/pointer_predicates.h>
25+
#include <util/replace_symbol.h>
2426
#include <util/simplify_expr.h>
2527
#include <util/string_constant.h>
2628

@@ -1918,7 +1920,10 @@ void c_typecheck_baset::typecheck_side_effect_function_call(
19181920
if(entry!=asm_label_map.end())
19191921
identifier=entry->second;
19201922

1921-
if(symbol_table.symbols.find(identifier)==symbol_table.symbols.end())
1923+
symbol_tablet::symbolst::const_iterator sym_entry =
1924+
symbol_table.symbols.find(identifier);
1925+
1926+
if(sym_entry == symbol_table.symbols.end())
19221927
{
19231928
// This is an undeclared function.
19241929
// Is this a builtin?
@@ -1960,6 +1965,87 @@ void c_typecheck_baset::typecheck_side_effect_function_call(
19601965
warning() << "function `" << identifier << "' is not declared" << eom;
19611966
}
19621967
}
1968+
else if(
1969+
sym_entry->second.type.get_bool(ID_C_inlined) &&
1970+
sym_entry->second.is_macro && sym_entry->second.value.is_not_nil())
1971+
{
1972+
// calling a function marked as always_inline
1973+
const symbolt &func_sym = sym_entry->second;
1974+
const code_typet &func_type = to_code_type(func_sym.type);
1975+
1976+
replace_symbolt replace;
1977+
1978+
const code_typet::parameterst &parameters = func_type.parameters();
1979+
auto p_it = parameters.begin();
1980+
for(const auto &arg : expr.arguments())
1981+
{
1982+
if(p_it == parameters.end())
1983+
{
1984+
// we don't support varargs with always_inline
1985+
err_location(f_op);
1986+
error() << "function call has additional arguments, "
1987+
<< "cannot apply always_inline" << eom;
1988+
throw 0;
1989+
}
1990+
1991+
irep_idt p_id = p_it->get_identifier();
1992+
if(p_id.empty())
1993+
{
1994+
p_id = id2string(func_sym.base_name) + "::" +
1995+
id2string(p_it->get_base_name());
1996+
}
1997+
replace.insert(p_id, arg);
1998+
1999+
++p_it;
2000+
}
2001+
2002+
if(p_it != parameters.end())
2003+
{
2004+
err_location(f_op);
2005+
error() << "function call has missing arguments, "
2006+
<< "cannot apply always_inline" << eom;
2007+
throw 0;
2008+
}
2009+
2010+
codet body = to_code(func_sym.value);
2011+
replace(body);
2012+
2013+
side_effect_exprt side_effect_expr(
2014+
ID_statement_expression, func_type.return_type());
2015+
body.make_block();
2016+
2017+
// simulates parts of typecheck_function_body
2018+
typet cur_return_type = return_type;
2019+
return_type = func_type.return_type();
2020+
typecheck_code(body);
2021+
return_type.swap(cur_return_type);
2022+
2023+
// replace final return by an ID_expression
2024+
codet &last = to_code_block(body).find_last_statement();
2025+
2026+
if(last.get_statement() == ID_return)
2027+
last.set_statement(ID_expression);
2028+
2029+
// NOLINTNEXTLINE(whitespace/braces)
2030+
const bool has_returns = has_subexpr(body, [&](const exprt &e) {
2031+
return e.id() == ID_code && to_code(e).get_statement() == ID_return;
2032+
});
2033+
if(has_returns)
2034+
{
2035+
// we don't support multiple return statements with always_inline
2036+
err_location(last);
2037+
error() << "function has multiple return statements, "
2038+
<< "cannot apply always_inline" << eom;
2039+
throw 0;
2040+
}
2041+
2042+
side_effect_expr.copy_to_operands(body);
2043+
typecheck_side_effect_statement_expression(side_effect_expr);
2044+
2045+
expr.swap(side_effect_expr);
2046+
2047+
return;
2048+
}
19632049
}
19642050

19652051
// typecheck it now

src/ansi-c/parser.y

+3
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ extern char *yyansi_ctext;
150150
%token TOK_GCC_ATTRIBUTE_DESTRUCTOR "destructor"
151151
%token TOK_GCC_ATTRIBUTE_FALLTHROUGH "fallthrough"
152152
%token TOK_GCC_ATTRIBUTE_USED "used"
153+
%token TOK_GCC_ATTRIBUTE_ALWAYS_INLINE "always_inline"
153154
%token TOK_GCC_LABEL "__label__"
154155
%token TOK_MSC_ASM "__asm"
155156
%token TOK_MSC_BASED "__based"
@@ -1547,6 +1548,8 @@ gcc_type_attribute:
15471548
{ $$=$1; set($$, ID_destructor); }
15481549
| TOK_GCC_ATTRIBUTE_USED
15491550
{ $$=$1; set($$, ID_used); }
1551+
| TOK_GCC_ATTRIBUTE_ALWAYS_INLINE
1552+
{ $$=$1; set($$, ID_always_inline); }
15501553
;
15511554

15521555
gcc_attribute:

src/ansi-c/scanner.l

+3
Original file line numberDiff line numberDiff line change
@@ -1590,6 +1590,9 @@ __decltype { if(PARSER.cpp98 &&
15901590
"used" |
15911591
"__used__" { BEGIN(GCC_ATTRIBUTE3); loc(); return TOK_GCC_ATTRIBUTE_USED; }
15921592

1593+
"always_inline" |
1594+
"__always_inline__" { BEGIN(GCC_ATTRIBUTE3); loc(); return TOK_GCC_ATTRIBUTE_ALWAYS_INLINE; }
1595+
15931596
{ws} { /* ignore */ }
15941597
{newline} { /* ignore */ }
15951598
{identifier} { BEGIN(GCC_ATTRIBUTE4); }

src/linking/remove_internal_symbols.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ void remove_internal_symbols(
126126
is_file_local=false;
127127
}
128128

129-
if(is_type)
129+
if(is_type || symbol.is_macro)
130130
{
131131
// never EXPORTED by itself
132132
}

src/util/irep_ids.def

+2
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,8 @@ IREP_ID_TWO(C_abstract, #abstract)
671671
IREP_ID_ONE(synthetic)
672672
IREP_ID_ONE(interface)
673673
IREP_ID_TWO(C_must_not_throw, #must_not_throw)
674+
IREP_ID_ONE(always_inline)
675+
IREP_ID_ONE(is_always_inline)
674676

675677
// Projects depending on this code base that wish to extend the list of
676678
// available ids should provide a file local_irep_ids.h in their source tree and

src/util/replace_symbol.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ bool replace_symbolt::replace(
100100
if(c_sizeof_type.is_not_nil() && have_to_replace(c_sizeof_type))
101101
result &= replace(static_cast<typet&>(dest.add(ID_C_c_sizeof_type)));
102102

103+
const typet &type_arg = static_cast<const typet &>(dest.find(ID_type_arg));
104+
if(type_arg.is_not_nil() && have_to_replace(type_arg))
105+
result &= replace(static_cast<typet &>(dest.add(ID_type_arg)));
106+
103107
const typet &va_arg_type =
104108
static_cast<const typet&>(dest.find(ID_C_va_arg_type));
105109
if(va_arg_type.is_not_nil() && have_to_replace(va_arg_type))
@@ -136,6 +140,12 @@ bool replace_symbolt::have_to_replace(const exprt &dest) const
136140
if(have_to_replace(static_cast<const typet &>(c_sizeof_type)))
137141
return true;
138142

143+
const irept &type_arg = dest.find(ID_type_arg);
144+
145+
if(type_arg.is_not_nil())
146+
if(have_to_replace(static_cast<const typet &>(type_arg)))
147+
return true;
148+
139149
const irept &va_arg_type=dest.find(ID_C_va_arg_type);
140150

141151
if(va_arg_type.is_not_nil())

0 commit comments

Comments
 (0)