diff options
author | 2021-10-27 09:16:37 +0000 | |
---|---|---|
committer | 2021-10-29 13:06:57 +0000 | |
commit | 8cb989f1019c4fa30845bf2deb5bc996ed4e8966 (patch) | |
tree | fa793ebd540cad757df2fbf8e91a00e1c111f024 | |
parent | 3693b2ae3bac182f1e483738bae2cd80cad5dc3d (diff) |
Revert "Inline across dex files for bootclaspath's methods"
This reverts commit d690f8ae8f8e2675bc52089a83ac18c749f8e6d2.
Reason for revert: Test breakage related to VarHandles e.g.
https://ci.chromium.org/ui/p/art/builders/ci/angler-armv7-ndebug/2409/overview
Change-Id: I0d3537cd0907866c628478fad1d77aff4ee65113
-rw-r--r-- | compiler/optimizing/inliner.cc | 66 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier.cc | 45 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 18 | ||||
-rw-r--r-- | compiler/optimizing/optimizing_compiler_stats.h | 1 | ||||
-rw-r--r-- | compiler/optimizing/stack_map_stream.cc | 37 | ||||
-rw-r--r-- | runtime/entrypoints/entrypoint_utils-inl.h | 34 | ||||
-rw-r--r-- | runtime/oat.h | 4 | ||||
-rw-r--r-- | runtime/stack_map.cc | 9 | ||||
-rw-r--r-- | runtime/stack_map.h | 11 | ||||
-rw-r--r-- | test/580-checker-string-fact-intrinsics/src-art/Main.java | 8 |
10 files changed, 54 insertions, 179 deletions
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 0e4f9ef0ed..3abbbae573 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1696,26 +1696,18 @@ static inline Handle<T> NewHandleIfDifferent(ObjPtr<T> object, Handle<T> hint, H return (object != hint.Get()) ? graph->GetHandleCache()->NewHandle(object) : hint; } -static bool CanEncodeInlinedMethodInStackMap(const DexFile& outer_dex_file, - ArtMethod* callee, - bool* out_needs_bss_check) +static bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee) REQUIRES_SHARED(Locks::mutator_lock_) { if (!Runtime::Current()->IsAotCompiler()) { // JIT can always encode methods in stack maps. return true; } - if (IsSameDexFile(outer_dex_file, *callee->GetDexFile())) { + if (IsSameDexFile(caller_dex_file, *callee->GetDexFile())) { return true; } - - // Inline across dexfiles if the callee's DexFile is in the bootclasspath. - if (callee->GetDeclaringClass()->GetClassLoader() == nullptr) { - *out_needs_bss_check = true; - return true; - } - // TODO(ngeoffray): Support more AOT cases for inlining: // - methods in multidex + // - methods in boot image for on-device non-PIC compilation. return false; } @@ -1825,11 +1817,6 @@ bool HInliner::CanInlineBody(const HGraph* callee_graph, return false; } - const bool too_many_registers = - total_number_of_dex_registers_ > kMaximumNumberOfCumulatedDexRegisters; - bool needs_bss_check = false; - const bool can_encode_in_stack_map = CanEncodeInlinedMethodInStackMap( - *outer_compilation_unit_.GetDexFile(), resolved_method, &needs_bss_check); size_t number_of_instructions = 0; // Skip the entry block, it does not contain instructions that prevent inlining. for (HBasicBlock* block : callee_graph->GetReversePostOrderSkipEntryBlock()) { @@ -1864,22 +1851,24 @@ bool HInliner::CanInlineBody(const HGraph* callee_graph, return false; } HInstruction* current = instr_it.Current(); - if (current->NeedsEnvironment()) { - if (too_many_registers) { - LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedEnvironmentBudget) - << "Method " << resolved_method->PrettyMethod() - << " is not inlined because its caller has reached" - << " its environment budget limit."; - return false; - } + if (current->NeedsEnvironment() && + (total_number_of_dex_registers_ > kMaximumNumberOfCumulatedDexRegisters)) { + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedEnvironmentBudget) + << "Method " << resolved_method->PrettyMethod() + << " is not inlined because its caller has reached" + << " its environment budget limit."; + return false; + } - if (!can_encode_in_stack_map) { - LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedStackMaps) - << "Method " << resolved_method->PrettyMethod() << " could not be inlined because " - << current->DebugName() << " needs an environment, is in a different dex file" - << ", and cannot be encoded in the stack maps."; - return false; - } + if (current->NeedsEnvironment() && + !CanEncodeInlinedMethodInStackMap(*caller_compilation_unit_.GetDexFile(), + resolved_method)) { + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedStackMaps) + << "Method " << resolved_method->PrettyMethod() + << " could not be inlined because " << current->DebugName() + << " needs an environment, is in a different dex file" + << ", and cannot be encoded in the stack maps."; + return false; } if (current->IsUnresolvedStaticFieldGet() || @@ -1893,21 +1882,6 @@ bool HInliner::CanInlineBody(const HGraph* callee_graph, << " entrypoint"; return false; } - - // We currently don't have support for inlining across dex files if the inlined method needs a - // .bss entry. This only happens when we are: - // 1) In AoT, - // 2) cross-dex inlining, and - // 3) have an instruction that needs a bss entry, which will always be - // 3)b) an instruction that needs an environment. - // TODO(solanes, 154012332): Add this support. - if (needs_bss_check && current->NeedsBss()) { - DCHECK(current->NeedsEnvironment()); - LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedBss) - << "Method " << resolved_method->PrettyMethod() - << " could not be inlined because it needs a BSS check"; - return false; - } } } diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 3f1f3b0f8d..91b2e8bdc6 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -2645,15 +2645,12 @@ static bool TryReplaceStringBuilderAppend(HInvoke* invoke) { // Collect args and check for unexpected uses. // We expect one call to a constructor with no arguments, one constructor fence (unless // eliminated), some number of append calls and one call to StringBuilder.toString(). - bool constructor_inlined = false; bool seen_constructor = false; bool seen_constructor_fence = false; bool seen_to_string = false; uint32_t format = 0u; uint32_t num_args = 0u; HInstruction* args[StringBuilderAppend::kMaxArgs]; // Added in reverse order. - // When inlining, `maybe_new_array` tracks an environment use that we want to allow. - HInstruction* maybe_new_array = nullptr; for (HBackwardInstructionIterator iter(block->GetInstructions()); !iter.Done(); iter.Advance()) { HInstruction* user = iter.Current(); // Instructions of interest apply to `sb`, skip those that do not involve `sb`. @@ -2734,25 +2731,13 @@ static bool TryReplaceStringBuilderAppend(HInvoke* invoke) { format = (format << StringBuilderAppend::kBitsPerArg) | static_cast<uint32_t>(arg); args[num_args] = as_invoke_virtual->InputAt(1u); ++num_args; - } else if (!seen_constructor) { - // At this point, we should see the constructor. However, we might have inlined it so we have - // to take care of both cases. We accept only the constructor with no extra arguments. This - // means that if we inline it, we have to check it is setting its field to a new array. - if (user->IsInvokeStaticOrDirect() && - user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr && - user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() && - user->AsInvokeStaticOrDirect()->GetNumberOfArguments() == 1u) { - constructor_inlined = false; - } else if (user->IsInstanceFieldSet() && - user->AsInstanceFieldSet()->GetFieldType() == DataType::Type::kReference && - user->AsInstanceFieldSet()->InputAt(0) == sb && - user->AsInstanceFieldSet()->GetValue()->IsNewArray()) { - maybe_new_array = user->AsInstanceFieldSet()->GetValue(); - constructor_inlined = true; - } else { - // We were expecting a constructor but we haven't seen it. Abort optimization. - return false; - } + } else if (user->IsInvokeStaticOrDirect() && + user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr && + user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() && + user->AsInvokeStaticOrDirect()->GetNumberOfArguments() == 1u) { + // After arguments, we should see the constructor. + // We accept only the constructor with no extra arguments. + DCHECK(!seen_constructor); DCHECK(!seen_constructor_fence); seen_constructor = true; } else if (user->IsConstructorFence()) { @@ -2778,10 +2763,6 @@ static bool TryReplaceStringBuilderAppend(HInvoke* invoke) { // Accept only calls on the StringBuilder (which shall all be removed). // TODO: Carve-out for const-string? Or rely on environment pruning (to be implemented)? if (holder->InputCount() == 0 || holder->InputAt(0) != sb) { - // When inlining the constructor, we have a NewArray as an environment use. - if (constructor_inlined && holder == maybe_new_array) { - continue; - } return false; } } @@ -2815,18 +2796,6 @@ static bool TryReplaceStringBuilderAppend(HInvoke* invoke) { while (sb->HasNonEnvironmentUses()) { block->RemoveInstruction(sb->GetUses().front().GetUser()); } - if (constructor_inlined) { - // We need to remove the inlined constructor instructions. That also removes all remaining - // environment uses. - DCHECK(sb->HasEnvironmentUses()); - DCHECK(maybe_new_array != nullptr); - DCHECK(maybe_new_array->IsNewArray()); - DCHECK(maybe_new_array->HasNonEnvironmentUses()); - HInstruction* fence = maybe_new_array->GetUses().front().GetUser(); - DCHECK(fence->IsConstructorFence()); - block->RemoveInstruction(fence); - block->RemoveInstruction(maybe_new_array); - } DCHECK(!sb->HasEnvironmentUses()); block->RemoveInstruction(sb); return true; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 6ef29bf93e..06fb88e837 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -2286,9 +2286,6 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { } virtual bool NeedsEnvironment() const { return false; } - virtual bool NeedsBss() const { - return false; - } uint32_t GetDexPc() const { return dex_pc_; } @@ -4885,9 +4882,6 @@ class HInvokeStaticOrDirect final : public HInvoke { } bool IsClonable() const override { return true; } - bool NeedsBss() const override { - return GetMethodLoadKind() == MethodLoadKind::kBssEntry; - } void SetDispatchInfo(DispatchInfo dispatch_info) { bool had_current_method_input = HasCurrentMethodInput(); @@ -5173,9 +5167,6 @@ class HInvokeInterface final : public HInvoke { } bool IsClonable() const override { return true; } - bool NeedsBss() const override { - return GetHiddenArgumentLoadKind() == MethodLoadKind::kBssEntry; - } bool CanDoImplicitNullCheckOn(HInstruction* obj) const override { // TODO: Add implicit null checks in intrinsics. @@ -6822,12 +6813,6 @@ class HLoadClass final : public HInstruction { bool NeedsEnvironment() const override { return CanCallRuntime(); } - bool NeedsBss() const override { - LoadKind load_kind = GetLoadKind(); - return load_kind == LoadKind::kBssEntry || - load_kind == LoadKind::kBssEntryPublic || - load_kind == LoadKind::kBssEntryPackage; - } void SetMustGenerateClinitCheck(bool generate_clinit_check) { SetPackedFlag<kFlagGenerateClInitCheck>(generate_clinit_check); @@ -7025,9 +7010,6 @@ class HLoadString final : public HInstruction { } bool IsClonable() const override { return true; } - bool NeedsBss() const override { - return GetLoadKind() == LoadKind::kBssEntry; - } void SetLoadKind(LoadKind load_kind); diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 622fec3521..58d65bb97d 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -81,7 +81,6 @@ enum class MethodCompilationStat { kSimplifyThrowingInvoke, kInstructionSunk, kNotInlinedUnresolvedEntrypoint, - kNotInlinedBss, kNotInlinedDexCache, kNotInlinedStackMaps, kNotInlinedEnvironmentBudget, diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 964b48c6ac..e52a3ce272 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -17,14 +17,10 @@ #include "stack_map_stream.h" #include <memory> -#include <vector> #include "art_method-inl.h" #include "base/stl_util.h" -#include "class_linker.h" -#include "dex/dex_file.h" #include "dex/dex_file_types.h" -#include "optimizing/nodes.h" #include "optimizing/optimizing_compiler.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" @@ -215,26 +211,12 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, entry[InlineInfo::kArtMethodHi] = High32Bits(reinterpret_cast<uintptr_t>(method)); entry[InlineInfo::kArtMethodLo] = Low32Bits(reinterpret_cast<uintptr_t>(method)); } else { - uint32_t bootclasspath_index = MethodInfo::kSameDexFile; - if (dex_pc != static_cast<uint32_t>(-1)) { + if (dex_pc != static_cast<uint32_t>(-1) && kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); - const DexFile* dex_file = method->GetDexFile(); - if (method->GetDeclaringClass()->GetClassLoader() == nullptr) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - const std::vector<const DexFile*>& boot_class_path = class_linker->GetBootClassPath(); - auto it = std::find_if( - boot_class_path.begin(), boot_class_path.end(), [dex_file](const DexFile* df) { - return IsSameDexFile(*df, *dex_file); - }); - DCHECK(it != boot_class_path.end()); - bootclasspath_index = std::distance(boot_class_path.begin(), it); - } else { - DCHECK(IsSameDexFile(*outer_dex_file, *dex_file)); - } + DCHECK(IsSameDexFile(*outer_dex_file, *method->GetDexFile())); } uint32_t dex_method_index = method->GetDexMethodIndex(); - entry[InlineInfo::kMethodInfoIndex] = - method_infos_.Dedup({dex_method_index, bootclasspath_index}); + entry[InlineInfo::kMethodInfoIndex] = method_infos_.Dedup({dex_method_index}); } current_inline_infos_.push_back(entry); @@ -250,18 +232,7 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, if (encode_art_method) { CHECK_EQ(inline_info.GetArtMethod(), method); } else { - MethodInfo method_info = code_info.GetMethodInfoOf(inline_info); - CHECK_EQ(method_info.GetMethodIndex(), method->GetDexMethodIndex()); - if (inline_info.GetDexPc() != static_cast<uint32_t>(-1)) { - ScopedObjectAccess soa(Thread::Current()); - if (method->GetDeclaringClass()->GetClassLoader() == nullptr) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - const std::vector<const DexFile*>& boot_class_path = class_linker->GetBootClassPath(); - DCHECK_LT(method_info.GetDexFileIndex(), boot_class_path.size()); - CHECK(IsSameDexFile(*boot_class_path[method_info.GetDexFileIndex()], - *method->GetDexFile())); - } - } + CHECK_EQ(code_info.GetMethodIndexOf(inline_info), method->GetDexMethodIndex()); } }); } diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 5b931a15be..84299d5077 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -80,20 +80,10 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, for (InlineInfo inline_info : inline_infos) { DCHECK(!inline_info.EncodesArtMethod()); DCHECK_NE(inline_info.GetDexPc(), static_cast<uint32_t>(-1)); - MethodInfo method_info = code_info.GetMethodInfoOf(inline_info); - uint32_t method_index = method_info.GetMethodIndex(); - ArtMethod* inlined_method; - if (method_info.HasDexFileIndex()) { - const DexFile* dex_file = class_linker->GetBootClassPath()[method_info.GetDexFileIndex()]; - ObjPtr<mirror::DexCache> dex_cache = class_linker->FindDexCache(Thread::Current(), *dex_file); - // The class loader is always nullptr for this case so we can simplify the call. - DCHECK_EQ(dex_cache->GetClassLoader(), nullptr); - inlined_method = class_linker->LookupResolvedMethod(method_index, dex_cache, nullptr); - } else { - inlined_method = class_linker->LookupResolvedMethod( - method_index, outer_method->GetDexCache(), outer_method->GetClassLoader()); - } - + uint32_t method_index = code_info.GetMethodIndexOf(inline_info); + ArtMethod* inlined_method = class_linker->LookupResolvedMethod(method_index, + method->GetDexCache(), + method->GetClassLoader()); if (UNLIKELY(inlined_method == nullptr)) { LOG(FATAL) << "Could not find an inlined method from an .oat file: " << method->GetDexFile()->PrettyMethod(method_index) << " . " @@ -101,8 +91,7 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, UNREACHABLE(); } DCHECK(!inlined_method->IsRuntimeMethod()); - if (UNLIKELY(inlined_method->GetDexFile() != method->GetDexFile() && - !method_info.HasDexFileIndex())) { + if (UNLIKELY(inlined_method->GetDexFile() != method->GetDexFile())) { // TODO: We could permit inlining within a multi-dex oat file and the boot image, // even going back from boot image methods to the same oat file. However, this is // not currently implemented in the compiler. Therefore crossing dex file boundary @@ -110,14 +99,13 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method, bool target_sdk_at_least_p = IsSdkVersionSetAndAtLeast(Runtime::Current()->GetTargetSdkVersion(), SdkVersion::kP); LOG(target_sdk_at_least_p ? FATAL : WARNING) - << "Inlined method resolution crossed dex file boundary: from " << method->PrettyMethod() + << "Inlined method resolution crossed dex file boundary: from " + << method->PrettyMethod() << " in " << method->GetDexFile()->GetLocation() << "/" - << static_cast<const void*>(method->GetDexFile()) << " to " - << inlined_method->PrettyMethod() << " in " << inlined_method->GetDexFile()->GetLocation() - << "/" << static_cast<const void*>(inlined_method->GetDexFile()) << ". " - << "The outermost method in the chain is: " << outer_method->PrettyMethod() << " in " - << outer_method->GetDexFile()->GetLocation() << "/" - << static_cast<const void*>(outer_method->GetDexFile()) + << static_cast<const void*>(method->GetDexFile()) + << " to " << inlined_method->PrettyMethod() + << " in " << inlined_method->GetDexFile()->GetLocation() << "/" + << static_cast<const void*>(inlined_method->GetDexFile()) << ". " << "This must be due to duplicate classes or playing wrongly with class loaders. " << "The runtime is in an unsafe state."; } diff --git a/runtime/oat.h b/runtime/oat.h index 6a20cc1bd9..95eb0e14ed 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } }; - // Last oat version changed reason: Inlining across dex files for bootclasspath methods. - static constexpr std::array<uint8_t, 4> kOatVersion{ {'2', '0', '5', '\0'} }; + // Last oat version changed reason: Inline IRT frame push/pop into JNI stubs. + static constexpr std::array<uint8_t, 4> kOatVersion { { '2', '0', '3', '\0' } }; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; static constexpr const char* kDebuggableKey = "debuggable"; diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 939c4b34cf..c160e2b4b3 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -347,12 +347,9 @@ void InlineInfo::Dump(VariableIndentationOutputStream* vios, ScopedObjectAccess soa(Thread::Current()); vios->Stream() << ", method=" << GetArtMethod()->PrettyMethod(); } else { - MethodInfo method_info = code_info.GetMethodInfoOf(*this); - vios->Stream() << std::dec << ", method_index=" << method_info.GetMethodIndex(); - if (method_info.HasDexFileIndex()) { - vios->Stream() << std::dec - << ", boot_class_path_dex_file_index=" << method_info.GetDexFileIndex(); - } + vios->Stream() + << std::dec + << ", method_index=" << code_info.GetMethodIndexOf(*this); } vios->Stream() << ")\n"; code_info.GetInlineDexRegisterMapOf(stack_map, *this).Dump(vios); diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 714895744b..103402baa6 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -262,13 +262,10 @@ class RegisterMask : public BitTableAccessor<2> { // Method indices are not very dedup friendly. // Separating them greatly improves dedup efficiency of the other tables. -class MethodInfo : public BitTableAccessor<2> { +class MethodInfo : public BitTableAccessor<1> { public: BIT_TABLE_HEADER(MethodInfo) BIT_TABLE_COLUMN(0, MethodIndex) - BIT_TABLE_COLUMN(1, DexFileIndex) - - static constexpr uint32_t kSameDexFile = -1; }; /** @@ -363,12 +360,8 @@ class CodeInfo { return stack_maps_.NumRows(); } - MethodInfo GetMethodInfoOf(InlineInfo inline_info) const { - return method_infos_.GetRow(inline_info.GetMethodInfoIndex()); - } - uint32_t GetMethodIndexOf(InlineInfo inline_info) const { - return GetMethodInfoOf(inline_info).GetMethodIndex(); + return method_infos_.GetRow(inline_info.GetMethodInfoIndex()).GetMethodIndex(); } ALWAYS_INLINE DexRegisterMap GetDexRegisterMapOf(StackMap stack_map) const { diff --git a/test/580-checker-string-fact-intrinsics/src-art/Main.java b/test/580-checker-string-fact-intrinsics/src-art/Main.java index 34af579a4b..d0750f9ae8 100644 --- a/test/580-checker-string-fact-intrinsics/src-art/Main.java +++ b/test/580-checker-string-fact-intrinsics/src-art/Main.java @@ -40,14 +40,16 @@ public class Main { // java.lang.StringFactory.newStringFromChars(char[] data) // // which contains a call to the former (non-public) native method. - // After the inliner runs, we can see the inlined call and check - // that the compiler intrinsifies it. + // However, this call will not be inlined (because it is a method in + // another Dex file and which contains a call, which needs an + // environment), so we cannot use Checker here to ensure the native + // call was intrinsified either. /// CHECK-START: void Main.testNewStringFromChars() builder (after) /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromChars intrinsic:None /// CHECK-START: void Main.testNewStringFromChars() inliner (after) - /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromChars intrinsic:StringNewStringFromChars + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromChars intrinsic:None public static void testNewStringFromChars() { char[] chars = { 'b', 'a', 'r' }; |