From: Fred Shih Date: Wed, 6 Aug 2014 17:46:37 +0000 (-0700) Subject: Added support for patching classes from different dex files. X-Git-Tag: android-x86-6.0-r1~145^2~2387^2^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=e7f82e2515f47f3c3292281312d7031a34a58ffc;p=android-x86%2Fart.git Added support for patching classes from different dex files. Added support for class patching from different dex files and moved ScopedObjectAccess from the quick compiler to driver. Slight refactoring for clarity. Bug: 16656190 Change-Id: I107fcbce75db42ca61321ea1c5d5f236680a1b3d --- diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 2a51b496a..9f604277a 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -394,6 +394,18 @@ LIR* Mir2Lir::ScanLiteralPoolMethod(LIR* data_target, const MethodReference& met return nullptr; } +/* Search the existing constants in the literal pool for an exact class match */ +LIR* Mir2Lir::ScanLiteralPoolClass(LIR* data_target, const DexFile& dex_file, uint32_t type_idx) { + while (data_target) { + if (static_cast(data_target->operands[0]) == type_idx && + UnwrapPointer(data_target->operands[1]) == &dex_file) { + return data_target; + } + data_target = data_target->next; + } + return nullptr; +} + /* * The following are building blocks to insert constants into the pool or * instruction streams. @@ -492,10 +504,13 @@ void Mir2Lir::InstallLiteralPools() { data_lir = class_literal_list_; while (data_lir != NULL) { uint32_t target_method_idx = data_lir->operands[0]; + const DexFile* class_dex_file = + reinterpret_cast(UnwrapPointer(data_lir->operands[1])); cu_->compiler_driver->AddClassPatch(cu_->dex_file, cu_->class_def_idx, cu_->method_idx, target_method_idx, + class_dex_file, code_buffer_.size()); const DexFile::TypeId& target_method_id = cu_->dex_file->GetTypeId(target_method_idx); // unique value based on target to ensure code deduplication works @@ -1222,12 +1237,14 @@ void Mir2Lir::LoadMethodAddress(const MethodReference& target_method, InvokeType DCHECK_NE(cu_->instruction_set, kMips) << reinterpret_cast(data_target); } -void Mir2Lir::LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_reg) { +void Mir2Lir::LoadClassType(const DexFile& dex_file, uint32_t type_idx, + SpecialTargetRegister symbolic_reg) { // Use the literal pool and a PC-relative load from a data word. - LIR* data_target = ScanLiteralPool(class_literal_list_, type_idx, 0); + LIR* data_target = ScanLiteralPoolClass(class_literal_list_, dex_file, type_idx); if (data_target == nullptr) { data_target = AddWordData(&class_literal_list_, type_idx); } + data_target->operands[1] = WrapPointer(const_cast(&dex_file)); // Loads a Class pointer, which is a reference as it lives in the heap. LIR* load_pc_rel = OpPcRelLoad(TargetReg(symbolic_reg, kRef), data_target); AppendLIR(load_pc_rel); diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index 7abf3e7ed..dbceafffb 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -53,7 +53,7 @@ static constexpr bool kIntrinsicIsStatic[] = { true, // kIntrinsicRint true, // kIntrinsicRoundFloat true, // kIntrinsicRoundDouble - false, // kIntrinsicGet + false, // kIntrinsicReferenceGet false, // kIntrinsicCharAt false, // kIntrinsicCompareTo false, // kIntrinsicIsEmptyOrLength @@ -85,7 +85,7 @@ COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicFloor], Floor_must_be_static); COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicRint], Rint_must_be_static); COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicRoundFloat], RoundFloat_must_be_static); COMPILE_ASSERT(kIntrinsicIsStatic[kIntrinsicRoundDouble], RoundDouble_must_be_static); -COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicGet], Get_must_not_be_static); +COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicReferenceGet], Get_must_not_be_static); COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicCharAt], CharAt_must_not_be_static); COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicCompareTo], CompareTo_must_not_be_static); COMPILE_ASSERT(!kIntrinsicIsStatic[kIntrinsicIsEmptyOrLength], IsEmptyOrLength_must_not_be_static); @@ -169,7 +169,7 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = { "floor", // kNameCacheFloor "rint", // kNameCacheRint "round", // kNameCacheRound - "get", // kNameCacheGet + "get", // kNameCacheReferenceGet "charAt", // kNameCacheCharAt "compareTo", // kNameCacheCompareTo "isEmpty", // kNameCacheIsEmpty @@ -339,7 +339,7 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangMath, Round, D_J, kIntrinsicRoundDouble, 0), INTRINSIC(JavaLangStrictMath, Round, D_J, kIntrinsicRoundDouble, 0), - INTRINSIC(JavaLangRefReference, Get, _Object, kIntrinsicGet, 0), + INTRINSIC(JavaLangRefReference, ReferenceGet, _Object, kIntrinsicReferenceGet, 0), INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), @@ -471,8 +471,8 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { return backend->GenInlinedRound(info, false /* is_double */); case kIntrinsicRoundDouble: return backend->GenInlinedRound(info, true /* is_double */); - case kIntrinsicGet: - return backend->GenInlinedGet(info); + case kIntrinsicReferenceGet: + return backend->GenInlinedReferenceGet(info); case kIntrinsicCharAt: return backend->GenInlinedCharAt(info); case kIntrinsicCompareTo: diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 1bd3c48e3..b875e2bcb 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -145,7 +145,7 @@ class DexFileMethodInliner { kNameCacheFloor, kNameCacheRint, kNameCacheRound, - kNameCacheGet, + kNameCacheReferenceGet, kNameCacheCharAt, kNameCacheCompareTo, kNameCacheIsEmpty, diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 0054f3432..2df436efb 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -361,7 +361,7 @@ void Mir2Lir::GenNewArray(uint32_t type_idx, RegLocation rl_dest, &direct_type_ptr, &is_finalizable)) { // The fast path. if (!use_direct_type_ptr) { - LoadClassType(type_idx, kArg0); + LoadClassType(*dex_file, type_idx, kArg0); CallRuntimeHelperRegMethodRegLocation(kQuickAllocArrayResolved, TargetReg(kArg0, kNotWide), rl_src, true); } else { @@ -961,7 +961,7 @@ void Mir2Lir::GenNewInstance(uint32_t type_idx, RegLocation rl_dest) { !is_finalizable) { // The fast path. if (!use_direct_type_ptr) { - LoadClassType(type_idx, kArg0); + LoadClassType(*dex_file, type_idx, kArg0); if (!is_type_initialized) { CallRuntimeHelperRegMethod(kQuickAllocObjectResolved, TargetReg(kArg0, kRef), true); } else { diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 8e7f6a6c5..3cfc9a6c9 100755 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -25,10 +25,8 @@ #include "mirror/class-inl.h" #include "mirror/dex_cache.h" #include "mirror/object_array-inl.h" -#include "mirror/reference-inl.h" #include "mirror/string.h" #include "mir_to_lir-inl.h" -#include "scoped_thread_state_change.h" #include "x86/codegen_x86.h" namespace art { @@ -1129,57 +1127,32 @@ RegLocation Mir2Lir::InlineTargetWide(CallInfo* info) { return res; } -bool Mir2Lir::GenInlinedGet(CallInfo* info) { +bool Mir2Lir::GenInlinedReferenceGet(CallInfo* info) { if (cu_->instruction_set == kMips) { // TODO - add Mips implementation return false; } - // the refrence class is stored in the image dex file which might not be the same as the cu's - // dex file. Query the reference class for the image dex file then reset to starting dex file - // in after loading class type. - uint16_t type_idx = 0; - const DexFile* ref_dex_file = nullptr; - { - ScopedObjectAccess soa(Thread::Current()); - type_idx = mirror::Reference::GetJavaLangRefReference()->GetDexTypeIndex(); - ref_dex_file = mirror::Reference::GetJavaLangRefReference()->GetDexCache()->GetDexFile(); - } - CHECK(LIKELY(ref_dex_file != nullptr)); - - // address is either static within the image file, or needs to be patched up after compilation. - bool unused_type_initialized; bool use_direct_type_ptr; uintptr_t direct_type_ptr; - bool is_finalizable; - const DexFile* old_dex = cu_->dex_file; - cu_->dex_file = ref_dex_file; + ClassReference ref; + if (!cu_->compiler_driver->CanEmbedReferenceTypeInCode(&ref, + &use_direct_type_ptr, &direct_type_ptr)) { + return false; + } + RegStorage reg_class = TargetReg(kArg1, kRef); Clobber(reg_class); LockTemp(reg_class); - if (!cu_->compiler_driver->CanEmbedTypeInCode(*ref_dex_file, type_idx, &unused_type_initialized, - &use_direct_type_ptr, &direct_type_ptr, - &is_finalizable) || is_finalizable) { - cu_->dex_file = old_dex; - // address is not known and post-compile patch is not possible, cannot insert intrinsic. - return false; - } if (use_direct_type_ptr) { LoadConstant(reg_class, direct_type_ptr); } else { - LoadClassType(type_idx, kArg1); + uint16_t type_idx = ref.first->GetClassDef(ref.second).class_idx_; + LoadClassType(*ref.first, type_idx, kArg1); } - cu_->dex_file = old_dex; - // get the offset for flags in reference class. - uint32_t slow_path_flag_offset = 0; - uint32_t disable_flag_offset = 0; - { - ScopedObjectAccess soa(Thread::Current()); - mirror::Class* reference_class = mirror::Reference::GetJavaLangRefReference(); - slow_path_flag_offset = reference_class->GetSlowPathFlagOffset().Uint32Value(); - disable_flag_offset = reference_class->GetDisableIntrinsicFlagOffset().Uint32Value(); - } + uint32_t slow_path_flag_offset = cu_->compiler_driver->GetReferenceSlowFlagOffset(); + uint32_t disable_flag_offset = cu_->compiler_driver->GetReferenceDisableFlagOffset(); CHECK(slow_path_flag_offset && disable_flag_offset && (slow_path_flag_offset != disable_flag_offset)); diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 4ed992933..afab2b459 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -682,6 +682,7 @@ class Mir2Lir : public Backend { LIR* ScanLiteralPool(LIR* data_target, int value, unsigned int delta); LIR* ScanLiteralPoolWide(LIR* data_target, int val_lo, int val_hi); LIR* ScanLiteralPoolMethod(LIR* data_target, const MethodReference& method); + LIR* ScanLiteralPoolClass(LIR* data_target, const DexFile& dex_file, uint32_t type_idx); LIR* AddWordData(LIR* *constant_list_p, int value); LIR* AddWideData(LIR* *constant_list_p, int val_lo, int val_hi); void ProcessSwitchTables(); @@ -955,7 +956,7 @@ class Mir2Lir : public Backend { */ RegLocation InlineTargetWide(CallInfo* info); - bool GenInlinedGet(CallInfo* info); + bool GenInlinedReferenceGet(CallInfo* info); virtual bool GenInlinedCharAt(CallInfo* info); bool GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty); virtual bool GenInlinedReverseBits(CallInfo* info, OpSize size); @@ -1110,11 +1111,13 @@ class Mir2Lir : public Backend { /* * @brief Load the Class* of a Dex Class type into the register. + * @param dex DexFile that contains the class type. * @param type How the method will be invoked. * @param register that will contain the code address. * @note register will be passed to TargetReg to get physical register. */ - virtual void LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_reg); + virtual void LoadClassType(const DexFile& dex_file, uint32_t type_idx, + SpecialTargetRegister symbolic_reg); // Routines that work for the generic case, but may be overriden by target. /* diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 266191adc..21667b0be 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -319,11 +319,13 @@ class X86Mir2Lir : public Mir2Lir { /* * @brief Load the Class* of a Dex Class type into the register. + * @param dex DexFile that contains the class type. * @param type How the method will be invoked. * @param register that will contain the code address. * @note register will be passed to TargetReg to get physical register. */ - void LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_reg) OVERRIDE; + void LoadClassType(const DexFile& dex_file, uint32_t type_idx, + SpecialTargetRegister symbolic_reg) OVERRIDE; void FlushIns(RegLocation* ArgLocs, RegLocation rl_method) OVERRIDE; diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index bd2e0f31b..901d62779 100755 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -971,19 +971,21 @@ void X86Mir2Lir::LoadMethodAddress(const MethodReference& target_method, InvokeT method_address_insns_.Insert(move); } -void X86Mir2Lir::LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_reg) { +void X86Mir2Lir::LoadClassType(const DexFile& dex_file, uint32_t type_idx, + SpecialTargetRegister symbolic_reg) { /* * For x86, just generate a 32 bit move immediate instruction, that will be filled * in at 'link time'. For now, put a unique value based on target to ensure that * code deduplication works. */ - const DexFile::TypeId& id = cu_->dex_file->GetTypeId(type_idx); + const DexFile::TypeId& id = dex_file.GetTypeId(type_idx); uintptr_t ptr = reinterpret_cast(&id); // Generate the move instruction with the unique pointer and save index and type. LIR *move = RawLIR(current_dalvik_offset_, kX86Mov32RI, TargetReg(symbolic_reg, kNotWide).GetReg(), - static_cast(ptr), type_idx); + static_cast(ptr), type_idx, + WrapPointer(const_cast(&dex_file))); AppendLIR(move); class_type_address_insns_.Insert(move); } @@ -1068,12 +1070,16 @@ void X86Mir2Lir::InstallLiteralPools() { for (uint32_t i = 0; i < class_type_address_insns_.Size(); i++) { LIR* p = class_type_address_insns_.Get(i); DCHECK_EQ(p->opcode, kX86Mov32RI); + + const DexFile* class_dex_file = + reinterpret_cast(UnwrapPointer(p->operands[3])); uint32_t target_method_idx = p->operands[2]; // The offset to patch is the last 4 bytes of the instruction. int patch_offset = p->offset + p->flags.size - 4; cu_->compiler_driver->AddClassPatch(cu_->dex_file, cu_->class_def_idx, - cu_->method_idx, target_method_idx, patch_offset); + cu_->method_idx, target_method_idx, class_dex_file, + patch_offset); } // And now the PC-relative calls to methods. diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index ed126adfe..f3f34aea0 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -969,6 +969,43 @@ bool CompilerDriver::CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_i } } +bool CompilerDriver::CanEmbedReferenceTypeInCode(ClassReference* ref, + bool* use_direct_ptr, + uintptr_t* direct_type_ptr) { + CHECK(ref != nullptr); + CHECK(use_direct_ptr != nullptr); + CHECK(direct_type_ptr != nullptr); + + ScopedObjectAccess soa(Thread::Current()); + mirror::Class* reference_class = mirror::Reference::GetJavaLangRefReference(); + bool is_initialized; + bool unused_finalizable; + // Make sure we have a finished Reference class object before attempting to use it. + if (!CanEmbedTypeInCode(*reference_class->GetDexCache()->GetDexFile(), + reference_class->GetDexTypeIndex(), &is_initialized, + use_direct_ptr, direct_type_ptr, &unused_finalizable) || + !is_initialized) { + return false; + } + ref->first = &reference_class->GetDexFile(); + ref->second = reference_class->GetDexClassDefIndex(); + return true; +} + +uint32_t CompilerDriver::GetReferenceSlowFlagOffset() const { + ScopedObjectAccess soa(Thread::Current()); + mirror::Class* klass = mirror::Reference::GetJavaLangRefReference(); + DCHECK(klass->IsInitialized()); + return klass->GetSlowPathFlagOffset().Uint32Value(); +} + +uint32_t CompilerDriver::GetReferenceDisableFlagOffset() const { + ScopedObjectAccess soa(Thread::Current()); + mirror::Class* klass = mirror::Reference::GetJavaLangRefReference(); + DCHECK(klass->IsInitialized()); + return klass->GetDisableIntrinsicFlagOffset().Uint32Value(); +} + void CompilerDriver::ProcessedInstanceField(bool resolved) { if (!resolved) { stats_->UnresolvedInstanceField(); @@ -1340,12 +1377,14 @@ void CompilerDriver::AddClassPatch(const DexFile* dex_file, uint16_t referrer_class_def_idx, uint32_t referrer_method_idx, uint32_t target_type_idx, + const DexFile* target_type_dex_file, size_t literal_offset) { MutexLock mu(Thread::Current(), compiled_methods_lock_); classes_to_patch_.push_back(new TypePatchInformation(dex_file, referrer_class_def_idx, referrer_method_idx, target_type_idx, + target_type_dex_file, literal_offset)); } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 6dae39837..6dea8dc38 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -213,6 +213,12 @@ class CompilerDriver { bool* is_type_initialized, bool* use_direct_type_ptr, uintptr_t* direct_type_ptr, bool* out_is_finalizable); + // Query methods for the java.lang.ref.Reference class. + bool CanEmbedReferenceTypeInCode(ClassReference* ref, + bool* use_direct_type_ptr, uintptr_t* direct_type_ptr); + uint32_t GetReferenceSlowFlagOffset() const; + uint32_t GetReferenceDisableFlagOffset() const; + // Get the DexCache for the mirror::DexCache* GetDexCache(const DexCompilationUnit* mUnit) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -356,6 +362,7 @@ class CompilerDriver { uint16_t referrer_class_def_idx, uint32_t referrer_method_idx, uint32_t target_method_idx, + const DexFile* target_dex_file, size_t literal_offset) LOCKS_EXCLUDED(compiled_methods_lock_); @@ -549,6 +556,10 @@ class CompilerDriver { class TypePatchInformation : public PatchInformation { public: + const DexFile& GetTargetTypeDexFile() const { + return *target_type_dex_file_; + } + uint32_t GetTargetTypeIdx() const { return target_type_idx_; } @@ -565,13 +576,15 @@ class CompilerDriver { uint16_t referrer_class_def_idx, uint32_t referrer_method_idx, uint32_t target_type_idx, + const DexFile* target_type_dex_file, size_t literal_offset) : PatchInformation(dex_file, referrer_class_def_idx, referrer_method_idx, literal_offset), - target_type_idx_(target_type_idx) { + target_type_idx_(target_type_idx), target_type_dex_file_(target_type_dex_file) { } const uint32_t target_type_idx_; + const DexFile* target_type_dex_file_; friend class CompilerDriver; DISALLOW_COPY_AND_ASSIGN(TypePatchInformation); diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc index 137110f5a..9ae755d45 100644 --- a/compiler/elf_patcher.cc +++ b/compiler/elf_patcher.cc @@ -99,11 +99,13 @@ mirror::ArtMethod* ElfPatcher::GetTargetMethod(const CompilerDriver::CallPatchIn mirror::Class* ElfPatcher::GetTargetType(const CompilerDriver::TypePatchInformation* patch) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<2> hs(Thread::Current()); - Handle dex_cache(hs.NewHandle(class_linker->FindDexCache(patch->GetDexFile()))); - mirror::Class* klass = class_linker->ResolveType(patch->GetDexFile(), patch->GetTargetTypeIdx(), + Handle dex_cache(hs.NewHandle(class_linker->FindDexCache( + patch->GetTargetTypeDexFile()))); + mirror::Class* klass = class_linker->ResolveType(patch->GetTargetTypeDexFile(), + patch->GetTargetTypeIdx(), dex_cache, NullHandle()); CHECK(klass != NULL) - << patch->GetDexFile().GetLocation() << " " << patch->GetTargetTypeIdx(); + << patch->GetTargetTypeDexFile().GetLocation() << " " << patch->GetTargetTypeIdx(); CHECK(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx()) == klass) << patch->GetDexFile().GetLocation() << " " << patch->GetReferrerMethodIdx() << " " << PrettyClass(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx())) << " " @@ -152,7 +154,7 @@ void ElfPatcher::SetPatchLocation(const CompilerDriver::PatchInformation* patch, } if (patch->IsType()) { const CompilerDriver::TypePatchInformation* tpatch = patch->AsType(); - const DexFile::TypeId& id = tpatch->GetDexFile().GetTypeId(tpatch->GetTargetTypeIdx()); + const DexFile::TypeId& id = tpatch->GetTargetTypeDexFile().GetTypeId(tpatch->GetTargetTypeIdx()); uint32_t expected = reinterpret_cast(&id) & 0xFFFFFFFF; uint32_t actual = *patch_location; CHECK(actual == expected || actual == value) << "Patching type failed: " << std::hex diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h index 23b9aed92..c4d51cb17 100644 --- a/runtime/quick/inline_method_analyser.h +++ b/runtime/quick/inline_method_analyser.h @@ -53,7 +53,7 @@ enum InlineMethodOpcode : uint16_t { kIntrinsicRint, kIntrinsicRoundFloat, kIntrinsicRoundDouble, - kIntrinsicGet, + kIntrinsicReferenceGet, kIntrinsicCharAt, kIntrinsicCompareTo, kIntrinsicIsEmptyOrLength,