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