Skip to content

Commit 24da9ab

Browse files
committed
Fully interpret __attribute__((always_inline))
The Linux kernel uses tests on __builtin_constant_p that are required to evalute to true for compilationt to succeed. These can only evaluate to true when inlining is actually done (and expressions are simplified).
1 parent f7700c6 commit 24da9ab

File tree

12 files changed

+167
-3
lines changed

12 files changed

+167
-3
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
#endif
26+
27+
int main()
28+
{
29+
#ifdef __GNUC__
30+
kfree_rcu(42);
31+
#endif
32+
33+
return 0;
34+
}
Lines changed: 11 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ void ansi_c_convert_typet::read_rec(const typet &type)
155155
c_storage_spec.is_register=true;
156156
else if(type.id()==ID_weak)
157157
c_storage_spec.is_weak=true;
158+
else if(type.id() == ID_always_inline)
159+
c_storage_spec.is_always_inline = true;
158160
else if(type.id()==ID_auto)
159161
{
160162
// ignore

src/ansi-c/ansi_c_declaration.cpp

Lines changed: 5 additions & 0 deletions
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
}

src/ansi-c/ansi_c_declaration.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,16 @@ class ansi_c_declarationt:public exprt
195195
set(ID_is_weak, is_weak);
196196
}
197197

198+
bool get_is_always_inline() const
199+
{
200+
return get_bool(ID_is_always_inline);
201+
}
202+
203+
void set_is_always_inline(bool is_always_inline)
204+
{
205+
set(ID_is_always_inline, is_always_inline);
206+
}
207+
198208
void to_symbol(
199209
const ansi_c_declaratort &,
200210
symbolt &symbol) const;

src/ansi-c/c_storage_spec.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ void c_storage_spect::read(const typet &type)
3232
is_register=true;
3333
else if(type.id()==ID_weak)
3434
is_weak=true;
35+
else if(type.id() == ID_always_inline)
36+
is_always_inline = true;
3537
else if(type.id()==ID_auto)
3638
{
3739
// ignore

src/ansi-c/c_storage_spec.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ class c_storage_spect
3535
is_register=false;
3636
is_inline=false;
3737
is_weak=false;
38+
is_always_inline = false;
3839
alias.clear();
3940
asm_label.clear();
4041
section.clear();
4142
}
4243

43-
bool is_typedef, is_extern, is_static, is_register,
44-
is_inline, is_thread_local, is_weak;
44+
bool is_typedef, is_extern, is_static, is_register, is_inline,
45+
is_thread_local, is_weak, is_always_inline;
4546

4647
// __attribute__((alias("foo")))
4748
irep_idt alias;
@@ -52,16 +53,19 @@ class c_storage_spect
5253

5354
bool operator==(const c_storage_spect &other) const
5455
{
56+
// clang-format off
5557
return is_typedef==other.is_typedef &&
5658
is_extern==other.is_extern &&
5759
is_static==other.is_static &&
5860
is_register==other.is_register &&
5961
is_thread_local==other.is_thread_local &&
6062
is_inline==other.is_inline &&
6163
is_weak==other.is_weak &&
64+
is_always_inline == other.is_always_inline &&
6265
alias==other.alias &&
6366
asm_label==other.asm_label &&
6467
section==other.section;
68+
// clang-format on
6569
}
6670

6771
bool operator!=(const c_storage_spect &other) const
@@ -78,6 +82,7 @@ class c_storage_spect
7882
is_inline |=other.is_inline;
7983
is_thread_local |=other.is_thread_local;
8084
is_weak |=other.is_weak;
85+
is_always_inline |= other.is_always_inline;
8186
if(alias.empty())
8287
alias=other.alias;
8388
if(asm_label.empty())

src/ansi-c/c_typecheck_base.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,7 @@ void c_typecheck_baset::typecheck_declaration(
688688
declaration.set_is_register(full_spec.is_register);
689689
declaration.set_is_typedef(full_spec.is_typedef);
690690
declaration.set_is_weak(full_spec.is_weak);
691+
declaration.set_is_always_inline(full_spec.is_always_inline);
691692

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

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Author: Daniel Kroening, [email protected]
1616
#include <util/arith_tools.h>
1717
#include <util/c_types.h>
1818
#include <util/config.h>
19+
#include <util/expr_util.h>
1920
#include <util/std_types.h>
2021
#include <util/prefix.h>
2122
#include <util/cprover_prefix.h>
@@ -25,6 +26,7 @@ Author: Daniel Kroening, [email protected]
2526
#include <util/string_constant.h>
2627
#include <util/pointer_offset_size.h>
2728
#include <util/pointer_predicates.h>
29+
#include <util/replace_symbol.h>
2830

2931
#include "builtin_factory.h"
3032
#include "c_typecast.h"
@@ -1912,7 +1914,10 @@ void c_typecheck_baset::typecheck_side_effect_function_call(
19121914
if(entry!=asm_label_map.end())
19131915
identifier=entry->second;
19141916

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

19592045
// typecheck it now

src/ansi-c/parser.y

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ extern char *yyansi_ctext;
142142
%token TOK_GCC_ATTRIBUTE_NORETURN "noreturn"
143143
%token TOK_GCC_ATTRIBUTE_CONSTRUCTOR "constructor"
144144
%token TOK_GCC_ATTRIBUTE_DESTRUCTOR "destructor"
145+
%token TOK_GCC_ATTRIBUTE_ALWAYS_INLINE "always_inline"
145146
%token TOK_GCC_LABEL "__label__"
146147
%token TOK_MSC_ASM "__asm"
147148
%token TOK_MSC_BASED "__based"
@@ -1531,6 +1532,8 @@ gcc_type_attribute:
15311532
{ $$=$1; set($$, ID_constructor); }
15321533
| TOK_GCC_ATTRIBUTE_DESTRUCTOR
15331534
{ $$=$1; set($$, ID_destructor); }
1535+
| TOK_GCC_ATTRIBUTE_ALWAYS_INLINE
1536+
{ $$=$1; set($$, ID_always_inline); }
15341537
;
15351538

15361539
gcc_attribute:

0 commit comments

Comments
 (0)