1 /*
2  * Copyright (C) 2011 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_LIBARTBASE_ARCH_INSTRUCTION_SET_H_
18 #define ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_
19 
20 #include <iosfwd>
21 #include <string>
22 
23 #include "base/enums.h"
24 #include "base/macros.h"
25 
26 namespace art {
27 
28 enum class InstructionSet {
29   kNone,
30   kArm,
31   kArm64,
32   kThumb2,
33   kX86,
34   kX86_64,
35   kLast = kX86_64
36 };
37 std::ostream& operator<<(std::ostream& os, InstructionSet rhs);
38 
39 #if defined(__arm__)
40 static constexpr InstructionSet kRuntimeISA = InstructionSet::kArm;
41 #elif defined(__aarch64__)
42 static constexpr InstructionSet kRuntimeISA = InstructionSet::kArm64;
43 #elif defined(__i386__)
44 static constexpr InstructionSet kRuntimeISA = InstructionSet::kX86;
45 #elif defined(__x86_64__)
46 static constexpr InstructionSet kRuntimeISA = InstructionSet::kX86_64;
47 #else
48 static constexpr InstructionSet kRuntimeISA = InstructionSet::kNone;
49 #endif
50 
51 // Architecture-specific pointer sizes
52 static constexpr PointerSize kArmPointerSize = PointerSize::k32;
53 static constexpr PointerSize kArm64PointerSize = PointerSize::k64;
54 static constexpr PointerSize kX86PointerSize = PointerSize::k32;
55 static constexpr PointerSize kX86_64PointerSize = PointerSize::k64;
56 
57 // ARM instruction alignment. ARM processors require code to be 4-byte aligned,
58 // but ARM ELF requires 8..
59 static constexpr size_t kArmAlignment = 8;
60 
61 // ARM64 instruction alignment. This is the recommended alignment for maximum performance.
62 static constexpr size_t kArm64Alignment = 16;
63 
64 // X86 instruction alignment. This is the recommended alignment for maximum performance.
65 static constexpr size_t kX86Alignment = 16;
66 
67 // Different than code alignment since code alignment is only first instruction of method.
68 static constexpr size_t kThumb2InstructionAlignment = 2;
69 static constexpr size_t kArm64InstructionAlignment = 4;
70 static constexpr size_t kX86InstructionAlignment = 1;
71 static constexpr size_t kX86_64InstructionAlignment = 1;
72 
73 const char* GetInstructionSetString(InstructionSet isa);
74 
75 // Note: Returns kNone when the string cannot be parsed to a known value.
76 InstructionSet GetInstructionSetFromString(const char* instruction_set);
77 
78 // Fatal logging out of line to keep the header clean of logging.h.
79 NO_RETURN void InstructionSetAbort(InstructionSet isa);
80 
GetInstructionSetPointerSize(InstructionSet isa)81 constexpr PointerSize GetInstructionSetPointerSize(InstructionSet isa) {
82   switch (isa) {
83     case InstructionSet::kArm:
84       // Fall-through.
85     case InstructionSet::kThumb2:
86       return kArmPointerSize;
87     case InstructionSet::kArm64:
88       return kArm64PointerSize;
89     case InstructionSet::kX86:
90       return kX86PointerSize;
91     case InstructionSet::kX86_64:
92       return kX86_64PointerSize;
93 
94     case InstructionSet::kNone:
95       break;
96   }
97   InstructionSetAbort(isa);
98 }
99 
GetInstructionSetInstructionAlignment(InstructionSet isa)100 constexpr size_t GetInstructionSetInstructionAlignment(InstructionSet isa) {
101   switch (isa) {
102     case InstructionSet::kArm:
103       // Fall-through.
104     case InstructionSet::kThumb2:
105       return kThumb2InstructionAlignment;
106     case InstructionSet::kArm64:
107       return kArm64InstructionAlignment;
108     case InstructionSet::kX86:
109       return kX86InstructionAlignment;
110     case InstructionSet::kX86_64:
111       return kX86_64InstructionAlignment;
112 
113     case InstructionSet::kNone:
114       break;
115   }
116   InstructionSetAbort(isa);
117 }
118 
IsValidInstructionSet(InstructionSet isa)119 constexpr bool IsValidInstructionSet(InstructionSet isa) {
120   switch (isa) {
121     case InstructionSet::kArm:
122     case InstructionSet::kThumb2:
123     case InstructionSet::kArm64:
124     case InstructionSet::kX86:
125     case InstructionSet::kX86_64:
126       return true;
127 
128     case InstructionSet::kNone:
129       return false;
130   }
131   return false;
132 }
133 
134 size_t GetInstructionSetAlignment(InstructionSet isa);
135 
Is64BitInstructionSet(InstructionSet isa)136 constexpr bool Is64BitInstructionSet(InstructionSet isa) {
137   switch (isa) {
138     case InstructionSet::kArm:
139     case InstructionSet::kThumb2:
140     case InstructionSet::kX86:
141       return false;
142 
143     case InstructionSet::kArm64:
144     case InstructionSet::kX86_64:
145       return true;
146 
147     case InstructionSet::kNone:
148       break;
149   }
150   InstructionSetAbort(isa);
151 }
152 
InstructionSetPointerSize(InstructionSet isa)153 constexpr PointerSize InstructionSetPointerSize(InstructionSet isa) {
154   return Is64BitInstructionSet(isa) ? PointerSize::k64 : PointerSize::k32;
155 }
156 
GetBytesPerGprSpillLocation(InstructionSet isa)157 constexpr size_t GetBytesPerGprSpillLocation(InstructionSet isa) {
158   switch (isa) {
159     case InstructionSet::kArm:
160       // Fall-through.
161     case InstructionSet::kThumb2:
162       return 4;
163     case InstructionSet::kArm64:
164       return 8;
165     case InstructionSet::kX86:
166       return 4;
167     case InstructionSet::kX86_64:
168       return 8;
169 
170     case InstructionSet::kNone:
171       break;
172   }
173   InstructionSetAbort(isa);
174 }
175 
GetBytesPerFprSpillLocation(InstructionSet isa)176 constexpr size_t GetBytesPerFprSpillLocation(InstructionSet isa) {
177   switch (isa) {
178     case InstructionSet::kArm:
179       // Fall-through.
180     case InstructionSet::kThumb2:
181       return 4;
182     case InstructionSet::kArm64:
183       return 8;
184     case InstructionSet::kX86:
185       return 8;
186     case InstructionSet::kX86_64:
187       return 8;
188 
189     case InstructionSet::kNone:
190       break;
191   }
192   InstructionSetAbort(isa);
193 }
194 
195 namespace instruction_set_details {
196 
197 #if !defined(ART_STACK_OVERFLOW_GAP_arm) || !defined(ART_STACK_OVERFLOW_GAP_arm64) || \
198     !defined(ART_STACK_OVERFLOW_GAP_x86) || !defined(ART_STACK_OVERFLOW_GAP_x86_64)
199 #error "Missing defines for stack overflow gap"
200 #endif
201 
202 static constexpr size_t kArmStackOverflowReservedBytes    = ART_STACK_OVERFLOW_GAP_arm;
203 static constexpr size_t kArm64StackOverflowReservedBytes  = ART_STACK_OVERFLOW_GAP_arm64;
204 static constexpr size_t kX86StackOverflowReservedBytes    = ART_STACK_OVERFLOW_GAP_x86;
205 static constexpr size_t kX86_64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_x86_64;
206 
207 NO_RETURN void GetStackOverflowReservedBytesFailure(const char* error_msg);
208 
209 }  // namespace instruction_set_details
210 
211 ALWAYS_INLINE
GetStackOverflowReservedBytes(InstructionSet isa)212 constexpr size_t GetStackOverflowReservedBytes(InstructionSet isa) {
213   switch (isa) {
214     case InstructionSet::kArm:      // Intentional fall-through.
215     case InstructionSet::kThumb2:
216       return instruction_set_details::kArmStackOverflowReservedBytes;
217 
218     case InstructionSet::kArm64:
219       return instruction_set_details::kArm64StackOverflowReservedBytes;
220 
221     case InstructionSet::kX86:
222       return instruction_set_details::kX86StackOverflowReservedBytes;
223 
224     case InstructionSet::kX86_64:
225       return instruction_set_details::kX86_64StackOverflowReservedBytes;
226 
227     case InstructionSet::kNone:
228       instruction_set_details::GetStackOverflowReservedBytesFailure(
229           "kNone has no stack overflow size");
230   }
231   instruction_set_details::GetStackOverflowReservedBytesFailure("Unknown instruction set");
232 }
233 
234 // The following definitions create return types for two word-sized entities that will be passed
235 // in registers so that memory operations for the interface trampolines can be avoided. The entities
236 // are the resolved method and the pointer to the code to be invoked.
237 //
238 // On x86 and ARM32, this is given for a *scalar* 64bit value. The definition thus *must* be
239 // uint64_t or long long int.
240 //
241 // On x86_64 and ARM64, structs are decomposed for allocation, so we can create a structs of
242 // two size_t-sized values.
243 //
244 // We need two operations:
245 //
246 // 1) A flag value that signals failure. The assembly stubs expect the lower part to be "0".
247 //    GetTwoWordFailureValue() will return a value that has lower part == 0.
248 //
249 // 2) A value that combines two word-sized values.
250 //    GetTwoWordSuccessValue() constructs this.
251 //
252 // IMPORTANT: If you use this to transfer object pointers, it is your responsibility to ensure
253 //            that the object does not move or the value is updated. Simple use of this is NOT SAFE
254 //            when the garbage collector can move objects concurrently. Ensure that required locks
255 //            are held when using!
256 
257 #if defined(__i386__) || defined(__arm__)
258 typedef uint64_t TwoWordReturn;
259 
260 // Encodes method_ptr==nullptr and code_ptr==nullptr
GetTwoWordFailureValue()261 static inline constexpr TwoWordReturn GetTwoWordFailureValue() {
262   return 0;
263 }
264 
265 // Use the lower 32b for the method pointer and the upper 32b for the code pointer.
GetTwoWordSuccessValue(uintptr_t hi,uintptr_t lo)266 static inline constexpr TwoWordReturn GetTwoWordSuccessValue(uintptr_t hi, uintptr_t lo) {
267   static_assert(sizeof(uint32_t) == sizeof(uintptr_t), "Unexpected size difference");
268   uint32_t lo32 = lo;
269   uint64_t hi64 = static_cast<uint64_t>(hi);
270   return ((hi64 << 32) | lo32);
271 }
272 
273 #elif defined(__x86_64__) || defined(__aarch64__)
274 
275 // Note: TwoWordReturn can't be constexpr for 64-bit targets. We'd need a constexpr constructor,
276 //       which would violate C-linkage in the entrypoint functions.
277 
278 struct TwoWordReturn {
279   uintptr_t lo;
280   uintptr_t hi;
281 };
282 
283 // Encodes method_ptr==nullptr. Leaves random value in code pointer.
GetTwoWordFailureValue()284 static inline TwoWordReturn GetTwoWordFailureValue() {
285   TwoWordReturn ret;
286   ret.lo = 0;
287   return ret;
288 }
289 
290 // Write values into their respective members.
GetTwoWordSuccessValue(uintptr_t hi,uintptr_t lo)291 static inline TwoWordReturn GetTwoWordSuccessValue(uintptr_t hi, uintptr_t lo) {
292   TwoWordReturn ret;
293   ret.lo = lo;
294   ret.hi = hi;
295   return ret;
296 }
297 #else
298 #error "Unsupported architecture"
299 #endif
300 
301 }  // namespace art
302 
303 #endif  // ART_LIBARTBASE_ARCH_INSTRUCTION_SET_H_
304