diff options
| -rw-r--r-- | compiler/compiled_method.h | 74 | ||||
| -rw-r--r-- | compiler/linker/arm/relative_patcher_arm_base.cc | 2 | ||||
| -rw-r--r-- | compiler/linker/arm64/relative_patcher_arm64.cc | 16 | ||||
| -rw-r--r-- | compiler/linker/relative_patcher_test.h | 8 | ||||
| -rw-r--r-- | compiler/oat_writer.cc | 18 | ||||
| -rw-r--r-- | compiler/optimizing/sharpening.cc | 53 | ||||
| -rw-r--r-- | runtime/base/casts.h | 26 | ||||
| -rw-r--r-- | runtime/mirror/object.h | 7 |
8 files changed, 113 insertions, 91 deletions
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index acd508997d..b5c99e8c95 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -159,29 +159,27 @@ class SrcMap FINAL : public std::vector<SrcMapElem, Allocator> { using DefaultSrcMap = SrcMap<std::allocator<SrcMapElem>>; - -enum LinkerPatchType { - kLinkerPatchRecordPosition, // Just record patch position for patchoat. - kLinkerPatchMethod, - kLinkerPatchCall, - kLinkerPatchCallRelative, // NOTE: Actual patching is instruction_set-dependent. - kLinkerPatchType, - kLinkerPatchString, - kLinkerPatchStringRelative, // NOTE: Actual patching is instruction_set-dependent. - kLinkerPatchDexCacheArray, // NOTE: Actual patching is instruction_set-dependent. -}; -std::ostream& operator<<(std::ostream& os, const LinkerPatchType& type); - class LinkerPatch { public: + enum class Type { + kRecordPosition, // Just record patch position for patchoat. + kMethod, + kCall, + kCallRelative, // NOTE: Actual patching is instruction_set-dependent. + kType, + kString, + kStringRelative, // NOTE: Actual patching is instruction_set-dependent. + kDexCacheArray, // NOTE: Actual patching is instruction_set-dependent. + }; + static LinkerPatch RecordPosition(size_t literal_offset) { - return LinkerPatch(literal_offset, kLinkerPatchRecordPosition, /* target_dex_file */ nullptr); + return LinkerPatch(literal_offset, Type::kRecordPosition, /* target_dex_file */ nullptr); } static LinkerPatch MethodPatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t target_method_idx) { - LinkerPatch patch(literal_offset, kLinkerPatchMethod, target_dex_file); + LinkerPatch patch(literal_offset, Type::kMethod, target_dex_file); patch.method_idx_ = target_method_idx; return patch; } @@ -189,7 +187,7 @@ class LinkerPatch { static LinkerPatch CodePatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t target_method_idx) { - LinkerPatch patch(literal_offset, kLinkerPatchCall, target_dex_file); + LinkerPatch patch(literal_offset, Type::kCall, target_dex_file); patch.method_idx_ = target_method_idx; return patch; } @@ -197,7 +195,7 @@ class LinkerPatch { static LinkerPatch RelativeCodePatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t target_method_idx) { - LinkerPatch patch(literal_offset, kLinkerPatchCallRelative, target_dex_file); + LinkerPatch patch(literal_offset, Type::kCallRelative, target_dex_file); patch.method_idx_ = target_method_idx; return patch; } @@ -205,7 +203,7 @@ class LinkerPatch { static LinkerPatch TypePatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t target_type_idx) { - LinkerPatch patch(literal_offset, kLinkerPatchType, target_dex_file); + LinkerPatch patch(literal_offset, Type::kType, target_dex_file); patch.type_idx_ = target_type_idx; return patch; } @@ -213,7 +211,7 @@ class LinkerPatch { static LinkerPatch StringPatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t target_string_idx) { - LinkerPatch patch(literal_offset, kLinkerPatchString, target_dex_file); + LinkerPatch patch(literal_offset, Type::kString, target_dex_file); patch.string_idx_ = target_string_idx; return patch; } @@ -222,7 +220,7 @@ class LinkerPatch { const DexFile* target_dex_file, uint32_t pc_insn_offset, uint32_t target_string_idx) { - LinkerPatch patch(literal_offset, kLinkerPatchStringRelative, target_dex_file); + LinkerPatch patch(literal_offset, Type::kStringRelative, target_dex_file); patch.string_idx_ = target_string_idx; patch.pc_insn_offset_ = pc_insn_offset; return patch; @@ -233,7 +231,7 @@ class LinkerPatch { uint32_t pc_insn_offset, size_t element_offset) { DCHECK(IsUint<32>(element_offset)); - LinkerPatch patch(literal_offset, kLinkerPatchDexCacheArray, target_dex_file); + LinkerPatch patch(literal_offset, Type::kDexCacheArray, target_dex_file); patch.pc_insn_offset_ = pc_insn_offset; patch.element_offset_ = element_offset; return patch; @@ -246,15 +244,15 @@ class LinkerPatch { return literal_offset_; } - LinkerPatchType Type() const { + Type GetType() const { return patch_type_; } bool IsPcRelative() const { - switch (Type()) { - case kLinkerPatchCallRelative: - case kLinkerPatchStringRelative: - case kLinkerPatchDexCacheArray: + switch (GetType()) { + case Type::kCallRelative: + case Type::kStringRelative: + case Type::kDexCacheArray: return true; default: return false; @@ -262,48 +260,49 @@ class LinkerPatch { } MethodReference TargetMethod() const { - DCHECK(patch_type_ == kLinkerPatchMethod || - patch_type_ == kLinkerPatchCall || patch_type_ == kLinkerPatchCallRelative); + DCHECK(patch_type_ == Type::kMethod || + patch_type_ == Type::kCall || + patch_type_ == Type::kCallRelative); return MethodReference(target_dex_file_, method_idx_); } const DexFile* TargetTypeDexFile() const { - DCHECK(patch_type_ == kLinkerPatchType); + DCHECK(patch_type_ == Type::kType); return target_dex_file_; } uint32_t TargetTypeIndex() const { - DCHECK(patch_type_ == kLinkerPatchType); + DCHECK(patch_type_ == Type::kType); return type_idx_; } const DexFile* TargetStringDexFile() const { - DCHECK(patch_type_ == kLinkerPatchString || patch_type_ == kLinkerPatchStringRelative); + DCHECK(patch_type_ == Type::kString || patch_type_ == Type::kStringRelative); return target_dex_file_; } uint32_t TargetStringIndex() const { - DCHECK(patch_type_ == kLinkerPatchString || patch_type_ == kLinkerPatchStringRelative); + DCHECK(patch_type_ == Type::kString || patch_type_ == Type::kStringRelative); return string_idx_; } const DexFile* TargetDexCacheDexFile() const { - DCHECK(patch_type_ == kLinkerPatchDexCacheArray); + DCHECK(patch_type_ == Type::kDexCacheArray); return target_dex_file_; } size_t TargetDexCacheElementOffset() const { - DCHECK(patch_type_ == kLinkerPatchDexCacheArray); + DCHECK(patch_type_ == Type::kDexCacheArray); return element_offset_; } uint32_t PcInsnOffset() const { - DCHECK(patch_type_ == kLinkerPatchStringRelative || patch_type_ == kLinkerPatchDexCacheArray); + DCHECK(patch_type_ == Type::kStringRelative || patch_type_ == Type::kDexCacheArray); return pc_insn_offset_; } private: - LinkerPatch(size_t literal_offset, LinkerPatchType patch_type, const DexFile* target_dex_file) + LinkerPatch(size_t literal_offset, Type patch_type, const DexFile* target_dex_file) : target_dex_file_(target_dex_file), literal_offset_(literal_offset), patch_type_(patch_type) { @@ -316,7 +315,7 @@ class LinkerPatch { const DexFile* target_dex_file_; uint32_t literal_offset_ : 24; // Method code size up to 16MiB. - LinkerPatchType patch_type_ : 8; + Type patch_type_ : 8; union { uint32_t cmp1_; // Used for relational operators. uint32_t method_idx_; // Method index for Call/Method patches. @@ -341,6 +340,7 @@ class LinkerPatch { friend bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs); friend bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs); }; +std::ostream& operator<<(std::ostream& os, const LinkerPatch::Type& type); inline bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs) { return lhs.literal_offset_ == rhs.literal_offset_ && diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc index 682b008219..d4dd978c5c 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.cc +++ b/compiler/linker/arm/relative_patcher_arm_base.cc @@ -112,7 +112,7 @@ uint32_t ArmBaseRelativePatcher::ReserveSpaceInternal(uint32_t offset, } } for (const LinkerPatch& patch : compiled_method->GetPatches()) { - if (patch.Type() == kLinkerPatchCallRelative) { + if (patch.GetType() == LinkerPatch::Type::kCallRelative) { unprocessed_patches_.emplace_back(patch.TargetMethod(), quick_code_offset + patch.LiteralOffset()); } diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 0549327e7a..e3e31213ba 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -31,8 +31,9 @@ namespace linker { namespace { inline bool IsAdrpPatch(const LinkerPatch& patch) { - LinkerPatchType type = patch.Type(); - return (type == kLinkerPatchStringRelative || type == kLinkerPatchDexCacheArray) && + LinkerPatch::Type type = patch.GetType(); + return + (type == LinkerPatch::Type::kStringRelative || type == LinkerPatch::Type::kDexCacheArray) && patch.LiteralOffset() == patch.PcInsnOffset(); } @@ -209,11 +210,11 @@ void Arm64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, } else { if ((insn & 0xfffffc00) == 0x91000000) { // ADD immediate, 64-bit with imm12 == 0 (unset). - DCHECK(patch.Type() == kLinkerPatchStringRelative) << patch.Type(); + DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative) << patch.GetType(); shift = 0u; // No shift for ADD. } else { // LDR 32-bit or 64-bit with imm12 == 0 (unset). - DCHECK(patch.Type() == kLinkerPatchDexCacheArray) << patch.Type(); + DCHECK(patch.GetType() == LinkerPatch::Type::kDexCacheArray) << patch.GetType(); DCHECK_EQ(insn & 0xbffffc00, 0xb9400000) << std::hex << insn; } if (kIsDebugBuild) { @@ -292,9 +293,10 @@ bool Arm64RelativePatcher::NeedsErratum843419Thunk(ArrayRef<const uint8_t> code, return false; } - // And since kLinkerPatchStringRelative is using the result of the ADRP for an ADD immediate, - // check for that as well. We generalize a bit to include ADD/ADDS/SUB/SUBS immediate that - // either uses the ADRP destination or stores the result to a different register. + // And since LinkerPatch::Type::kStringRelative is using the result of the ADRP + // for an ADD immediate, check for that as well. We generalize a bit to include + // ADD/ADDS/SUB/SUBS immediate that either uses the ADRP destination or stores + // the result to a different register. if ((next_insn & 0x1f000000) == 0x11000000 && ((((next_insn >> 5) ^ adrp) & 0x1f) == 0 || ((next_insn ^ adrp) & 0x1f) != 0)) { return false; diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index c9fb543d2e..bf61ea0570 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -142,26 +142,26 @@ class RelativePatcherTest : public testing::Test { patched_code_.assign(code.begin(), code.end()); code = ArrayRef<const uint8_t>(patched_code_); for (const LinkerPatch& patch : compiled_method->GetPatches()) { - if (patch.Type() == kLinkerPatchCallRelative) { + if (patch.GetType() == LinkerPatch::Type::kCallRelative) { auto result = method_offset_map_.FindMethodOffset(patch.TargetMethod()); uint32_t target_offset = result.first ? result.second : kTrampolineOffset + compiled_method->CodeDelta(); patcher_->PatchCall(&patched_code_, patch.LiteralOffset(), offset + patch.LiteralOffset(), target_offset); - } else if (patch.Type() == kLinkerPatchDexCacheArray) { + } else if (patch.GetType() == LinkerPatch::Type::kDexCacheArray) { uint32_t target_offset = dex_cache_arrays_begin_ + patch.TargetDexCacheElementOffset(); patcher_->PatchPcRelativeReference(&patched_code_, patch, offset + patch.LiteralOffset(), target_offset); - } else if (patch.Type() == kLinkerPatchStringRelative) { + } else if (patch.GetType() == LinkerPatch::Type::kStringRelative) { uint32_t target_offset = string_index_to_offset_map_.Get(patch.TargetStringIndex()); patcher_->PatchPcRelativeReference(&patched_code_, patch, offset + patch.LiteralOffset(), target_offset); } else { - LOG(FATAL) << "Bad patch type. " << patch.Type(); + LOG(FATAL) << "Bad patch type. " << patch.GetType(); UNREACHABLE(); } } diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index a7d574c077..3a67b1ec2a 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -1116,8 +1116,8 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { quick_code = ArrayRef<const uint8_t>(patched_code_); for (const LinkerPatch& patch : compiled_method->GetPatches()) { uint32_t literal_offset = patch.LiteralOffset(); - switch (patch.Type()) { - case kLinkerPatchCallRelative: { + switch (patch.GetType()) { + case LinkerPatch::Type::kCallRelative: { // NOTE: Relative calls across oat files are not supported. uint32_t target_offset = GetTargetOffset(patch); writer_->relative_patcher_->PatchCall(&patched_code_, @@ -1126,7 +1126,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { target_offset); break; } - case kLinkerPatchDexCacheArray: { + case LinkerPatch::Type::kDexCacheArray: { uint32_t target_offset = GetDexCacheOffset(patch); writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, patch, @@ -1134,7 +1134,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { target_offset); break; } - case kLinkerPatchStringRelative: { + case LinkerPatch::Type::kStringRelative: { uint32_t target_offset = GetTargetObjectOffset(GetTargetString(patch)); writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, patch, @@ -1142,28 +1142,28 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { target_offset); break; } - case kLinkerPatchCall: { + case LinkerPatch::Type::kCall: { uint32_t target_offset = GetTargetOffset(patch); PatchCodeAddress(&patched_code_, literal_offset, target_offset); break; } - case kLinkerPatchMethod: { + case LinkerPatch::Type::kMethod: { ArtMethod* method = GetTargetMethod(patch); PatchMethodAddress(&patched_code_, literal_offset, method); break; } - case kLinkerPatchString: { + case LinkerPatch::Type::kString: { mirror::String* string = GetTargetString(patch); PatchObjectAddress(&patched_code_, literal_offset, string); break; } - case kLinkerPatchType: { + case LinkerPatch::Type::kType: { mirror::Class* type = GetTargetType(patch); PatchObjectAddress(&patched_code_, literal_offset, type); break; } default: { - DCHECK_EQ(patch.Type(), kLinkerPatchRecordPosition); + DCHECK_EQ(patch.GetType(), LinkerPatch::Type::kRecordPosition); break; } } diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 45ae3366c1..7a1bb316e4 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -16,6 +16,7 @@ #include "sharpening.h" +#include "base/casts.h" #include "class_linker.h" #include "code_generator.h" #include "driver/dex_compilation_unit.h" @@ -180,46 +181,42 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { // MIPS/MIPS64 or compiler_driver_test. Do not sharpen. desired_load_kind = HLoadString::LoadKind::kDexCacheViaMethod; } else { - DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), - &load_string->GetDexFile())); + DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file)); is_in_dex_cache = true; desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic() ? HLoadString::LoadKind::kBootImageLinkTimePcRelative : HLoadString::LoadKind::kBootImageLinkTimeAddress; } + } else if (runtime->UseJit()) { + // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. + // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); + mirror::String* string = dex_cache->GetResolvedString(string_index); + is_in_dex_cache = (string != nullptr); + if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { + desired_load_kind = HLoadString::LoadKind::kBootImageAddress; + address = reinterpret_cast64<uint64_t>(string); + } else { + // Note: If the string is not in the dex cache, the instruction needs environment + // and will not be inlined across dex files. Within a dex file, the slow-path helper + // loads the correct string and inlined frames are used correctly for OOM stack trace. + // TODO: Write a test for this. + desired_load_kind = HLoadString::LoadKind::kDexCacheAddress; + void* dex_cache_element_address = &dex_cache->GetStrings()[string_index]; + address = reinterpret_cast64<uint64_t>(dex_cache_element_address); + } } else { - // Not compiling boot image. Try to lookup the string without allocating if not found. + // AOT app compilation. Try to lookup the string without allocating if not found. mirror::String* string = class_linker->LookupString(dex_file, string_index, dex_cache); - if (runtime->UseJit()) { - // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. - // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); - is_in_dex_cache = (string != nullptr); - if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { - desired_load_kind = HLoadString::LoadKind::kBootImageAddress; - // Convert to uintptr_t first to avoid sign-extension if a 32-bit pointer is "signed." - address = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(string)); - } else { - // Note: If the string is not in the dex cache, the instruction needs environment - // and will not be inlined across dex files. Within a dex file, the slow-path helper - // loads the correct string and inlined frames are used correctly for OOM stack trace. - // TODO: Write a test for this. - desired_load_kind = HLoadString::LoadKind::kDexCacheAddress; - void* dex_cache_element_address = &dex_cache->GetStrings()[string_index]; - // Convert to uintptr_t first to avoid sign-extension if a 32-bit pointer is "signed." - address = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(dex_cache_element_address)); - } - } else if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { + if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { if (codegen_->GetCompilerOptions().GetCompilePic()) { // Use PC-relative load from the dex cache if the dex file belongs // to the oat file that we're currently compiling. - desired_load_kind = - ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &load_string->GetDexFile()) - ? HLoadString::LoadKind::kDexCachePcRelative - : HLoadString::LoadKind::kDexCacheViaMethod; + desired_load_kind = ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file) + ? HLoadString::LoadKind::kDexCachePcRelative + : HLoadString::LoadKind::kDexCacheViaMethod; } else { desired_load_kind = HLoadString::LoadKind::kBootImageAddress; - // Convert to uintptr_t first to avoid sign-extension if a 32-bit pointer is "signed." - address = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(string)); + address = reinterpret_cast64<uint64_t>(string); } } else { // Not JIT and the string is not in boot image. diff --git a/runtime/base/casts.h b/runtime/base/casts.h index f8846498df..6b67864b08 100644 --- a/runtime/base/casts.h +++ b/runtime/base/casts.h @@ -19,6 +19,7 @@ #include <assert.h> #include <limits> +#include <stdint.h> #include <string.h> #include <type_traits> @@ -34,7 +35,7 @@ namespace art { // When you use implicit_cast, the compiler checks that the cast is safe. // Such explicit implicit_casts are necessary in surprisingly many // situations where C++ demands an exact type match instead of an -// argument type convertable to a target type. +// argument type convertible to a target type. // // The From type can be inferred, so the preferred syntax for using // implicit_cast is the same as for static_cast etc.: @@ -102,6 +103,29 @@ inline Dest dchecked_integral_cast(const Source source) { return static_cast<Dest>(source); } +// A version of reinterpret_cast<>() between pointers and int64_t/uint64_t +// that goes through uintptr_t to avoid treating the pointer as "signed." + +template <typename Dest, typename Source> +inline Dest reinterpret_cast64(Source source) { + // This is the overload for casting from int64_t/uint64_t to a pointer. + static_assert(std::is_same<Source, int64_t>::value || std::is_same<Source, uint64_t>::value, + "Source must be int64_t or uint64_t."); + static_assert(std::is_pointer<Dest>::value, "Dest must be a pointer."); + // Check that we don't lose any non-0 bits here. + DCHECK_EQ(static_cast<Source>(static_cast<uintptr_t>(source)), source); + return reinterpret_cast<Dest>(static_cast<uintptr_t>(source)); +} + +template <typename Dest, typename Source> +inline Dest reinterpret_cast64(Source* ptr) { + // This is the overload for casting from a pointer to int64_t/uint64_t. + static_assert(std::is_same<Dest, int64_t>::value || std::is_same<Dest, uint64_t>::value, + "Dest must be int64_t or uint64_t."); + static_assert(sizeof(uintptr_t) <= sizeof(Dest), "Expecting at most 64-bit pointers."); + return static_cast<Dest>(reinterpret_cast<uintptr_t>(ptr)); +} + } // namespace art #endif // ART_RUNTIME_BASE_CASTS_H_ diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 3f739df67b..0ee46c3556 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -17,6 +17,7 @@ #ifndef ART_RUNTIME_MIRROR_OBJECT_H_ #define ART_RUNTIME_MIRROR_OBJECT_H_ +#include "base/casts.h" #include "globals.h" #include "object_reference.h" #include "offsets.h" @@ -490,7 +491,7 @@ class MANAGED LOCKABLE Object { field_offset, static_cast<int32_t>(ptr)); } else { SetField64<kTransactionActive, kCheckTransaction, kVerifyFlags>( - field_offset, static_cast<int64_t>(reinterpret_cast<uintptr_t>(new_value))); + field_offset, reinterpret_cast64<int64_t>(new_value)); } } // TODO fix thread safety analysis broken by the use of template. This should be @@ -531,9 +532,7 @@ class MANAGED LOCKABLE Object { return reinterpret_cast<T>(GetField32<kVerifyFlags, kIsVolatile>(field_offset)); } else { int64_t v = GetField64<kVerifyFlags, kIsVolatile>(field_offset); - // Check that we dont lose any non 0 bits. - DCHECK_EQ(static_cast<int64_t>(static_cast<uintptr_t>(v)), v); - return reinterpret_cast<T>(static_cast<uintptr_t>(v)); + return reinterpret_cast64<T>(v); } } |