1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "profiling_info_builder.h"
18 
19 #include "art_method-inl.h"
20 #include "code_generator.h"
21 #include "driver/compiler_options.h"
22 #include "dex/code_item_accessors-inl.h"
23 #include "inliner.h"
24 #include "jit/profiling_info.h"
25 #include "optimizing_compiler_stats.h"
26 #include "scoped_thread_state_change-inl.h"
27 
28 namespace art HIDDEN {
29 
Run()30 void ProfilingInfoBuilder::Run() {
31   DCHECK(GetGraph()->IsUsefulOptimizing());
32   DCHECK_EQ(GetGraph()->GetProfilingInfo(), nullptr);
33   // Order does not matter.
34   for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) {
35     // No need to visit the phis.
36     VisitNonPhiInstructions(block);
37   }
38 
39   ScopedObjectAccess soa(Thread::Current());
40   GetGraph()->SetProfilingInfo(
41       ProfilingInfo::Create(soa.Self(), GetGraph()->GetArtMethod(), inline_caches_));
42 }
43 
44 
EncodeInlinedDexPc(const HInliner * inliner,const CompilerOptions & compiler_options,HInvoke * invoke)45 uint32_t ProfilingInfoBuilder::EncodeInlinedDexPc(const HInliner* inliner,
46                                                   const CompilerOptions& compiler_options,
47                                                   HInvoke* invoke) {
48   DCHECK(inliner->GetCallerEnvironment() != nullptr);
49   DCHECK(inliner->GetParent() != nullptr);
50   std::vector<uint32_t> temp_vector;
51   temp_vector.push_back(invoke->GetDexPc());
52   while (inliner->GetCallerEnvironment() != nullptr) {
53     temp_vector.push_back(inliner->GetCallerEnvironment()->GetDexPc());
54     inliner = inliner->GetParent();
55   }
56 
57   DCHECK_EQ(inliner->GetOutermostGraph(), inliner->GetGraph());
58   return InlineCache::EncodeDexPc(
59       inliner->GetOutermostGraph()->GetArtMethod(),
60       temp_vector,
61       compiler_options.GetInlineMaxCodeUnits());
62 }
63 
EncodeDexPc(HInvoke * invoke,const CompilerOptions & compiler_options)64 static uint32_t EncodeDexPc(HInvoke* invoke, const CompilerOptions& compiler_options) {
65   std::vector<uint32_t> dex_pcs;
66   ArtMethod* outer_method = nullptr;
67   for (HEnvironment* environment = invoke->GetEnvironment();
68        environment != nullptr;
69        environment = environment->GetParent()) {
70     outer_method = environment->GetMethod();
71     dex_pcs.push_back(environment->GetDexPc());
72   }
73 
74   ScopedObjectAccess soa(Thread::Current());
75   return InlineCache::EncodeDexPc(
76       outer_method,
77       dex_pcs,
78       compiler_options.GetInlineMaxCodeUnits());
79 }
80 
HandleInvoke(HInvoke * invoke)81 void ProfilingInfoBuilder::HandleInvoke(HInvoke* invoke) {
82   if (IsInlineCacheUseful(invoke, codegen_)) {
83     uint32_t dex_pc = EncodeDexPc(invoke, compiler_options_);
84     if (dex_pc != kNoDexPc) {
85       inline_caches_.push_back(dex_pc);
86     } else {
87       ScopedObjectAccess soa(Thread::Current());
88       LOG(WARNING) << "Could not encode dex pc for "
89                    << invoke->GetResolvedMethod()->PrettyMethod();
90     }
91   }
92 }
93 
VisitInvokeInterface(HInvokeInterface * invoke)94 void ProfilingInfoBuilder::VisitInvokeInterface(HInvokeInterface* invoke) {
95   HandleInvoke(invoke);
96 }
97 
VisitInvokeVirtual(HInvokeVirtual * invoke)98 void ProfilingInfoBuilder::VisitInvokeVirtual(HInvokeVirtual* invoke) {
99   HandleInvoke(invoke);
100 }
101 
IsInlineCacheUseful(HInvoke * invoke,CodeGenerator * codegen)102 bool ProfilingInfoBuilder::IsInlineCacheUseful(HInvoke* invoke, CodeGenerator* codegen) {
103   DCHECK(invoke->IsInvokeVirtual() || invoke->IsInvokeInterface());
104   if (codegen->IsImplementedIntrinsic(invoke)) {
105     return false;
106   }
107   if (!invoke->GetBlock()->GetGraph()->IsCompilingBaseline()) {
108     return false;
109   }
110   if (Runtime::Current()->IsAotCompiler()) {
111     return false;
112   }
113   if (invoke->InputAt(0)->GetReferenceTypeInfo().IsExact()) {
114     return false;
115   }
116   if (invoke->GetResolvedMethod() != nullptr) {
117     ScopedObjectAccess soa(Thread::Current());
118     if (invoke->GetResolvedMethod()->IsFinal() ||
119         invoke->GetResolvedMethod()->GetDeclaringClass()->IsFinal()) {
120       return false;
121     }
122   }
123 
124   if (!codegen->GetGraph()->IsUsefulOptimizing()) {
125     // Earlier pass knew what the calling target was. No need for an inline
126     // cache.
127     return false;
128   }
129   return true;
130 }
131 
GetInlineCache(ProfilingInfo * info,const CompilerOptions & compiler_options,HInvoke * instruction)132 InlineCache* ProfilingInfoBuilder::GetInlineCache(ProfilingInfo* info,
133                                                   const CompilerOptions& compiler_options,
134                                                   HInvoke* instruction) {
135   ScopedObjectAccess soa(Thread::Current());
136   uint32_t dex_pc = EncodeDexPc(instruction, compiler_options);
137   if (dex_pc == kNoDexPc) {
138     return nullptr;
139   }
140   return info->GetInlineCache(dex_pc);
141 }
142 
143 }  // namespace art
144