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.
FixUpArguments(HInvokeStaticOrDirect * invoke)31 static void FixUpArguments(HInvokeStaticOrDirect* invoke) {
32   DCHECK_EQ(invoke->GetCodePtrLocation(), CodePtrLocation::kCallCriticalNative);
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);
52       DCHECK(resolved_method->IsIntrinsic());
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 = {
61           MethodLoadKind::kBssEntry,
62           CodePtrLocation::kCallArtMethod,
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(),
72           /*method_reference=*/ MethodReference(nullptr, dex::kDexNoIndex),
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);
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 
Run()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() ==
100               CodePtrLocation::kCallCriticalNative) {
101         FixUpArguments(instruction->AsInvokeStaticOrDirect());
102       }
103     }
104   }
105   return true;
106 }
107 
108 }  // namespace arm
109 }  // namespace art
110