Vladimir Marko | d3e9c62 | 2020-08-05 12:20:28 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2020 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 "critical_native_abi_fixup_arm.h" |
| 18 | |
| 19 | #include "art_method-inl.h" |
| 20 | #include "intrinsics.h" |
| 21 | #include "jni/jni_internal.h" |
| 22 | #include "nodes.h" |
| 23 | #include "scoped_thread_state_change-inl.h" |
| 24 | #include "well_known_classes.h" |
| 25 | |
| 26 | namespace art { |
| 27 | namespace arm { |
| 28 | |
| 29 | // Fix up FP arguments passed in core registers for call to @CriticalNative by inserting fake calls |
| 30 | // to Float.floatToRawIntBits() or Double.doubleToRawLongBits() to satisfy type consistency checks. |
| 31 | static void FixUpArguments(HInvokeStaticOrDirect* invoke) { |
Nicolas Geoffray | 6d69b52 | 2020-09-23 14:47:28 +0100 | [diff] [blame] | 32 | DCHECK_EQ(invoke->GetCodePtrLocation(), CodePtrLocation::kCallCriticalNative); |
Vladimir Marko | d3e9c62 | 2020-08-05 12:20:28 +0100 | [diff] [blame] | 33 | size_t reg = 0u; |
| 34 | for (size_t i = 0, num_args = invoke->GetNumberOfArguments(); i != num_args; ++i) { |
| 35 | HInstruction* input = invoke->InputAt(i); |
| 36 | DataType::Type input_type = input->GetType(); |
| 37 | size_t next_reg = reg + 1u; |
| 38 | if (DataType::Is64BitType(input_type)) { |
| 39 | reg = RoundUp(reg, 2u); |
| 40 | next_reg = reg + 2u; |
| 41 | } |
| 42 | if (reg == 4u) { |
| 43 | break; // Remaining arguments are passed on stack. |
| 44 | } |
| 45 | if (DataType::IsFloatingPointType(input_type)) { |
| 46 | bool is_double = (input_type == DataType::Type::kFloat64); |
| 47 | DataType::Type converted_type = is_double ? DataType::Type::kInt64 : DataType::Type::kInt32; |
| 48 | jmethodID known_method = is_double ? WellKnownClasses::java_lang_Double_doubleToRawLongBits |
| 49 | : WellKnownClasses::java_lang_Float_floatToRawIntBits; |
| 50 | ArtMethod* resolved_method = jni::DecodeArtMethod(known_method); |
| 51 | DCHECK(resolved_method != nullptr); |
Vladimir Marko | d429026 | 2021-05-21 16:36:23 +0100 | [diff] [blame] | 52 | DCHECK(resolved_method->IsIntrinsic()); |
Vladimir Marko | d3e9c62 | 2020-08-05 12:20:28 +0100 | [diff] [blame] | 53 | MethodReference target_method(nullptr, 0); |
| 54 | { |
| 55 | ScopedObjectAccess soa(Thread::Current()); |
| 56 | target_method = |
| 57 | MethodReference(resolved_method->GetDexFile(), resolved_method->GetDexMethodIndex()); |
| 58 | } |
| 59 | // Use arbitrary dispatch info that does not require the method argument. |
| 60 | HInvokeStaticOrDirect::DispatchInfo dispatch_info = { |
Nicolas Geoffray | 6d69b52 | 2020-09-23 14:47:28 +0100 | [diff] [blame] | 61 | MethodLoadKind::kBssEntry, |
| 62 | CodePtrLocation::kCallArtMethod, |
Vladimir Marko | d3e9c62 | 2020-08-05 12:20:28 +0100 | [diff] [blame] | 63 | /*method_load_data=*/ 0u |
| 64 | }; |
| 65 | HBasicBlock* block = invoke->GetBlock(); |
| 66 | ArenaAllocator* allocator = block->GetGraph()->GetAllocator(); |
| 67 | HInvokeStaticOrDirect* new_input = new (allocator) HInvokeStaticOrDirect( |
| 68 | allocator, |
| 69 | /*number_of_arguments=*/ 1u, |
| 70 | converted_type, |
| 71 | invoke->GetDexPc(), |
Nicolas Geoffray | e6c0f2a | 2020-09-07 08:30:52 +0100 | [diff] [blame] | 72 | /*method_reference=*/ MethodReference(nullptr, dex::kDexNoIndex), |
Vladimir Marko | d3e9c62 | 2020-08-05 12:20:28 +0100 | [diff] [blame] | 73 | resolved_method, |
| 74 | dispatch_info, |
| 75 | kStatic, |
| 76 | target_method, |
| 77 | HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); |
| 78 | // The intrinsic has no side effects and does not need environment or dex cache on ARM. |
| 79 | new_input->SetSideEffects(SideEffects::None()); |
| 80 | IntrinsicOptimizations opt(new_input); |
Vladimir Marko | d3e9c62 | 2020-08-05 12:20:28 +0100 | [diff] [blame] | 81 | opt.SetDoesNotNeedEnvironment(); |
| 82 | new_input->SetRawInputAt(0u, input); |
| 83 | block->InsertInstructionBefore(new_input, invoke); |
| 84 | invoke->ReplaceInput(new_input, i); |
| 85 | } |
| 86 | reg = next_reg; |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | bool CriticalNativeAbiFixupArm::Run() { |
| 91 | if (!graph_->HasDirectCriticalNativeCall()) { |
| 92 | return false; |
| 93 | } |
| 94 | |
| 95 | for (HBasicBlock* block : graph_->GetReversePostOrder()) { |
| 96 | for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { |
| 97 | HInstruction* instruction = it.Current(); |
| 98 | if (instruction->IsInvokeStaticOrDirect() && |
| 99 | instruction->AsInvokeStaticOrDirect()->GetCodePtrLocation() == |
Nicolas Geoffray | 6d69b52 | 2020-09-23 14:47:28 +0100 | [diff] [blame] | 100 | CodePtrLocation::kCallCriticalNative) { |
Vladimir Marko | d3e9c62 | 2020-08-05 12:20:28 +0100 | [diff] [blame] | 101 | FixUpArguments(instruction->AsInvokeStaticOrDirect()); |
| 102 | } |
| 103 | } |
| 104 | } |
| 105 | return true; |
| 106 | } |
| 107 | |
| 108 | } // namespace arm |
| 109 | } // namespace art |