constchar* GetMethodName() { // PrettyMethod() is expensive, so we delay calling it until we actually have to. if (cached_method_name_.empty()) {
cached_method_name_ = graph_->GetDexFile().PrettyMethod(graph_->GetMethodIdx());
} return cached_method_name_.c_str();
}
void EndPass(constchar* pass_name, bool pass_change) { // Pause timer first, then dump graph. if (timing_logger_enabled_) {
timing_logger_.EndTiming();
} if (visualizer_enabled_) {
visualizer_.DumpGraph(pass_name, /* is_after_pass= */ true, graph_in_bad_state_);
FlushVisualizer();
}
// Validate the HGraph if running in debug mode. if (kIsDebugBuild) { if (!graph_in_bad_state_) {
GraphChecker checker(graph_, codegen_);
last_seen_graph_size_ = checker.Run(pass_change, last_seen_graph_size_); if (!checker.IsValid()) {
std::ostringstream stream;
graph_->Dump(stream, codegen_);
LOG(FATAL_WITHOUT_ABORT) << "Error after " << pass_name << "(" << graph_->PrettyMethod()
<< "): " << stream.str();
LOG(FATAL) << "(" << pass_name << "): " << Dumpable<GraphChecker>(checker);
}
}
}
}
staticbool IsVerboseMethod(const CompilerOptions& compiler_options, constchar* method_name) { // Test an exact match to --verbose-methods. If verbose-methods is set, this overrides an // empty kStringFilter matching all methods. if (compiler_options.HasVerboseMethods()) { return compiler_options.IsVerboseMethod(method_name);
}
// Test the kStringFilter sub-string. constexpr helper variable to silence unreachable-code // warning when the string is empty.
constexpr bool kStringFilterEmpty = arraysize(kStringFilter) <= 1; if (kStringFilterEmpty || strstr(method_name, kStringFilter) != nullptr) { returntrue;
}
// Try compiling a method and return the code generator used for // compiling it. // This method: // 1) Builds the graph. Returns null if it failed to build it. // 2) Transforms the graph to SSA. Returns null if it failed. // 3) Runs optimizations on the graph, including register allocator.
CodeGenerator* TryCompile(ArenaAllocator* allocator,
ArenaStack* arena_stack, const DexCompilationUnit& dex_compilation_unit,
ArtMethod* method,
CompilationKind compilation_kind,
VariableSizedHandleScope* handles, bool dynamic_instrumentation) const;
NO_INLINE // Avoid increasing caller's frame size by large stack-allocated objects. staticvoid AllocateRegisters(HGraph* graph,
CodeGenerator* codegen,
PassObserver* pass_observer,
OptimizingCompilerStats* stats) {
{
PassScope scope(PrepareForRegisterAllocation::kPrepareForRegisterAllocationPassName,
pass_observer);
PrepareForRegisterAllocation(graph, codegen->GetCompilerOptions(), stats).Run();
} // Use local allocator shared by SSA liveness analysis and register allocator. // (Register allocator creates new objects in the liveness data.)
ScopedArenaAllocator local_allocator(graph->GetArenaStack());
SsaLivenessAnalysis liveness(graph, codegen, &local_allocator);
{
PassScope scope(SsaLivenessAnalysis::kLivenessPassName, pass_observer);
liveness.Analyze();
}
{
PassScope scope(RegisterAllocator::kRegisterAllocatorPassName, pass_observer);
std::unique_ptr<RegisterAllocator> register_allocator =
RegisterAllocator::Create(&local_allocator, codegen, liveness);
register_allocator->AllocateRegisters();
}
}
// Strip pass name suffix to get optimization name. static std::string ConvertPassNameToOptimizationName(const std::string& pass_name) {
size_t pos = pass_name.find(kPassNameSeparator); return pos == std::string::npos ? pass_name : pass_name.substr(0, pos);
}
void OptimizingCompiler::RunOptimizations(HGraph* graph,
CodeGenerator* codegen, const DexCompilationUnit& dex_compilation_unit,
PassObserver* pass_observer) const { const std::vector<std::string>* pass_names = GetCompilerOptions().GetPassesToRun(); if (pass_names != nullptr) { // If passes were defined on command-line, build the optimization // passes and run these instead of the built-in optimizations. // TODO: a way to define depends_on via command-line? const size_t length = pass_names->size();
std::vector<OptimizationDef> optimizations; for (const std::string& pass_name : *pass_names) {
std::string opt_name = ConvertPassNameToOptimizationName(pass_name);
optimizations.push_back(OptDef(OptimizationPassByName(opt_name), pass_name.c_str()));
}
RunOptimizations(graph,
codegen,
dex_compilation_unit,
pass_observer,
optimizations.data(),
length); return;
}
static constexpr OptimizationDef optimizations[] = { // Initial optimizations.
OptDef(OptimizationPass::kConstantFolding),
OptDef(OptimizationPass::kReferenceTypePropagation, "reference_type_propagation$initial",
OptimizationPass::kConstantFolding),
OptDef(OptimizationPass::kInstructionSimplifier),
OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$initial"), // Inlining.
OptDef(OptimizationPass::kInliner), // Simplification (if inlining occurred, or if we analyzed the invoke as "always throwing").
OptDef(OptimizationPass::kConstantFolding, "constant_folding$after_inlining",
OptimizationPass::kInliner),
OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_inlining",
OptimizationPass::kInliner),
OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$after_inlining",
OptimizationPass::kInliner), // GVN.
OptDef(OptimizationPass::kGlobalValueNumbering),
OptDef(OptimizationPass::kReferenceTypePropagation, "reference_type_propagation$after_gvn",
OptimizationPass::kGlobalValueNumbering), // Simplification (TODO: only if GVN occurred).
OptDef(OptimizationPass::kControlFlowSimplifier),
OptDef(OptimizationPass::kConstantFolding, "constant_folding$after_gvn"),
OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_gvn"),
OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$after_gvn"), // High-level optimizations.
OptDef(OptimizationPass::kInvariantCodeMotion),
OptDef(OptimizationPass::kInductionVarAnalysis),
OptDef(OptimizationPass::kBoundsCheckElimination),
OptDef(OptimizationPass::kLoopOptimization), // Simplification.
OptDef(OptimizationPass::kConstantFolding, "constant_folding$after_loop_opt"),
OptDef(OptimizationPass::kAggressiveInstructionSimplifier, "instruction_simplifier$after_loop_opt"),
OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$after_loop_opt"), // Other high-level optimizations.
OptDef(OptimizationPass::kLoadStoreElimination),
OptDef(OptimizationPass::kCHAGuardOptimization), // NB: Environment optimization pass shouldn't be before // - Inliner // - Bounds check elimination // - CHA guard elimination // - Loop optimization // As these passes require full environment.
OptDef(OptimizationPass::kEnvironmentInputElimination),
OptDef(OptimizationPass::kCodeSinking), // Simplification.
OptDef(OptimizationPass::kConstantFolding, "constant_folding$before_codegen"), // The codegen has a few assumptions that only the instruction simplifier // can satisfy. For example, the code generator does not expect to see a // HTypeConversion from a type to the same type.
OptDef(OptimizationPass::kAggressiveInstructionSimplifier, "instruction_simplifier$before_codegen"), // Simplification may result in dead code that should be removed prior to // code generation.
OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$before_codegen"), // Eliminate constructor fences after code sinking to avoid // complicated sinking logic to split a fence with many inputs.
OptDef(OptimizationPass::kConstructorFenceRedundancyElimination)
};
RunOptimizations(graph,
codegen,
dex_compilation_unit,
pass_observer,
optimizations);
// Always use the Thumb-2 assembler: some runtime functionality // (like implicit stack overflow checks) assume Thumb-2.
DCHECK_NE(instruction_set, InstructionSet::kArm);
// Do not attempt to compile on architectures we do not support. if (!IsInstructionSetSupported(instruction_set)) {
SCOPED_TRACE << "Not compiling: unsupported ISA";
MaybeRecordStat(compilation_stats_.get(),
MethodCompilationStat::kNotCompiledUnsupportedIsa); return nullptr;
}
// Implementation of the space filter: do not compile a code item whose size in // code units is bigger than 128. static constexpr size_t kSpaceFilterOptimizingThreshold = 128; if ((compiler_options.GetCompilerFilter() == CompilerFilter::kSpace)
&& (CodeItemInstructionAccessor(dex_file, code_item).InsnsSizeInCodeUnits() >
kSpaceFilterOptimizingThreshold)) {
SCOPED_TRACE << "Not compiling because of space filter";
MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledSpaceFilter); return nullptr;
}
bool dead_reference_safe; // For AOT compilation, we may not get a method, for example if its class is erroneous, // possibly due to an unavailable superclass. JIT should always have a method.
DCHECK(Runtime::Current()->IsAotCompiler() || method != nullptr); if (method != nullptr) { const dex::ClassDef* containing_class;
{
ScopedObjectAccess soa(Thread::Current());
containing_class = &method->GetClassDef();
} // MethodContainsRSensitiveAccess is currently slow, but HasDeadReferenceSafeAnnotation() // is currently rarely true.
dead_reference_safe =
annotations::HasDeadReferenceSafeAnnotation(dex_file, *containing_class)
&& !annotations::MethodContainsRSensitiveAccess(dex_file, *containing_class, method_idx);
} else { // If we could not resolve the class, conservatively assume it's dead-reference unsafe.
dead_reference_safe = false;
}
// If we are compiling baseline and we haven't created a profiling info for // this method already, do it now. if (jit != nullptr &&
compilation_kind == CompilationKind::kBaseline &&
graph->IsUsefulOptimizing() &&
graph->GetProfilingInfo() == nullptr) {
ProfilingInfoBuilder(graph, codegen->GetCompilerOptions(), codegen.get()).Run(); // We expect a profiling info to be created and attached to the graph. // However, we may have run out of memory trying to create it, so in this // case just abort the compilation. if (graph->GetProfilingInfo() == nullptr) {
SCOPED_TRACE << "Not compiling because of out of memory";
MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kJitOutOfMemoryForCommit); return nullptr;
}
}
if (UNLIKELY(codegen->GetFrameSize() > codegen->GetMaximumFrameSize())) {
SCOPED_TRACE << "Not compiling because of stack frame too large";
LOG(WARNING) << "Stack frame size is " << codegen->GetFrameSize()
<< " which is larger than the maximum of " << codegen->GetMaximumFrameSize()
<< " bytes. Method: " << graph->PrettyMethod();
MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledFrameTooBig); return nullptr;
}
// Always use the Thumb-2 assembler: some runtime functionality // (like implicit stack overflow checks) assume Thumb-2.
DCHECK_NE(instruction_set, InstructionSet::kArm);
// Do not attempt to compile on architectures we do not support. if (!IsInstructionSetSupported(instruction_set)) {
SCOPED_TRACE << "Not compiling: unsupported ISA"; return nullptr;
}
static constexpr OptimizationDef optimizations[] = { // The codegen has a few assumptions that only the instruction simplifier // can satisfy.
OptDef(OptimizationPass::kInstructionSimplifier),
};
RunOptimizations(graph,
codegen.get(),
dex_compilation_unit,
&pass_observer,
optimizations);
if (kIsDebugBuild &&
compiler_options.CompileArtTest() &&
IsInstructionSetSupported(compiler_options.GetInstructionSet())) { // For testing purposes, we put a special marker on method names // that should be compiled with this compiler (when the // instruction set is supported). This makes sure we're not // regressing.
std::string method_name = dex_file.PrettyMethod(method_idx); bool shouldCompile = method_name.find("$opt$") != std::string::npos;
DCHECK_IMPLIES(compiled_method == nullptr, !shouldCompile) << "Didn't compile " << method_name;
}
return compiled_method;
}
static ScopedArenaVector<uint8_t> CreateJniStackMap(ScopedArenaAllocator* allocator, const JniCompiledMethod& jni_compiled_method,
size_t code_size, bool debuggable) { // StackMapStream is quite large, so allocate it using the ScopedArenaAllocator // to stay clear of the frame size limit.
std::unique_ptr<StackMapStream> stack_map_stream( new (allocator) StackMapStream(allocator, jni_compiled_method.GetInstructionSet()));
stack_map_stream->BeginMethod(jni_compiled_method.GetFrameSize(),
jni_compiled_method.GetCoreSpillMask(),
jni_compiled_method.GetFpSpillMask(), /* num_dex_registers= */ 0, /* baseline= */ false,
debuggable);
stack_map_stream->EndMethod(code_size); return stack_map_stream->Encode();
}
const CompilerOptions& compiler_options = GetCompilerOptions(); if (compiler_options.IsBootImage()) {
ScopedObjectAccess soa(Thread::Current());
ArtMethod* method = runtime->GetClassLinker()->LookupResolvedMethod(
method_idx, dex_cache.Get(), /*class_loader=*/ nullptr); // Try to compile a fully intrinsified implementation. Do not try to do this for // signature polymorphic methods as the InstructionBuilder cannot handle them; // and it would be useless as they always have a slow path for type conversions. if (method != nullptr && UNLIKELY(method->IsIntrinsic()) && !method->IsSignaturePolymorphic()) {
VariableSizedHandleScope handles(soa.Self());
ScopedNullHandle<mirror::ClassLoader> class_loader; // null means boot class path loader.
Handle<mirror::Class> compiling_class = handles.NewHandle(method->GetDeclaringClass());
DexCompilationUnit dex_compilation_unit(
class_loader,
runtime->GetClassLinker(),
dex_file, /*code_item=*/ nullptr, /*class_def_idx=*/ DexFile::kDexNoIndex16,
method_idx,
access_flags, /*verified_method=*/ nullptr,
dex_cache,
compiling_class); // Go to native so that we don't block GC during compilation.
ScopedThreadSuspension sts(soa.Self(), ThreadState::kNative);
std::unique_ptr<CodeGenerator> codegen(
TryCompileIntrinsic(&allocator,
&arena_stack,
dex_compilation_unit,
method,
&handles)); if (codegen != nullptr) { return Emit(&allocator, codegen.get(), /*is_intrinsic=*/ true, /*item=*/ nullptr);
}
}
}
bool EncodeArtMethodInInlineInfo([[maybe_unused]] ArtMethod* method) { // Note: the runtime is null only for unit testing. return Runtime::Current() == nullptr || !Runtime::Current()->IsAotCompiler();
}
if (UNLIKELY(method->IsNative())) { // Use GenericJniTrampoline for critical native methods in debuggable runtimes. We don't // support calling method entry / exit hooks for critical native methods yet. // TODO(mythria): Add support for calling method entry / exit hooks in JITed stubs for critical // native methods too. if (compiler_options.GetDebuggable() && method->IsCriticalNative()) {
SCOPED_TRACE << "Not compiling: critical native method in debuggable runtime";
DCHECK(compiler_options.IsJitCompiler()); returnfalse;
} // Java debuggable runtimes should set compiler options to debuggable, so that we either // generate method entry / exit hooks or skip JITing. For critical native methods we don't // generate method entry / exit hooks so we shouldn't JIT them in debuggable runtimes.
DCHECK_IMPLIES(method->IsCriticalNative(), !runtime->IsJavaDebuggable());
JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod(
compiler_options, dex_file->GetMethodShortyView(method_idx), access_flags, &allocator);
std::vector<Handle<mirror::Object>> roots;
ArenaSet<ArtMethod*, std::less<ArtMethod*>> cha_single_implementation_list(
allocator.Adapter(kArenaAllocCHA));
ArenaStack arena_stack(runtime->GetJitArenaPool()); // StackMapStream is large and it does not fit into this frame, so we need helper method.
ScopedArenaAllocator stack_map_allocator(&arena_stack); // Will hold the stack map.
ScopedArenaVector<uint8_t> stack_map =
CreateJniStackMap(&stack_map_allocator,
jni_compiled_method,
jni_compiled_method.GetCode().size(),
compiler_options.GetDebuggable() && compiler_options.IsJitCompiler());
std::vector<Handle<mirror::Object>> roots;
codegen->EmitJitRoots(const_cast<uint8_t*>(codegen->GetAssembler()->CodeBufferBaseAddress()),
code,
roots_data,
&roots); // The root Handle<>s filled by the codegen reference entries in the VariableSizedHandleScope.
DCHECK(std::all_of(roots.begin(),
roots.end(),
[&handles](Handle<mirror::Object> root){ return handles.Contains(root.GetReference());
}));
// Add debug info after we know the code location but before we update entry-point. if (compiler_options.GenerateAnyDebugInfo()) {
debug::MethodDebugInfo method_debug_info = create_method_debug_info();
method_debug_info.code_address = reinterpret_cast<uintptr_t>(code);
method_debug_info.code_size = codegen->GetAssembler()->CodeSize();
method_debug_info.frame_size_in_bytes = codegen->GetFrameSize();
method_debug_info.code_info = stack_map.size() == 0 ? nullptr : stack_map.data();
method_debug_info.cfi = ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data());
debug_info = GenerateJitDebugInfo(method_debug_info);
}
if (compilation_kind == CompilationKind::kBaseline &&
!codegen->GetGraph()->IsUsefulOptimizing()) { // The baseline compilation detected that it has done all the optimizations // that the full compiler would do. Therefore we set the compilation kind to // be `kOptimized`
compilation_kind = CompilationKind::kOptimized;
}
std::vector<uint8_t> OptimizingCompiler::GenerateJitDebugInfo(const debug::MethodDebugInfo& info) { const CompilerOptions& compiler_options = GetCompilerOptions(); if (compiler_options.GenerateAnyDebugInfo()) { // If both flags are passed, generate full debug info. constbool mini_debug_info = !compiler_options.GetGenerateDebugInfo();
// Create entry for the single method that we just compiled.
InstructionSet isa = compiler_options.GetInstructionSet(); const InstructionSetFeatures* features = compiler_options.GetInstructionSetFeatures(); return debug::MakeElfFileForJIT(isa, features, mini_debug_info, info);
} return std::vector<uint8_t>();
}
} // namespace art
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet am 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.