Skip to content

Commit 6bd0b21

Browse files
agampemeta-codesync[bot]
authored andcommitted
Remove ConstProp state singletons
Summary: Rewrite with context storage instead. Reviewed By: xuhdev Differential Revision: D91178848 fbshipit-source-id: e0f1d4941c151d4475a36c3667095ddaa7f508dc
1 parent b40595a commit 6bd0b21

File tree

11 files changed

+104
-155
lines changed

11 files changed

+104
-155
lines changed

opt/constant-propagation/IPConstantPropagation.cpp

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ namespace {
6363
class AnalyzerGenerator {
6464
ImmutableAttributeAnalyzerState* m_immut_analyzer_state;
6565
ApiLevelAnalyzerState* m_api_level_analyzer_state;
66+
BoxedBooleanAnalyzerState m_boxed_boolean_analyzer_state{};
67+
EnumFieldAnalyzerState m_enum_field_analyzer_state;
6668
StringAnalyzerState* m_string_analyzer_state;
6769
PackageNameState* m_package_name_state;
6870
const State& m_cp_state;
@@ -78,13 +80,7 @@ class AnalyzerGenerator {
7880
m_api_level_analyzer_state(api_level_analyzer_state),
7981
m_string_analyzer_state(string_analyzer_state),
8082
m_package_name_state(package_name_state),
81-
m_cp_state(cp_state) {
82-
// Initialize the singletons that `operator()` needs ahead of time to
83-
// avoid a data race.
84-
static_cast<void>(EnumFieldAnalyzerState::get());
85-
static_cast<void>(BoxedBooleanAnalyzerState::get());
86-
static_cast<void>(ApiLevelAnalyzerState::get());
87-
}
83+
m_cp_state(cp_state) {}
8884

8985
std::unique_ptr<IntraproceduralAnalysis> operator()(
9086
const DexMethod* method,
@@ -115,7 +111,7 @@ class AnalyzerGenerator {
115111
&m_cp_state, std::move(wps_accessor), code.cfg(),
116112
CombinedAnalyzer(
117113
class_under_init, m_immut_analyzer_state, wps_accessor_ptr,
118-
EnumFieldAnalyzerState::get(), BoxedBooleanAnalyzerState::get(),
114+
m_enum_field_analyzer_state, m_boxed_boolean_analyzer_state,
119115
m_string_analyzer_state, nullptr, *m_api_level_analyzer_state,
120116
m_package_name_state, m_immut_analyzer_state, nullptr),
121117
std::move(env));
@@ -302,10 +298,9 @@ void PassImpl::run(const DexStoresVector& stores,
302298
// Hold the analyzer state of ImmutableAttributeAnalyzer.
303299
ImmutableAttributeAnalyzerState immut_analyzer_state;
304300
immutable_state::analyze_constructors(scope, &immut_analyzer_state);
305-
ApiLevelAnalyzerState api_level_analyzer_state =
306-
ApiLevelAnalyzerState::get(min_sdk);
307-
auto string_analyzer_state = StringAnalyzerState::get();
308-
auto package_name_state = PackageNameState::get(package_name);
301+
ApiLevelAnalyzerState api_level_analyzer_state{min_sdk};
302+
auto string_analyzer_state = StringAnalyzerState::make_default();
303+
auto package_name_state = PackageNameState::make(package_name);
309304
State cp_state;
310305
auto fp_iter =
311306
analyze(scope, &immut_analyzer_state, &api_level_analyzer_state,
@@ -319,8 +314,7 @@ void PassImpl::run(const DexStoresVector& stores,
319314
void PassImpl::eval_pass(DexStoresVector& /*stores*/,
320315
ConfigFiles& /*conf*/,
321316
PassManager&) {
322-
auto string_analyzer_state = StringAnalyzerState::get();
323-
string_analyzer_state.set_methods_as_root();
317+
StringAnalyzerState::make_default().set_methods_as_root();
324318
}
325319

326320
void PassImpl::run_pass(DexStoresVector& stores,

opt/shrinker/ShrinkerPass.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ void ShrinkerPass::bind_config() {
4747
void ShrinkerPass::eval_pass(DexStoresVector& /*stores*/,
4848
ConfigFiles& /*conf*/,
4949
PassManager&) {
50-
auto string_analyzer_state = constant_propagation::StringAnalyzerState::get();
51-
string_analyzer_state.set_methods_as_root();
50+
constant_propagation::StringAnalyzerState::make_default()
51+
.set_methods_as_root();
5252
}
5353

5454
void ShrinkerPass::run_pass(DexStoresVector& stores,

opt/shrinker/TailDuplicationPass.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ void TailDuplicationPass::bind_config() {
228228
void TailDuplicationPass::eval_pass(DexStoresVector&,
229229
ConfigFiles&,
230230
PassManager&) {
231-
auto string_analyzer_state = constant_propagation::StringAnalyzerState::get();
232-
string_analyzer_state.set_methods_as_root();
231+
constant_propagation::StringAnalyzerState::make_default()
232+
.set_methods_as_root();
233233
}
234234

235235
void TailDuplicationPass::run_pass(DexStoresVector& stores,

service/constant-propagation/ConstantPropagationAnalysis.cpp

Lines changed: 19 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,31 +1117,6 @@ bool InitFieldAnalyzer::analyze_invoke(const DexType* class_under_init,
11171117
return false;
11181118
}
11191119

1120-
boost::optional<EnumFieldAnalyzerState> enum_field_singleton{boost::none};
1121-
const EnumFieldAnalyzerState& EnumFieldAnalyzerState::get() {
1122-
if (!enum_field_singleton) {
1123-
// Be careful, there could be a data race here if this is called in parallel
1124-
enum_field_singleton = EnumFieldAnalyzerState();
1125-
// In tests, we create and destroy g_redex repeatedly. So we need to reset
1126-
// the singleton.
1127-
g_redex->add_destruction_task([]() { enum_field_singleton = boost::none; });
1128-
}
1129-
return *enum_field_singleton;
1130-
}
1131-
1132-
boost::optional<BoxedBooleanAnalyzerState> boxed_boolean_singleton{boost::none};
1133-
const BoxedBooleanAnalyzerState& BoxedBooleanAnalyzerState::get() {
1134-
if (!boxed_boolean_singleton) {
1135-
// Be careful, there could be a data race here if this is called in parallel
1136-
boxed_boolean_singleton = BoxedBooleanAnalyzerState();
1137-
// In tests, we create and destroy g_redex repeatedly. So we need to reset
1138-
// the singleton.
1139-
g_redex->add_destruction_task(
1140-
[]() { boxed_boolean_singleton = boost::none; });
1141-
}
1142-
return *boxed_boolean_singleton;
1143-
}
1144-
11451120
bool EnumFieldAnalyzer::analyze_sget(const EnumFieldAnalyzerState&,
11461121
const IRInstruction* insn,
11471122
ConstantEnvironment* env) {
@@ -1259,27 +1234,15 @@ bool BoxedBooleanAnalyzer::analyze_invoke(
12591234
}
12601235
}
12611236

1262-
static boost::optional<StringAnalyzerState> s_string_analyzer_state{
1263-
boost::none};
1264-
static std::mutex s_string_analyzer_state_mtx;
1265-
StringAnalyzerState StringAnalyzerState::get() {
1266-
std::lock_guard<std::mutex> lock(s_string_analyzer_state_mtx);
1267-
if (s_string_analyzer_state == boost::none) {
1268-
UnorderedSet<DexMethod*> methods;
1269-
auto* kotlin_are_equal = DexMethod::get_method(
1270-
"Lkotlin/jvm/internal/Intrinsics;.areEqual:(Ljava/lang/Object;Ljava/"
1271-
"lang/Object;)Z");
1272-
if (kotlin_are_equal != nullptr && kotlin_are_equal->as_def() != nullptr) {
1273-
methods.emplace(kotlin_are_equal->as_def());
1274-
}
1275-
s_string_analyzer_state = StringAnalyzerState(methods);
1276-
// For tests.
1277-
g_redex->add_destruction_task([]() {
1278-
std::lock_guard<std::mutex> task_lock(s_string_analyzer_state_mtx);
1279-
s_string_analyzer_state = boost::none;
1280-
});
1237+
StringAnalyzerState StringAnalyzerState::make_default() {
1238+
UnorderedSet<DexMethod*> methods;
1239+
auto* kotlin_are_equal = DexMethod::get_method(
1240+
"Lkotlin/jvm/internal/Intrinsics;.areEqual:(Ljava/lang/Object;Ljava/"
1241+
"lang/Object;)Z");
1242+
if (kotlin_are_equal != nullptr && kotlin_are_equal->as_def() != nullptr) {
1243+
methods.emplace(kotlin_are_equal->as_def());
12811244
}
1282-
return *s_string_analyzer_state;
1245+
return StringAnalyzerState(methods);
12831246
}
12841247

12851248
void StringAnalyzerState::set_methods_as_root() {
@@ -1826,17 +1789,9 @@ bool EnumUtilsFieldAnalyzer::analyze_sget(
18261789
return true;
18271790
}
18281791

1829-
boost::optional<DexFieldRef*> g_sdk_int_field{boost::none};
1830-
ApiLevelAnalyzerState ApiLevelAnalyzerState::get(int32_t min_sdk) {
1831-
if (!g_sdk_int_field) {
1832-
// Be careful, there could be a data race here if this is called in parallel
1833-
g_sdk_int_field =
1834-
DexField::get_field("Landroid/os/Build$VERSION;.SDK_INT:I");
1835-
// In tests, we create and destroy g_redex repeatedly. So we need to reset
1836-
// the singleton.
1837-
g_redex->add_destruction_task([]() { g_sdk_int_field = boost::none; });
1838-
}
1839-
return {*g_sdk_int_field, min_sdk};
1792+
ApiLevelAnalyzerState::ApiLevelAnalyzerState(int32_t min_sdk)
1793+
: min_sdk(min_sdk) {
1794+
sdk_int_field = DexField::get_field("Landroid/os/Build$VERSION;.SDK_INT:I");
18401795
}
18411796

18421797
bool ApiLevelAnalyzer::analyze_sget(const ApiLevelAnalyzerState& state,
@@ -1853,35 +1808,18 @@ bool ApiLevelAnalyzer::analyze_sget(const ApiLevelAnalyzerState& state,
18531808
return false;
18541809
}
18551810

1856-
boost::optional<UnorderedSet<DexMethodRef*>> g_get_package_name_refs{
1857-
boost::none};
1858-
static std::mutex s_package_name_mutex;
1859-
PackageNameState PackageNameState::get(const std::string& package_name) {
1860-
return PackageNameState::get(boost::optional<std::string>(package_name));
1861-
}
1862-
1863-
PackageNameState PackageNameState::get(
1811+
PackageNameState PackageNameState::make(
18641812
const boost::optional<std::string>& package_name) {
1865-
std::lock_guard<std::mutex> lock(s_package_name_mutex);
1866-
if (!g_get_package_name_refs) {
1867-
UnorderedSet<DexMethodRef*> refs{
1868-
DexMethod::get_method(
1869-
"Landroid/content/Context;.getPackageName:()Ljava/lang/String;"),
1870-
DexMethod::get_method(
1871-
"Landroid/content/ContextWrapper;.getPackageName:()Ljava/lang/"
1872-
"String;")};
1873-
g_get_package_name_refs = refs;
1874-
// In tests, we create and destroy g_redex repeatedly. So we need to reset
1875-
// the singleton.
1876-
g_redex->add_destruction_task([]() {
1877-
std::lock_guard<std::mutex> task_lock(s_package_name_mutex);
1878-
g_get_package_name_refs = boost::none;
1879-
});
1880-
}
1813+
UnorderedSet<DexMethodRef*> refs{
1814+
DexMethod::get_method(
1815+
"Landroid/content/Context;.getPackageName:()Ljava/lang/String;"),
1816+
DexMethod::get_method(
1817+
"Landroid/content/ContextWrapper;.getPackageName:()Ljava/lang/"
1818+
"String;")};
18811819
const DexString* dex_string = package_name != boost::none
18821820
? DexString::make_string(*package_name)
18831821
: nullptr;
1884-
return {*g_get_package_name_refs, dex_string};
1822+
return {std::move(refs), dex_string};
18851823
}
18861824

18871825
bool PackageNameAnalyzer::analyze_invoke(const PackageNameState* state,

service/constant-propagation/ConstantPropagationAnalysis.h

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,9 @@ class InitFieldAnalyzer final
279279
ConstantEnvironment* env);
280280
};
281281

282+
// Construction of this object is expensive because get_method acquires a
283+
// global lock. Make sure to cache it!
282284
struct EnumFieldAnalyzerState {
283-
// Construction of this object is expensive because get_method acquires a
284-
// global lock. `get` creates a singleton once and reuses it.
285-
static const EnumFieldAnalyzerState& get();
286-
287285
const DexMethod* enum_equals;
288286

289287
EnumFieldAnalyzerState()
@@ -402,11 +400,9 @@ class ImmutableAttributeAnalyzer final
402400
ConstantEnvironment* env);
403401
};
404402

403+
// Construction of this object is expensive because get_type/field/method
404+
// acquires a global lock. Make sure to cache it.
405405
struct BoxedBooleanAnalyzerState {
406-
// Construction of this object is expensive because get_type/field/method
407-
// acquires a global lock. `get` creates a singleton once and reuses it.
408-
static const BoxedBooleanAnalyzerState& get();
409-
410406
const DexType* boolean_class{DexType::get_type("Ljava/lang/Boolean;")};
411407
const DexField* boolean_true{dynamic_cast<DexField*>(
412408
DexField::get_field("Ljava/lang/Boolean;.TRUE:Ljava/lang/Boolean;"))};
@@ -433,9 +429,11 @@ class BoxedBooleanAnalyzer final
433429
};
434430

435431
struct StringAnalyzerState {
436-
static StringAnalyzerState get();
437432
explicit StringAnalyzerState(const UnorderedSet<DexMethod*>& methods)
438433
: string_equality_methods(methods) {}
434+
435+
static StringAnalyzerState make_default();
436+
439437
void set_methods_as_root();
440438
UnorderedSet<DexMethod*> string_equality_methods;
441439
};
@@ -467,10 +465,10 @@ class ConstantClassObjectAnalyzer
467465
}
468466
};
469467

468+
// Construction of this object is expensive because get_method acquires a
469+
// global lock. Make sure to cache it!
470470
struct ApiLevelAnalyzerState {
471-
// Construction of this object is expensive because get_method acquires a
472-
// global lock. `get` caches those lookups.
473-
static ApiLevelAnalyzerState get(int32_t min_sdk = 0);
471+
explicit ApiLevelAnalyzerState(int32_t min_sdk);
474472

475473
const DexFieldRef* sdk_int_field;
476474
int32_t min_sdk;
@@ -487,11 +485,11 @@ class ApiLevelAnalyzer final
487485
};
488486

489487
struct PackageNameState {
490-
static PackageNameState get(const std::string& package_name);
491-
static PackageNameState get(const boost::optional<std::string>& package_name);
492-
493488
const UnorderedSet<DexMethodRef*> getter_methods;
494489
const DexString* package_name;
490+
491+
static PackageNameState make(
492+
const boost::optional<std::string>& package_name);
495493
};
496494

497495
class PackageNameAnalyzer final

service/method-inliner/CallSiteSummaries.cpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,13 @@ const CallSiteSummary* CallSiteSummarizer::internalize_call_site_summary(
178178
return m_call_site_summaries.insert(call_site_summary).first;
179179
}
180180

181+
struct CallSiteSummarizer::InvokeCallSiteSummariesContext {
182+
constant_propagation::ApiLevelAnalyzerState api_level_analyzer_state{0};
183+
constant_propagation::BoxedBooleanAnalyzerState
184+
boxed_boolean_analyzer_state{};
185+
constant_propagation::EnumFieldAnalyzerState enum_field_analyzer_state;
186+
};
187+
181188
void CallSiteSummarizer::summarize() {
182189
Timer t("compute_call_site_summaries");
183190

@@ -204,6 +211,8 @@ void CallSiteSummarizer::summarize() {
204211
return ptr;
205212
};
206213

214+
InvokeCallSiteSummariesContext context{};
215+
207216
summaries_scheduler.set_executor([&](DexMethod* method) {
208217
CallSiteArguments arguments;
209218
if (get_dependencies(method) == nullptr) {
@@ -243,7 +252,8 @@ void CallSiteSummarizer::summarize() {
243252
ConstantEnvironment initial_env =
244253
constant_propagation::interprocedural::env_with_params(
245254
is_static(method), method->get_code(), arguments);
246-
auto res = get_invoke_call_site_summaries(method, callees, initial_env);
255+
auto res =
256+
get_invoke_call_site_summaries(method, callees, initial_env, context);
247257
for (auto& p : res.invoke_call_site_summaries) {
248258
auto* insn = p.first;
249259
auto* callee = m_get_callee_fn(method, insn);
@@ -287,7 +297,8 @@ InvokeCallSiteSummariesAndDeadBlocks
287297
CallSiteSummarizer::get_invoke_call_site_summaries(
288298
DexMethod* caller,
289299
const UnorderedMap<DexMethod*, size_t>& callees,
290-
const ConstantEnvironment& initial_env) {
300+
const ConstantEnvironment& initial_env,
301+
const InvokeCallSiteSummariesContext& context) {
291302
IRCode* code = caller->get_code();
292303

293304
InvokeCallSiteSummariesAndDeadBlocks res;
@@ -298,12 +309,11 @@ CallSiteSummarizer::get_invoke_call_site_summaries(
298309
constant_propagation::ConstantPrimitiveAndBoxedAnalyzer(
299310
m_shrinker.get_immut_analyzer_state(),
300311
m_shrinker.get_immut_analyzer_state(),
301-
constant_propagation::EnumFieldAnalyzerState::get(),
302-
constant_propagation::BoxedBooleanAnalyzerState::get(),
312+
context.enum_field_analyzer_state,
313+
context.boxed_boolean_analyzer_state,
303314
m_shrinker.get_string_analyzer_state(),
304-
constant_propagation::ApiLevelAnalyzerState::get(),
305-
m_shrinker.get_package_name_state(), nullptr,
306-
m_shrinker.get_immut_analyzer_state(), nullptr));
315+
context.api_level_analyzer_state, m_shrinker.get_package_name_state(),
316+
nullptr, m_shrinker.get_immut_analyzer_state(), nullptr));
307317
intra_cp.run(initial_env);
308318
for (const auto& block : cfg.blocks()) {
309319
auto env = intra_cp.get_entry_state_at(block);

service/method-inliner/CallSiteSummaries.h

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,6 @@ class CallSiteSummarizer {
111111
InsertOnlyConcurrentSet<CallSiteSummary, CallSiteSummaryHasher>
112112
m_call_site_summaries;
113113

114-
/**
115-
* For all (reachable) invoke instructions in a given method, collect
116-
* information about their arguments, i.e. whether particular arguments
117-
* are constants.
118-
*/
119-
InvokeCallSiteSummariesAndDeadBlocks get_invoke_call_site_summaries(
120-
DexMethod* caller,
121-
const UnorderedMap<DexMethod*, size_t>& callees,
122-
const ConstantEnvironment& initial_env);
123-
124114
public:
125115
CallSiteSummarizer(
126116
shrinker::Shrinker& shrinker,
@@ -144,6 +134,19 @@ class CallSiteSummarizer {
144134

145135
const CallSiteSummary* get_instruction_call_site_summary(
146136
const IRInstruction* invoke_insn) const;
137+
138+
struct InvokeCallSiteSummariesContext;
139+
140+
/**
141+
* For all (reachable) invoke instructions in a given method, collect
142+
* information about their arguments, i.e. whether particular arguments
143+
* are constants.
144+
*/
145+
InvokeCallSiteSummariesAndDeadBlocks get_invoke_call_site_summaries(
146+
DexMethod* caller,
147+
const UnorderedMap<DexMethod*, size_t>& callees,
148+
const ConstantEnvironment& initial_env,
149+
const InvokeCallSiteSummariesContext& context);
147150
};
148151

149152
} // namespace inliner

0 commit comments

Comments
 (0)