Skip to content

Commit 78fc2b7

Browse files
Merge pull request #5078 from romainbrenguier/refactor/user-specified-clinit
Avoid redundant computations in get_user_specified_clinit
2 parents fc0a0cd + 30bc2b6 commit 78fc2b7

File tree

6 files changed

+183
-77
lines changed

6 files changed

+183
-77
lines changed

jbmc/src/java_bytecode/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -674,10 +674,11 @@ determine which loading strategy to use.
674674
If [lazy_methods_mode](\ref java_bytecode_languaget::lazy_methods_mode) is
675675
\ref java_bytecode_languaget::LAZY_METHODS_MODE_EAGER then eager loading is
676676
used. Under eager loading
677-
\ref java_bytecode_languaget::convert_single_method(const irep_idt &, symbol_table_baset &)
677+
\ref java_bytecode_languaget::convert_single_method(const irep_idt &, symbol_table_baset &, lazy_class_to_declared_symbols_mapt &)
678678
is called once for each method listed in method_bytecode (described above). This
679679
then calls
680-
\ref java_bytecode_languaget::convert_single_method(const irep_idt &, symbol_table_baset &, optionalt<ci_lazy_methods_neededt>)
680+
\ref java_bytecode_languaget::convert_single_method(const irep_idt &, symbol_table_baset &, optionalt<ci_lazy_methods_neededt>, lazy_class_to_declared_symbols_mapt &);
681+
681682
without a ci_lazy_methods_neededt object, which calls
682683
\ref java_bytecode_convert_method, passing in the method parse tree. This in
683684
turn uses \ref java_bytecode_convert_methodt to turn the bytecode into symbols

jbmc/src/java_bytecode/java_bytecode_language.cpp

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,23 @@ static prefix_filtert get_context(const optionst &options)
115115
return prefix_filtert(std::move(context_include), std::move(context_exclude));
116116
}
117117

118+
std::unordered_multimap<irep_idt, symbolt> &
119+
lazy_class_to_declared_symbols_mapt::get(const symbol_tablet &symbol_table)
120+
{
121+
if(!initialized)
122+
{
123+
map = class_to_declared_symbols(symbol_table);
124+
initialized = true;
125+
}
126+
return map;
127+
}
128+
129+
void lazy_class_to_declared_symbols_mapt::reinitialize()
130+
{
131+
initialized = false;
132+
map.clear();
133+
}
134+
118135
/// Consume options that are java bytecode specific.
119136
void java_bytecode_languaget::set_language_options(const optionst &options)
120137
{
@@ -203,7 +220,26 @@ void java_bytecode_languaget::set_language_options(const optionst &options)
203220
java_cp_include_files=".*";
204221

205222
nondet_static = options.get_bool_option("nondet-static");
206-
static_values_file = options.get_option("static-values");
223+
if(options.is_set("static-values"))
224+
{
225+
const std::string filename = options.get_option("static-values");
226+
jsont tmp_json;
227+
if(parse_json(filename, *message_handler, tmp_json))
228+
{
229+
warning() << "Provided JSON file for static-values cannot be parsed; it"
230+
<< " will be ignored." << messaget::eom;
231+
}
232+
else
233+
{
234+
if(!tmp_json.is_object())
235+
{
236+
warning() << "Provided JSON file for static-values is not a JSON "
237+
<< "object; it will be ignored." << messaget::eom;
238+
}
239+
else
240+
static_values_json = std::move(to_json_object(tmp_json));
241+
}
242+
}
207243

208244
ignore_manifest_main_class =
209245
options.get_bool_option("ignore-manifest-main-class");
@@ -853,7 +889,9 @@ bool java_bytecode_languaget::typecheck(
853889
symbol_table,
854890
synthetic_methods,
855891
threading_support,
856-
!static_values_file.empty());
892+
static_values_json.has_value());
893+
894+
lazy_class_to_declared_symbols_mapt class_to_declared_symbols;
857895

858896
// Now incrementally elaborate methods
859897
// that are reachable from this entry point.
@@ -876,18 +914,21 @@ bool java_bytecode_languaget::typecheck(
876914
for(const auto &function_id_and_type : synthetic_methods)
877915
{
878916
convert_single_method(
879-
function_id_and_type.first, journalling_symbol_table);
917+
function_id_and_type.first,
918+
journalling_symbol_table,
919+
class_to_declared_symbols);
880920
}
881921
// Convert all methods for which we have bytecode now
882922
for(const auto &method_sig : method_bytecode)
883923
{
884-
convert_single_method(method_sig.first, journalling_symbol_table);
924+
convert_single_method(
925+
method_sig.first, journalling_symbol_table, class_to_declared_symbols);
885926
}
886927
// Now convert all newly added string methods
887928
for(const auto &fn_name : journalling_symbol_table.get_inserted())
888929
{
889930
if(string_preprocess.implements_function(fn_name))
890-
convert_single_method(fn_name, symbol_table);
931+
convert_single_method(fn_name, symbol_table, class_to_declared_symbols);
891932
}
892933
}
893934
break;
@@ -982,12 +1023,17 @@ bool java_bytecode_languaget::do_ci_lazy_method_conversion(
9821023
symbol_table_buildert symbol_table_builder =
9831024
symbol_table_buildert::wrap(symbol_table);
9841025

1026+
lazy_class_to_declared_symbols_mapt class_to_declared_symbols;
1027+
9851028
const method_convertert method_converter =
986-
[this, &symbol_table_builder](
1029+
[this, &symbol_table_builder, &class_to_declared_symbols](
9871030
const irep_idt &function_id,
9881031
ci_lazy_methods_neededt lazy_methods_needed) {
9891032
return convert_single_method(
990-
function_id, symbol_table_builder, std::move(lazy_methods_needed));
1033+
function_id,
1034+
symbol_table_builder,
1035+
std::move(lazy_methods_needed),
1036+
class_to_declared_symbols);
9911037
};
9921038

9931039
ci_lazy_methodst method_gather(
@@ -1050,7 +1096,8 @@ void java_bytecode_languaget::convert_lazy_method(
10501096
journalling_symbol_tablet symbol_table=
10511097
journalling_symbol_tablet::wrap(symtab);
10521098

1053-
convert_single_method(function_id, symbol_table);
1099+
lazy_class_to_declared_symbols_mapt class_to_declared_symbols;
1100+
convert_single_method(function_id, symbol_table, class_to_declared_symbols);
10541101

10551102
// Instrument runtime exceptions (unless symbol is a stub)
10561103
if(symbol.value.is_not_nil())
@@ -1118,10 +1165,13 @@ static void notify_static_method_calls(
11181165
/// \param symbol_table: global symbol table
11191166
/// \param needed_lazy_methods: optionally a collection of needed methods to
11201167
/// update with any methods touched during the conversion
1168+
/// \param class_to_declared_symbols: maps classes to the symbols that
1169+
/// they declare.
11211170
bool java_bytecode_languaget::convert_single_method(
11221171
const irep_idt &function_id,
11231172
symbol_table_baset &symbol_table,
1124-
optionalt<ci_lazy_methods_neededt> needed_lazy_methods)
1173+
optionalt<ci_lazy_methods_neededt> needed_lazy_methods,
1174+
lazy_class_to_declared_symbols_mapt &class_to_declared_symbols)
11251175
{
11261176
// Do not convert if method is not in context
11271177
if(method_in_context && !(*method_in_context)(id2string(function_id)))
@@ -1177,7 +1227,7 @@ bool java_bytecode_languaget::convert_single_method(
11771227
function_id,
11781228
symbol_table,
11791229
nondet_static,
1180-
!static_values_file.empty(),
1230+
static_values_json.has_value(),
11811231
object_factory_parameters,
11821232
get_pointer_type_selector(),
11831233
get_message_handler());
@@ -1186,7 +1236,7 @@ bool java_bytecode_languaget::convert_single_method(
11861236
function_id,
11871237
symbol_table,
11881238
nondet_static,
1189-
!static_values_file.empty(),
1239+
static_values_json.has_value(),
11901240
object_factory_parameters,
11911241
get_pointer_type_selector(),
11921242
get_message_handler());
@@ -1197,14 +1247,16 @@ bool java_bytecode_languaget::convert_single_method(
11971247
declaring_class(symbol_table.lookup_ref(function_id));
11981248
INVARIANT(
11991249
class_name, "user_specified_clinit must be declared by a class.");
1250+
INVARIANT(
1251+
static_values_json.has_value(), "static-values JSON must be available");
12001252
writable_symbol.value = get_user_specified_clinit_body(
12011253
*class_name,
1202-
static_values_file,
1254+
*static_values_json,
12031255
symbol_table,
1204-
get_message_handler(),
12051256
needed_lazy_methods,
12061257
max_user_array_length,
1207-
references);
1258+
references,
1259+
class_to_declared_symbols.get(symbol_table));
12081260
break;
12091261
}
12101262
case synthetic_method_typet::STUB_CLASS_STATIC_INITIALIZER:

jbmc/src/java_bytecode/java_bytecode_language.h

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,31 @@ enum lazy_methods_modet
121121
LAZY_METHODS_MODE_EXTERNAL_DRIVER
122122
};
123123

124+
/// Map classes to the symbols they declare but is only computed once it is
125+
/// needed and the map is then kept.
126+
/// Note that it only includes function and field symbols (and not for example,
127+
/// local variables), these are produced in the convert-class phase.
128+
/// Calling `get` before the symbol table is properly filled with these symbols,
129+
/// would make later calls return an outdated map. The
130+
/// lazy_class_to_declared_symbols_mapt would then need to be reinitialized.
131+
/// Similarly if some transformation creates or deletes function or field
132+
/// symbols in the symbol table, then the map would get out of date and would
133+
/// need to be reinitialized.
134+
class lazy_class_to_declared_symbols_mapt
135+
{
136+
public:
137+
lazy_class_to_declared_symbols_mapt() = default;
138+
139+
std::unordered_multimap<irep_idt, symbolt> &
140+
get(const symbol_tablet &symbol_table);
141+
142+
void reinitialize();
143+
144+
private:
145+
bool initialized = false;
146+
std::unordered_multimap<irep_idt, symbolt> map;
147+
};
148+
124149
#define JAVA_CLASS_MODEL_SUFFIX "@class_model"
125150

126151
class java_bytecode_languaget:public languaget
@@ -205,15 +230,20 @@ class java_bytecode_languaget:public languaget
205230
protected:
206231
void convert_single_method(
207232
const irep_idt &function_id,
208-
symbol_table_baset &symbol_table)
233+
symbol_table_baset &symbol_table,
234+
lazy_class_to_declared_symbols_mapt &class_to_declared_symbols)
209235
{
210236
convert_single_method(
211-
function_id, symbol_table, optionalt<ci_lazy_methods_neededt>());
237+
function_id,
238+
symbol_table,
239+
optionalt<ci_lazy_methods_neededt>(),
240+
class_to_declared_symbols);
212241
}
213242
bool convert_single_method(
214243
const irep_idt &function_id,
215244
symbol_table_baset &symbol_table,
216-
optionalt<ci_lazy_methods_neededt> needed_lazy_methods);
245+
optionalt<ci_lazy_methods_neededt> needed_lazy_methods,
246+
lazy_class_to_declared_symbols_mapt &class_to_declared_symbols);
217247

218248
bool do_ci_lazy_method_conversion(symbol_tablet &);
219249
const select_pointer_typet &get_pointer_type_selector() const;
@@ -236,10 +266,10 @@ class java_bytecode_languaget:public languaget
236266
java_string_library_preprocesst string_preprocess;
237267
std::string java_cp_include_files;
238268
bool nondet_static;
239-
/// Path to a JSON file which contains initial values of static fields (right
269+
/// JSON which contains initial values of static fields (right
240270
/// after the static initializer of the class was run). This is read from the
241-
/// --static-values command-line option.
242-
std::string static_values_file;
271+
/// file specified by the --static-values command-line option.
272+
optionalt<json_objectt> static_values_json;
243273

244274
// list of classes to force load even without reference from the entry point
245275
std::vector<irep_idt> java_load_classes;

jbmc/src/java_bytecode/java_static_initializers.cpp

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -767,65 +767,72 @@ code_ifthenelset get_clinit_wrapper_body(
767767
return code_ifthenelset(std::move(check_already_run), std::move(init_body));
768768
}
769769

770+
/// \return map associating classes to the symbols they declare
771+
std::unordered_multimap<irep_idt, symbolt>
772+
class_to_declared_symbols(const symbol_tablet &symbol_table)
773+
{
774+
std::unordered_multimap<irep_idt, symbolt> result;
775+
for(const auto &symbol_pair : symbol_table)
776+
{
777+
const symbolt &symbol = symbol_pair.second;
778+
if(optionalt<irep_idt> declaring = declaring_class(symbol))
779+
result.emplace(*declaring, symbol);
780+
}
781+
return result;
782+
}
783+
770784
code_blockt get_user_specified_clinit_body(
771785
const irep_idt &class_id,
772-
const std::string &static_values_file,
786+
const json_objectt &static_values_json,
773787
symbol_table_baset &symbol_table,
774-
message_handlert &message_handler,
775788
optionalt<ci_lazy_methods_neededt> needed_lazy_methods,
776789
size_t max_user_array_length,
777-
std::unordered_map<std::string, object_creation_referencet> &references)
790+
std::unordered_map<std::string, object_creation_referencet> &references,
791+
const std::unordered_multimap<irep_idt, symbolt>
792+
&class_to_declared_symbols_map)
778793
{
779-
jsont json;
780-
if(
781-
!static_values_file.empty() &&
782-
!parse_json(static_values_file, message_handler, json) && json.is_object())
794+
const irep_idt &real_clinit_name = clinit_function_name(class_id);
795+
const auto class_entry =
796+
static_values_json.find(id2string(strip_java_namespace_prefix(class_id)));
797+
if(class_entry != static_values_json.end())
783798
{
784-
const auto &json_object = to_json_object(json);
785-
const auto class_entry =
786-
json_object.find(id2string(strip_java_namespace_prefix(class_id)));
787-
if(class_entry != json_object.end())
799+
const auto &class_json_value = class_entry->second;
800+
if(class_json_value.is_object())
788801
{
789-
const auto &class_json_value = class_entry->second;
790-
if(class_json_value.is_object())
802+
const auto &class_json_object = to_json_object(class_json_value);
803+
std::map<symbol_exprt, jsont> static_field_values;
804+
for(const auto &class_symbol_pair :
805+
equal_range(class_to_declared_symbols_map, class_id))
791806
{
792-
const auto &class_json_object = to_json_object(class_json_value);
793-
std::map<symbol_exprt, jsont> static_field_values;
794-
for(const auto &symbol_pair : symbol_table)
807+
const symbolt &symbol = class_symbol_pair.second;
808+
if(symbol.is_static_lifetime)
795809
{
796-
const symbolt &symbol = symbol_pair.second;
797-
if(
798-
declaring_class(symbol) && *declaring_class(symbol) == class_id &&
799-
symbol.is_static_lifetime)
810+
const symbol_exprt &static_field_expr = symbol.symbol_expr();
811+
const auto &static_field_entry =
812+
class_json_object.find(id2string(symbol.base_name));
813+
if(static_field_entry != class_json_object.end())
800814
{
801-
const symbol_exprt &static_field_expr = symbol.symbol_expr();
802-
const auto &static_field_entry =
803-
class_json_object.find(id2string(symbol.base_name));
804-
if(static_field_entry != class_json_object.end())
805-
{
806-
static_field_values.insert(
807-
{static_field_expr, static_field_entry->second});
808-
}
815+
static_field_values.insert(
816+
{static_field_expr, static_field_entry->second});
809817
}
810818
}
811-
code_blockt body;
812-
for(const auto &value_pair : static_field_values)
813-
{
814-
assign_from_json(
815-
value_pair.first,
816-
value_pair.second,
817-
clinit_function_name(class_id),
818-
body,
819-
symbol_table,
820-
needed_lazy_methods,
821-
max_user_array_length,
822-
references);
823-
}
824-
return body;
825819
}
820+
code_blockt body;
821+
for(const auto &value_pair : static_field_values)
822+
{
823+
assign_from_json(
824+
value_pair.first,
825+
value_pair.second,
826+
real_clinit_name,
827+
body,
828+
symbol_table,
829+
needed_lazy_methods,
830+
max_user_array_length,
831+
references);
832+
}
833+
return body;
826834
}
827835
}
828-
const irep_idt &real_clinit_name = clinit_function_name(class_id);
829836
if(const auto clinit_func = symbol_table.lookup(real_clinit_name))
830837
return code_blockt{{code_function_callt{clinit_func->symbol_expr()}}};
831838
return code_blockt{};
@@ -996,21 +1003,21 @@ code_blockt stub_global_initializer_factoryt::get_stub_initializer_body(
9961003
// class. Note this is the same invocation used in
9971004
// java_static_lifetime_init.
9981005

999-
auto class_globals = stub_globals_by_class.equal_range(*class_id);
1006+
auto class_globals = equal_range(stub_globals_by_class, *class_id);
10001007
INVARIANT(
1001-
class_globals.first != class_globals.second,
1008+
!class_globals.empty(),
10021009
"class with synthetic clinit should have at least one global to init");
10031010

10041011
java_object_factory_parameterst parameters = object_factory_parameters;
10051012
parameters.function_id = function_id;
10061013

1007-
for(auto it = class_globals.first; it != class_globals.second; ++it)
1014+
for(const auto &pair : class_globals)
10081015
{
10091016
const symbol_exprt new_global_symbol =
1010-
symbol_table.lookup_ref(it->second).symbol_expr();
1017+
symbol_table.lookup_ref(pair.second).symbol_expr();
10111018

10121019
parameters.min_null_tree_depth =
1013-
is_non_null_library_global(it->second)
1020+
is_non_null_library_global(pair.second)
10141021
? object_factory_parameters.min_null_tree_depth + 1
10151022
: object_factory_parameters.min_null_tree_depth;
10161023

0 commit comments

Comments
 (0)