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#include "intrinsics.h" 18 19#include "dex/quick/dex_file_method_inliner.h" 20#include "dex/quick/dex_file_to_method_inliner_map.h" 21#include "driver/compiler_driver.h" 22#include "invoke_type.h" 23#include "nodes.h" 24#include "quick/inline_method_analyser.h" 25#include "utils.h" 26 27namespace art { 28 29// Function that returns whether an intrinsic is static/direct or virtual. 30static inline InvokeType GetIntrinsicInvokeType(Intrinsics i) { 31 switch (i) { 32 case Intrinsics::kNone: 33 return kInterface; // Non-sensical for intrinsic. 34#define OPTIMIZING_INTRINSICS(Name, IsStatic) \ 35 case Intrinsics::k ## Name: \ 36 return IsStatic; 37#include "intrinsics_list.h" 38INTRINSICS_LIST(OPTIMIZING_INTRINSICS) 39#undef INTRINSICS_LIST 40#undef OPTIMIZING_INTRINSICS 41 } 42 return kInterface; 43} 44 45 46 47static Primitive::Type GetType(uint64_t data, bool is_op_size) { 48 if (is_op_size) { 49 switch (static_cast<OpSize>(data)) { 50 case kSignedByte: 51 return Primitive::kPrimByte; 52 case kSignedHalf: 53 return Primitive::kPrimShort; 54 case k32: 55 return Primitive::kPrimInt; 56 case k64: 57 return Primitive::kPrimLong; 58 default: 59 LOG(FATAL) << "Unknown/unsupported op size " << data; 60 UNREACHABLE(); 61 } 62 } else { 63 if ((data & kIntrinsicFlagIsLong) != 0) { 64 return Primitive::kPrimLong; 65 } 66 if ((data & kIntrinsicFlagIsObject) != 0) { 67 return Primitive::kPrimNot; 68 } 69 return Primitive::kPrimInt; 70 } 71} 72 73static Intrinsics GetIntrinsic(InlineMethod method) { 74 switch (method.opcode) { 75 // Floating-point conversions. 76 case kIntrinsicDoubleCvt: 77 return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ? 78 Intrinsics::kDoubleDoubleToRawLongBits : Intrinsics::kDoubleLongBitsToDouble; 79 case kIntrinsicFloatCvt: 80 return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ? 81 Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat; 82 83 // Bit manipulations. 84 case kIntrinsicReverseBits: 85 switch (GetType(method.d.data, true)) { 86 case Primitive::kPrimInt: 87 return Intrinsics::kIntegerReverse; 88 case Primitive::kPrimLong: 89 return Intrinsics::kLongReverse; 90 default: 91 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 92 UNREACHABLE(); 93 } 94 case kIntrinsicReverseBytes: 95 switch (GetType(method.d.data, true)) { 96 case Primitive::kPrimShort: 97 return Intrinsics::kShortReverseBytes; 98 case Primitive::kPrimInt: 99 return Intrinsics::kIntegerReverseBytes; 100 case Primitive::kPrimLong: 101 return Intrinsics::kLongReverseBytes; 102 default: 103 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 104 UNREACHABLE(); 105 } 106 107 // Abs. 108 case kIntrinsicAbsDouble: 109 return Intrinsics::kMathAbsDouble; 110 case kIntrinsicAbsFloat: 111 return Intrinsics::kMathAbsFloat; 112 case kIntrinsicAbsInt: 113 return Intrinsics::kMathAbsInt; 114 case kIntrinsicAbsLong: 115 return Intrinsics::kMathAbsLong; 116 117 // Min/max. 118 case kIntrinsicMinMaxDouble: 119 return ((method.d.data & kIntrinsicFlagMin) == 0) ? 120 Intrinsics::kMathMaxDoubleDouble : Intrinsics::kMathMinDoubleDouble; 121 case kIntrinsicMinMaxFloat: 122 return ((method.d.data & kIntrinsicFlagMin) == 0) ? 123 Intrinsics::kMathMaxFloatFloat : Intrinsics::kMathMinFloatFloat; 124 case kIntrinsicMinMaxInt: 125 return ((method.d.data & kIntrinsicFlagMin) == 0) ? 126 Intrinsics::kMathMaxIntInt : Intrinsics::kMathMinIntInt; 127 case kIntrinsicMinMaxLong: 128 return ((method.d.data & kIntrinsicFlagMin) == 0) ? 129 Intrinsics::kMathMaxLongLong : Intrinsics::kMathMinLongLong; 130 131 // Misc math. 132 case kIntrinsicSqrt: 133 return Intrinsics::kMathSqrt; 134 case kIntrinsicCeil: 135 return Intrinsics::kMathCeil; 136 case kIntrinsicFloor: 137 return Intrinsics::kMathFloor; 138 case kIntrinsicRint: 139 return Intrinsics::kMathRint; 140 case kIntrinsicRoundDouble: 141 return Intrinsics::kMathRoundDouble; 142 case kIntrinsicRoundFloat: 143 return Intrinsics::kMathRoundFloat; 144 145 // System.arraycopy. 146 case kIntrinsicSystemArrayCopyCharArray: 147 return Intrinsics::kSystemArrayCopyChar; 148 149 // Thread.currentThread. 150 case kIntrinsicCurrentThread: 151 return Intrinsics::kThreadCurrentThread; 152 153 // Memory.peek. 154 case kIntrinsicPeek: 155 switch (GetType(method.d.data, true)) { 156 case Primitive::kPrimByte: 157 return Intrinsics::kMemoryPeekByte; 158 case Primitive::kPrimShort: 159 return Intrinsics::kMemoryPeekShortNative; 160 case Primitive::kPrimInt: 161 return Intrinsics::kMemoryPeekIntNative; 162 case Primitive::kPrimLong: 163 return Intrinsics::kMemoryPeekLongNative; 164 default: 165 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 166 UNREACHABLE(); 167 } 168 169 // Memory.poke. 170 case kIntrinsicPoke: 171 switch (GetType(method.d.data, true)) { 172 case Primitive::kPrimByte: 173 return Intrinsics::kMemoryPokeByte; 174 case Primitive::kPrimShort: 175 return Intrinsics::kMemoryPokeShortNative; 176 case Primitive::kPrimInt: 177 return Intrinsics::kMemoryPokeIntNative; 178 case Primitive::kPrimLong: 179 return Intrinsics::kMemoryPokeLongNative; 180 default: 181 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 182 UNREACHABLE(); 183 } 184 185 // String. 186 case kIntrinsicCharAt: 187 return Intrinsics::kStringCharAt; 188 case kIntrinsicCompareTo: 189 return Intrinsics::kStringCompareTo; 190 case kIntrinsicGetCharsNoCheck: 191 return Intrinsics::kStringGetCharsNoCheck; 192 case kIntrinsicIsEmptyOrLength: 193 // The inliner can handle these two cases - and this is the preferred approach 194 // since after inlining the call is no longer visible (as opposed to waiting 195 // until codegen to handle intrinsic). 196 return Intrinsics::kNone; 197 case kIntrinsicIndexOf: 198 return ((method.d.data & kIntrinsicFlagBase0) == 0) ? 199 Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf; 200 case kIntrinsicNewStringFromBytes: 201 return Intrinsics::kStringNewStringFromBytes; 202 case kIntrinsicNewStringFromChars: 203 return Intrinsics::kStringNewStringFromChars; 204 case kIntrinsicNewStringFromString: 205 return Intrinsics::kStringNewStringFromString; 206 207 case kIntrinsicCas: 208 switch (GetType(method.d.data, false)) { 209 case Primitive::kPrimNot: 210 return Intrinsics::kUnsafeCASObject; 211 case Primitive::kPrimInt: 212 return Intrinsics::kUnsafeCASInt; 213 case Primitive::kPrimLong: 214 return Intrinsics::kUnsafeCASLong; 215 default: 216 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 217 UNREACHABLE(); 218 } 219 case kIntrinsicUnsafeGet: { 220 const bool is_volatile = (method.d.data & kIntrinsicFlagIsVolatile); 221 switch (GetType(method.d.data, false)) { 222 case Primitive::kPrimInt: 223 return is_volatile ? Intrinsics::kUnsafeGetVolatile : Intrinsics::kUnsafeGet; 224 case Primitive::kPrimLong: 225 return is_volatile ? Intrinsics::kUnsafeGetLongVolatile : Intrinsics::kUnsafeGetLong; 226 case Primitive::kPrimNot: 227 return is_volatile ? Intrinsics::kUnsafeGetObjectVolatile : Intrinsics::kUnsafeGetObject; 228 default: 229 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 230 UNREACHABLE(); 231 } 232 } 233 case kIntrinsicUnsafePut: { 234 enum Sync { kNoSync, kVolatile, kOrdered }; 235 const Sync sync = 236 ((method.d.data & kIntrinsicFlagIsVolatile) != 0) ? kVolatile : 237 ((method.d.data & kIntrinsicFlagIsOrdered) != 0) ? kOrdered : 238 kNoSync; 239 switch (GetType(method.d.data, false)) { 240 case Primitive::kPrimInt: 241 switch (sync) { 242 case kNoSync: 243 return Intrinsics::kUnsafePut; 244 case kVolatile: 245 return Intrinsics::kUnsafePutVolatile; 246 case kOrdered: 247 return Intrinsics::kUnsafePutOrdered; 248 } 249 break; 250 case Primitive::kPrimLong: 251 switch (sync) { 252 case kNoSync: 253 return Intrinsics::kUnsafePutLong; 254 case kVolatile: 255 return Intrinsics::kUnsafePutLongVolatile; 256 case kOrdered: 257 return Intrinsics::kUnsafePutLongOrdered; 258 } 259 break; 260 case Primitive::kPrimNot: 261 switch (sync) { 262 case kNoSync: 263 return Intrinsics::kUnsafePutObject; 264 case kVolatile: 265 return Intrinsics::kUnsafePutObjectVolatile; 266 case kOrdered: 267 return Intrinsics::kUnsafePutObjectOrdered; 268 } 269 break; 270 default: 271 LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; 272 UNREACHABLE(); 273 } 274 break; 275 } 276 277 // Virtual cases. 278 279 case kIntrinsicReferenceGetReferent: 280 return Intrinsics::kReferenceGetReferent; 281 282 // Quick inliner cases. Remove after refactoring. They are here so that we can use the 283 // compiler to warn on missing cases. 284 285 case kInlineOpNop: 286 case kInlineOpReturnArg: 287 case kInlineOpNonWideConst: 288 case kInlineOpIGet: 289 case kInlineOpIPut: 290 return Intrinsics::kNone; 291 292 // String init cases, not intrinsics. 293 294 case kInlineStringInit: 295 return Intrinsics::kNone; 296 297 // No default case to make the compiler warn on missing cases. 298 } 299 return Intrinsics::kNone; 300} 301 302static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) { 303 // The DexFileMethodInliner should have checked whether the methods are agreeing with 304 // what we expect, i.e., static methods are called as such. Add another check here for 305 // our expectations: 306 // Whenever the intrinsic is marked as static-or-direct, report an error if we find an 307 // InvokeVirtual. The other direction is not possible: we have intrinsics for virtual 308 // functions that will perform a check inline. If the precise type is known, however, 309 // the instruction will be sharpened to an InvokeStaticOrDirect. 310 InvokeType intrinsic_type = GetIntrinsicInvokeType(intrinsic); 311 InvokeType invoke_type = invoke->IsInvokeStaticOrDirect() ? 312 invoke->AsInvokeStaticOrDirect()->GetInvokeType() : 313 invoke->IsInvokeVirtual() ? kVirtual : kSuper; 314 switch (intrinsic_type) { 315 case kStatic: 316 return (invoke_type == kStatic); 317 case kDirect: 318 return (invoke_type == kDirect); 319 case kVirtual: 320 // Call might be devirtualized. 321 return (invoke_type == kVirtual || invoke_type == kDirect); 322 323 default: 324 return false; 325 } 326} 327 328// TODO: Refactor DexFileMethodInliner and have something nicer than InlineMethod. 329void IntrinsicsRecognizer::Run() { 330 DexFileMethodInliner* inliner = driver_->GetMethodInlinerMap()->GetMethodInliner(dex_file_); 331 DCHECK(inliner != nullptr); 332 333 for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { 334 HBasicBlock* block = it.Current(); 335 for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done(); 336 inst_it.Advance()) { 337 HInstruction* inst = inst_it.Current(); 338 if (inst->IsInvoke()) { 339 HInvoke* invoke = inst->AsInvoke(); 340 InlineMethod method; 341 if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) { 342 Intrinsics intrinsic = GetIntrinsic(method); 343 344 if (intrinsic != Intrinsics::kNone) { 345 if (!CheckInvokeType(intrinsic, invoke)) { 346 LOG(WARNING) << "Found an intrinsic with unexpected invoke type: " 347 << intrinsic << " for " 348 << PrettyMethod(invoke->GetDexMethodIndex(), *dex_file_); 349 } else { 350 invoke->SetIntrinsic(intrinsic); 351 } 352 } 353 } 354 } 355 } 356 } 357} 358 359std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) { 360 switch (intrinsic) { 361 case Intrinsics::kNone: 362 os << "No intrinsic."; 363 break; 364#define OPTIMIZING_INTRINSICS(Name, IsStatic) \ 365 case Intrinsics::k ## Name: \ 366 os << # Name; \ 367 break; 368#include "intrinsics_list.h" 369INTRINSICS_LIST(OPTIMIZING_INTRINSICS) 370#undef STATIC_INTRINSICS_LIST 371#undef VIRTUAL_INTRINSICS_LIST 372#undef OPTIMIZING_INTRINSICS 373 } 374 return os; 375} 376 377} // namespace art 378