1 /*
2  * Copyright (C) 2015 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 #ifndef ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_
18 #define ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_
19 
20 #include <deque>
21 #include <vector>
22 
23 #include "linker/relative_patcher.h"
24 #include "method_reference.h"
25 #include "safe_map.h"
26 
27 namespace art {
28 namespace linker {
29 
30 class ArmBaseRelativePatcher : public RelativePatcher {
31  public:
32   uint32_t ReserveSpace(uint32_t offset,
33                         const CompiledMethod* compiled_method,
34                         MethodReference method_ref) OVERRIDE;
35   uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
36   uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
37 
38  protected:
39   ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider,
40                          InstructionSet instruction_set);
41   ~ArmBaseRelativePatcher();
42 
43   enum class ThunkType {
44     kMethodCall,              // Method call thunk.
45     kBakerReadBarrierField,   // Baker read barrier, load field or array element at known offset.
46     kBakerReadBarrierRoot,    // Baker read barrier, GC root load.
47   };
48 
49   struct BakerReadBarrierOffsetParams {
50     uint32_t holder_reg;      // Holder object for reading lock word.
51     uint32_t base_reg;        // Base register, different from holder for large offset.
52                               // If base differs from holder, it should be a pre-defined
53                               // register to limit the number of thunks we need to emit.
54                               // The offset is retrieved using introspection.
55   };
56 
57   struct BakerReadBarrierRootParams {
58     uint32_t root_reg;        // The register holding the GC root.
59     uint32_t dummy;
60   };
61 
62   struct RawThunkParams {
63     uint32_t first;
64     uint32_t second;
65   };
66 
67   union ThunkParams {
68     RawThunkParams raw_params;
69     BakerReadBarrierOffsetParams offset_params;
70     BakerReadBarrierRootParams root_params;
71   };
72 
73   class ThunkKey {
74    public:
ThunkKey(ThunkType type,ThunkParams params)75     ThunkKey(ThunkType type, ThunkParams params) : type_(type), params_(params) { }
76 
GetType()77     ThunkType GetType() const {
78       return type_;
79     }
80 
GetOffsetParams()81     BakerReadBarrierOffsetParams GetOffsetParams() const {
82       DCHECK(type_ == ThunkType::kBakerReadBarrierField);
83       return params_.offset_params;
84     }
85 
GetRootParams()86     BakerReadBarrierRootParams GetRootParams() const {
87       DCHECK(type_ == ThunkType::kBakerReadBarrierRoot);
88       return params_.root_params;
89     }
90 
GetRawParams()91     RawThunkParams GetRawParams() const {
92       return params_.raw_params;
93     }
94 
95    private:
96     ThunkType type_;
97     ThunkParams params_;
98   };
99 
100   class ThunkKeyCompare {
101    public:
operator()102     bool operator()(const ThunkKey& lhs, const ThunkKey& rhs) const {
103       if (lhs.GetType() != rhs.GetType()) {
104         return lhs.GetType() < rhs.GetType();
105       }
106       if (lhs.GetRawParams().first != rhs.GetRawParams().first) {
107         return lhs.GetRawParams().first < rhs.GetRawParams().first;
108       }
109       return lhs.GetRawParams().second < rhs.GetRawParams().second;
110     }
111   };
112 
113   uint32_t ReserveSpaceInternal(uint32_t offset,
114                                 const CompiledMethod* compiled_method,
115                                 MethodReference method_ref,
116                                 uint32_t max_extra_space);
117   uint32_t GetThunkTargetOffset(const ThunkKey& key, uint32_t patch_offset);
118 
119   uint32_t CalculateMethodCallDisplacement(uint32_t patch_offset,
120                                            uint32_t target_offset);
121 
122   virtual ThunkKey GetBakerReadBarrierKey(const LinkerPatch& patch) = 0;
123   virtual std::vector<uint8_t> CompileThunk(const ThunkKey& key) = 0;
124   virtual uint32_t MaxPositiveDisplacement(ThunkType type) = 0;
125   virtual uint32_t MaxNegativeDisplacement(ThunkType type) = 0;
126 
127  private:
128   class ThunkData;
129 
130   void ProcessPatches(const CompiledMethod* compiled_method, uint32_t code_offset);
131   void AddUnreservedThunk(ThunkData* data);
132 
133   void ResolveMethodCalls(uint32_t quick_code_offset, MethodReference method_ref);
134 
135   uint32_t CalculateMaxNextOffset(uint32_t patch_offset, ThunkType type);
136 
137   RelativePatcherTargetProvider* const provider_;
138   const InstructionSet instruction_set_;
139 
140   // The data for all thunks.
141   // SafeMap<> nodes don't move after being inserted, so we can use direct pointers to the data.
142   using ThunkMap = SafeMap<ThunkKey, ThunkData, ThunkKeyCompare>;
143   ThunkMap thunks_;
144 
145   // ReserveSpace() tracks unprocessed method call patches. These may be resolved later.
146   class UnprocessedMethodCallPatch {
147    public:
UnprocessedMethodCallPatch(uint32_t patch_offset,MethodReference target_method)148     UnprocessedMethodCallPatch(uint32_t patch_offset, MethodReference target_method)
149         : patch_offset_(patch_offset), target_method_(target_method) { }
150 
GetPatchOffset()151     uint32_t GetPatchOffset() const {
152       return patch_offset_;
153     }
154 
GetTargetMethod()155     MethodReference GetTargetMethod() const {
156       return target_method_;
157     }
158 
159    private:
160     uint32_t patch_offset_;
161     MethodReference target_method_;
162   };
163   std::deque<UnprocessedMethodCallPatch> unprocessed_method_call_patches_;
164   // Once we have compiled a method call thunk, cache pointer to the data.
165   ThunkData* method_call_thunk_;
166 
167   // Thunks
168   std::deque<ThunkData*> unreserved_thunks_;
169 
170   class PendingThunkComparator;
171   std::vector<ThunkData*> pending_thunks_;  // Heap with the PendingThunkComparator.
172 
173   friend class Arm64RelativePatcherTest;
174   friend class Thumb2RelativePatcherTest;
175 
176   DISALLOW_COPY_AND_ASSIGN(ArmBaseRelativePatcher);
177 };
178 
179 }  // namespace linker
180 }  // namespace art
181 
182 #endif  // ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_
183