| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 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_x86.h" | 
 | 18 |  | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 19 | #include <limits> | 
 | 20 |  | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 21 | #include "arch/x86/instruction_set_features_x86.h" | 
| Mathieu Chartier | e401d14 | 2015-04-22 13:56:20 -0700 | [diff] [blame] | 22 | #include "art_method.h" | 
| Mark Mendell | d589767 | 2015-08-12 21:16:41 -0400 | [diff] [blame] | 23 | #include "base/bit_utils.h" | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 24 | #include "code_generator_x86.h" | 
 | 25 | #include "entrypoints/quick/quick_entrypoints.h" | 
 | 26 | #include "intrinsics.h" | 
| Andreas Gampe | 85b62f2 | 2015-09-09 13:15:38 -0700 | [diff] [blame] | 27 | #include "intrinsics_utils.h" | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 28 | #include "mirror/array-inl.h" | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 29 | #include "mirror/string.h" | 
 | 30 | #include "thread.h" | 
 | 31 | #include "utils/x86/assembler_x86.h" | 
 | 32 | #include "utils/x86/constants_x86.h" | 
 | 33 |  | 
 | 34 | namespace art { | 
 | 35 |  | 
 | 36 | namespace x86 { | 
 | 37 |  | 
 | 38 | static constexpr int kDoubleNaNHigh = 0x7FF80000; | 
 | 39 | static constexpr int kDoubleNaNLow = 0x00000000; | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 40 | static constexpr int64_t kDoubleNaN = INT64_C(0x7FF8000000000000); | 
 | 41 | static constexpr int32_t kFloatNaN = INT32_C(0x7FC00000); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 42 |  | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 43 | IntrinsicLocationsBuilderX86::IntrinsicLocationsBuilderX86(CodeGeneratorX86* codegen) | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 44 |   : arena_(codegen->GetGraph()->GetArena()), | 
 | 45 |     codegen_(codegen) { | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 46 | } | 
 | 47 |  | 
 | 48 |  | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 49 | X86Assembler* IntrinsicCodeGeneratorX86::GetAssembler() { | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 50 |   return down_cast<X86Assembler*>(codegen_->GetAssembler()); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 51 | } | 
 | 52 |  | 
 | 53 | ArenaAllocator* IntrinsicCodeGeneratorX86::GetAllocator() { | 
 | 54 |   return codegen_->GetGraph()->GetArena(); | 
 | 55 | } | 
 | 56 |  | 
 | 57 | bool IntrinsicLocationsBuilderX86::TryDispatch(HInvoke* invoke) { | 
 | 58 |   Dispatch(invoke); | 
 | 59 |   LocationSummary* res = invoke->GetLocations(); | 
| Roland Levillain | 0d5a281 | 2015-11-13 10:07:31 +0000 | [diff] [blame] | 60 |   if (res == nullptr) { | 
 | 61 |     return false; | 
 | 62 |   } | 
| Roland Levillain | 0d5a281 | 2015-11-13 10:07:31 +0000 | [diff] [blame] | 63 |   return res->Intrinsified(); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 64 | } | 
 | 65 |  | 
| Roland Levillain | ec525fc | 2015-04-28 15:50:20 +0100 | [diff] [blame] | 66 | static void MoveArguments(HInvoke* invoke, CodeGeneratorX86* codegen) { | 
| Roland Levillain | 2d27c8e | 2015-04-28 15:48:45 +0100 | [diff] [blame] | 67 |   InvokeDexCallingConventionVisitorX86 calling_convention_visitor; | 
| Roland Levillain | ec525fc | 2015-04-28 15:50:20 +0100 | [diff] [blame] | 68 |   IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 69 | } | 
 | 70 |  | 
| Andreas Gampe | 85b62f2 | 2015-09-09 13:15:38 -0700 | [diff] [blame] | 71 | using IntrinsicSlowPathX86 = IntrinsicSlowPath<InvokeDexCallingConventionVisitorX86>; | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 72 |  | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 73 | // NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy. | 
 | 74 | #define __ down_cast<X86Assembler*>(codegen->GetAssembler())->  // NOLINT | 
 | 75 |  | 
 | 76 | // Slow path implementing the SystemArrayCopy intrinsic copy loop with read barriers. | 
 | 77 | class ReadBarrierSystemArrayCopySlowPathX86 : public SlowPathCode { | 
 | 78 |  public: | 
 | 79 |   explicit ReadBarrierSystemArrayCopySlowPathX86(HInstruction* instruction) | 
 | 80 |       : SlowPathCode(instruction) { | 
 | 81 |     DCHECK(kEmitCompilerReadBarrier); | 
 | 82 |     DCHECK(kUseBakerReadBarrier); | 
 | 83 |   } | 
 | 84 |  | 
 | 85 |   void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { | 
 | 86 |     CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); | 
 | 87 |     LocationSummary* locations = instruction_->GetLocations(); | 
 | 88 |     DCHECK(locations->CanCall()); | 
 | 89 |     DCHECK(instruction_->IsInvokeStaticOrDirect()) | 
 | 90 |         << "Unexpected instruction in read barrier arraycopy slow path: " | 
 | 91 |         << instruction_->DebugName(); | 
 | 92 |     DCHECK(instruction_->GetLocations()->Intrinsified()); | 
 | 93 |     DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kSystemArrayCopy); | 
 | 94 |  | 
 | 95 |     int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot); | 
 | 96 |     uint32_t offset = mirror::Array::DataOffset(element_size).Uint32Value(); | 
 | 97 |  | 
 | 98 |     Register src = locations->InAt(0).AsRegister<Register>(); | 
 | 99 |     Location src_pos = locations->InAt(1); | 
 | 100 |     Register dest = locations->InAt(2).AsRegister<Register>(); | 
 | 101 |     Location dest_pos = locations->InAt(3); | 
 | 102 |     Location length = locations->InAt(4); | 
 | 103 |     Location temp1_loc = locations->GetTemp(0); | 
 | 104 |     Register temp1 = temp1_loc.AsRegister<Register>(); | 
 | 105 |     Register temp2 = locations->GetTemp(1).AsRegister<Register>(); | 
 | 106 |     Register temp3 = locations->GetTemp(2).AsRegister<Register>(); | 
 | 107 |  | 
 | 108 |     __ Bind(GetEntryLabel()); | 
 | 109 |     // In this code path, registers `temp1`, `temp2`, and `temp3` | 
 | 110 |     // (resp.) are not used for the base source address, the base | 
 | 111 |     // destination address, and the end source address (resp.), as in | 
 | 112 |     // other SystemArrayCopy intrinsic code paths.  Instead they are | 
 | 113 |     // (resp.) used for: | 
 | 114 |     // - the loop index (`i`); | 
 | 115 |     // - the source index (`src_index`) and the loaded (source) | 
 | 116 |     //   reference (`value`); and | 
 | 117 |     // - the destination index (`dest_index`). | 
 | 118 |  | 
 | 119 |     // i = 0 | 
 | 120 |     __ xorl(temp1, temp1); | 
 | 121 |     NearLabel loop; | 
 | 122 |     __ Bind(&loop); | 
 | 123 |     // value = src_array[i + src_pos] | 
 | 124 |     if (src_pos.IsConstant()) { | 
 | 125 |       int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue(); | 
 | 126 |       int32_t adjusted_offset = offset + constant * element_size; | 
 | 127 |       __ movl(temp2, Address(src, temp1, ScaleFactor::TIMES_4, adjusted_offset)); | 
 | 128 |     } else { | 
 | 129 |       __ leal(temp2, Address(src_pos.AsRegister<Register>(), temp1, ScaleFactor::TIMES_1, 0)); | 
 | 130 |       __ movl(temp2, Address(src, temp2, ScaleFactor::TIMES_4, offset)); | 
 | 131 |     } | 
 | 132 |     __ MaybeUnpoisonHeapReference(temp2); | 
 | 133 |     // TODO: Inline the mark bit check before calling the runtime? | 
 | 134 |     // value = ReadBarrier::Mark(value) | 
 | 135 |     // No need to save live registers; it's taken care of by the | 
 | 136 |     // entrypoint. Also, there is no need to update the stack mask, | 
 | 137 |     // as this runtime call will not trigger a garbage collection. | 
 | 138 |     // (See ReadBarrierMarkSlowPathX86::EmitNativeCode for more | 
 | 139 |     // explanations.) | 
 | 140 |     DCHECK_NE(temp2, ESP); | 
 | 141 |     DCHECK(0 <= temp2 && temp2 < kNumberOfCpuRegisters) << temp2; | 
 | 142 |     int32_t entry_point_offset = | 
 | 143 |         CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86PointerSize>(temp2); | 
 | 144 |     // This runtime call does not require a stack map. | 
 | 145 |     x86_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this); | 
 | 146 |     __ MaybePoisonHeapReference(temp2); | 
 | 147 |     // dest_array[i + dest_pos] = value | 
 | 148 |     if (dest_pos.IsConstant()) { | 
 | 149 |       int32_t constant = dest_pos.GetConstant()->AsIntConstant()->GetValue(); | 
 | 150 |       int32_t adjusted_offset = offset + constant * element_size; | 
 | 151 |       __ movl(Address(dest, temp1, ScaleFactor::TIMES_4, adjusted_offset), temp2); | 
 | 152 |     } else { | 
 | 153 |       __ leal(temp3, Address(dest_pos.AsRegister<Register>(), temp1, ScaleFactor::TIMES_1, 0)); | 
 | 154 |       __ movl(Address(dest, temp3, ScaleFactor::TIMES_4, offset), temp2); | 
 | 155 |     } | 
 | 156 |     // ++i | 
 | 157 |     __ addl(temp1, Immediate(1)); | 
 | 158 |     // if (i != length) goto loop | 
 | 159 |     x86_codegen->GenerateIntCompare(temp1_loc, length); | 
 | 160 |     __ j(kNotEqual, &loop); | 
 | 161 |     __ jmp(GetExitLabel()); | 
 | 162 |   } | 
 | 163 |  | 
 | 164 |   const char* GetDescription() const OVERRIDE { return "ReadBarrierSystemArrayCopySlowPathX86"; } | 
 | 165 |  | 
 | 166 |  private: | 
 | 167 |   DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathX86); | 
 | 168 | }; | 
 | 169 |  | 
 | 170 | #undef __ | 
 | 171 |  | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 172 | #define __ assembler-> | 
 | 173 |  | 
 | 174 | static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke, bool is64bit) { | 
 | 175 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 176 |                                                            LocationSummary::kNoCall, | 
 | 177 |                                                            kIntrinsified); | 
 | 178 |   locations->SetInAt(0, Location::RequiresFpuRegister()); | 
 | 179 |   locations->SetOut(Location::RequiresRegister()); | 
 | 180 |   if (is64bit) { | 
 | 181 |     locations->AddTemp(Location::RequiresFpuRegister()); | 
 | 182 |   } | 
 | 183 | } | 
 | 184 |  | 
 | 185 | static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke, bool is64bit) { | 
 | 186 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 187 |                                                            LocationSummary::kNoCall, | 
 | 188 |                                                            kIntrinsified); | 
 | 189 |   locations->SetInAt(0, Location::RequiresRegister()); | 
 | 190 |   locations->SetOut(Location::RequiresFpuRegister()); | 
 | 191 |   if (is64bit) { | 
 | 192 |     locations->AddTemp(Location::RequiresFpuRegister()); | 
 | 193 |     locations->AddTemp(Location::RequiresFpuRegister()); | 
 | 194 |   } | 
 | 195 | } | 
 | 196 |  | 
 | 197 | static void MoveFPToInt(LocationSummary* locations, bool is64bit, X86Assembler* assembler) { | 
 | 198 |   Location input = locations->InAt(0); | 
 | 199 |   Location output = locations->Out(); | 
 | 200 |   if (is64bit) { | 
 | 201 |     // Need to use the temporary. | 
 | 202 |     XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); | 
 | 203 |     __ movsd(temp, input.AsFpuRegister<XmmRegister>()); | 
 | 204 |     __ movd(output.AsRegisterPairLow<Register>(), temp); | 
 | 205 |     __ psrlq(temp, Immediate(32)); | 
 | 206 |     __ movd(output.AsRegisterPairHigh<Register>(), temp); | 
 | 207 |   } else { | 
 | 208 |     __ movd(output.AsRegister<Register>(), input.AsFpuRegister<XmmRegister>()); | 
 | 209 |   } | 
 | 210 | } | 
 | 211 |  | 
 | 212 | static void MoveIntToFP(LocationSummary* locations, bool is64bit, X86Assembler* assembler) { | 
 | 213 |   Location input = locations->InAt(0); | 
 | 214 |   Location output = locations->Out(); | 
 | 215 |   if (is64bit) { | 
 | 216 |     // Need to use the temporary. | 
 | 217 |     XmmRegister temp1 = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); | 
 | 218 |     XmmRegister temp2 = locations->GetTemp(1).AsFpuRegister<XmmRegister>(); | 
 | 219 |     __ movd(temp1, input.AsRegisterPairLow<Register>()); | 
 | 220 |     __ movd(temp2, input.AsRegisterPairHigh<Register>()); | 
 | 221 |     __ punpckldq(temp1, temp2); | 
 | 222 |     __ movsd(output.AsFpuRegister<XmmRegister>(), temp1); | 
 | 223 |   } else { | 
 | 224 |     __ movd(output.AsFpuRegister<XmmRegister>(), input.AsRegister<Register>()); | 
 | 225 |   } | 
 | 226 | } | 
 | 227 |  | 
 | 228 | void IntrinsicLocationsBuilderX86::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 229 |   CreateFPToIntLocations(arena_, invoke, /* is64bit */ true); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 230 | } | 
 | 231 | void IntrinsicLocationsBuilderX86::VisitDoubleLongBitsToDouble(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 232 |   CreateIntToFPLocations(arena_, invoke, /* is64bit */ true); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 233 | } | 
 | 234 |  | 
 | 235 | void IntrinsicCodeGeneratorX86::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 236 |   MoveFPToInt(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 237 | } | 
 | 238 | void IntrinsicCodeGeneratorX86::VisitDoubleLongBitsToDouble(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 239 |   MoveIntToFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 240 | } | 
 | 241 |  | 
 | 242 | void IntrinsicLocationsBuilderX86::VisitFloatFloatToRawIntBits(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 243 |   CreateFPToIntLocations(arena_, invoke, /* is64bit */ false); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 244 | } | 
 | 245 | void IntrinsicLocationsBuilderX86::VisitFloatIntBitsToFloat(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 246 |   CreateIntToFPLocations(arena_, invoke, /* is64bit */ false); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 247 | } | 
 | 248 |  | 
 | 249 | void IntrinsicCodeGeneratorX86::VisitFloatFloatToRawIntBits(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 250 |   MoveFPToInt(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 251 | } | 
 | 252 | void IntrinsicCodeGeneratorX86::VisitFloatIntBitsToFloat(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 253 |   MoveIntToFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 254 | } | 
 | 255 |  | 
 | 256 | static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { | 
 | 257 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 258 |                                                            LocationSummary::kNoCall, | 
 | 259 |                                                            kIntrinsified); | 
 | 260 |   locations->SetInAt(0, Location::RequiresRegister()); | 
 | 261 |   locations->SetOut(Location::SameAsFirstInput()); | 
 | 262 | } | 
 | 263 |  | 
 | 264 | static void CreateLongToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { | 
 | 265 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 266 |                                                            LocationSummary::kNoCall, | 
 | 267 |                                                            kIntrinsified); | 
 | 268 |   locations->SetInAt(0, Location::RequiresRegister()); | 
 | 269 |   locations->SetOut(Location::RequiresRegister()); | 
 | 270 | } | 
 | 271 |  | 
 | 272 | static void CreateLongToLongLocations(ArenaAllocator* arena, HInvoke* invoke) { | 
 | 273 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 274 |                                                            LocationSummary::kNoCall, | 
 | 275 |                                                            kIntrinsified); | 
 | 276 |   locations->SetInAt(0, Location::RequiresRegister()); | 
 | 277 |   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); | 
 | 278 | } | 
 | 279 |  | 
 | 280 | static void GenReverseBytes(LocationSummary* locations, | 
 | 281 |                             Primitive::Type size, | 
 | 282 |                             X86Assembler* assembler) { | 
 | 283 |   Register out = locations->Out().AsRegister<Register>(); | 
 | 284 |  | 
 | 285 |   switch (size) { | 
 | 286 |     case Primitive::kPrimShort: | 
 | 287 |       // TODO: Can be done with an xchg of 8b registers. This is straight from Quick. | 
 | 288 |       __ bswapl(out); | 
 | 289 |       __ sarl(out, Immediate(16)); | 
 | 290 |       break; | 
 | 291 |     case Primitive::kPrimInt: | 
 | 292 |       __ bswapl(out); | 
 | 293 |       break; | 
 | 294 |     default: | 
 | 295 |       LOG(FATAL) << "Unexpected size for reverse-bytes: " << size; | 
 | 296 |       UNREACHABLE(); | 
 | 297 |   } | 
 | 298 | } | 
 | 299 |  | 
 | 300 | void IntrinsicLocationsBuilderX86::VisitIntegerReverseBytes(HInvoke* invoke) { | 
 | 301 |   CreateIntToIntLocations(arena_, invoke); | 
 | 302 | } | 
 | 303 |  | 
 | 304 | void IntrinsicCodeGeneratorX86::VisitIntegerReverseBytes(HInvoke* invoke) { | 
 | 305 |   GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler()); | 
 | 306 | } | 
 | 307 |  | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 308 | void IntrinsicLocationsBuilderX86::VisitLongReverseBytes(HInvoke* invoke) { | 
 | 309 |   CreateLongToLongLocations(arena_, invoke); | 
 | 310 | } | 
 | 311 |  | 
 | 312 | void IntrinsicCodeGeneratorX86::VisitLongReverseBytes(HInvoke* invoke) { | 
 | 313 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 314 |   Location input = locations->InAt(0); | 
 | 315 |   Register input_lo = input.AsRegisterPairLow<Register>(); | 
 | 316 |   Register input_hi = input.AsRegisterPairHigh<Register>(); | 
 | 317 |   Location output = locations->Out(); | 
 | 318 |   Register output_lo = output.AsRegisterPairLow<Register>(); | 
 | 319 |   Register output_hi = output.AsRegisterPairHigh<Register>(); | 
 | 320 |  | 
 | 321 |   X86Assembler* assembler = GetAssembler(); | 
 | 322 |   // Assign the inputs to the outputs, mixing low/high. | 
 | 323 |   __ movl(output_lo, input_hi); | 
 | 324 |   __ movl(output_hi, input_lo); | 
 | 325 |   __ bswapl(output_lo); | 
 | 326 |   __ bswapl(output_hi); | 
 | 327 | } | 
 | 328 |  | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 329 | void IntrinsicLocationsBuilderX86::VisitShortReverseBytes(HInvoke* invoke) { | 
 | 330 |   CreateIntToIntLocations(arena_, invoke); | 
 | 331 | } | 
 | 332 |  | 
 | 333 | void IntrinsicCodeGeneratorX86::VisitShortReverseBytes(HInvoke* invoke) { | 
 | 334 |   GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler()); | 
 | 335 | } | 
 | 336 |  | 
 | 337 |  | 
 | 338 | // TODO: Consider Quick's way of doing Double abs through integer operations, as the immediate we | 
 | 339 | //       need is 64b. | 
 | 340 |  | 
 | 341 | static void CreateFloatToFloat(ArenaAllocator* arena, HInvoke* invoke) { | 
 | 342 |   // TODO: Enable memory operations when the assembler supports them. | 
 | 343 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 344 |                                                            LocationSummary::kNoCall, | 
 | 345 |                                                            kIntrinsified); | 
 | 346 |   locations->SetInAt(0, Location::RequiresFpuRegister()); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 347 |   locations->SetOut(Location::SameAsFirstInput()); | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 348 |   HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect(); | 
 | 349 |   DCHECK(static_or_direct != nullptr); | 
| Nicolas Geoffray | 9779307 | 2016-02-16 15:33:54 +0000 | [diff] [blame] | 350 |   if (static_or_direct->HasSpecialInput() && | 
 | 351 |       invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 352 |     // We need addressibility for the constant area. | 
 | 353 |     locations->SetInAt(1, Location::RequiresRegister()); | 
 | 354 |     // We need a temporary to hold the constant. | 
 | 355 |     locations->AddTemp(Location::RequiresFpuRegister()); | 
 | 356 |   } | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 357 | } | 
 | 358 |  | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 359 | static void MathAbsFP(HInvoke* invoke, | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 360 |                       bool is64bit, | 
 | 361 |                       X86Assembler* assembler, | 
 | 362 |                       CodeGeneratorX86* codegen) { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 363 |   LocationSummary* locations = invoke->GetLocations(); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 364 |   Location output = locations->Out(); | 
 | 365 |  | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 366 |   DCHECK(output.IsFpuRegister()); | 
| Nicolas Geoffray | 9779307 | 2016-02-16 15:33:54 +0000 | [diff] [blame] | 367 |   if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 368 |     HX86ComputeBaseMethodAddress* method_address = | 
 | 369 |         invoke->InputAt(1)->AsX86ComputeBaseMethodAddress(); | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 370 |     DCHECK(locations->InAt(1).IsRegister()); | 
 | 371 |     // We also have a constant area pointer. | 
 | 372 |     Register constant_area = locations->InAt(1).AsRegister<Register>(); | 
 | 373 |     XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); | 
 | 374 |     if (is64bit) { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 375 |       __ movsd(temp, codegen->LiteralInt64Address( | 
 | 376 |           INT64_C(0x7FFFFFFFFFFFFFFF), method_address, constant_area)); | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 377 |       __ andpd(output.AsFpuRegister<XmmRegister>(), temp); | 
 | 378 |     } else { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 379 |       __ movss(temp, codegen->LiteralInt32Address( | 
 | 380 |           INT32_C(0x7FFFFFFF), method_address, constant_area)); | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 381 |       __ andps(output.AsFpuRegister<XmmRegister>(), temp); | 
 | 382 |     } | 
 | 383 |   } else { | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 384 |     // Create the right constant on an aligned stack. | 
 | 385 |     if (is64bit) { | 
 | 386 |       __ subl(ESP, Immediate(8)); | 
 | 387 |       __ pushl(Immediate(0x7FFFFFFF)); | 
 | 388 |       __ pushl(Immediate(0xFFFFFFFF)); | 
 | 389 |       __ andpd(output.AsFpuRegister<XmmRegister>(), Address(ESP, 0)); | 
 | 390 |     } else { | 
 | 391 |       __ subl(ESP, Immediate(12)); | 
 | 392 |       __ pushl(Immediate(0x7FFFFFFF)); | 
 | 393 |       __ andps(output.AsFpuRegister<XmmRegister>(), Address(ESP, 0)); | 
 | 394 |     } | 
 | 395 |     __ addl(ESP, Immediate(16)); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 396 |   } | 
 | 397 | } | 
 | 398 |  | 
 | 399 | void IntrinsicLocationsBuilderX86::VisitMathAbsDouble(HInvoke* invoke) { | 
 | 400 |   CreateFloatToFloat(arena_, invoke); | 
 | 401 | } | 
 | 402 |  | 
 | 403 | void IntrinsicCodeGeneratorX86::VisitMathAbsDouble(HInvoke* invoke) { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 404 |   MathAbsFP(invoke, /* is64bit */ true, GetAssembler(), codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 405 | } | 
 | 406 |  | 
 | 407 | void IntrinsicLocationsBuilderX86::VisitMathAbsFloat(HInvoke* invoke) { | 
 | 408 |   CreateFloatToFloat(arena_, invoke); | 
 | 409 | } | 
 | 410 |  | 
 | 411 | void IntrinsicCodeGeneratorX86::VisitMathAbsFloat(HInvoke* invoke) { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 412 |   MathAbsFP(invoke, /* is64bit */ false, GetAssembler(), codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 413 | } | 
 | 414 |  | 
 | 415 | static void CreateAbsIntLocation(ArenaAllocator* arena, HInvoke* invoke) { | 
 | 416 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 417 |                                                            LocationSummary::kNoCall, | 
 | 418 |                                                            kIntrinsified); | 
 | 419 |   locations->SetInAt(0, Location::RegisterLocation(EAX)); | 
 | 420 |   locations->SetOut(Location::SameAsFirstInput()); | 
 | 421 |   locations->AddTemp(Location::RegisterLocation(EDX)); | 
 | 422 | } | 
 | 423 |  | 
 | 424 | static void GenAbsInteger(LocationSummary* locations, X86Assembler* assembler) { | 
 | 425 |   Location output = locations->Out(); | 
 | 426 |   Register out = output.AsRegister<Register>(); | 
 | 427 |   DCHECK_EQ(out, EAX); | 
 | 428 |   Register temp = locations->GetTemp(0).AsRegister<Register>(); | 
 | 429 |   DCHECK_EQ(temp, EDX); | 
 | 430 |  | 
 | 431 |   // Sign extend EAX into EDX. | 
 | 432 |   __ cdq(); | 
 | 433 |  | 
 | 434 |   // XOR EAX with sign. | 
 | 435 |   __ xorl(EAX, EDX); | 
 | 436 |  | 
 | 437 |   // Subtract out sign to correct. | 
 | 438 |   __ subl(EAX, EDX); | 
 | 439 |  | 
 | 440 |   // The result is in EAX. | 
 | 441 | } | 
 | 442 |  | 
 | 443 | static void CreateAbsLongLocation(ArenaAllocator* arena, HInvoke* invoke) { | 
 | 444 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 445 |                                                            LocationSummary::kNoCall, | 
 | 446 |                                                            kIntrinsified); | 
 | 447 |   locations->SetInAt(0, Location::RequiresRegister()); | 
 | 448 |   locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); | 
 | 449 |   locations->AddTemp(Location::RequiresRegister()); | 
 | 450 | } | 
 | 451 |  | 
 | 452 | static void GenAbsLong(LocationSummary* locations, X86Assembler* assembler) { | 
 | 453 |   Location input = locations->InAt(0); | 
 | 454 |   Register input_lo = input.AsRegisterPairLow<Register>(); | 
 | 455 |   Register input_hi = input.AsRegisterPairHigh<Register>(); | 
 | 456 |   Location output = locations->Out(); | 
 | 457 |   Register output_lo = output.AsRegisterPairLow<Register>(); | 
 | 458 |   Register output_hi = output.AsRegisterPairHigh<Register>(); | 
 | 459 |   Register temp = locations->GetTemp(0).AsRegister<Register>(); | 
 | 460 |  | 
 | 461 |   // Compute the sign into the temporary. | 
 | 462 |   __ movl(temp, input_hi); | 
 | 463 |   __ sarl(temp, Immediate(31)); | 
 | 464 |  | 
 | 465 |   // Store the sign into the output. | 
 | 466 |   __ movl(output_lo, temp); | 
 | 467 |   __ movl(output_hi, temp); | 
 | 468 |  | 
 | 469 |   // XOR the input to the output. | 
 | 470 |   __ xorl(output_lo, input_lo); | 
 | 471 |   __ xorl(output_hi, input_hi); | 
 | 472 |  | 
 | 473 |   // Subtract the sign. | 
 | 474 |   __ subl(output_lo, temp); | 
 | 475 |   __ sbbl(output_hi, temp); | 
 | 476 | } | 
 | 477 |  | 
 | 478 | void IntrinsicLocationsBuilderX86::VisitMathAbsInt(HInvoke* invoke) { | 
 | 479 |   CreateAbsIntLocation(arena_, invoke); | 
 | 480 | } | 
 | 481 |  | 
 | 482 | void IntrinsicCodeGeneratorX86::VisitMathAbsInt(HInvoke* invoke) { | 
 | 483 |   GenAbsInteger(invoke->GetLocations(), GetAssembler()); | 
 | 484 | } | 
 | 485 |  | 
 | 486 | void IntrinsicLocationsBuilderX86::VisitMathAbsLong(HInvoke* invoke) { | 
 | 487 |   CreateAbsLongLocation(arena_, invoke); | 
 | 488 | } | 
 | 489 |  | 
 | 490 | void IntrinsicCodeGeneratorX86::VisitMathAbsLong(HInvoke* invoke) { | 
 | 491 |   GenAbsLong(invoke->GetLocations(), GetAssembler()); | 
 | 492 | } | 
 | 493 |  | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 494 | static void GenMinMaxFP(HInvoke* invoke, | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 495 |                         bool is_min, | 
 | 496 |                         bool is_double, | 
 | 497 |                         X86Assembler* assembler, | 
 | 498 |                         CodeGeneratorX86* codegen) { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 499 |   LocationSummary* locations = invoke->GetLocations(); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 500 |   Location op1_loc = locations->InAt(0); | 
 | 501 |   Location op2_loc = locations->InAt(1); | 
 | 502 |   Location out_loc = locations->Out(); | 
 | 503 |   XmmRegister out = out_loc.AsFpuRegister<XmmRegister>(); | 
 | 504 |  | 
 | 505 |   // Shortcut for same input locations. | 
 | 506 |   if (op1_loc.Equals(op2_loc)) { | 
 | 507 |     DCHECK(out_loc.Equals(op1_loc)); | 
 | 508 |     return; | 
 | 509 |   } | 
 | 510 |  | 
 | 511 |   //  (out := op1) | 
 | 512 |   //  out <=? op2 | 
 | 513 |   //  if Nan jmp Nan_label | 
 | 514 |   //  if out is min jmp done | 
 | 515 |   //  if op2 is min jmp op2_label | 
 | 516 |   //  handle -0/+0 | 
 | 517 |   //  jmp done | 
 | 518 |   // Nan_label: | 
 | 519 |   //  out := NaN | 
 | 520 |   // op2_label: | 
 | 521 |   //  out := op2 | 
 | 522 |   // done: | 
 | 523 |   // | 
 | 524 |   // This removes one jmp, but needs to copy one input (op1) to out. | 
 | 525 |   // | 
 | 526 |   // TODO: This is straight from Quick (except literal pool). Make NaN an out-of-line slowpath? | 
 | 527 |  | 
 | 528 |   XmmRegister op2 = op2_loc.AsFpuRegister<XmmRegister>(); | 
 | 529 |  | 
| Mark Mendell | 0c9497d | 2015-08-21 09:30:05 -0400 | [diff] [blame] | 530 |   NearLabel nan, done, op2_label; | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 531 |   if (is_double) { | 
 | 532 |     __ ucomisd(out, op2); | 
 | 533 |   } else { | 
 | 534 |     __ ucomiss(out, op2); | 
 | 535 |   } | 
 | 536 |  | 
 | 537 |   __ j(Condition::kParityEven, &nan); | 
 | 538 |  | 
 | 539 |   __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label); | 
 | 540 |   __ j(is_min ? Condition::kBelow : Condition::kAbove, &done); | 
 | 541 |  | 
 | 542 |   // Handle 0.0/-0.0. | 
 | 543 |   if (is_min) { | 
 | 544 |     if (is_double) { | 
 | 545 |       __ orpd(out, op2); | 
 | 546 |     } else { | 
 | 547 |       __ orps(out, op2); | 
 | 548 |     } | 
 | 549 |   } else { | 
 | 550 |     if (is_double) { | 
 | 551 |       __ andpd(out, op2); | 
 | 552 |     } else { | 
 | 553 |       __ andps(out, op2); | 
 | 554 |     } | 
 | 555 |   } | 
 | 556 |   __ jmp(&done); | 
 | 557 |  | 
 | 558 |   // NaN handling. | 
 | 559 |   __ Bind(&nan); | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 560 |   // Do we have a constant area pointer? | 
| Nicolas Geoffray | 9779307 | 2016-02-16 15:33:54 +0000 | [diff] [blame] | 561 |   if (locations->GetInputCount() == 3 && locations->InAt(2).IsValid()) { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 562 |     HX86ComputeBaseMethodAddress* method_address = | 
 | 563 |         invoke->InputAt(2)->AsX86ComputeBaseMethodAddress(); | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 564 |     DCHECK(locations->InAt(2).IsRegister()); | 
 | 565 |     Register constant_area = locations->InAt(2).AsRegister<Register>(); | 
 | 566 |     if (is_double) { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 567 |       __ movsd(out, codegen->LiteralInt64Address(kDoubleNaN, method_address, constant_area)); | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 568 |     } else { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 569 |       __ movss(out, codegen->LiteralInt32Address(kFloatNaN, method_address, constant_area)); | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 570 |     } | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 571 |   } else { | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 572 |     if (is_double) { | 
 | 573 |       __ pushl(Immediate(kDoubleNaNHigh)); | 
 | 574 |       __ pushl(Immediate(kDoubleNaNLow)); | 
 | 575 |       __ movsd(out, Address(ESP, 0)); | 
 | 576 |       __ addl(ESP, Immediate(8)); | 
 | 577 |     } else { | 
 | 578 |       __ pushl(Immediate(kFloatNaN)); | 
 | 579 |       __ movss(out, Address(ESP, 0)); | 
 | 580 |       __ addl(ESP, Immediate(4)); | 
 | 581 |     } | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 582 |   } | 
 | 583 |   __ jmp(&done); | 
 | 584 |  | 
 | 585 |   // out := op2; | 
 | 586 |   __ Bind(&op2_label); | 
 | 587 |   if (is_double) { | 
 | 588 |     __ movsd(out, op2); | 
 | 589 |   } else { | 
 | 590 |     __ movss(out, op2); | 
 | 591 |   } | 
 | 592 |  | 
 | 593 |   // Done. | 
 | 594 |   __ Bind(&done); | 
 | 595 | } | 
 | 596 |  | 
 | 597 | static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { | 
 | 598 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 599 |                                                            LocationSummary::kNoCall, | 
 | 600 |                                                            kIntrinsified); | 
 | 601 |   locations->SetInAt(0, Location::RequiresFpuRegister()); | 
 | 602 |   locations->SetInAt(1, Location::RequiresFpuRegister()); | 
 | 603 |   // The following is sub-optimal, but all we can do for now. It would be fine to also accept | 
 | 604 |   // the second input to be the output (we can simply swap inputs). | 
 | 605 |   locations->SetOut(Location::SameAsFirstInput()); | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 606 |   HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect(); | 
 | 607 |   DCHECK(static_or_direct != nullptr); | 
| Nicolas Geoffray | 9779307 | 2016-02-16 15:33:54 +0000 | [diff] [blame] | 608 |   if (static_or_direct->HasSpecialInput() && | 
 | 609 |       invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 610 |     locations->SetInAt(2, Location::RequiresRegister()); | 
 | 611 |   } | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 612 | } | 
 | 613 |  | 
 | 614 | void IntrinsicLocationsBuilderX86::VisitMathMinDoubleDouble(HInvoke* invoke) { | 
 | 615 |   CreateFPFPToFPLocations(arena_, invoke); | 
 | 616 | } | 
 | 617 |  | 
 | 618 | void IntrinsicCodeGeneratorX86::VisitMathMinDoubleDouble(HInvoke* invoke) { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 619 |   GenMinMaxFP(invoke, | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 620 |               /* is_min */ true, | 
 | 621 |               /* is_double */ true, | 
 | 622 |               GetAssembler(), | 
 | 623 |               codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 624 | } | 
 | 625 |  | 
 | 626 | void IntrinsicLocationsBuilderX86::VisitMathMinFloatFloat(HInvoke* invoke) { | 
 | 627 |   CreateFPFPToFPLocations(arena_, invoke); | 
 | 628 | } | 
 | 629 |  | 
 | 630 | void IntrinsicCodeGeneratorX86::VisitMathMinFloatFloat(HInvoke* invoke) { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 631 |   GenMinMaxFP(invoke, | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 632 |               /* is_min */ true, | 
 | 633 |               /* is_double */ false, | 
 | 634 |               GetAssembler(), | 
 | 635 |               codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 636 | } | 
 | 637 |  | 
 | 638 | void IntrinsicLocationsBuilderX86::VisitMathMaxDoubleDouble(HInvoke* invoke) { | 
 | 639 |   CreateFPFPToFPLocations(arena_, invoke); | 
 | 640 | } | 
 | 641 |  | 
 | 642 | void IntrinsicCodeGeneratorX86::VisitMathMaxDoubleDouble(HInvoke* invoke) { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 643 |   GenMinMaxFP(invoke, | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 644 |               /* is_min */ false, | 
 | 645 |               /* is_double */ true, | 
 | 646 |               GetAssembler(), | 
 | 647 |               codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 648 | } | 
 | 649 |  | 
 | 650 | void IntrinsicLocationsBuilderX86::VisitMathMaxFloatFloat(HInvoke* invoke) { | 
 | 651 |   CreateFPFPToFPLocations(arena_, invoke); | 
 | 652 | } | 
 | 653 |  | 
 | 654 | void IntrinsicCodeGeneratorX86::VisitMathMaxFloatFloat(HInvoke* invoke) { | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 655 |   GenMinMaxFP(invoke, | 
| Mark P Mendell | 2f10a5f | 2016-01-25 14:47:50 +0000 | [diff] [blame] | 656 |               /* is_min */ false, | 
 | 657 |               /* is_double */ false, | 
 | 658 |               GetAssembler(), | 
 | 659 |               codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 660 | } | 
 | 661 |  | 
 | 662 | static void GenMinMax(LocationSummary* locations, bool is_min, bool is_long, | 
 | 663 |                       X86Assembler* assembler) { | 
 | 664 |   Location op1_loc = locations->InAt(0); | 
 | 665 |   Location op2_loc = locations->InAt(1); | 
 | 666 |  | 
 | 667 |   // Shortcut for same input locations. | 
 | 668 |   if (op1_loc.Equals(op2_loc)) { | 
 | 669 |     // Can return immediately, as op1_loc == out_loc. | 
 | 670 |     // Note: if we ever support separate registers, e.g., output into memory, we need to check for | 
 | 671 |     //       a copy here. | 
 | 672 |     DCHECK(locations->Out().Equals(op1_loc)); | 
 | 673 |     return; | 
 | 674 |   } | 
 | 675 |  | 
 | 676 |   if (is_long) { | 
 | 677 |     // Need to perform a subtract to get the sign right. | 
 | 678 |     // op1 is already in the same location as the output. | 
 | 679 |     Location output = locations->Out(); | 
 | 680 |     Register output_lo = output.AsRegisterPairLow<Register>(); | 
 | 681 |     Register output_hi = output.AsRegisterPairHigh<Register>(); | 
 | 682 |  | 
 | 683 |     Register op2_lo = op2_loc.AsRegisterPairLow<Register>(); | 
 | 684 |     Register op2_hi = op2_loc.AsRegisterPairHigh<Register>(); | 
 | 685 |  | 
 | 686 |     // Spare register to compute the subtraction to set condition code. | 
 | 687 |     Register temp = locations->GetTemp(0).AsRegister<Register>(); | 
 | 688 |  | 
 | 689 |     // Subtract off op2_low. | 
 | 690 |     __ movl(temp, output_lo); | 
 | 691 |     __ subl(temp, op2_lo); | 
 | 692 |  | 
 | 693 |     // Now use the same tempo and the borrow to finish the subtraction of op2_hi. | 
 | 694 |     __ movl(temp, output_hi); | 
 | 695 |     __ sbbl(temp, op2_hi); | 
 | 696 |  | 
 | 697 |     // Now the condition code is correct. | 
 | 698 |     Condition cond = is_min ? Condition::kGreaterEqual : Condition::kLess; | 
 | 699 |     __ cmovl(cond, output_lo, op2_lo); | 
 | 700 |     __ cmovl(cond, output_hi, op2_hi); | 
 | 701 |   } else { | 
 | 702 |     Register out = locations->Out().AsRegister<Register>(); | 
 | 703 |     Register op2 = op2_loc.AsRegister<Register>(); | 
 | 704 |  | 
 | 705 |     //  (out := op1) | 
 | 706 |     //  out <=? op2 | 
 | 707 |     //  if out is min jmp done | 
 | 708 |     //  out := op2 | 
 | 709 |     // done: | 
 | 710 |  | 
 | 711 |     __ cmpl(out, op2); | 
 | 712 |     Condition cond = is_min ? Condition::kGreater : Condition::kLess; | 
 | 713 |     __ cmovl(cond, out, op2); | 
 | 714 |   } | 
 | 715 | } | 
 | 716 |  | 
 | 717 | static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { | 
 | 718 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 719 |                                                            LocationSummary::kNoCall, | 
 | 720 |                                                            kIntrinsified); | 
 | 721 |   locations->SetInAt(0, Location::RequiresRegister()); | 
 | 722 |   locations->SetInAt(1, Location::RequiresRegister()); | 
 | 723 |   locations->SetOut(Location::SameAsFirstInput()); | 
 | 724 | } | 
 | 725 |  | 
 | 726 | static void CreateLongLongToLongLocations(ArenaAllocator* arena, HInvoke* invoke) { | 
 | 727 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 728 |                                                            LocationSummary::kNoCall, | 
 | 729 |                                                            kIntrinsified); | 
 | 730 |   locations->SetInAt(0, Location::RequiresRegister()); | 
 | 731 |   locations->SetInAt(1, Location::RequiresRegister()); | 
 | 732 |   locations->SetOut(Location::SameAsFirstInput()); | 
 | 733 |   // Register to use to perform a long subtract to set cc. | 
 | 734 |   locations->AddTemp(Location::RequiresRegister()); | 
 | 735 | } | 
 | 736 |  | 
 | 737 | void IntrinsicLocationsBuilderX86::VisitMathMinIntInt(HInvoke* invoke) { | 
 | 738 |   CreateIntIntToIntLocations(arena_, invoke); | 
 | 739 | } | 
 | 740 |  | 
 | 741 | void IntrinsicCodeGeneratorX86::VisitMathMinIntInt(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 742 |   GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ false, GetAssembler()); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 743 | } | 
 | 744 |  | 
 | 745 | void IntrinsicLocationsBuilderX86::VisitMathMinLongLong(HInvoke* invoke) { | 
 | 746 |   CreateLongLongToLongLocations(arena_, invoke); | 
 | 747 | } | 
 | 748 |  | 
 | 749 | void IntrinsicCodeGeneratorX86::VisitMathMinLongLong(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 750 |   GenMinMax(invoke->GetLocations(), /* is_min */ true, /* is_long */ true, GetAssembler()); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 751 | } | 
 | 752 |  | 
 | 753 | void IntrinsicLocationsBuilderX86::VisitMathMaxIntInt(HInvoke* invoke) { | 
 | 754 |   CreateIntIntToIntLocations(arena_, invoke); | 
 | 755 | } | 
 | 756 |  | 
 | 757 | void IntrinsicCodeGeneratorX86::VisitMathMaxIntInt(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 758 |   GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ false, GetAssembler()); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 759 | } | 
 | 760 |  | 
 | 761 | void IntrinsicLocationsBuilderX86::VisitMathMaxLongLong(HInvoke* invoke) { | 
 | 762 |   CreateLongLongToLongLocations(arena_, invoke); | 
 | 763 | } | 
 | 764 |  | 
 | 765 | void IntrinsicCodeGeneratorX86::VisitMathMaxLongLong(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 766 |   GenMinMax(invoke->GetLocations(), /* is_min */ false, /* is_long */ true, GetAssembler()); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 767 | } | 
 | 768 |  | 
 | 769 | static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { | 
 | 770 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 771 |                                                            LocationSummary::kNoCall, | 
 | 772 |                                                            kIntrinsified); | 
 | 773 |   locations->SetInAt(0, Location::RequiresFpuRegister()); | 
 | 774 |   locations->SetOut(Location::RequiresFpuRegister()); | 
 | 775 | } | 
 | 776 |  | 
 | 777 | void IntrinsicLocationsBuilderX86::VisitMathSqrt(HInvoke* invoke) { | 
 | 778 |   CreateFPToFPLocations(arena_, invoke); | 
 | 779 | } | 
 | 780 |  | 
 | 781 | void IntrinsicCodeGeneratorX86::VisitMathSqrt(HInvoke* invoke) { | 
 | 782 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 783 |   XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>(); | 
 | 784 |   XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>(); | 
 | 785 |  | 
 | 786 |   GetAssembler()->sqrtsd(out, in); | 
 | 787 | } | 
 | 788 |  | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 789 | static void InvokeOutOfLineIntrinsic(CodeGeneratorX86* codegen, HInvoke* invoke) { | 
| Roland Levillain | ec525fc | 2015-04-28 15:50:20 +0100 | [diff] [blame] | 790 |   MoveArguments(invoke, codegen); | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 791 |  | 
 | 792 |   DCHECK(invoke->IsInvokeStaticOrDirect()); | 
| Nicolas Geoffray | 94015b9 | 2015-06-04 18:21:04 +0100 | [diff] [blame] | 793 |   codegen->GenerateStaticOrDirectCall(invoke->AsInvokeStaticOrDirect(), | 
 | 794 |                                       Location::RegisterLocation(EAX)); | 
| Mingyao Yang | e90db12 | 2015-04-03 17:56:54 -0700 | [diff] [blame] | 795 |   codegen->RecordPcInfo(invoke, invoke->GetDexPc()); | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 796 |  | 
 | 797 |   // Copy the result back to the expected output. | 
 | 798 |   Location out = invoke->GetLocations()->Out(); | 
 | 799 |   if (out.IsValid()) { | 
 | 800 |     DCHECK(out.IsRegister()); | 
| Andreas Gampe | 85b62f2 | 2015-09-09 13:15:38 -0700 | [diff] [blame] | 801 |     codegen->MoveFromReturnRegister(out, invoke->GetType()); | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 802 |   } | 
 | 803 | } | 
 | 804 |  | 
 | 805 | static void CreateSSE41FPToFPLocations(ArenaAllocator* arena, | 
 | 806 |                                       HInvoke* invoke, | 
 | 807 |                                       CodeGeneratorX86* codegen) { | 
 | 808 |   // Do we have instruction support? | 
 | 809 |   if (codegen->GetInstructionSetFeatures().HasSSE4_1()) { | 
 | 810 |     CreateFPToFPLocations(arena, invoke); | 
 | 811 |     return; | 
 | 812 |   } | 
 | 813 |  | 
 | 814 |   // We have to fall back to a call to the intrinsic. | 
 | 815 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
| Serban Constantinescu | 54ff482 | 2016-07-07 18:03:19 +0100 | [diff] [blame] | 816 |                                                            LocationSummary::kCallOnMainOnly); | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 817 |   InvokeRuntimeCallingConvention calling_convention; | 
 | 818 |   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetFpuRegisterAt(0))); | 
 | 819 |   locations->SetOut(Location::FpuRegisterLocation(XMM0)); | 
 | 820 |   // Needs to be EAX for the invoke. | 
 | 821 |   locations->AddTemp(Location::RegisterLocation(EAX)); | 
 | 822 | } | 
 | 823 |  | 
 | 824 | static void GenSSE41FPToFPIntrinsic(CodeGeneratorX86* codegen, | 
 | 825 |                                    HInvoke* invoke, | 
 | 826 |                                    X86Assembler* assembler, | 
 | 827 |                                    int round_mode) { | 
 | 828 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 829 |   if (locations->WillCall()) { | 
 | 830 |     InvokeOutOfLineIntrinsic(codegen, invoke); | 
 | 831 |   } else { | 
 | 832 |     XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>(); | 
 | 833 |     XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>(); | 
 | 834 |     __ roundsd(out, in, Immediate(round_mode)); | 
 | 835 |   } | 
 | 836 | } | 
 | 837 |  | 
 | 838 | void IntrinsicLocationsBuilderX86::VisitMathCeil(HInvoke* invoke) { | 
 | 839 |   CreateSSE41FPToFPLocations(arena_, invoke, codegen_); | 
 | 840 | } | 
 | 841 |  | 
 | 842 | void IntrinsicCodeGeneratorX86::VisitMathCeil(HInvoke* invoke) { | 
 | 843 |   GenSSE41FPToFPIntrinsic(codegen_, invoke, GetAssembler(), 2); | 
 | 844 | } | 
 | 845 |  | 
 | 846 | void IntrinsicLocationsBuilderX86::VisitMathFloor(HInvoke* invoke) { | 
 | 847 |   CreateSSE41FPToFPLocations(arena_, invoke, codegen_); | 
 | 848 | } | 
 | 849 |  | 
 | 850 | void IntrinsicCodeGeneratorX86::VisitMathFloor(HInvoke* invoke) { | 
 | 851 |   GenSSE41FPToFPIntrinsic(codegen_, invoke, GetAssembler(), 1); | 
 | 852 | } | 
 | 853 |  | 
 | 854 | void IntrinsicLocationsBuilderX86::VisitMathRint(HInvoke* invoke) { | 
 | 855 |   CreateSSE41FPToFPLocations(arena_, invoke, codegen_); | 
 | 856 | } | 
 | 857 |  | 
 | 858 | void IntrinsicCodeGeneratorX86::VisitMathRint(HInvoke* invoke) { | 
 | 859 |   GenSSE41FPToFPIntrinsic(codegen_, invoke, GetAssembler(), 0); | 
 | 860 | } | 
 | 861 |  | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 862 | void IntrinsicLocationsBuilderX86::VisitMathRoundFloat(HInvoke* invoke) { | 
 | 863 |   // Do we have instruction support? | 
 | 864 |   if (codegen_->GetInstructionSetFeatures().HasSSE4_1()) { | 
| Aart Bik | 2c9f495 | 2016-08-01 16:52:27 -0700 | [diff] [blame] | 865 |     HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect(); | 
 | 866 |     DCHECK(static_or_direct != nullptr); | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 867 |     LocationSummary* locations = new (arena_) LocationSummary(invoke, | 
 | 868 |                                                               LocationSummary::kNoCall, | 
 | 869 |                                                               kIntrinsified); | 
 | 870 |     locations->SetInAt(0, Location::RequiresFpuRegister()); | 
| Aart Bik | 2c9f495 | 2016-08-01 16:52:27 -0700 | [diff] [blame] | 871 |     if (static_or_direct->HasSpecialInput() && | 
 | 872 |         invoke->InputAt( | 
 | 873 |             static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) { | 
 | 874 |       locations->SetInAt(1, Location::RequiresRegister()); | 
 | 875 |     } | 
| Nicolas Geoffray | d9b9240 | 2015-04-21 10:02:22 +0100 | [diff] [blame] | 876 |     locations->SetOut(Location::RequiresRegister()); | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 877 |     locations->AddTemp(Location::RequiresFpuRegister()); | 
 | 878 |     locations->AddTemp(Location::RequiresFpuRegister()); | 
 | 879 |     return; | 
 | 880 |   } | 
 | 881 |  | 
 | 882 |   // We have to fall back to a call to the intrinsic. | 
 | 883 |   LocationSummary* locations = new (arena_) LocationSummary(invoke, | 
| Aart Bik | 2c9f495 | 2016-08-01 16:52:27 -0700 | [diff] [blame] | 884 |                                                             LocationSummary::kCallOnMainOnly); | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 885 |   InvokeRuntimeCallingConvention calling_convention; | 
 | 886 |   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetFpuRegisterAt(0))); | 
 | 887 |   locations->SetOut(Location::RegisterLocation(EAX)); | 
 | 888 |   // Needs to be EAX for the invoke. | 
 | 889 |   locations->AddTemp(Location::RegisterLocation(EAX)); | 
 | 890 | } | 
 | 891 |  | 
 | 892 | void IntrinsicCodeGeneratorX86::VisitMathRoundFloat(HInvoke* invoke) { | 
 | 893 |   LocationSummary* locations = invoke->GetLocations(); | 
| Aart Bik | 2c9f495 | 2016-08-01 16:52:27 -0700 | [diff] [blame] | 894 |   if (locations->WillCall()) {  // TODO: can we reach this? | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 895 |     InvokeOutOfLineIntrinsic(codegen_, invoke); | 
 | 896 |     return; | 
 | 897 |   } | 
 | 898 |  | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 899 |   XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>(); | 
| Aart Bik | 2c9f495 | 2016-08-01 16:52:27 -0700 | [diff] [blame] | 900 |   XmmRegister t1 = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); | 
 | 901 |   XmmRegister t2 = locations->GetTemp(1).AsFpuRegister<XmmRegister>(); | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 902 |   Register out = locations->Out().AsRegister<Register>(); | 
| Aart Bik | 2c9f495 | 2016-08-01 16:52:27 -0700 | [diff] [blame] | 903 |   NearLabel skip_incr, done; | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 904 |   X86Assembler* assembler = GetAssembler(); | 
 | 905 |  | 
| Aart Bik | 2c9f495 | 2016-08-01 16:52:27 -0700 | [diff] [blame] | 906 |   // Since no direct x86 rounding instruction matches the required semantics, | 
 | 907 |   // this intrinsic is implemented as follows: | 
 | 908 |   //  result = floor(in); | 
 | 909 |   //  if (in - result >= 0.5f) | 
 | 910 |   //    result = result + 1.0f; | 
 | 911 |   __ movss(t2, in); | 
 | 912 |   __ roundss(t1, in, Immediate(1)); | 
 | 913 |   __ subss(t2, t1); | 
| Aart Bik | 0cf8d9c | 2016-08-10 14:05:54 -0700 | [diff] [blame] | 914 |   if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) { | 
 | 915 |     // Direct constant area available. | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 916 |     HX86ComputeBaseMethodAddress* method_address = | 
 | 917 |         invoke->InputAt(1)->AsX86ComputeBaseMethodAddress(); | 
| Aart Bik | 0cf8d9c | 2016-08-10 14:05:54 -0700 | [diff] [blame] | 918 |     Register constant_area = locations->InAt(1).AsRegister<Register>(); | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 919 |     __ comiss(t2, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(0.5f), | 
 | 920 |                                                 method_address, | 
 | 921 |                                                 constant_area)); | 
| Aart Bik | 0cf8d9c | 2016-08-10 14:05:54 -0700 | [diff] [blame] | 922 |     __ j(kBelow, &skip_incr); | 
| Nicolas Geoffray | 133719e | 2017-01-22 15:44:39 +0000 | [diff] [blame] | 923 |     __ addss(t1, codegen_->LiteralInt32Address(bit_cast<int32_t, float>(1.0f), | 
 | 924 |                                                method_address, | 
 | 925 |                                                constant_area)); | 
| Aart Bik | 0cf8d9c | 2016-08-10 14:05:54 -0700 | [diff] [blame] | 926 |     __ Bind(&skip_incr); | 
 | 927 |   } else { | 
 | 928 |     // No constant area: go through stack. | 
 | 929 |     __ pushl(Immediate(bit_cast<int32_t, float>(0.5f))); | 
 | 930 |     __ pushl(Immediate(bit_cast<int32_t, float>(1.0f))); | 
 | 931 |     __ comiss(t2, Address(ESP, 4)); | 
 | 932 |     __ j(kBelow, &skip_incr); | 
 | 933 |     __ addss(t1, Address(ESP, 0)); | 
 | 934 |     __ Bind(&skip_incr); | 
 | 935 |     __ addl(ESP, Immediate(8)); | 
 | 936 |   } | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 937 |  | 
| Aart Bik | 2c9f495 | 2016-08-01 16:52:27 -0700 | [diff] [blame] | 938 |   // Final conversion to an integer. Unfortunately this also does not have a | 
 | 939 |   // direct x86 instruction, since NaN should map to 0 and large positive | 
 | 940 |   // values need to be clipped to the extreme value. | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 941 |   __ movl(out, Immediate(kPrimIntMax)); | 
| Aart Bik | 2c9f495 | 2016-08-01 16:52:27 -0700 | [diff] [blame] | 942 |   __ cvtsi2ss(t2, out); | 
 | 943 |   __ comiss(t1, t2); | 
 | 944 |   __ j(kAboveEqual, &done);  // clipped to max (already in out), does not jump on unordered | 
 | 945 |   __ movl(out, Immediate(0));  // does not change flags | 
 | 946 |   __ j(kUnordered, &done);  // NaN mapped to 0 (just moved in out) | 
 | 947 |   __ cvttss2si(out, t1); | 
| Mark Mendell | fb8d279 | 2015-03-31 22:16:59 -0400 | [diff] [blame] | 948 |   __ Bind(&done); | 
 | 949 | } | 
 | 950 |  | 
| Mark Mendell | a4f1220 | 2015-08-06 15:23:34 -0400 | [diff] [blame] | 951 | static void CreateFPToFPCallLocations(ArenaAllocator* arena, | 
 | 952 |                                       HInvoke* invoke) { | 
 | 953 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
| Serban Constantinescu | 54ff482 | 2016-07-07 18:03:19 +0100 | [diff] [blame] | 954 |                                                            LocationSummary::kCallOnMainOnly, | 
| Mark Mendell | a4f1220 | 2015-08-06 15:23:34 -0400 | [diff] [blame] | 955 |                                                            kIntrinsified); | 
 | 956 |   InvokeRuntimeCallingConvention calling_convention; | 
 | 957 |   locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0))); | 
 | 958 |   locations->SetOut(Location::FpuRegisterLocation(XMM0)); | 
 | 959 | } | 
 | 960 |  | 
 | 961 | static void GenFPToFPCall(HInvoke* invoke, CodeGeneratorX86* codegen, QuickEntrypointEnum entry) { | 
 | 962 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 963 |   DCHECK(locations->WillCall()); | 
 | 964 |   DCHECK(invoke->IsInvokeStaticOrDirect()); | 
 | 965 |   X86Assembler* assembler = codegen->GetAssembler(); | 
 | 966 |  | 
 | 967 |   // We need some place to pass the parameters. | 
 | 968 |   __ subl(ESP, Immediate(16)); | 
 | 969 |   __ cfi().AdjustCFAOffset(16); | 
 | 970 |  | 
 | 971 |   // Pass the parameters at the bottom of the stack. | 
 | 972 |   __ movsd(Address(ESP, 0), XMM0); | 
 | 973 |  | 
 | 974 |   // If we have a second parameter, pass it next. | 
 | 975 |   if (invoke->GetNumberOfArguments() == 2) { | 
 | 976 |     __ movsd(Address(ESP, 8), XMM1); | 
 | 977 |   } | 
 | 978 |  | 
 | 979 |   // Now do the actual call. | 
| Serban Constantinescu | ba45db0 | 2016-07-12 22:53:02 +0100 | [diff] [blame] | 980 |   codegen->InvokeRuntime(entry, invoke, invoke->GetDexPc()); | 
| Mark Mendell | a4f1220 | 2015-08-06 15:23:34 -0400 | [diff] [blame] | 981 |  | 
 | 982 |   // Extract the return value from the FP stack. | 
 | 983 |   __ fstpl(Address(ESP, 0)); | 
 | 984 |   __ movsd(XMM0, Address(ESP, 0)); | 
 | 985 |  | 
 | 986 |   // And clean up the stack. | 
 | 987 |   __ addl(ESP, Immediate(16)); | 
 | 988 |   __ cfi().AdjustCFAOffset(-16); | 
| Mark Mendell | a4f1220 | 2015-08-06 15:23:34 -0400 | [diff] [blame] | 989 | } | 
 | 990 |  | 
 | 991 | void IntrinsicLocationsBuilderX86::VisitMathCos(HInvoke* invoke) { | 
 | 992 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 993 | } | 
 | 994 |  | 
 | 995 | void IntrinsicCodeGeneratorX86::VisitMathCos(HInvoke* invoke) { | 
 | 996 |   GenFPToFPCall(invoke, codegen_, kQuickCos); | 
 | 997 | } | 
 | 998 |  | 
 | 999 | void IntrinsicLocationsBuilderX86::VisitMathSin(HInvoke* invoke) { | 
 | 1000 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 1001 | } | 
 | 1002 |  | 
 | 1003 | void IntrinsicCodeGeneratorX86::VisitMathSin(HInvoke* invoke) { | 
 | 1004 |   GenFPToFPCall(invoke, codegen_, kQuickSin); | 
 | 1005 | } | 
 | 1006 |  | 
 | 1007 | void IntrinsicLocationsBuilderX86::VisitMathAcos(HInvoke* invoke) { | 
 | 1008 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 1009 | } | 
 | 1010 |  | 
 | 1011 | void IntrinsicCodeGeneratorX86::VisitMathAcos(HInvoke* invoke) { | 
 | 1012 |   GenFPToFPCall(invoke, codegen_, kQuickAcos); | 
 | 1013 | } | 
 | 1014 |  | 
 | 1015 | void IntrinsicLocationsBuilderX86::VisitMathAsin(HInvoke* invoke) { | 
 | 1016 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 1017 | } | 
 | 1018 |  | 
 | 1019 | void IntrinsicCodeGeneratorX86::VisitMathAsin(HInvoke* invoke) { | 
 | 1020 |   GenFPToFPCall(invoke, codegen_, kQuickAsin); | 
 | 1021 | } | 
 | 1022 |  | 
 | 1023 | void IntrinsicLocationsBuilderX86::VisitMathAtan(HInvoke* invoke) { | 
 | 1024 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 1025 | } | 
 | 1026 |  | 
 | 1027 | void IntrinsicCodeGeneratorX86::VisitMathAtan(HInvoke* invoke) { | 
 | 1028 |   GenFPToFPCall(invoke, codegen_, kQuickAtan); | 
 | 1029 | } | 
 | 1030 |  | 
 | 1031 | void IntrinsicLocationsBuilderX86::VisitMathCbrt(HInvoke* invoke) { | 
 | 1032 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 1033 | } | 
 | 1034 |  | 
 | 1035 | void IntrinsicCodeGeneratorX86::VisitMathCbrt(HInvoke* invoke) { | 
 | 1036 |   GenFPToFPCall(invoke, codegen_, kQuickCbrt); | 
 | 1037 | } | 
 | 1038 |  | 
 | 1039 | void IntrinsicLocationsBuilderX86::VisitMathCosh(HInvoke* invoke) { | 
 | 1040 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 1041 | } | 
 | 1042 |  | 
 | 1043 | void IntrinsicCodeGeneratorX86::VisitMathCosh(HInvoke* invoke) { | 
 | 1044 |   GenFPToFPCall(invoke, codegen_, kQuickCosh); | 
 | 1045 | } | 
 | 1046 |  | 
 | 1047 | void IntrinsicLocationsBuilderX86::VisitMathExp(HInvoke* invoke) { | 
 | 1048 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 1049 | } | 
 | 1050 |  | 
 | 1051 | void IntrinsicCodeGeneratorX86::VisitMathExp(HInvoke* invoke) { | 
 | 1052 |   GenFPToFPCall(invoke, codegen_, kQuickExp); | 
 | 1053 | } | 
 | 1054 |  | 
 | 1055 | void IntrinsicLocationsBuilderX86::VisitMathExpm1(HInvoke* invoke) { | 
 | 1056 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 1057 | } | 
 | 1058 |  | 
 | 1059 | void IntrinsicCodeGeneratorX86::VisitMathExpm1(HInvoke* invoke) { | 
 | 1060 |   GenFPToFPCall(invoke, codegen_, kQuickExpm1); | 
 | 1061 | } | 
 | 1062 |  | 
 | 1063 | void IntrinsicLocationsBuilderX86::VisitMathLog(HInvoke* invoke) { | 
 | 1064 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 1065 | } | 
 | 1066 |  | 
 | 1067 | void IntrinsicCodeGeneratorX86::VisitMathLog(HInvoke* invoke) { | 
 | 1068 |   GenFPToFPCall(invoke, codegen_, kQuickLog); | 
 | 1069 | } | 
 | 1070 |  | 
 | 1071 | void IntrinsicLocationsBuilderX86::VisitMathLog10(HInvoke* invoke) { | 
 | 1072 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 1073 | } | 
 | 1074 |  | 
 | 1075 | void IntrinsicCodeGeneratorX86::VisitMathLog10(HInvoke* invoke) { | 
 | 1076 |   GenFPToFPCall(invoke, codegen_, kQuickLog10); | 
 | 1077 | } | 
 | 1078 |  | 
 | 1079 | void IntrinsicLocationsBuilderX86::VisitMathSinh(HInvoke* invoke) { | 
 | 1080 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 1081 | } | 
 | 1082 |  | 
 | 1083 | void IntrinsicCodeGeneratorX86::VisitMathSinh(HInvoke* invoke) { | 
 | 1084 |   GenFPToFPCall(invoke, codegen_, kQuickSinh); | 
 | 1085 | } | 
 | 1086 |  | 
 | 1087 | void IntrinsicLocationsBuilderX86::VisitMathTan(HInvoke* invoke) { | 
 | 1088 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 1089 | } | 
 | 1090 |  | 
 | 1091 | void IntrinsicCodeGeneratorX86::VisitMathTan(HInvoke* invoke) { | 
 | 1092 |   GenFPToFPCall(invoke, codegen_, kQuickTan); | 
 | 1093 | } | 
 | 1094 |  | 
 | 1095 | void IntrinsicLocationsBuilderX86::VisitMathTanh(HInvoke* invoke) { | 
 | 1096 |   CreateFPToFPCallLocations(arena_, invoke); | 
 | 1097 | } | 
 | 1098 |  | 
 | 1099 | void IntrinsicCodeGeneratorX86::VisitMathTanh(HInvoke* invoke) { | 
 | 1100 |   GenFPToFPCall(invoke, codegen_, kQuickTanh); | 
 | 1101 | } | 
 | 1102 |  | 
 | 1103 | static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, | 
 | 1104 |                                         HInvoke* invoke) { | 
 | 1105 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
| Serban Constantinescu | 54ff482 | 2016-07-07 18:03:19 +0100 | [diff] [blame] | 1106 |                                                            LocationSummary::kCallOnMainOnly, | 
| Mark Mendell | a4f1220 | 2015-08-06 15:23:34 -0400 | [diff] [blame] | 1107 |                                                            kIntrinsified); | 
 | 1108 |   InvokeRuntimeCallingConvention calling_convention; | 
 | 1109 |   locations->SetInAt(0, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(0))); | 
 | 1110 |   locations->SetInAt(1, Location::FpuRegisterLocation(calling_convention.GetFpuRegisterAt(1))); | 
 | 1111 |   locations->SetOut(Location::FpuRegisterLocation(XMM0)); | 
 | 1112 | } | 
 | 1113 |  | 
 | 1114 | void IntrinsicLocationsBuilderX86::VisitMathAtan2(HInvoke* invoke) { | 
 | 1115 |   CreateFPFPToFPCallLocations(arena_, invoke); | 
 | 1116 | } | 
 | 1117 |  | 
 | 1118 | void IntrinsicCodeGeneratorX86::VisitMathAtan2(HInvoke* invoke) { | 
 | 1119 |   GenFPToFPCall(invoke, codegen_, kQuickAtan2); | 
 | 1120 | } | 
 | 1121 |  | 
 | 1122 | void IntrinsicLocationsBuilderX86::VisitMathHypot(HInvoke* invoke) { | 
 | 1123 |   CreateFPFPToFPCallLocations(arena_, invoke); | 
 | 1124 | } | 
 | 1125 |  | 
 | 1126 | void IntrinsicCodeGeneratorX86::VisitMathHypot(HInvoke* invoke) { | 
 | 1127 |   GenFPToFPCall(invoke, codegen_, kQuickHypot); | 
 | 1128 | } | 
 | 1129 |  | 
 | 1130 | void IntrinsicLocationsBuilderX86::VisitMathNextAfter(HInvoke* invoke) { | 
 | 1131 |   CreateFPFPToFPCallLocations(arena_, invoke); | 
 | 1132 | } | 
 | 1133 |  | 
 | 1134 | void IntrinsicCodeGeneratorX86::VisitMathNextAfter(HInvoke* invoke) { | 
 | 1135 |   GenFPToFPCall(invoke, codegen_, kQuickNextAfter); | 
 | 1136 | } | 
 | 1137 |  | 
| Mark Mendell | 6bc53a9 | 2015-07-01 14:26:52 -0400 | [diff] [blame] | 1138 | void IntrinsicLocationsBuilderX86::VisitSystemArrayCopyChar(HInvoke* invoke) { | 
 | 1139 |   // We need at least two of the positions or length to be an integer constant, | 
 | 1140 |   // or else we won't have enough free registers. | 
 | 1141 |   HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant(); | 
 | 1142 |   HIntConstant* dest_pos = invoke->InputAt(3)->AsIntConstant(); | 
 | 1143 |   HIntConstant* length = invoke->InputAt(4)->AsIntConstant(); | 
 | 1144 |  | 
 | 1145 |   int num_constants = | 
 | 1146 |       ((src_pos != nullptr) ? 1 : 0) | 
 | 1147 |       + ((dest_pos != nullptr) ? 1 : 0) | 
 | 1148 |       + ((length != nullptr) ? 1 : 0); | 
 | 1149 |  | 
 | 1150 |   if (num_constants < 2) { | 
 | 1151 |     // Not enough free registers. | 
 | 1152 |     return; | 
 | 1153 |   } | 
 | 1154 |  | 
 | 1155 |   // As long as we are checking, we might as well check to see if the src and dest | 
 | 1156 |   // positions are >= 0. | 
 | 1157 |   if ((src_pos != nullptr && src_pos->GetValue() < 0) || | 
 | 1158 |       (dest_pos != nullptr && dest_pos->GetValue() < 0)) { | 
 | 1159 |     // We will have to fail anyways. | 
 | 1160 |     return; | 
 | 1161 |   } | 
 | 1162 |  | 
 | 1163 |   // And since we are already checking, check the length too. | 
 | 1164 |   if (length != nullptr) { | 
 | 1165 |     int32_t len = length->GetValue(); | 
 | 1166 |     if (len < 0) { | 
 | 1167 |       // Just call as normal. | 
 | 1168 |       return; | 
 | 1169 |     } | 
 | 1170 |   } | 
 | 1171 |  | 
 | 1172 |   // Okay, it is safe to generate inline code. | 
 | 1173 |   LocationSummary* locations = | 
 | 1174 |     new (arena_) LocationSummary(invoke, LocationSummary::kCallOnSlowPath, kIntrinsified); | 
 | 1175 |   // arraycopy(Object src, int srcPos, Object dest, int destPos, int length). | 
 | 1176 |   locations->SetInAt(0, Location::RequiresRegister()); | 
 | 1177 |   locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); | 
 | 1178 |   locations->SetInAt(2, Location::RequiresRegister()); | 
 | 1179 |   locations->SetInAt(3, Location::RegisterOrConstant(invoke->InputAt(3))); | 
 | 1180 |   locations->SetInAt(4, Location::RegisterOrConstant(invoke->InputAt(4))); | 
 | 1181 |  | 
 | 1182 |   // And we need some temporaries.  We will use REP MOVSW, so we need fixed registers. | 
 | 1183 |   locations->AddTemp(Location::RegisterLocation(ESI)); | 
 | 1184 |   locations->AddTemp(Location::RegisterLocation(EDI)); | 
 | 1185 |   locations->AddTemp(Location::RegisterLocation(ECX)); | 
 | 1186 | } | 
 | 1187 |  | 
 | 1188 | static void CheckPosition(X86Assembler* assembler, | 
 | 1189 |                           Location pos, | 
 | 1190 |                           Register input, | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 1191 |                           Location length, | 
| Andreas Gampe | 85b62f2 | 2015-09-09 13:15:38 -0700 | [diff] [blame] | 1192 |                           SlowPathCode* slow_path, | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 1193 |                           Register temp, | 
 | 1194 |                           bool length_is_input_length = false) { | 
 | 1195 |   // Where is the length in the Array? | 
| Mark Mendell | 6bc53a9 | 2015-07-01 14:26:52 -0400 | [diff] [blame] | 1196 |   const uint32_t length_offset = mirror::Array::LengthOffset().Uint32Value(); | 
 | 1197 |  | 
 | 1198 |   if (pos.IsConstant()) { | 
 | 1199 |     int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue(); | 
 | 1200 |     if (pos_const == 0) { | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 1201 |       if (!length_is_input_length) { | 
 | 1202 |         // Check that length(input) >= length. | 
 | 1203 |         if (length.IsConstant()) { | 
 | 1204 |           __ cmpl(Address(input, length_offset), | 
 | 1205 |                   Immediate(length.GetConstant()->AsIntConstant()->GetValue())); | 
 | 1206 |         } else { | 
 | 1207 |           __ cmpl(Address(input, length_offset), length.AsRegister<Register>()); | 
 | 1208 |         } | 
 | 1209 |         __ j(kLess, slow_path->GetEntryLabel()); | 
 | 1210 |       } | 
| Mark Mendell | 6bc53a9 | 2015-07-01 14:26:52 -0400 | [diff] [blame] | 1211 |     } else { | 
 | 1212 |       // Check that length(input) >= pos. | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 1213 |       __ movl(temp, Address(input, length_offset)); | 
 | 1214 |       __ subl(temp, Immediate(pos_const)); | 
| Mark Mendell | 6bc53a9 | 2015-07-01 14:26:52 -0400 | [diff] [blame] | 1215 |       __ j(kLess, slow_path->GetEntryLabel()); | 
 | 1216 |  | 
 | 1217 |       // Check that (length(input) - pos) >= length. | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 1218 |       if (length.IsConstant()) { | 
 | 1219 |         __ cmpl(temp, Immediate(length.GetConstant()->AsIntConstant()->GetValue())); | 
 | 1220 |       } else { | 
 | 1221 |         __ cmpl(temp, length.AsRegister<Register>()); | 
 | 1222 |       } | 
| Mark Mendell | 6bc53a9 | 2015-07-01 14:26:52 -0400 | [diff] [blame] | 1223 |       __ j(kLess, slow_path->GetEntryLabel()); | 
 | 1224 |     } | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 1225 |   } else if (length_is_input_length) { | 
 | 1226 |     // The only way the copy can succeed is if pos is zero. | 
 | 1227 |     Register pos_reg = pos.AsRegister<Register>(); | 
 | 1228 |     __ testl(pos_reg, pos_reg); | 
 | 1229 |     __ j(kNotEqual, slow_path->GetEntryLabel()); | 
| Mark Mendell | 6bc53a9 | 2015-07-01 14:26:52 -0400 | [diff] [blame] | 1230 |   } else { | 
 | 1231 |     // Check that pos >= 0. | 
 | 1232 |     Register pos_reg = pos.AsRegister<Register>(); | 
 | 1233 |     __ testl(pos_reg, pos_reg); | 
 | 1234 |     __ j(kLess, slow_path->GetEntryLabel()); | 
 | 1235 |  | 
 | 1236 |     // Check that pos <= length(input). | 
 | 1237 |     __ cmpl(Address(input, length_offset), pos_reg); | 
 | 1238 |     __ j(kLess, slow_path->GetEntryLabel()); | 
 | 1239 |  | 
 | 1240 |     // Check that (length(input) - pos) >= length. | 
 | 1241 |     __ movl(temp, Address(input, length_offset)); | 
 | 1242 |     __ subl(temp, pos_reg); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 1243 |     if (length.IsConstant()) { | 
 | 1244 |       __ cmpl(temp, Immediate(length.GetConstant()->AsIntConstant()->GetValue())); | 
 | 1245 |     } else { | 
 | 1246 |       __ cmpl(temp, length.AsRegister<Register>()); | 
 | 1247 |     } | 
| Mark Mendell | 6bc53a9 | 2015-07-01 14:26:52 -0400 | [diff] [blame] | 1248 |     __ j(kLess, slow_path->GetEntryLabel()); | 
 | 1249 |   } | 
 | 1250 | } | 
 | 1251 |  | 
 | 1252 | void IntrinsicCodeGeneratorX86::VisitSystemArrayCopyChar(HInvoke* invoke) { | 
 | 1253 |   X86Assembler* assembler = GetAssembler(); | 
 | 1254 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 1255 |  | 
 | 1256 |   Register src = locations->InAt(0).AsRegister<Register>(); | 
 | 1257 |   Location srcPos = locations->InAt(1); | 
 | 1258 |   Register dest = locations->InAt(2).AsRegister<Register>(); | 
 | 1259 |   Location destPos = locations->InAt(3); | 
 | 1260 |   Location length = locations->InAt(4); | 
 | 1261 |  | 
 | 1262 |   // Temporaries that we need for MOVSW. | 
 | 1263 |   Register src_base = locations->GetTemp(0).AsRegister<Register>(); | 
 | 1264 |   DCHECK_EQ(src_base, ESI); | 
 | 1265 |   Register dest_base = locations->GetTemp(1).AsRegister<Register>(); | 
 | 1266 |   DCHECK_EQ(dest_base, EDI); | 
 | 1267 |   Register count = locations->GetTemp(2).AsRegister<Register>(); | 
 | 1268 |   DCHECK_EQ(count, ECX); | 
 | 1269 |  | 
| Andreas Gampe | 85b62f2 | 2015-09-09 13:15:38 -0700 | [diff] [blame] | 1270 |   SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke); | 
| Mark Mendell | 6bc53a9 | 2015-07-01 14:26:52 -0400 | [diff] [blame] | 1271 |   codegen_->AddSlowPath(slow_path); | 
 | 1272 |  | 
 | 1273 |   // Bail out if the source and destination are the same (to handle overlap). | 
 | 1274 |   __ cmpl(src, dest); | 
 | 1275 |   __ j(kEqual, slow_path->GetEntryLabel()); | 
 | 1276 |  | 
 | 1277 |   // Bail out if the source is null. | 
 | 1278 |   __ testl(src, src); | 
 | 1279 |   __ j(kEqual, slow_path->GetEntryLabel()); | 
 | 1280 |  | 
 | 1281 |   // Bail out if the destination is null. | 
 | 1282 |   __ testl(dest, dest); | 
 | 1283 |   __ j(kEqual, slow_path->GetEntryLabel()); | 
 | 1284 |  | 
 | 1285 |   // If the length is negative, bail out. | 
 | 1286 |   // We have already checked in the LocationsBuilder for the constant case. | 
 | 1287 |   if (!length.IsConstant()) { | 
 | 1288 |     __ cmpl(length.AsRegister<Register>(), length.AsRegister<Register>()); | 
 | 1289 |     __ j(kLess, slow_path->GetEntryLabel()); | 
 | 1290 |   } | 
 | 1291 |  | 
 | 1292 |   // We need the count in ECX. | 
 | 1293 |   if (length.IsConstant()) { | 
 | 1294 |     __ movl(count, Immediate(length.GetConstant()->AsIntConstant()->GetValue())); | 
 | 1295 |   } else { | 
 | 1296 |     __ movl(count, length.AsRegister<Register>()); | 
 | 1297 |   } | 
 | 1298 |  | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 1299 |   // Validity checks: source. Use src_base as a temporary register. | 
 | 1300 |   CheckPosition(assembler, srcPos, src, Location::RegisterLocation(count), slow_path, src_base); | 
| Mark Mendell | 6bc53a9 | 2015-07-01 14:26:52 -0400 | [diff] [blame] | 1301 |  | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 1302 |   // Validity checks: dest. Use src_base as a temporary register. | 
 | 1303 |   CheckPosition(assembler, destPos, dest, Location::RegisterLocation(count), slow_path, src_base); | 
| Mark Mendell | 6bc53a9 | 2015-07-01 14:26:52 -0400 | [diff] [blame] | 1304 |  | 
 | 1305 |   // Okay, everything checks out.  Finally time to do the copy. | 
 | 1306 |   // Check assumption that sizeof(Char) is 2 (used in scaling below). | 
 | 1307 |   const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); | 
 | 1308 |   DCHECK_EQ(char_size, 2u); | 
 | 1309 |  | 
 | 1310 |   const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); | 
 | 1311 |  | 
 | 1312 |   if (srcPos.IsConstant()) { | 
 | 1313 |     int32_t srcPos_const = srcPos.GetConstant()->AsIntConstant()->GetValue(); | 
 | 1314 |     __ leal(src_base, Address(src, char_size * srcPos_const + data_offset)); | 
 | 1315 |   } else { | 
 | 1316 |     __ leal(src_base, Address(src, srcPos.AsRegister<Register>(), | 
 | 1317 |                               ScaleFactor::TIMES_2, data_offset)); | 
 | 1318 |   } | 
 | 1319 |   if (destPos.IsConstant()) { | 
 | 1320 |     int32_t destPos_const = destPos.GetConstant()->AsIntConstant()->GetValue(); | 
 | 1321 |  | 
 | 1322 |     __ leal(dest_base, Address(dest, char_size * destPos_const + data_offset)); | 
 | 1323 |   } else { | 
 | 1324 |     __ leal(dest_base, Address(dest, destPos.AsRegister<Register>(), | 
 | 1325 |                                ScaleFactor::TIMES_2, data_offset)); | 
 | 1326 |   } | 
 | 1327 |  | 
 | 1328 |   // Do the move. | 
 | 1329 |   __ rep_movsw(); | 
 | 1330 |  | 
 | 1331 |   __ Bind(slow_path->GetExitLabel()); | 
 | 1332 | } | 
 | 1333 |  | 
| Nicolas Geoffray | d75948a | 2015-03-27 09:53:16 +0000 | [diff] [blame] | 1334 | void IntrinsicLocationsBuilderX86::VisitStringCompareTo(HInvoke* invoke) { | 
 | 1335 |   // The inputs plus one temp. | 
 | 1336 |   LocationSummary* locations = new (arena_) LocationSummary(invoke, | 
| Serban Constantinescu | 806f012 | 2016-03-09 11:10:16 +0000 | [diff] [blame] | 1337 |                                                             LocationSummary::kCallOnMainAndSlowPath, | 
| Nicolas Geoffray | d75948a | 2015-03-27 09:53:16 +0000 | [diff] [blame] | 1338 |                                                             kIntrinsified); | 
 | 1339 |   InvokeRuntimeCallingConvention calling_convention; | 
 | 1340 |   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); | 
 | 1341 |   locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); | 
 | 1342 |   locations->SetOut(Location::RegisterLocation(EAX)); | 
| Nicolas Geoffray | d75948a | 2015-03-27 09:53:16 +0000 | [diff] [blame] | 1343 | } | 
 | 1344 |  | 
 | 1345 | void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) { | 
 | 1346 |   X86Assembler* assembler = GetAssembler(); | 
 | 1347 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 1348 |  | 
| Nicolas Geoffray | 512e04d | 2015-03-27 17:21:24 +0000 | [diff] [blame] | 1349 |   // Note that the null check must have been done earlier. | 
| Calin Juravle | 641547a | 2015-04-21 22:08:51 +0100 | [diff] [blame] | 1350 |   DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); | 
| Nicolas Geoffray | d75948a | 2015-03-27 09:53:16 +0000 | [diff] [blame] | 1351 |  | 
 | 1352 |   Register argument = locations->InAt(1).AsRegister<Register>(); | 
 | 1353 |   __ testl(argument, argument); | 
| Andreas Gampe | 85b62f2 | 2015-09-09 13:15:38 -0700 | [diff] [blame] | 1354 |   SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke); | 
| Nicolas Geoffray | d75948a | 2015-03-27 09:53:16 +0000 | [diff] [blame] | 1355 |   codegen_->AddSlowPath(slow_path); | 
 | 1356 |   __ j(kEqual, slow_path->GetEntryLabel()); | 
 | 1357 |  | 
| Serban Constantinescu | ba45db0 | 2016-07-12 22:53:02 +0100 | [diff] [blame] | 1358 |   codegen_->InvokeRuntime(kQuickStringCompareTo, invoke, invoke->GetDexPc(), slow_path); | 
| Nicolas Geoffray | d75948a | 2015-03-27 09:53:16 +0000 | [diff] [blame] | 1359 |   __ Bind(slow_path->GetExitLabel()); | 
 | 1360 | } | 
 | 1361 |  | 
| Agi Csaki | d7138c8 | 2015-08-13 17:46:44 -0700 | [diff] [blame] | 1362 | void IntrinsicLocationsBuilderX86::VisitStringEquals(HInvoke* invoke) { | 
 | 1363 |   LocationSummary* locations = new (arena_) LocationSummary(invoke, | 
 | 1364 |                                                             LocationSummary::kNoCall, | 
 | 1365 |                                                             kIntrinsified); | 
 | 1366 |   locations->SetInAt(0, Location::RequiresRegister()); | 
 | 1367 |   locations->SetInAt(1, Location::RequiresRegister()); | 
 | 1368 |  | 
 | 1369 |   // Request temporary registers, ECX and EDI needed for repe_cmpsl instruction. | 
 | 1370 |   locations->AddTemp(Location::RegisterLocation(ECX)); | 
 | 1371 |   locations->AddTemp(Location::RegisterLocation(EDI)); | 
 | 1372 |  | 
 | 1373 |   // Set output, ESI needed for repe_cmpsl instruction anyways. | 
 | 1374 |   locations->SetOut(Location::RegisterLocation(ESI), Location::kOutputOverlap); | 
 | 1375 | } | 
 | 1376 |  | 
 | 1377 | void IntrinsicCodeGeneratorX86::VisitStringEquals(HInvoke* invoke) { | 
 | 1378 |   X86Assembler* assembler = GetAssembler(); | 
 | 1379 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 1380 |  | 
 | 1381 |   Register str = locations->InAt(0).AsRegister<Register>(); | 
 | 1382 |   Register arg = locations->InAt(1).AsRegister<Register>(); | 
 | 1383 |   Register ecx = locations->GetTemp(0).AsRegister<Register>(); | 
 | 1384 |   Register edi = locations->GetTemp(1).AsRegister<Register>(); | 
 | 1385 |   Register esi = locations->Out().AsRegister<Register>(); | 
 | 1386 |  | 
| Mark Mendell | 0c9497d | 2015-08-21 09:30:05 -0400 | [diff] [blame] | 1387 |   NearLabel end, return_true, return_false; | 
| Agi Csaki | d7138c8 | 2015-08-13 17:46:44 -0700 | [diff] [blame] | 1388 |  | 
 | 1389 |   // Get offsets of count, value, and class fields within a string object. | 
 | 1390 |   const uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); | 
 | 1391 |   const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value(); | 
 | 1392 |   const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value(); | 
 | 1393 |  | 
 | 1394 |   // Note that the null check must have been done earlier. | 
 | 1395 |   DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); | 
 | 1396 |  | 
| Nicolas Geoffray | a83a54d | 2015-10-02 17:30:26 +0100 | [diff] [blame] | 1397 |   StringEqualsOptimizations optimizations(invoke); | 
 | 1398 |   if (!optimizations.GetArgumentNotNull()) { | 
 | 1399 |     // Check if input is null, return false if it is. | 
 | 1400 |     __ testl(arg, arg); | 
 | 1401 |     __ j(kEqual, &return_false); | 
 | 1402 |   } | 
| Agi Csaki | d7138c8 | 2015-08-13 17:46:44 -0700 | [diff] [blame] | 1403 |  | 
| Nicolas Geoffray | a83a54d | 2015-10-02 17:30:26 +0100 | [diff] [blame] | 1404 |   if (!optimizations.GetArgumentIsString()) { | 
| Vladimir Marko | 53b5200 | 2016-05-24 19:30:45 +0100 | [diff] [blame] | 1405 |     // Instanceof check for the argument by comparing class fields. | 
 | 1406 |     // All string objects must have the same type since String cannot be subclassed. | 
 | 1407 |     // Receiver must be a string object, so its class field is equal to all strings' class fields. | 
 | 1408 |     // If the argument is a string object, its class field must be equal to receiver's class field. | 
| Nicolas Geoffray | a83a54d | 2015-10-02 17:30:26 +0100 | [diff] [blame] | 1409 |     __ movl(ecx, Address(str, class_offset)); | 
 | 1410 |     __ cmpl(ecx, Address(arg, class_offset)); | 
 | 1411 |     __ j(kNotEqual, &return_false); | 
 | 1412 |   } | 
| Agi Csaki | d7138c8 | 2015-08-13 17:46:44 -0700 | [diff] [blame] | 1413 |  | 
 | 1414 |   // Reference equality check, return true if same reference. | 
 | 1415 |   __ cmpl(str, arg); | 
 | 1416 |   __ j(kEqual, &return_true); | 
 | 1417 |  | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1418 |   // Load length and compression flag of receiver string. | 
| Agi Csaki | d7138c8 | 2015-08-13 17:46:44 -0700 | [diff] [blame] | 1419 |   __ movl(ecx, Address(str, count_offset)); | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1420 |   // Check if lengths and compression flags are equal, return false if they're not. | 
 | 1421 |   // Two identical strings will always have same compression style since | 
 | 1422 |   // compression style is decided on alloc. | 
| Agi Csaki | d7138c8 | 2015-08-13 17:46:44 -0700 | [diff] [blame] | 1423 |   __ cmpl(ecx, Address(arg, count_offset)); | 
 | 1424 |   __ j(kNotEqual, &return_false); | 
| Vladimir Marko | fdaf0f4 | 2016-10-13 19:29:53 +0100 | [diff] [blame] | 1425 |   // Return true if strings are empty. Even with string compression `count == 0` means empty. | 
 | 1426 |   static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, | 
 | 1427 |                 "Expecting 0=compressed, 1=uncompressed"); | 
 | 1428 |   __ jecxz(&return_true); | 
| Agi Csaki | d7138c8 | 2015-08-13 17:46:44 -0700 | [diff] [blame] | 1429 |  | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1430 |   if (mirror::kUseStringCompression) { | 
 | 1431 |     NearLabel string_uncompressed; | 
| Vladimir Marko | fdaf0f4 | 2016-10-13 19:29:53 +0100 | [diff] [blame] | 1432 |     // Extract length and differentiate between both compressed or both uncompressed. | 
 | 1433 |     // Different compression style is cut above. | 
 | 1434 |     __ shrl(ecx, Immediate(1)); | 
 | 1435 |     __ j(kCarrySet, &string_uncompressed); | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1436 |     // Divide string length by 2, rounding up, and continue as if uncompressed. | 
| Vladimir Marko | fdaf0f4 | 2016-10-13 19:29:53 +0100 | [diff] [blame] | 1437 |     __ addl(ecx, Immediate(1)); | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1438 |     __ shrl(ecx, Immediate(1)); | 
 | 1439 |     __ Bind(&string_uncompressed); | 
 | 1440 |   } | 
| Agi Csaki | d7138c8 | 2015-08-13 17:46:44 -0700 | [diff] [blame] | 1441 |   // Load starting addresses of string values into ESI/EDI as required for repe_cmpsl instruction. | 
 | 1442 |   __ leal(esi, Address(str, value_offset)); | 
 | 1443 |   __ leal(edi, Address(arg, value_offset)); | 
 | 1444 |  | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1445 |   // Divide string length by 2 to compare characters 2 at a time and adjust for lengths not | 
 | 1446 |   // divisible by 2. | 
| Agi Csaki | d7138c8 | 2015-08-13 17:46:44 -0700 | [diff] [blame] | 1447 |   __ addl(ecx, Immediate(1)); | 
 | 1448 |   __ shrl(ecx, Immediate(1)); | 
 | 1449 |  | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1450 |   // Assertions that must hold in order to compare strings 2 characters (uncompressed) | 
 | 1451 |   // or 4 characters (compressed) at a time. | 
| Agi Csaki | d7138c8 | 2015-08-13 17:46:44 -0700 | [diff] [blame] | 1452 |   DCHECK_ALIGNED(value_offset, 4); | 
 | 1453 |   static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded"); | 
 | 1454 |  | 
 | 1455 |   // Loop to compare strings two characters at a time starting at the beginning of the string. | 
 | 1456 |   __ repe_cmpsl(); | 
 | 1457 |   // If strings are not equal, zero flag will be cleared. | 
 | 1458 |   __ j(kNotEqual, &return_false); | 
 | 1459 |  | 
 | 1460 |   // Return true and exit the function. | 
 | 1461 |   // If loop does not result in returning false, we return true. | 
 | 1462 |   __ Bind(&return_true); | 
 | 1463 |   __ movl(esi, Immediate(1)); | 
 | 1464 |   __ jmp(&end); | 
 | 1465 |  | 
 | 1466 |   // Return false and exit the function. | 
 | 1467 |   __ Bind(&return_false); | 
 | 1468 |   __ xorl(esi, esi); | 
 | 1469 |   __ Bind(&end); | 
 | 1470 | } | 
 | 1471 |  | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1472 | static void CreateStringIndexOfLocations(HInvoke* invoke, | 
 | 1473 |                                          ArenaAllocator* allocator, | 
 | 1474 |                                          bool start_at_zero) { | 
 | 1475 |   LocationSummary* locations = new (allocator) LocationSummary(invoke, | 
 | 1476 |                                                                LocationSummary::kCallOnSlowPath, | 
 | 1477 |                                                                kIntrinsified); | 
 | 1478 |   // The data needs to be in EDI for scasw. So request that the string is there, anyways. | 
 | 1479 |   locations->SetInAt(0, Location::RegisterLocation(EDI)); | 
 | 1480 |   // If we look for a constant char, we'll still have to copy it into EAX. So just request the | 
 | 1481 |   // allocator to do that, anyways. We can still do the constant check by checking the parameter | 
 | 1482 |   // of the instruction explicitly. | 
 | 1483 |   // Note: This works as we don't clobber EAX anywhere. | 
 | 1484 |   locations->SetInAt(1, Location::RegisterLocation(EAX)); | 
 | 1485 |   if (!start_at_zero) { | 
 | 1486 |     locations->SetInAt(2, Location::RequiresRegister());          // The starting index. | 
 | 1487 |   } | 
 | 1488 |   // As we clobber EDI during execution anyways, also use it as the output. | 
 | 1489 |   locations->SetOut(Location::SameAsFirstInput()); | 
 | 1490 |  | 
 | 1491 |   // repne scasw uses ECX as the counter. | 
 | 1492 |   locations->AddTemp(Location::RegisterLocation(ECX)); | 
 | 1493 |   // Need another temporary to be able to compute the result. | 
 | 1494 |   locations->AddTemp(Location::RequiresRegister()); | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1495 |   if (mirror::kUseStringCompression) { | 
 | 1496 |     // Need another temporary to be able to save unflagged string length. | 
 | 1497 |     locations->AddTemp(Location::RequiresRegister()); | 
 | 1498 |   } | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1499 | } | 
 | 1500 |  | 
 | 1501 | static void GenerateStringIndexOf(HInvoke* invoke, | 
 | 1502 |                                   X86Assembler* assembler, | 
 | 1503 |                                   CodeGeneratorX86* codegen, | 
 | 1504 |                                   ArenaAllocator* allocator, | 
 | 1505 |                                   bool start_at_zero) { | 
 | 1506 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 1507 |  | 
 | 1508 |   // Note that the null check must have been done earlier. | 
 | 1509 |   DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); | 
 | 1510 |  | 
 | 1511 |   Register string_obj = locations->InAt(0).AsRegister<Register>(); | 
 | 1512 |   Register search_value = locations->InAt(1).AsRegister<Register>(); | 
 | 1513 |   Register counter = locations->GetTemp(0).AsRegister<Register>(); | 
 | 1514 |   Register string_length = locations->GetTemp(1).AsRegister<Register>(); | 
 | 1515 |   Register out = locations->Out().AsRegister<Register>(); | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1516 |   // Only used when string compression feature is on. | 
 | 1517 |   Register string_length_flagged; | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1518 |  | 
 | 1519 |   // Check our assumptions for registers. | 
 | 1520 |   DCHECK_EQ(string_obj, EDI); | 
 | 1521 |   DCHECK_EQ(search_value, EAX); | 
 | 1522 |   DCHECK_EQ(counter, ECX); | 
 | 1523 |   DCHECK_EQ(out, EDI); | 
 | 1524 |  | 
 | 1525 |   // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically, | 
| Vladimir Marko | fb6c90a | 2016-05-06 15:52:12 +0100 | [diff] [blame] | 1526 |   // or directly dispatch for a large constant, or omit slow-path for a small constant or a char. | 
| Andreas Gampe | 85b62f2 | 2015-09-09 13:15:38 -0700 | [diff] [blame] | 1527 |   SlowPathCode* slow_path = nullptr; | 
| Vladimir Marko | fb6c90a | 2016-05-06 15:52:12 +0100 | [diff] [blame] | 1528 |   HInstruction* code_point = invoke->InputAt(1); | 
 | 1529 |   if (code_point->IsIntConstant()) { | 
| Vladimir Marko | da05108 | 2016-05-17 16:10:20 +0100 | [diff] [blame] | 1530 |     if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1531 |     std::numeric_limits<uint16_t>::max()) { | 
 | 1532 |       // Always needs the slow-path. We could directly dispatch to it, but this case should be | 
 | 1533 |       // rare, so for simplicity just put the full slow-path down and branch unconditionally. | 
 | 1534 |       slow_path = new (allocator) IntrinsicSlowPathX86(invoke); | 
 | 1535 |       codegen->AddSlowPath(slow_path); | 
 | 1536 |       __ jmp(slow_path->GetEntryLabel()); | 
 | 1537 |       __ Bind(slow_path->GetExitLabel()); | 
 | 1538 |       return; | 
 | 1539 |     } | 
| Vladimir Marko | fb6c90a | 2016-05-06 15:52:12 +0100 | [diff] [blame] | 1540 |   } else if (code_point->GetType() != Primitive::kPrimChar) { | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1541 |     __ cmpl(search_value, Immediate(std::numeric_limits<uint16_t>::max())); | 
 | 1542 |     slow_path = new (allocator) IntrinsicSlowPathX86(invoke); | 
 | 1543 |     codegen->AddSlowPath(slow_path); | 
 | 1544 |     __ j(kAbove, slow_path->GetEntryLabel()); | 
 | 1545 |   } | 
 | 1546 |  | 
 | 1547 |   // From here down, we know that we are looking for a char that fits in 16 bits. | 
 | 1548 |   // Location of reference to data array within the String object. | 
 | 1549 |   int32_t value_offset = mirror::String::ValueOffset().Int32Value(); | 
 | 1550 |   // Location of count within the String object. | 
 | 1551 |   int32_t count_offset = mirror::String::CountOffset().Int32Value(); | 
 | 1552 |  | 
| Vladimir Marko | fdaf0f4 | 2016-10-13 19:29:53 +0100 | [diff] [blame] | 1553 |   // Load the count field of the string containing the length and compression flag. | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1554 |   __ movl(string_length, Address(string_obj, count_offset)); | 
 | 1555 |  | 
| Vladimir Marko | fdaf0f4 | 2016-10-13 19:29:53 +0100 | [diff] [blame] | 1556 |   // Do a zero-length check. Even with string compression `count == 0` means empty. | 
 | 1557 |   static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, | 
 | 1558 |                 "Expecting 0=compressed, 1=uncompressed"); | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1559 |   // TODO: Support jecxz. | 
| Mark Mendell | 0c9497d | 2015-08-21 09:30:05 -0400 | [diff] [blame] | 1560 |   NearLabel not_found_label; | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1561 |   __ testl(string_length, string_length); | 
 | 1562 |   __ j(kEqual, ¬_found_label); | 
 | 1563 |  | 
| Vladimir Marko | fdaf0f4 | 2016-10-13 19:29:53 +0100 | [diff] [blame] | 1564 |   if (mirror::kUseStringCompression) { | 
 | 1565 |     string_length_flagged = locations->GetTemp(2).AsRegister<Register>(); | 
 | 1566 |     __ movl(string_length_flagged, string_length); | 
 | 1567 |     // Extract the length and shift out the least significant bit used as compression flag. | 
 | 1568 |     __ shrl(string_length, Immediate(1)); | 
 | 1569 |   } | 
 | 1570 |  | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1571 |   if (start_at_zero) { | 
 | 1572 |     // Number of chars to scan is the same as the string length. | 
 | 1573 |     __ movl(counter, string_length); | 
 | 1574 |  | 
 | 1575 |     // Move to the start of the string. | 
 | 1576 |     __ addl(string_obj, Immediate(value_offset)); | 
 | 1577 |   } else { | 
 | 1578 |     Register start_index = locations->InAt(2).AsRegister<Register>(); | 
 | 1579 |  | 
 | 1580 |     // Do a start_index check. | 
 | 1581 |     __ cmpl(start_index, string_length); | 
 | 1582 |     __ j(kGreaterEqual, ¬_found_label); | 
 | 1583 |  | 
 | 1584 |     // Ensure we have a start index >= 0; | 
 | 1585 |     __ xorl(counter, counter); | 
 | 1586 |     __ cmpl(start_index, Immediate(0)); | 
 | 1587 |     __ cmovl(kGreater, counter, start_index); | 
 | 1588 |  | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1589 |     if (mirror::kUseStringCompression) { | 
 | 1590 |       NearLabel modify_counter, offset_uncompressed_label; | 
| Vladimir Marko | fdaf0f4 | 2016-10-13 19:29:53 +0100 | [diff] [blame] | 1591 |       __ testl(string_length_flagged, Immediate(1)); | 
 | 1592 |       __ j(kNotZero, &offset_uncompressed_label); | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1593 |       // Move to the start of the string: string_obj + value_offset + start_index. | 
 | 1594 |       __ leal(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_1, value_offset)); | 
 | 1595 |       __ jmp(&modify_counter); | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1596 |  | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1597 |       // Move to the start of the string: string_obj + value_offset + 2 * start_index. | 
 | 1598 |       __ Bind(&offset_uncompressed_label); | 
 | 1599 |       __ leal(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset)); | 
 | 1600 |  | 
 | 1601 |       // Now update ecx (the repne scasw work counter). We have string.length - start_index left to | 
 | 1602 |       // compare. | 
 | 1603 |       __ Bind(&modify_counter); | 
 | 1604 |     } else { | 
 | 1605 |       __ leal(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset)); | 
 | 1606 |     } | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1607 |     __ negl(counter); | 
 | 1608 |     __ leal(counter, Address(string_length, counter, ScaleFactor::TIMES_1, 0)); | 
 | 1609 |   } | 
 | 1610 |  | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1611 |   if (mirror::kUseStringCompression) { | 
 | 1612 |     NearLabel uncompressed_string_comparison; | 
 | 1613 |     NearLabel comparison_done; | 
| Vladimir Marko | fdaf0f4 | 2016-10-13 19:29:53 +0100 | [diff] [blame] | 1614 |     __ testl(string_length_flagged, Immediate(1)); | 
 | 1615 |     __ j(kNotZero, &uncompressed_string_comparison); | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1616 |  | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1617 |     // Check if EAX (search_value) is ASCII. | 
 | 1618 |     __ cmpl(search_value, Immediate(127)); | 
 | 1619 |     __ j(kGreater, ¬_found_label); | 
 | 1620 |     // Comparing byte-per-byte. | 
 | 1621 |     __ repne_scasb(); | 
 | 1622 |     __ jmp(&comparison_done); | 
 | 1623 |  | 
 | 1624 |     // Everything is set up for repne scasw: | 
 | 1625 |     //   * Comparison address in EDI. | 
 | 1626 |     //   * Counter in ECX. | 
 | 1627 |     __ Bind(&uncompressed_string_comparison); | 
 | 1628 |     __ repne_scasw(); | 
 | 1629 |     __ Bind(&comparison_done); | 
 | 1630 |   } else { | 
 | 1631 |     __ repne_scasw(); | 
 | 1632 |   } | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1633 |   // Did we find a match? | 
 | 1634 |   __ j(kNotEqual, ¬_found_label); | 
 | 1635 |  | 
 | 1636 |   // Yes, we matched.  Compute the index of the result. | 
 | 1637 |   __ subl(string_length, counter); | 
 | 1638 |   __ leal(out, Address(string_length, -1)); | 
 | 1639 |  | 
| Mark Mendell | 0c9497d | 2015-08-21 09:30:05 -0400 | [diff] [blame] | 1640 |   NearLabel done; | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1641 |   __ jmp(&done); | 
 | 1642 |  | 
 | 1643 |   // Failed to match; return -1. | 
 | 1644 |   __ Bind(¬_found_label); | 
 | 1645 |   __ movl(out, Immediate(-1)); | 
 | 1646 |  | 
 | 1647 |   // And join up at the end. | 
 | 1648 |   __ Bind(&done); | 
 | 1649 |   if (slow_path != nullptr) { | 
 | 1650 |     __ Bind(slow_path->GetExitLabel()); | 
 | 1651 |   } | 
 | 1652 | } | 
 | 1653 |  | 
 | 1654 | void IntrinsicLocationsBuilderX86::VisitStringIndexOf(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 1655 |   CreateStringIndexOfLocations(invoke, arena_, /* start_at_zero */ true); | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1656 | } | 
 | 1657 |  | 
 | 1658 | void IntrinsicCodeGeneratorX86::VisitStringIndexOf(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 1659 |   GenerateStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ true); | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1660 | } | 
 | 1661 |  | 
 | 1662 | void IntrinsicLocationsBuilderX86::VisitStringIndexOfAfter(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 1663 |   CreateStringIndexOfLocations(invoke, arena_, /* start_at_zero */ false); | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1664 | } | 
 | 1665 |  | 
 | 1666 | void IntrinsicCodeGeneratorX86::VisitStringIndexOfAfter(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 1667 |   GenerateStringIndexOf( | 
 | 1668 |       invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false); | 
| Andreas Gampe | 21030dd | 2015-05-07 14:46:15 -0700 | [diff] [blame] | 1669 | } | 
 | 1670 |  | 
| Jeff Hao | 848f70a | 2014-01-15 13:49:50 -0800 | [diff] [blame] | 1671 | void IntrinsicLocationsBuilderX86::VisitStringNewStringFromBytes(HInvoke* invoke) { | 
 | 1672 |   LocationSummary* locations = new (arena_) LocationSummary(invoke, | 
| Serban Constantinescu | 806f012 | 2016-03-09 11:10:16 +0000 | [diff] [blame] | 1673 |                                                             LocationSummary::kCallOnMainAndSlowPath, | 
| Jeff Hao | 848f70a | 2014-01-15 13:49:50 -0800 | [diff] [blame] | 1674 |                                                             kIntrinsified); | 
 | 1675 |   InvokeRuntimeCallingConvention calling_convention; | 
 | 1676 |   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); | 
 | 1677 |   locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); | 
 | 1678 |   locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); | 
 | 1679 |   locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3))); | 
 | 1680 |   locations->SetOut(Location::RegisterLocation(EAX)); | 
| Jeff Hao | 848f70a | 2014-01-15 13:49:50 -0800 | [diff] [blame] | 1681 | } | 
 | 1682 |  | 
 | 1683 | void IntrinsicCodeGeneratorX86::VisitStringNewStringFromBytes(HInvoke* invoke) { | 
 | 1684 |   X86Assembler* assembler = GetAssembler(); | 
 | 1685 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 1686 |  | 
 | 1687 |   Register byte_array = locations->InAt(0).AsRegister<Register>(); | 
 | 1688 |   __ testl(byte_array, byte_array); | 
| Andreas Gampe | 85b62f2 | 2015-09-09 13:15:38 -0700 | [diff] [blame] | 1689 |   SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke); | 
| Jeff Hao | 848f70a | 2014-01-15 13:49:50 -0800 | [diff] [blame] | 1690 |   codegen_->AddSlowPath(slow_path); | 
 | 1691 |   __ j(kEqual, slow_path->GetEntryLabel()); | 
 | 1692 |  | 
| Serban Constantinescu | ba45db0 | 2016-07-12 22:53:02 +0100 | [diff] [blame] | 1693 |   codegen_->InvokeRuntime(kQuickAllocStringFromBytes, invoke, invoke->GetDexPc()); | 
| Roland Levillain | f969a20 | 2016-03-09 16:14:00 +0000 | [diff] [blame] | 1694 |   CheckEntrypointTypes<kQuickAllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t>(); | 
| Jeff Hao | 848f70a | 2014-01-15 13:49:50 -0800 | [diff] [blame] | 1695 |   __ Bind(slow_path->GetExitLabel()); | 
 | 1696 | } | 
 | 1697 |  | 
 | 1698 | void IntrinsicLocationsBuilderX86::VisitStringNewStringFromChars(HInvoke* invoke) { | 
 | 1699 |   LocationSummary* locations = new (arena_) LocationSummary(invoke, | 
| Serban Constantinescu | 54ff482 | 2016-07-07 18:03:19 +0100 | [diff] [blame] | 1700 |                                                             LocationSummary::kCallOnMainOnly, | 
| Jeff Hao | 848f70a | 2014-01-15 13:49:50 -0800 | [diff] [blame] | 1701 |                                                             kIntrinsified); | 
 | 1702 |   InvokeRuntimeCallingConvention calling_convention; | 
 | 1703 |   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); | 
 | 1704 |   locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); | 
 | 1705 |   locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); | 
 | 1706 |   locations->SetOut(Location::RegisterLocation(EAX)); | 
 | 1707 | } | 
 | 1708 |  | 
 | 1709 | void IntrinsicCodeGeneratorX86::VisitStringNewStringFromChars(HInvoke* invoke) { | 
| Roland Levillain | cc3839c | 2016-02-29 16:23:48 +0000 | [diff] [blame] | 1710 |   // No need to emit code checking whether `locations->InAt(2)` is a null | 
 | 1711 |   // pointer, as callers of the native method | 
 | 1712 |   // | 
 | 1713 |   //   java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) | 
 | 1714 |   // | 
 | 1715 |   // all include a null check on `data` before calling that method. | 
| Serban Constantinescu | ba45db0 | 2016-07-12 22:53:02 +0100 | [diff] [blame] | 1716 |   codegen_->InvokeRuntime(kQuickAllocStringFromChars, invoke, invoke->GetDexPc()); | 
| Roland Levillain | f969a20 | 2016-03-09 16:14:00 +0000 | [diff] [blame] | 1717 |   CheckEntrypointTypes<kQuickAllocStringFromChars, void*, int32_t, int32_t, void*>(); | 
| Jeff Hao | 848f70a | 2014-01-15 13:49:50 -0800 | [diff] [blame] | 1718 | } | 
 | 1719 |  | 
 | 1720 | void IntrinsicLocationsBuilderX86::VisitStringNewStringFromString(HInvoke* invoke) { | 
 | 1721 |   LocationSummary* locations = new (arena_) LocationSummary(invoke, | 
| Serban Constantinescu | 806f012 | 2016-03-09 11:10:16 +0000 | [diff] [blame] | 1722 |                                                             LocationSummary::kCallOnMainAndSlowPath, | 
| Jeff Hao | 848f70a | 2014-01-15 13:49:50 -0800 | [diff] [blame] | 1723 |                                                             kIntrinsified); | 
 | 1724 |   InvokeRuntimeCallingConvention calling_convention; | 
 | 1725 |   locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); | 
 | 1726 |   locations->SetOut(Location::RegisterLocation(EAX)); | 
| Jeff Hao | 848f70a | 2014-01-15 13:49:50 -0800 | [diff] [blame] | 1727 | } | 
 | 1728 |  | 
 | 1729 | void IntrinsicCodeGeneratorX86::VisitStringNewStringFromString(HInvoke* invoke) { | 
 | 1730 |   X86Assembler* assembler = GetAssembler(); | 
 | 1731 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 1732 |  | 
 | 1733 |   Register string_to_copy = locations->InAt(0).AsRegister<Register>(); | 
 | 1734 |   __ testl(string_to_copy, string_to_copy); | 
| Andreas Gampe | 85b62f2 | 2015-09-09 13:15:38 -0700 | [diff] [blame] | 1735 |   SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke); | 
| Jeff Hao | 848f70a | 2014-01-15 13:49:50 -0800 | [diff] [blame] | 1736 |   codegen_->AddSlowPath(slow_path); | 
 | 1737 |   __ j(kEqual, slow_path->GetEntryLabel()); | 
 | 1738 |  | 
| Serban Constantinescu | ba45db0 | 2016-07-12 22:53:02 +0100 | [diff] [blame] | 1739 |   codegen_->InvokeRuntime(kQuickAllocStringFromString, invoke, invoke->GetDexPc()); | 
| Roland Levillain | f969a20 | 2016-03-09 16:14:00 +0000 | [diff] [blame] | 1740 |   CheckEntrypointTypes<kQuickAllocStringFromString, void*, void*>(); | 
| Jeff Hao | 848f70a | 2014-01-15 13:49:50 -0800 | [diff] [blame] | 1741 |   __ Bind(slow_path->GetExitLabel()); | 
 | 1742 | } | 
 | 1743 |  | 
| Mark Mendell | 8f8926a | 2015-08-17 11:39:06 -0400 | [diff] [blame] | 1744 | void IntrinsicLocationsBuilderX86::VisitStringGetCharsNoCheck(HInvoke* invoke) { | 
 | 1745 |   // public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin); | 
 | 1746 |   LocationSummary* locations = new (arena_) LocationSummary(invoke, | 
 | 1747 |                                                             LocationSummary::kNoCall, | 
 | 1748 |                                                             kIntrinsified); | 
 | 1749 |   locations->SetInAt(0, Location::RequiresRegister()); | 
 | 1750 |   locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); | 
 | 1751 |   // Place srcEnd in ECX to save a move below. | 
 | 1752 |   locations->SetInAt(2, Location::RegisterLocation(ECX)); | 
 | 1753 |   locations->SetInAt(3, Location::RequiresRegister()); | 
 | 1754 |   locations->SetInAt(4, Location::RequiresRegister()); | 
 | 1755 |  | 
 | 1756 |   // And we need some temporaries.  We will use REP MOVSW, so we need fixed registers. | 
 | 1757 |   // We don't have enough registers to also grab ECX, so handle below. | 
 | 1758 |   locations->AddTemp(Location::RegisterLocation(ESI)); | 
 | 1759 |   locations->AddTemp(Location::RegisterLocation(EDI)); | 
 | 1760 | } | 
 | 1761 |  | 
 | 1762 | void IntrinsicCodeGeneratorX86::VisitStringGetCharsNoCheck(HInvoke* invoke) { | 
 | 1763 |   X86Assembler* assembler = GetAssembler(); | 
 | 1764 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 1765 |  | 
 | 1766 |   size_t char_component_size = Primitive::ComponentSize(Primitive::kPrimChar); | 
 | 1767 |   // Location of data in char array buffer. | 
 | 1768 |   const uint32_t data_offset = mirror::Array::DataOffset(char_component_size).Uint32Value(); | 
 | 1769 |   // Location of char array data in string. | 
 | 1770 |   const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value(); | 
 | 1771 |  | 
 | 1772 |   // public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin); | 
 | 1773 |   Register obj = locations->InAt(0).AsRegister<Register>(); | 
 | 1774 |   Location srcBegin = locations->InAt(1); | 
 | 1775 |   int srcBegin_value = | 
 | 1776 |     srcBegin.IsConstant() ? srcBegin.GetConstant()->AsIntConstant()->GetValue() : 0; | 
 | 1777 |   Register srcEnd = locations->InAt(2).AsRegister<Register>(); | 
 | 1778 |   Register dst = locations->InAt(3).AsRegister<Register>(); | 
 | 1779 |   Register dstBegin = locations->InAt(4).AsRegister<Register>(); | 
 | 1780 |  | 
 | 1781 |   // Check assumption that sizeof(Char) is 2 (used in scaling below). | 
 | 1782 |   const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); | 
 | 1783 |   DCHECK_EQ(char_size, 2u); | 
 | 1784 |  | 
| Mark Mendell | 8f8926a | 2015-08-17 11:39:06 -0400 | [diff] [blame] | 1785 |   // Compute the number of chars (words) to move. | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1786 |   // Save ECX, since we don't know if it will be used later. | 
| Mark Mendell | 8f8926a | 2015-08-17 11:39:06 -0400 | [diff] [blame] | 1787 |   __ pushl(ECX); | 
 | 1788 |   int stack_adjust = kX86WordSize; | 
 | 1789 |   __ cfi().AdjustCFAOffset(stack_adjust); | 
 | 1790 |   DCHECK_EQ(srcEnd, ECX); | 
 | 1791 |   if (srcBegin.IsConstant()) { | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1792 |     __ subl(ECX, Immediate(srcBegin_value)); | 
| Mark Mendell | 8f8926a | 2015-08-17 11:39:06 -0400 | [diff] [blame] | 1793 |   } else { | 
 | 1794 |     DCHECK(srcBegin.IsRegister()); | 
 | 1795 |     __ subl(ECX, srcBegin.AsRegister<Register>()); | 
 | 1796 |   } | 
 | 1797 |  | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1798 |   NearLabel done; | 
 | 1799 |   if (mirror::kUseStringCompression) { | 
 | 1800 |     // Location of count in string | 
 | 1801 |     const uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); | 
 | 1802 |     const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte); | 
 | 1803 |     DCHECK_EQ(c_char_size, 1u); | 
 | 1804 |     __ pushl(EAX); | 
 | 1805 |     __ cfi().AdjustCFAOffset(stack_adjust); | 
 | 1806 |  | 
 | 1807 |     NearLabel copy_loop, copy_uncompressed; | 
| Vladimir Marko | fdaf0f4 | 2016-10-13 19:29:53 +0100 | [diff] [blame] | 1808 |     __ testl(Address(obj, count_offset), Immediate(1)); | 
 | 1809 |     static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, | 
 | 1810 |                   "Expecting 0=compressed, 1=uncompressed"); | 
 | 1811 |     __ j(kNotZero, ©_uncompressed); | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1812 |     // Compute the address of the source string by adding the number of chars from | 
 | 1813 |     // the source beginning to the value offset of a string. | 
 | 1814 |     __ leal(ESI, CodeGeneratorX86::ArrayAddress(obj, srcBegin, TIMES_1, value_offset)); | 
 | 1815 |  | 
 | 1816 |     // Start the loop to copy String's value to Array of Char. | 
 | 1817 |     __ leal(EDI, Address(dst, dstBegin, ScaleFactor::TIMES_2, data_offset)); | 
 | 1818 |     __ Bind(©_loop); | 
 | 1819 |     __ jecxz(&done); | 
 | 1820 |     // Use EAX temporary (convert byte from ESI to word). | 
 | 1821 |     // TODO: Use LODSB/STOSW (not supported by X86Assembler) with AH initialized to 0. | 
 | 1822 |     __ movzxb(EAX, Address(ESI, 0)); | 
 | 1823 |     __ movw(Address(EDI, 0), EAX); | 
 | 1824 |     __ leal(EDI, Address(EDI, char_size)); | 
 | 1825 |     __ leal(ESI, Address(ESI, c_char_size)); | 
 | 1826 |     // TODO: Add support for LOOP to X86Assembler. | 
 | 1827 |     __ subl(ECX, Immediate(1)); | 
 | 1828 |     __ jmp(©_loop); | 
 | 1829 |     __ Bind(©_uncompressed); | 
 | 1830 |   } | 
 | 1831 |  | 
 | 1832 |   // Do the copy for uncompressed string. | 
 | 1833 |   // Compute the address of the destination buffer. | 
 | 1834 |   __ leal(EDI, Address(dst, dstBegin, ScaleFactor::TIMES_2, data_offset)); | 
 | 1835 |   __ leal(ESI, CodeGeneratorX86::ArrayAddress(obj, srcBegin, TIMES_2, value_offset)); | 
| Mark Mendell | 8f8926a | 2015-08-17 11:39:06 -0400 | [diff] [blame] | 1836 |   __ rep_movsw(); | 
 | 1837 |  | 
| jessicahandojo | 4877b79 | 2016-09-08 19:49:13 -0700 | [diff] [blame] | 1838 |   __ Bind(&done); | 
 | 1839 |   if (mirror::kUseStringCompression) { | 
 | 1840 |     // Restore EAX. | 
 | 1841 |     __ popl(EAX); | 
 | 1842 |     __ cfi().AdjustCFAOffset(-stack_adjust); | 
 | 1843 |   } | 
 | 1844 |   // Restore ECX. | 
| Mark Mendell | 8f8926a | 2015-08-17 11:39:06 -0400 | [diff] [blame] | 1845 |   __ popl(ECX); | 
 | 1846 |   __ cfi().AdjustCFAOffset(-stack_adjust); | 
 | 1847 | } | 
 | 1848 |  | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 1849 | static void GenPeek(LocationSummary* locations, Primitive::Type size, X86Assembler* assembler) { | 
 | 1850 |   Register address = locations->InAt(0).AsRegisterPairLow<Register>(); | 
 | 1851 |   Location out_loc = locations->Out(); | 
 | 1852 |   // x86 allows unaligned access. We do not have to check the input or use specific instructions | 
 | 1853 |   // to avoid a SIGBUS. | 
 | 1854 |   switch (size) { | 
 | 1855 |     case Primitive::kPrimByte: | 
 | 1856 |       __ movsxb(out_loc.AsRegister<Register>(), Address(address, 0)); | 
 | 1857 |       break; | 
 | 1858 |     case Primitive::kPrimShort: | 
 | 1859 |       __ movsxw(out_loc.AsRegister<Register>(), Address(address, 0)); | 
 | 1860 |       break; | 
 | 1861 |     case Primitive::kPrimInt: | 
 | 1862 |       __ movl(out_loc.AsRegister<Register>(), Address(address, 0)); | 
 | 1863 |       break; | 
 | 1864 |     case Primitive::kPrimLong: | 
 | 1865 |       __ movl(out_loc.AsRegisterPairLow<Register>(), Address(address, 0)); | 
 | 1866 |       __ movl(out_loc.AsRegisterPairHigh<Register>(), Address(address, 4)); | 
 | 1867 |       break; | 
 | 1868 |     default: | 
 | 1869 |       LOG(FATAL) << "Type not recognized for peek: " << size; | 
 | 1870 |       UNREACHABLE(); | 
 | 1871 |   } | 
 | 1872 | } | 
 | 1873 |  | 
 | 1874 | void IntrinsicLocationsBuilderX86::VisitMemoryPeekByte(HInvoke* invoke) { | 
 | 1875 |   CreateLongToIntLocations(arena_, invoke); | 
 | 1876 | } | 
 | 1877 |  | 
 | 1878 | void IntrinsicCodeGeneratorX86::VisitMemoryPeekByte(HInvoke* invoke) { | 
 | 1879 |   GenPeek(invoke->GetLocations(), Primitive::kPrimByte, GetAssembler()); | 
 | 1880 | } | 
 | 1881 |  | 
 | 1882 | void IntrinsicLocationsBuilderX86::VisitMemoryPeekIntNative(HInvoke* invoke) { | 
 | 1883 |   CreateLongToIntLocations(arena_, invoke); | 
 | 1884 | } | 
 | 1885 |  | 
 | 1886 | void IntrinsicCodeGeneratorX86::VisitMemoryPeekIntNative(HInvoke* invoke) { | 
 | 1887 |   GenPeek(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler()); | 
 | 1888 | } | 
 | 1889 |  | 
 | 1890 | void IntrinsicLocationsBuilderX86::VisitMemoryPeekLongNative(HInvoke* invoke) { | 
 | 1891 |   CreateLongToLongLocations(arena_, invoke); | 
 | 1892 | } | 
 | 1893 |  | 
 | 1894 | void IntrinsicCodeGeneratorX86::VisitMemoryPeekLongNative(HInvoke* invoke) { | 
 | 1895 |   GenPeek(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler()); | 
 | 1896 | } | 
 | 1897 |  | 
 | 1898 | void IntrinsicLocationsBuilderX86::VisitMemoryPeekShortNative(HInvoke* invoke) { | 
 | 1899 |   CreateLongToIntLocations(arena_, invoke); | 
 | 1900 | } | 
 | 1901 |  | 
 | 1902 | void IntrinsicCodeGeneratorX86::VisitMemoryPeekShortNative(HInvoke* invoke) { | 
 | 1903 |   GenPeek(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler()); | 
 | 1904 | } | 
 | 1905 |  | 
 | 1906 | static void CreateLongIntToVoidLocations(ArenaAllocator* arena, Primitive::Type size, | 
 | 1907 |                                          HInvoke* invoke) { | 
 | 1908 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 1909 |                                                            LocationSummary::kNoCall, | 
 | 1910 |                                                            kIntrinsified); | 
 | 1911 |   locations->SetInAt(0, Location::RequiresRegister()); | 
| Roland Levillain | 4c0eb42 | 2015-04-24 16:43:49 +0100 | [diff] [blame] | 1912 |   HInstruction* value = invoke->InputAt(1); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 1913 |   if (size == Primitive::kPrimByte) { | 
 | 1914 |     locations->SetInAt(1, Location::ByteRegisterOrConstant(EDX, value)); | 
 | 1915 |   } else { | 
 | 1916 |     locations->SetInAt(1, Location::RegisterOrConstant(value)); | 
 | 1917 |   } | 
 | 1918 | } | 
 | 1919 |  | 
 | 1920 | static void GenPoke(LocationSummary* locations, Primitive::Type size, X86Assembler* assembler) { | 
 | 1921 |   Register address = locations->InAt(0).AsRegisterPairLow<Register>(); | 
 | 1922 |   Location value_loc = locations->InAt(1); | 
 | 1923 |   // x86 allows unaligned access. We do not have to check the input or use specific instructions | 
 | 1924 |   // to avoid a SIGBUS. | 
 | 1925 |   switch (size) { | 
 | 1926 |     case Primitive::kPrimByte: | 
 | 1927 |       if (value_loc.IsConstant()) { | 
 | 1928 |         __ movb(Address(address, 0), | 
 | 1929 |                 Immediate(value_loc.GetConstant()->AsIntConstant()->GetValue())); | 
 | 1930 |       } else { | 
 | 1931 |         __ movb(Address(address, 0), value_loc.AsRegister<ByteRegister>()); | 
 | 1932 |       } | 
 | 1933 |       break; | 
 | 1934 |     case Primitive::kPrimShort: | 
 | 1935 |       if (value_loc.IsConstant()) { | 
 | 1936 |         __ movw(Address(address, 0), | 
 | 1937 |                 Immediate(value_loc.GetConstant()->AsIntConstant()->GetValue())); | 
 | 1938 |       } else { | 
 | 1939 |         __ movw(Address(address, 0), value_loc.AsRegister<Register>()); | 
 | 1940 |       } | 
 | 1941 |       break; | 
 | 1942 |     case Primitive::kPrimInt: | 
 | 1943 |       if (value_loc.IsConstant()) { | 
 | 1944 |         __ movl(Address(address, 0), | 
 | 1945 |                 Immediate(value_loc.GetConstant()->AsIntConstant()->GetValue())); | 
 | 1946 |       } else { | 
 | 1947 |         __ movl(Address(address, 0), value_loc.AsRegister<Register>()); | 
 | 1948 |       } | 
 | 1949 |       break; | 
 | 1950 |     case Primitive::kPrimLong: | 
 | 1951 |       if (value_loc.IsConstant()) { | 
 | 1952 |         int64_t value = value_loc.GetConstant()->AsLongConstant()->GetValue(); | 
 | 1953 |         __ movl(Address(address, 0), Immediate(Low32Bits(value))); | 
 | 1954 |         __ movl(Address(address, 4), Immediate(High32Bits(value))); | 
 | 1955 |       } else { | 
 | 1956 |         __ movl(Address(address, 0), value_loc.AsRegisterPairLow<Register>()); | 
 | 1957 |         __ movl(Address(address, 4), value_loc.AsRegisterPairHigh<Register>()); | 
 | 1958 |       } | 
 | 1959 |       break; | 
 | 1960 |     default: | 
 | 1961 |       LOG(FATAL) << "Type not recognized for poke: " << size; | 
 | 1962 |       UNREACHABLE(); | 
 | 1963 |   } | 
 | 1964 | } | 
 | 1965 |  | 
 | 1966 | void IntrinsicLocationsBuilderX86::VisitMemoryPokeByte(HInvoke* invoke) { | 
 | 1967 |   CreateLongIntToVoidLocations(arena_, Primitive::kPrimByte, invoke); | 
 | 1968 | } | 
 | 1969 |  | 
 | 1970 | void IntrinsicCodeGeneratorX86::VisitMemoryPokeByte(HInvoke* invoke) { | 
 | 1971 |   GenPoke(invoke->GetLocations(), Primitive::kPrimByte, GetAssembler()); | 
 | 1972 | } | 
 | 1973 |  | 
 | 1974 | void IntrinsicLocationsBuilderX86::VisitMemoryPokeIntNative(HInvoke* invoke) { | 
 | 1975 |   CreateLongIntToVoidLocations(arena_, Primitive::kPrimInt, invoke); | 
 | 1976 | } | 
 | 1977 |  | 
 | 1978 | void IntrinsicCodeGeneratorX86::VisitMemoryPokeIntNative(HInvoke* invoke) { | 
 | 1979 |   GenPoke(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler()); | 
 | 1980 | } | 
 | 1981 |  | 
 | 1982 | void IntrinsicLocationsBuilderX86::VisitMemoryPokeLongNative(HInvoke* invoke) { | 
 | 1983 |   CreateLongIntToVoidLocations(arena_, Primitive::kPrimLong, invoke); | 
 | 1984 | } | 
 | 1985 |  | 
 | 1986 | void IntrinsicCodeGeneratorX86::VisitMemoryPokeLongNative(HInvoke* invoke) { | 
 | 1987 |   GenPoke(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler()); | 
 | 1988 | } | 
 | 1989 |  | 
 | 1990 | void IntrinsicLocationsBuilderX86::VisitMemoryPokeShortNative(HInvoke* invoke) { | 
 | 1991 |   CreateLongIntToVoidLocations(arena_, Primitive::kPrimShort, invoke); | 
 | 1992 | } | 
 | 1993 |  | 
 | 1994 | void IntrinsicCodeGeneratorX86::VisitMemoryPokeShortNative(HInvoke* invoke) { | 
 | 1995 |   GenPoke(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler()); | 
 | 1996 | } | 
 | 1997 |  | 
 | 1998 | void IntrinsicLocationsBuilderX86::VisitThreadCurrentThread(HInvoke* invoke) { | 
 | 1999 |   LocationSummary* locations = new (arena_) LocationSummary(invoke, | 
 | 2000 |                                                             LocationSummary::kNoCall, | 
 | 2001 |                                                             kIntrinsified); | 
 | 2002 |   locations->SetOut(Location::RequiresRegister()); | 
 | 2003 | } | 
 | 2004 |  | 
 | 2005 | void IntrinsicCodeGeneratorX86::VisitThreadCurrentThread(HInvoke* invoke) { | 
 | 2006 |   Register out = invoke->GetLocations()->Out().AsRegister<Register>(); | 
| Andreas Gampe | 542451c | 2016-07-26 09:02:02 -0700 | [diff] [blame] | 2007 |   GetAssembler()->fs()->movl(out, Address::Absolute(Thread::PeerOffset<kX86PointerSize>())); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2008 | } | 
 | 2009 |  | 
| Roland Levillain | 0d5a281 | 2015-11-13 10:07:31 +0000 | [diff] [blame] | 2010 | static void GenUnsafeGet(HInvoke* invoke, | 
 | 2011 |                          Primitive::Type type, | 
 | 2012 |                          bool is_volatile, | 
 | 2013 |                          CodeGeneratorX86* codegen) { | 
 | 2014 |   X86Assembler* assembler = down_cast<X86Assembler*>(codegen->GetAssembler()); | 
 | 2015 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 2016 |   Location base_loc = locations->InAt(1); | 
 | 2017 |   Register base = base_loc.AsRegister<Register>(); | 
 | 2018 |   Location offset_loc = locations->InAt(2); | 
 | 2019 |   Register offset = offset_loc.AsRegisterPairLow<Register>(); | 
 | 2020 |   Location output_loc = locations->Out(); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2021 |  | 
 | 2022 |   switch (type) { | 
| Roland Levillain | 7c1559a | 2015-12-15 10:55:36 +0000 | [diff] [blame] | 2023 |     case Primitive::kPrimInt: { | 
| Roland Levillain | 0d5a281 | 2015-11-13 10:07:31 +0000 | [diff] [blame] | 2024 |       Register output = output_loc.AsRegister<Register>(); | 
 | 2025 |       __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0)); | 
| Roland Levillain | 7c1559a | 2015-12-15 10:55:36 +0000 | [diff] [blame] | 2026 |       break; | 
 | 2027 |     } | 
 | 2028 |  | 
 | 2029 |     case Primitive::kPrimNot: { | 
 | 2030 |       Register output = output_loc.AsRegister<Register>(); | 
 | 2031 |       if (kEmitCompilerReadBarrier) { | 
 | 2032 |         if (kUseBakerReadBarrier) { | 
| Sang, Chunlei | 0fcd2b8 | 2016-04-05 17:12:59 +0800 | [diff] [blame] | 2033 |           Address src(base, offset, ScaleFactor::TIMES_1, 0); | 
 | 2034 |           codegen->GenerateReferenceLoadWithBakerReadBarrier( | 
| Vladimir Marko | 953437b | 2016-08-24 08:30:46 +0000 | [diff] [blame] | 2035 |               invoke, output_loc, base, src, /* needs_null_check */ false); | 
| Roland Levillain | 7c1559a | 2015-12-15 10:55:36 +0000 | [diff] [blame] | 2036 |         } else { | 
 | 2037 |           __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0)); | 
 | 2038 |           codegen->GenerateReadBarrierSlow( | 
 | 2039 |               invoke, output_loc, output_loc, base_loc, 0U, offset_loc); | 
 | 2040 |         } | 
 | 2041 |       } else { | 
 | 2042 |         __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0)); | 
 | 2043 |         __ MaybeUnpoisonHeapReference(output); | 
| Roland Levillain | 4d02711 | 2015-07-01 15:41:14 +0100 | [diff] [blame] | 2044 |       } | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2045 |       break; | 
| Roland Levillain | 4d02711 | 2015-07-01 15:41:14 +0100 | [diff] [blame] | 2046 |     } | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2047 |  | 
 | 2048 |     case Primitive::kPrimLong: { | 
| Roland Levillain | 0d5a281 | 2015-11-13 10:07:31 +0000 | [diff] [blame] | 2049 |         Register output_lo = output_loc.AsRegisterPairLow<Register>(); | 
 | 2050 |         Register output_hi = output_loc.AsRegisterPairHigh<Register>(); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2051 |         if (is_volatile) { | 
 | 2052 |           // Need to use a XMM to read atomically. | 
 | 2053 |           XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); | 
 | 2054 |           __ movsd(temp, Address(base, offset, ScaleFactor::TIMES_1, 0)); | 
 | 2055 |           __ movd(output_lo, temp); | 
 | 2056 |           __ psrlq(temp, Immediate(32)); | 
 | 2057 |           __ movd(output_hi, temp); | 
 | 2058 |         } else { | 
 | 2059 |           __ movl(output_lo, Address(base, offset, ScaleFactor::TIMES_1, 0)); | 
 | 2060 |           __ movl(output_hi, Address(base, offset, ScaleFactor::TIMES_1, 4)); | 
 | 2061 |         } | 
 | 2062 |       } | 
 | 2063 |       break; | 
 | 2064 |  | 
 | 2065 |     default: | 
 | 2066 |       LOG(FATAL) << "Unsupported op size " << type; | 
 | 2067 |       UNREACHABLE(); | 
 | 2068 |   } | 
 | 2069 | } | 
 | 2070 |  | 
| Roland Levillain | 7c1559a | 2015-12-15 10:55:36 +0000 | [diff] [blame] | 2071 | static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, | 
 | 2072 |                                           HInvoke* invoke, | 
 | 2073 |                                           Primitive::Type type, | 
 | 2074 |                                           bool is_volatile) { | 
| Roland Levillain | 0d5a281 | 2015-11-13 10:07:31 +0000 | [diff] [blame] | 2075 |   bool can_call = kEmitCompilerReadBarrier && | 
 | 2076 |       (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject || | 
 | 2077 |        invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2078 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2079 |                                                            (can_call | 
 | 2080 |                                                                 ? LocationSummary::kCallOnSlowPath | 
 | 2081 |                                                                 : LocationSummary::kNoCall), | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2082 |                                                            kIntrinsified); | 
| Vladimir Marko | 70e9746 | 2016-08-09 11:04:26 +0100 | [diff] [blame] | 2083 |   if (can_call && kUseBakerReadBarrier) { | 
| Vladimir Marko | 804b03f | 2016-09-14 16:26:36 +0100 | [diff] [blame] | 2084 |     locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty());  // No caller-save registers. | 
| Vladimir Marko | 70e9746 | 2016-08-09 11:04:26 +0100 | [diff] [blame] | 2085 |   } | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2086 |   locations->SetInAt(0, Location::NoLocation());        // Unused receiver. | 
 | 2087 |   locations->SetInAt(1, Location::RequiresRegister()); | 
 | 2088 |   locations->SetInAt(2, Location::RequiresRegister()); | 
| Roland Levillain | 7c1559a | 2015-12-15 10:55:36 +0000 | [diff] [blame] | 2089 |   if (type == Primitive::kPrimLong) { | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2090 |     if (is_volatile) { | 
 | 2091 |       // Need to use XMM to read volatile. | 
 | 2092 |       locations->AddTemp(Location::RequiresFpuRegister()); | 
| Roland Levillain | 3d31242 | 2016-06-23 13:53:42 +0100 | [diff] [blame] | 2093 |       locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2094 |     } else { | 
 | 2095 |       locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); | 
 | 2096 |     } | 
 | 2097 |   } else { | 
| Roland Levillain | 3d31242 | 2016-06-23 13:53:42 +0100 | [diff] [blame] | 2098 |     locations->SetOut(Location::RequiresRegister(), | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2099 |                       (can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap)); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2100 |   } | 
 | 2101 | } | 
 | 2102 |  | 
 | 2103 | void IntrinsicLocationsBuilderX86::VisitUnsafeGet(HInvoke* invoke) { | 
| Roland Levillain | 7c1559a | 2015-12-15 10:55:36 +0000 | [diff] [blame] | 2104 |   CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt, /* is_volatile */ false); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2105 | } | 
 | 2106 | void IntrinsicLocationsBuilderX86::VisitUnsafeGetVolatile(HInvoke* invoke) { | 
| Roland Levillain | 7c1559a | 2015-12-15 10:55:36 +0000 | [diff] [blame] | 2107 |   CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt, /* is_volatile */ true); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2108 | } | 
 | 2109 | void IntrinsicLocationsBuilderX86::VisitUnsafeGetLong(HInvoke* invoke) { | 
| Roland Levillain | 7c1559a | 2015-12-15 10:55:36 +0000 | [diff] [blame] | 2110 |   CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong, /* is_volatile */ false); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2111 | } | 
 | 2112 | void IntrinsicLocationsBuilderX86::VisitUnsafeGetLongVolatile(HInvoke* invoke) { | 
| Roland Levillain | 7c1559a | 2015-12-15 10:55:36 +0000 | [diff] [blame] | 2113 |   CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong, /* is_volatile */ true); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2114 | } | 
 | 2115 | void IntrinsicLocationsBuilderX86::VisitUnsafeGetObject(HInvoke* invoke) { | 
| Roland Levillain | 7c1559a | 2015-12-15 10:55:36 +0000 | [diff] [blame] | 2116 |   CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot, /* is_volatile */ false); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2117 | } | 
 | 2118 | void IntrinsicLocationsBuilderX86::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { | 
| Roland Levillain | 7c1559a | 2015-12-15 10:55:36 +0000 | [diff] [blame] | 2119 |   CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot, /* is_volatile */ true); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2120 | } | 
 | 2121 |  | 
 | 2122 |  | 
 | 2123 | void IntrinsicCodeGeneratorX86::VisitUnsafeGet(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2124 |   GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2125 | } | 
 | 2126 | void IntrinsicCodeGeneratorX86::VisitUnsafeGetVolatile(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2127 |   GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2128 | } | 
 | 2129 | void IntrinsicCodeGeneratorX86::VisitUnsafeGetLong(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2130 |   GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2131 | } | 
 | 2132 | void IntrinsicCodeGeneratorX86::VisitUnsafeGetLongVolatile(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2133 |   GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2134 | } | 
 | 2135 | void IntrinsicCodeGeneratorX86::VisitUnsafeGetObject(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2136 |   GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2137 | } | 
 | 2138 | void IntrinsicCodeGeneratorX86::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2139 |   GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2140 | } | 
 | 2141 |  | 
 | 2142 |  | 
 | 2143 | static void CreateIntIntIntIntToVoidPlusTempsLocations(ArenaAllocator* arena, | 
 | 2144 |                                                        Primitive::Type type, | 
 | 2145 |                                                        HInvoke* invoke, | 
 | 2146 |                                                        bool is_volatile) { | 
 | 2147 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 2148 |                                                            LocationSummary::kNoCall, | 
 | 2149 |                                                            kIntrinsified); | 
 | 2150 |   locations->SetInAt(0, Location::NoLocation());        // Unused receiver. | 
 | 2151 |   locations->SetInAt(1, Location::RequiresRegister()); | 
 | 2152 |   locations->SetInAt(2, Location::RequiresRegister()); | 
 | 2153 |   locations->SetInAt(3, Location::RequiresRegister()); | 
 | 2154 |   if (type == Primitive::kPrimNot) { | 
 | 2155 |     // Need temp registers for card-marking. | 
| Roland Levillain | 4d02711 | 2015-07-01 15:41:14 +0100 | [diff] [blame] | 2156 |     locations->AddTemp(Location::RequiresRegister());  // Possibly used for reference poisoning too. | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2157 |     // Ensure the value is in a byte register. | 
 | 2158 |     locations->AddTemp(Location::RegisterLocation(ECX)); | 
 | 2159 |   } else if (type == Primitive::kPrimLong && is_volatile) { | 
 | 2160 |     locations->AddTemp(Location::RequiresFpuRegister()); | 
 | 2161 |     locations->AddTemp(Location::RequiresFpuRegister()); | 
 | 2162 |   } | 
 | 2163 | } | 
 | 2164 |  | 
 | 2165 | void IntrinsicLocationsBuilderX86::VisitUnsafePut(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2166 |   CreateIntIntIntIntToVoidPlusTempsLocations( | 
 | 2167 |       arena_, Primitive::kPrimInt, invoke, /* is_volatile */ false); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2168 | } | 
 | 2169 | void IntrinsicLocationsBuilderX86::VisitUnsafePutOrdered(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2170 |   CreateIntIntIntIntToVoidPlusTempsLocations( | 
 | 2171 |       arena_, Primitive::kPrimInt, invoke, /* is_volatile */ false); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2172 | } | 
 | 2173 | void IntrinsicLocationsBuilderX86::VisitUnsafePutVolatile(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2174 |   CreateIntIntIntIntToVoidPlusTempsLocations( | 
 | 2175 |       arena_, Primitive::kPrimInt, invoke, /* is_volatile */ true); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2176 | } | 
 | 2177 | void IntrinsicLocationsBuilderX86::VisitUnsafePutObject(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2178 |   CreateIntIntIntIntToVoidPlusTempsLocations( | 
 | 2179 |       arena_, Primitive::kPrimNot, invoke, /* is_volatile */ false); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2180 | } | 
 | 2181 | void IntrinsicLocationsBuilderX86::VisitUnsafePutObjectOrdered(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2182 |   CreateIntIntIntIntToVoidPlusTempsLocations( | 
 | 2183 |       arena_, Primitive::kPrimNot, invoke, /* is_volatile */ false); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2184 | } | 
 | 2185 | void IntrinsicLocationsBuilderX86::VisitUnsafePutObjectVolatile(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2186 |   CreateIntIntIntIntToVoidPlusTempsLocations( | 
 | 2187 |       arena_, Primitive::kPrimNot, invoke, /* is_volatile */ true); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2188 | } | 
 | 2189 | void IntrinsicLocationsBuilderX86::VisitUnsafePutLong(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2190 |   CreateIntIntIntIntToVoidPlusTempsLocations( | 
 | 2191 |       arena_, Primitive::kPrimLong, invoke, /* is_volatile */ false); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2192 | } | 
 | 2193 | void IntrinsicLocationsBuilderX86::VisitUnsafePutLongOrdered(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2194 |   CreateIntIntIntIntToVoidPlusTempsLocations( | 
 | 2195 |       arena_, Primitive::kPrimLong, invoke, /* is_volatile */ false); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2196 | } | 
 | 2197 | void IntrinsicLocationsBuilderX86::VisitUnsafePutLongVolatile(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2198 |   CreateIntIntIntIntToVoidPlusTempsLocations( | 
 | 2199 |       arena_, Primitive::kPrimLong, invoke, /* is_volatile */ true); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2200 | } | 
 | 2201 |  | 
 | 2202 | // We don't care for ordered: it requires an AnyStore barrier, which is already given by the x86 | 
 | 2203 | // memory model. | 
 | 2204 | static void GenUnsafePut(LocationSummary* locations, | 
 | 2205 |                          Primitive::Type type, | 
 | 2206 |                          bool is_volatile, | 
 | 2207 |                          CodeGeneratorX86* codegen) { | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2208 |   X86Assembler* assembler = down_cast<X86Assembler*>(codegen->GetAssembler()); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2209 |   Register base = locations->InAt(1).AsRegister<Register>(); | 
 | 2210 |   Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); | 
 | 2211 |   Location value_loc = locations->InAt(3); | 
 | 2212 |  | 
 | 2213 |   if (type == Primitive::kPrimLong) { | 
 | 2214 |     Register value_lo = value_loc.AsRegisterPairLow<Register>(); | 
 | 2215 |     Register value_hi = value_loc.AsRegisterPairHigh<Register>(); | 
 | 2216 |     if (is_volatile) { | 
 | 2217 |       XmmRegister temp1 = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); | 
 | 2218 |       XmmRegister temp2 = locations->GetTemp(1).AsFpuRegister<XmmRegister>(); | 
 | 2219 |       __ movd(temp1, value_lo); | 
 | 2220 |       __ movd(temp2, value_hi); | 
 | 2221 |       __ punpckldq(temp1, temp2); | 
 | 2222 |       __ movsd(Address(base, offset, ScaleFactor::TIMES_1, 0), temp1); | 
 | 2223 |     } else { | 
 | 2224 |       __ movl(Address(base, offset, ScaleFactor::TIMES_1, 0), value_lo); | 
 | 2225 |       __ movl(Address(base, offset, ScaleFactor::TIMES_1, 4), value_hi); | 
 | 2226 |     } | 
| Roland Levillain | 4d02711 | 2015-07-01 15:41:14 +0100 | [diff] [blame] | 2227 |   } else if (kPoisonHeapReferences && type == Primitive::kPrimNot) { | 
 | 2228 |     Register temp = locations->GetTemp(0).AsRegister<Register>(); | 
 | 2229 |     __ movl(temp, value_loc.AsRegister<Register>()); | 
 | 2230 |     __ PoisonHeapReference(temp); | 
 | 2231 |     __ movl(Address(base, offset, ScaleFactor::TIMES_1, 0), temp); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2232 |   } else { | 
 | 2233 |     __ movl(Address(base, offset, ScaleFactor::TIMES_1, 0), value_loc.AsRegister<Register>()); | 
 | 2234 |   } | 
 | 2235 |  | 
 | 2236 |   if (is_volatile) { | 
| Mark P Mendell | 17077d8 | 2015-12-16 19:15:59 +0000 | [diff] [blame] | 2237 |     codegen->MemoryFence(); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2238 |   } | 
 | 2239 |  | 
 | 2240 |   if (type == Primitive::kPrimNot) { | 
| Nicolas Geoffray | 07276db | 2015-05-18 14:22:09 +0100 | [diff] [blame] | 2241 |     bool value_can_be_null = true;  // TODO: Worth finding out this information? | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2242 |     codegen->MarkGCCard(locations->GetTemp(0).AsRegister<Register>(), | 
 | 2243 |                         locations->GetTemp(1).AsRegister<Register>(), | 
 | 2244 |                         base, | 
| Nicolas Geoffray | 07276db | 2015-05-18 14:22:09 +0100 | [diff] [blame] | 2245 |                         value_loc.AsRegister<Register>(), | 
 | 2246 |                         value_can_be_null); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2247 |   } | 
 | 2248 | } | 
 | 2249 |  | 
 | 2250 | void IntrinsicCodeGeneratorX86::VisitUnsafePut(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2251 |   GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, /* is_volatile */ false, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2252 | } | 
 | 2253 | void IntrinsicCodeGeneratorX86::VisitUnsafePutOrdered(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2254 |   GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, /* is_volatile */ false, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2255 | } | 
 | 2256 | void IntrinsicCodeGeneratorX86::VisitUnsafePutVolatile(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2257 |   GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, /* is_volatile */ true, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2258 | } | 
 | 2259 | void IntrinsicCodeGeneratorX86::VisitUnsafePutObject(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2260 |   GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, /* is_volatile */ false, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2261 | } | 
 | 2262 | void IntrinsicCodeGeneratorX86::VisitUnsafePutObjectOrdered(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2263 |   GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, /* is_volatile */ false, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2264 | } | 
 | 2265 | void IntrinsicCodeGeneratorX86::VisitUnsafePutObjectVolatile(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2266 |   GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, /* is_volatile */ true, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2267 | } | 
 | 2268 | void IntrinsicCodeGeneratorX86::VisitUnsafePutLong(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2269 |   GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, /* is_volatile */ false, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2270 | } | 
 | 2271 | void IntrinsicCodeGeneratorX86::VisitUnsafePutLongOrdered(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2272 |   GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, /* is_volatile */ false, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2273 | } | 
 | 2274 | void IntrinsicCodeGeneratorX86::VisitUnsafePutLongVolatile(HInvoke* invoke) { | 
| Roland Levillain | bf84a3d | 2015-12-04 14:33:02 +0000 | [diff] [blame] | 2275 |   GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, /* is_volatile */ true, codegen_); | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 2276 | } | 
 | 2277 |  | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2278 | static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, | 
 | 2279 |                                        Primitive::Type type, | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2280 |                                        HInvoke* invoke) { | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2281 |   bool can_call = kEmitCompilerReadBarrier && | 
 | 2282 |       kUseBakerReadBarrier && | 
 | 2283 |       (invoke->GetIntrinsic() == Intrinsics::kUnsafeCASObject); | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2284 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2285 |                                                            (can_call | 
 | 2286 |                                                                 ? LocationSummary::kCallOnSlowPath | 
 | 2287 |                                                                 : LocationSummary::kNoCall), | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2288 |                                                            kIntrinsified); | 
 | 2289 |   locations->SetInAt(0, Location::NoLocation());        // Unused receiver. | 
 | 2290 |   locations->SetInAt(1, Location::RequiresRegister()); | 
 | 2291 |   // Offset is a long, but in 32 bit mode, we only need the low word. | 
 | 2292 |   // Can we update the invoke here to remove a TypeConvert to Long? | 
 | 2293 |   locations->SetInAt(2, Location::RequiresRegister()); | 
 | 2294 |   // Expected value must be in EAX or EDX:EAX. | 
 | 2295 |   // For long, new value must be in ECX:EBX. | 
 | 2296 |   if (type == Primitive::kPrimLong) { | 
 | 2297 |     locations->SetInAt(3, Location::RegisterPairLocation(EAX, EDX)); | 
 | 2298 |     locations->SetInAt(4, Location::RegisterPairLocation(EBX, ECX)); | 
 | 2299 |   } else { | 
 | 2300 |     locations->SetInAt(3, Location::RegisterLocation(EAX)); | 
 | 2301 |     locations->SetInAt(4, Location::RequiresRegister()); | 
 | 2302 |   } | 
 | 2303 |  | 
 | 2304 |   // Force a byte register for the output. | 
 | 2305 |   locations->SetOut(Location::RegisterLocation(EAX)); | 
 | 2306 |   if (type == Primitive::kPrimNot) { | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2307 |     // Need temporary registers for card-marking, and possibly for | 
 | 2308 |     // (Baker) read barrier. | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2309 |     locations->AddTemp(Location::RequiresRegister());  // Possibly used for reference poisoning too. | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2310 |     // Need a byte register for marking. | 
 | 2311 |     locations->AddTemp(Location::RegisterLocation(ECX)); | 
 | 2312 |   } | 
 | 2313 | } | 
 | 2314 |  | 
 | 2315 | void IntrinsicLocationsBuilderX86::VisitUnsafeCASInt(HInvoke* invoke) { | 
 | 2316 |   CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimInt, invoke); | 
 | 2317 | } | 
 | 2318 |  | 
 | 2319 | void IntrinsicLocationsBuilderX86::VisitUnsafeCASLong(HInvoke* invoke) { | 
 | 2320 |   CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimLong, invoke); | 
 | 2321 | } | 
 | 2322 |  | 
 | 2323 | void IntrinsicLocationsBuilderX86::VisitUnsafeCASObject(HInvoke* invoke) { | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2324 |   // The only read barrier implementation supporting the | 
 | 2325 |   // UnsafeCASObject intrinsic is the Baker-style read barriers. | 
 | 2326 |   if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) { | 
| Roland Levillain | 391b866 | 2015-12-18 11:43:38 +0000 | [diff] [blame] | 2327 |     return; | 
 | 2328 |   } | 
 | 2329 |  | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2330 |   CreateIntIntIntIntIntToInt(arena_, Primitive::kPrimNot, invoke); | 
 | 2331 | } | 
 | 2332 |  | 
 | 2333 | static void GenCAS(Primitive::Type type, HInvoke* invoke, CodeGeneratorX86* codegen) { | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2334 |   X86Assembler* assembler = down_cast<X86Assembler*>(codegen->GetAssembler()); | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2335 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 2336 |  | 
 | 2337 |   Register base = locations->InAt(1).AsRegister<Register>(); | 
 | 2338 |   Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); | 
 | 2339 |   Location out = locations->Out(); | 
 | 2340 |   DCHECK_EQ(out.AsRegister<Register>(), EAX); | 
 | 2341 |  | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2342 |   // The address of the field within the holding object. | 
 | 2343 |   Address field_addr(base, offset, ScaleFactor::TIMES_1, 0); | 
 | 2344 |  | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2345 |   if (type == Primitive::kPrimNot) { | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2346 |     // The only read barrier implementation supporting the | 
 | 2347 |     // UnsafeCASObject intrinsic is the Baker-style read barriers. | 
 | 2348 |     DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier); | 
 | 2349 |  | 
 | 2350 |     Location temp1_loc = locations->GetTemp(0); | 
 | 2351 |     Register temp1 = temp1_loc.AsRegister<Register>(); | 
 | 2352 |     Register temp2 = locations->GetTemp(1).AsRegister<Register>(); | 
 | 2353 |  | 
| Roland Levillain | 4d02711 | 2015-07-01 15:41:14 +0100 | [diff] [blame] | 2354 |     Register expected = locations->InAt(3).AsRegister<Register>(); | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2355 |     // Ensure `expected` is in EAX (required by the CMPXCHG instruction). | 
| Roland Levillain | 4d02711 | 2015-07-01 15:41:14 +0100 | [diff] [blame] | 2356 |     DCHECK_EQ(expected, EAX); | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2357 |     Register value = locations->InAt(4).AsRegister<Register>(); | 
| Roland Levillain | 4d02711 | 2015-07-01 15:41:14 +0100 | [diff] [blame] | 2358 |  | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2359 |     // Mark card for object assuming new value is stored. | 
 | 2360 |     bool value_can_be_null = true;  // TODO: Worth finding out this information? | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2361 |     codegen->MarkGCCard(temp1, temp2, base, value, value_can_be_null); | 
 | 2362 |  | 
 | 2363 |     if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { | 
 | 2364 |       // Need to make sure the reference stored in the field is a to-space | 
 | 2365 |       // one before attempting the CAS or the CAS could fail incorrectly. | 
 | 2366 |       codegen->GenerateReferenceLoadWithBakerReadBarrier( | 
 | 2367 |           invoke, | 
 | 2368 |           temp1_loc,  // Unused, used only as a "temporary" within the read barrier. | 
 | 2369 |           base, | 
 | 2370 |           field_addr, | 
 | 2371 |           /* needs_null_check */ false, | 
 | 2372 |           /* always_update_field */ true, | 
 | 2373 |           &temp2); | 
 | 2374 |     } | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2375 |  | 
 | 2376 |     bool base_equals_value = (base == value); | 
 | 2377 |     if (kPoisonHeapReferences) { | 
 | 2378 |       if (base_equals_value) { | 
 | 2379 |         // If `base` and `value` are the same register location, move | 
 | 2380 |         // `value` to a temporary register.  This way, poisoning | 
 | 2381 |         // `value` won't invalidate `base`. | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2382 |         value = temp1; | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2383 |         __ movl(value, base); | 
| Roland Levillain | 4d02711 | 2015-07-01 15:41:14 +0100 | [diff] [blame] | 2384 |       } | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2385 |  | 
 | 2386 |       // Check that the register allocator did not assign the location | 
 | 2387 |       // of `expected` (EAX) to `value` nor to `base`, so that heap | 
 | 2388 |       // poisoning (when enabled) works as intended below. | 
 | 2389 |       // - If `value` were equal to `expected`, both references would | 
 | 2390 |       //   be poisoned twice, meaning they would not be poisoned at | 
 | 2391 |       //   all, as heap poisoning uses address negation. | 
 | 2392 |       // - If `base` were equal to `expected`, poisoning `expected` | 
 | 2393 |       //   would invalidate `base`. | 
 | 2394 |       DCHECK_NE(value, expected); | 
 | 2395 |       DCHECK_NE(base, expected); | 
 | 2396 |  | 
 | 2397 |       __ PoisonHeapReference(expected); | 
 | 2398 |       __ PoisonHeapReference(value); | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2399 |     } | 
 | 2400 |  | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2401 |     __ LockCmpxchgl(field_addr, value); | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2402 |  | 
| Roland Levillain | 0d5a281 | 2015-11-13 10:07:31 +0000 | [diff] [blame] | 2403 |     // LOCK CMPXCHG has full barrier semantics, and we don't need | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2404 |     // scheduling barriers at this time. | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2405 |  | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2406 |     // Convert ZF into the Boolean result. | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2407 |     __ setb(kZero, out.AsRegister<Register>()); | 
 | 2408 |     __ movzxb(out.AsRegister<Register>(), out.AsRegister<ByteRegister>()); | 
| Roland Levillain | 4d02711 | 2015-07-01 15:41:14 +0100 | [diff] [blame] | 2409 |  | 
| Roland Levillain | 391b866 | 2015-12-18 11:43:38 +0000 | [diff] [blame] | 2410 |     // If heap poisoning is enabled, we need to unpoison the values | 
 | 2411 |     // that were poisoned earlier. | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2412 |     if (kPoisonHeapReferences) { | 
 | 2413 |       if (base_equals_value) { | 
 | 2414 |         // `value` has been moved to a temporary register, no need to | 
 | 2415 |         // unpoison it. | 
 | 2416 |       } else { | 
 | 2417 |         // Ensure `value` is different from `out`, so that unpoisoning | 
 | 2418 |         // the former does not invalidate the latter. | 
 | 2419 |         DCHECK_NE(value, out.AsRegister<Register>()); | 
 | 2420 |         __ UnpoisonHeapReference(value); | 
 | 2421 |       } | 
 | 2422 |       // Do not unpoison the reference contained in register | 
 | 2423 |       // `expected`, as it is the same as register `out` (EAX). | 
 | 2424 |     } | 
 | 2425 |   } else { | 
 | 2426 |     if (type == Primitive::kPrimInt) { | 
 | 2427 |       // Ensure the expected value is in EAX (required by the CMPXCHG | 
 | 2428 |       // instruction). | 
 | 2429 |       DCHECK_EQ(locations->InAt(3).AsRegister<Register>(), EAX); | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2430 |       __ LockCmpxchgl(field_addr, locations->InAt(4).AsRegister<Register>()); | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2431 |     } else if (type == Primitive::kPrimLong) { | 
 | 2432 |       // Ensure the expected value is in EAX:EDX and that the new | 
 | 2433 |       // value is in EBX:ECX (required by the CMPXCHG8B instruction). | 
 | 2434 |       DCHECK_EQ(locations->InAt(3).AsRegisterPairLow<Register>(), EAX); | 
 | 2435 |       DCHECK_EQ(locations->InAt(3).AsRegisterPairHigh<Register>(), EDX); | 
 | 2436 |       DCHECK_EQ(locations->InAt(4).AsRegisterPairLow<Register>(), EBX); | 
 | 2437 |       DCHECK_EQ(locations->InAt(4).AsRegisterPairHigh<Register>(), ECX); | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2438 |       __ LockCmpxchg8b(field_addr); | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2439 |     } else { | 
 | 2440 |       LOG(FATAL) << "Unexpected CAS type " << type; | 
 | 2441 |     } | 
 | 2442 |  | 
| Roland Levillain | 0d5a281 | 2015-11-13 10:07:31 +0000 | [diff] [blame] | 2443 |     // LOCK CMPXCHG/LOCK CMPXCHG8B have full barrier semantics, and we | 
 | 2444 |     // don't need scheduling barriers at this time. | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2445 |  | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2446 |     // Convert ZF into the Boolean result. | 
| Roland Levillain | b488b78 | 2015-10-22 11:38:49 +0100 | [diff] [blame] | 2447 |     __ setb(kZero, out.AsRegister<Register>()); | 
 | 2448 |     __ movzxb(out.AsRegister<Register>(), out.AsRegister<ByteRegister>()); | 
| Roland Levillain | 4d02711 | 2015-07-01 15:41:14 +0100 | [diff] [blame] | 2449 |   } | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2450 | } | 
 | 2451 |  | 
 | 2452 | void IntrinsicCodeGeneratorX86::VisitUnsafeCASInt(HInvoke* invoke) { | 
 | 2453 |   GenCAS(Primitive::kPrimInt, invoke, codegen_); | 
 | 2454 | } | 
 | 2455 |  | 
 | 2456 | void IntrinsicCodeGeneratorX86::VisitUnsafeCASLong(HInvoke* invoke) { | 
 | 2457 |   GenCAS(Primitive::kPrimLong, invoke, codegen_); | 
 | 2458 | } | 
 | 2459 |  | 
 | 2460 | void IntrinsicCodeGeneratorX86::VisitUnsafeCASObject(HInvoke* invoke) { | 
| Roland Levillain | a1aa3b1 | 2016-10-26 13:03:38 +0100 | [diff] [blame] | 2461 |   // The only read barrier implementation supporting the | 
 | 2462 |   // UnsafeCASObject intrinsic is the Baker-style read barriers. | 
 | 2463 |   DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier); | 
| Roland Levillain | 3d31242 | 2016-06-23 13:53:42 +0100 | [diff] [blame] | 2464 |  | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2465 |   GenCAS(Primitive::kPrimNot, invoke, codegen_); | 
 | 2466 | } | 
 | 2467 |  | 
 | 2468 | void IntrinsicLocationsBuilderX86::VisitIntegerReverse(HInvoke* invoke) { | 
 | 2469 |   LocationSummary* locations = new (arena_) LocationSummary(invoke, | 
 | 2470 |                                                            LocationSummary::kNoCall, | 
 | 2471 |                                                            kIntrinsified); | 
 | 2472 |   locations->SetInAt(0, Location::RequiresRegister()); | 
 | 2473 |   locations->SetOut(Location::SameAsFirstInput()); | 
 | 2474 |   locations->AddTemp(Location::RequiresRegister()); | 
 | 2475 | } | 
 | 2476 |  | 
 | 2477 | static void SwapBits(Register reg, Register temp, int32_t shift, int32_t mask, | 
 | 2478 |                      X86Assembler* assembler) { | 
 | 2479 |   Immediate imm_shift(shift); | 
 | 2480 |   Immediate imm_mask(mask); | 
 | 2481 |   __ movl(temp, reg); | 
 | 2482 |   __ shrl(reg, imm_shift); | 
 | 2483 |   __ andl(temp, imm_mask); | 
 | 2484 |   __ andl(reg, imm_mask); | 
 | 2485 |   __ shll(temp, imm_shift); | 
 | 2486 |   __ orl(reg, temp); | 
 | 2487 | } | 
 | 2488 |  | 
 | 2489 | void IntrinsicCodeGeneratorX86::VisitIntegerReverse(HInvoke* invoke) { | 
| Aart Bik | a19616e | 2016-02-01 18:57:58 -0800 | [diff] [blame] | 2490 |   X86Assembler* assembler = GetAssembler(); | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2491 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 2492 |  | 
 | 2493 |   Register reg = locations->InAt(0).AsRegister<Register>(); | 
 | 2494 |   Register temp = locations->GetTemp(0).AsRegister<Register>(); | 
 | 2495 |  | 
 | 2496 |   /* | 
 | 2497 |    * Use one bswap instruction to reverse byte order first and then use 3 rounds of | 
 | 2498 |    * swapping bits to reverse bits in a number x. Using bswap to save instructions | 
 | 2499 |    * compared to generic luni implementation which has 5 rounds of swapping bits. | 
 | 2500 |    * x = bswap x | 
 | 2501 |    * x = (x & 0x55555555) << 1 | (x >> 1) & 0x55555555; | 
 | 2502 |    * x = (x & 0x33333333) << 2 | (x >> 2) & 0x33333333; | 
 | 2503 |    * x = (x & 0x0F0F0F0F) << 4 | (x >> 4) & 0x0F0F0F0F; | 
 | 2504 |    */ | 
 | 2505 |   __ bswapl(reg); | 
 | 2506 |   SwapBits(reg, temp, 1, 0x55555555, assembler); | 
 | 2507 |   SwapBits(reg, temp, 2, 0x33333333, assembler); | 
 | 2508 |   SwapBits(reg, temp, 4, 0x0f0f0f0f, assembler); | 
 | 2509 | } | 
 | 2510 |  | 
 | 2511 | void IntrinsicLocationsBuilderX86::VisitLongReverse(HInvoke* invoke) { | 
 | 2512 |   LocationSummary* locations = new (arena_) LocationSummary(invoke, | 
 | 2513 |                                                            LocationSummary::kNoCall, | 
 | 2514 |                                                            kIntrinsified); | 
 | 2515 |   locations->SetInAt(0, Location::RequiresRegister()); | 
 | 2516 |   locations->SetOut(Location::SameAsFirstInput()); | 
 | 2517 |   locations->AddTemp(Location::RequiresRegister()); | 
 | 2518 | } | 
 | 2519 |  | 
 | 2520 | void IntrinsicCodeGeneratorX86::VisitLongReverse(HInvoke* invoke) { | 
| Aart Bik | a19616e | 2016-02-01 18:57:58 -0800 | [diff] [blame] | 2521 |   X86Assembler* assembler = GetAssembler(); | 
| Mark Mendell | 58d25fd | 2015-04-03 14:52:31 -0400 | [diff] [blame] | 2522 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 2523 |  | 
 | 2524 |   Register reg_low = locations->InAt(0).AsRegisterPairLow<Register>(); | 
 | 2525 |   Register reg_high = locations->InAt(0).AsRegisterPairHigh<Register>(); | 
 | 2526 |   Register temp = locations->GetTemp(0).AsRegister<Register>(); | 
 | 2527 |  | 
 | 2528 |   // We want to swap high/low, then bswap each one, and then do the same | 
 | 2529 |   // as a 32 bit reverse. | 
 | 2530 |   // Exchange high and low. | 
 | 2531 |   __ movl(temp, reg_low); | 
 | 2532 |   __ movl(reg_low, reg_high); | 
 | 2533 |   __ movl(reg_high, temp); | 
 | 2534 |  | 
 | 2535 |   // bit-reverse low | 
 | 2536 |   __ bswapl(reg_low); | 
 | 2537 |   SwapBits(reg_low, temp, 1, 0x55555555, assembler); | 
 | 2538 |   SwapBits(reg_low, temp, 2, 0x33333333, assembler); | 
 | 2539 |   SwapBits(reg_low, temp, 4, 0x0f0f0f0f, assembler); | 
 | 2540 |  | 
 | 2541 |   // bit-reverse high | 
 | 2542 |   __ bswapl(reg_high); | 
 | 2543 |   SwapBits(reg_high, temp, 1, 0x55555555, assembler); | 
 | 2544 |   SwapBits(reg_high, temp, 2, 0x33333333, assembler); | 
 | 2545 |   SwapBits(reg_high, temp, 4, 0x0f0f0f0f, assembler); | 
 | 2546 | } | 
 | 2547 |  | 
| Aart Bik | c39dac1 | 2016-01-21 08:59:48 -0800 | [diff] [blame] | 2548 | static void CreateBitCountLocations( | 
 | 2549 |     ArenaAllocator* arena, CodeGeneratorX86* codegen, HInvoke* invoke, bool is_long) { | 
 | 2550 |   if (!codegen->GetInstructionSetFeatures().HasPopCnt()) { | 
 | 2551 |     // Do nothing if there is no popcnt support. This results in generating | 
 | 2552 |     // a call for the intrinsic rather than direct code. | 
 | 2553 |     return; | 
 | 2554 |   } | 
 | 2555 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 2556 |                                                            LocationSummary::kNoCall, | 
 | 2557 |                                                            kIntrinsified); | 
 | 2558 |   if (is_long) { | 
| Aart Bik | c39dac1 | 2016-01-21 08:59:48 -0800 | [diff] [blame] | 2559 |     locations->AddTemp(Location::RequiresRegister()); | 
| Aart Bik | c39dac1 | 2016-01-21 08:59:48 -0800 | [diff] [blame] | 2560 |   } | 
| Aart Bik | 2a94607 | 2016-01-21 12:49:00 -0800 | [diff] [blame] | 2561 |   locations->SetInAt(0, Location::Any()); | 
| Aart Bik | c39dac1 | 2016-01-21 08:59:48 -0800 | [diff] [blame] | 2562 |   locations->SetOut(Location::RequiresRegister()); | 
 | 2563 | } | 
 | 2564 |  | 
| Aart Bik | a19616e | 2016-02-01 18:57:58 -0800 | [diff] [blame] | 2565 | static void GenBitCount(X86Assembler* assembler, | 
 | 2566 |                         CodeGeneratorX86* codegen, | 
 | 2567 |                         HInvoke* invoke, bool is_long) { | 
| Aart Bik | c39dac1 | 2016-01-21 08:59:48 -0800 | [diff] [blame] | 2568 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 2569 |   Location src = locations->InAt(0); | 
 | 2570 |   Register out = locations->Out().AsRegister<Register>(); | 
 | 2571 |  | 
 | 2572 |   if (invoke->InputAt(0)->IsConstant()) { | 
 | 2573 |     // Evaluate this at compile time. | 
 | 2574 |     int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant()); | 
| Roland Levillain | fa3912e | 2016-04-01 18:21:55 +0100 | [diff] [blame] | 2575 |     int32_t result = is_long | 
| Aart Bik | c39dac1 | 2016-01-21 08:59:48 -0800 | [diff] [blame] | 2576 |         ? POPCOUNT(static_cast<uint64_t>(value)) | 
 | 2577 |         : POPCOUNT(static_cast<uint32_t>(value)); | 
| Roland Levillain | fa3912e | 2016-04-01 18:21:55 +0100 | [diff] [blame] | 2578 |     codegen->Load32BitValue(out, result); | 
| Aart Bik | c39dac1 | 2016-01-21 08:59:48 -0800 | [diff] [blame] | 2579 |     return; | 
 | 2580 |   } | 
 | 2581 |  | 
 | 2582 |   // Handle the non-constant cases. | 
 | 2583 |   if (!is_long) { | 
 | 2584 |     if (src.IsRegister()) { | 
 | 2585 |       __ popcntl(out, src.AsRegister<Register>()); | 
 | 2586 |     } else { | 
 | 2587 |       DCHECK(src.IsStackSlot()); | 
 | 2588 |       __ popcntl(out, Address(ESP, src.GetStackIndex())); | 
 | 2589 |     } | 
| Aart Bik | 2a94607 | 2016-01-21 12:49:00 -0800 | [diff] [blame] | 2590 |   } else { | 
 | 2591 |     // The 64-bit case needs to worry about two parts. | 
 | 2592 |     Register temp = locations->GetTemp(0).AsRegister<Register>(); | 
 | 2593 |     if (src.IsRegisterPair()) { | 
 | 2594 |       __ popcntl(temp, src.AsRegisterPairLow<Register>()); | 
 | 2595 |       __ popcntl(out, src.AsRegisterPairHigh<Register>()); | 
 | 2596 |     } else { | 
 | 2597 |       DCHECK(src.IsDoubleStackSlot()); | 
 | 2598 |       __ popcntl(temp, Address(ESP, src.GetStackIndex())); | 
 | 2599 |       __ popcntl(out, Address(ESP, src.GetHighStackIndex(kX86WordSize))); | 
 | 2600 |     } | 
 | 2601 |     __ addl(out, temp); | 
| Aart Bik | c39dac1 | 2016-01-21 08:59:48 -0800 | [diff] [blame] | 2602 |   } | 
| Aart Bik | c39dac1 | 2016-01-21 08:59:48 -0800 | [diff] [blame] | 2603 | } | 
 | 2604 |  | 
 | 2605 | void IntrinsicLocationsBuilderX86::VisitIntegerBitCount(HInvoke* invoke) { | 
 | 2606 |   CreateBitCountLocations(arena_, codegen_, invoke, /* is_long */ false); | 
 | 2607 | } | 
 | 2608 |  | 
 | 2609 | void IntrinsicCodeGeneratorX86::VisitIntegerBitCount(HInvoke* invoke) { | 
| Aart Bik | a19616e | 2016-02-01 18:57:58 -0800 | [diff] [blame] | 2610 |   GenBitCount(GetAssembler(), codegen_, invoke, /* is_long */ false); | 
| Aart Bik | c39dac1 | 2016-01-21 08:59:48 -0800 | [diff] [blame] | 2611 | } | 
 | 2612 |  | 
 | 2613 | void IntrinsicLocationsBuilderX86::VisitLongBitCount(HInvoke* invoke) { | 
 | 2614 |   CreateBitCountLocations(arena_, codegen_, invoke, /* is_long */ true); | 
 | 2615 | } | 
 | 2616 |  | 
 | 2617 | void IntrinsicCodeGeneratorX86::VisitLongBitCount(HInvoke* invoke) { | 
| Aart Bik | a19616e | 2016-02-01 18:57:58 -0800 | [diff] [blame] | 2618 |   GenBitCount(GetAssembler(), codegen_, invoke, /* is_long */ true); | 
| Aart Bik | c39dac1 | 2016-01-21 08:59:48 -0800 | [diff] [blame] | 2619 | } | 
 | 2620 |  | 
| Mark Mendell | d589767 | 2015-08-12 21:16:41 -0400 | [diff] [blame] | 2621 | static void CreateLeadingZeroLocations(ArenaAllocator* arena, HInvoke* invoke, bool is_long) { | 
 | 2622 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 2623 |                                                            LocationSummary::kNoCall, | 
 | 2624 |                                                            kIntrinsified); | 
 | 2625 |   if (is_long) { | 
 | 2626 |     locations->SetInAt(0, Location::RequiresRegister()); | 
 | 2627 |   } else { | 
 | 2628 |     locations->SetInAt(0, Location::Any()); | 
 | 2629 |   } | 
 | 2630 |   locations->SetOut(Location::RequiresRegister()); | 
 | 2631 | } | 
 | 2632 |  | 
| Aart Bik | a19616e | 2016-02-01 18:57:58 -0800 | [diff] [blame] | 2633 | static void GenLeadingZeros(X86Assembler* assembler, | 
 | 2634 |                             CodeGeneratorX86* codegen, | 
 | 2635 |                             HInvoke* invoke, bool is_long) { | 
| Mark Mendell | d589767 | 2015-08-12 21:16:41 -0400 | [diff] [blame] | 2636 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 2637 |   Location src = locations->InAt(0); | 
 | 2638 |   Register out = locations->Out().AsRegister<Register>(); | 
 | 2639 |  | 
 | 2640 |   if (invoke->InputAt(0)->IsConstant()) { | 
 | 2641 |     // Evaluate this at compile time. | 
 | 2642 |     int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant()); | 
 | 2643 |     if (value == 0) { | 
 | 2644 |       value = is_long ? 64 : 32; | 
 | 2645 |     } else { | 
 | 2646 |       value = is_long ? CLZ(static_cast<uint64_t>(value)) : CLZ(static_cast<uint32_t>(value)); | 
 | 2647 |     } | 
| Aart Bik | a19616e | 2016-02-01 18:57:58 -0800 | [diff] [blame] | 2648 |     codegen->Load32BitValue(out, value); | 
| Mark Mendell | d589767 | 2015-08-12 21:16:41 -0400 | [diff] [blame] | 2649 |     return; | 
 | 2650 |   } | 
 | 2651 |  | 
 | 2652 |   // Handle the non-constant cases. | 
 | 2653 |   if (!is_long) { | 
 | 2654 |     if (src.IsRegister()) { | 
 | 2655 |       __ bsrl(out, src.AsRegister<Register>()); | 
 | 2656 |     } else { | 
 | 2657 |       DCHECK(src.IsStackSlot()); | 
 | 2658 |       __ bsrl(out, Address(ESP, src.GetStackIndex())); | 
 | 2659 |     } | 
 | 2660 |  | 
 | 2661 |     // BSR sets ZF if the input was zero, and the output is undefined. | 
| Mark Mendell | 0c9497d | 2015-08-21 09:30:05 -0400 | [diff] [blame] | 2662 |     NearLabel all_zeroes, done; | 
| Mark Mendell | d589767 | 2015-08-12 21:16:41 -0400 | [diff] [blame] | 2663 |     __ j(kEqual, &all_zeroes); | 
 | 2664 |  | 
 | 2665 |     // Correct the result from BSR to get the final CLZ result. | 
 | 2666 |     __ xorl(out, Immediate(31)); | 
 | 2667 |     __ jmp(&done); | 
 | 2668 |  | 
 | 2669 |     // Fix the zero case with the expected result. | 
 | 2670 |     __ Bind(&all_zeroes); | 
 | 2671 |     __ movl(out, Immediate(32)); | 
 | 2672 |  | 
 | 2673 |     __ Bind(&done); | 
 | 2674 |     return; | 
 | 2675 |   } | 
 | 2676 |  | 
 | 2677 |   // 64 bit case needs to worry about both parts of the register. | 
 | 2678 |   DCHECK(src.IsRegisterPair()); | 
 | 2679 |   Register src_lo = src.AsRegisterPairLow<Register>(); | 
 | 2680 |   Register src_hi = src.AsRegisterPairHigh<Register>(); | 
| Mark Mendell | 0c9497d | 2015-08-21 09:30:05 -0400 | [diff] [blame] | 2681 |   NearLabel handle_low, done, all_zeroes; | 
| Mark Mendell | d589767 | 2015-08-12 21:16:41 -0400 | [diff] [blame] | 2682 |  | 
 | 2683 |   // Is the high word zero? | 
 | 2684 |   __ testl(src_hi, src_hi); | 
 | 2685 |   __ j(kEqual, &handle_low); | 
 | 2686 |  | 
 | 2687 |   // High word is not zero. We know that the BSR result is defined in this case. | 
 | 2688 |   __ bsrl(out, src_hi); | 
 | 2689 |  | 
 | 2690 |   // Correct the result from BSR to get the final CLZ result. | 
 | 2691 |   __ xorl(out, Immediate(31)); | 
 | 2692 |   __ jmp(&done); | 
 | 2693 |  | 
 | 2694 |   // High word was zero.  We have to compute the low word count and add 32. | 
 | 2695 |   __ Bind(&handle_low); | 
 | 2696 |   __ bsrl(out, src_lo); | 
 | 2697 |   __ j(kEqual, &all_zeroes); | 
 | 2698 |  | 
 | 2699 |   // We had a valid result.  Use an XOR to both correct the result and add 32. | 
 | 2700 |   __ xorl(out, Immediate(63)); | 
 | 2701 |   __ jmp(&done); | 
 | 2702 |  | 
 | 2703 |   // All zero case. | 
 | 2704 |   __ Bind(&all_zeroes); | 
 | 2705 |   __ movl(out, Immediate(64)); | 
 | 2706 |  | 
 | 2707 |   __ Bind(&done); | 
 | 2708 | } | 
 | 2709 |  | 
 | 2710 | void IntrinsicLocationsBuilderX86::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) { | 
 | 2711 |   CreateLeadingZeroLocations(arena_, invoke, /* is_long */ false); | 
 | 2712 | } | 
 | 2713 |  | 
 | 2714 | void IntrinsicCodeGeneratorX86::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) { | 
| Aart Bik | a19616e | 2016-02-01 18:57:58 -0800 | [diff] [blame] | 2715 |   GenLeadingZeros(GetAssembler(), codegen_, invoke, /* is_long */ false); | 
| Mark Mendell | d589767 | 2015-08-12 21:16:41 -0400 | [diff] [blame] | 2716 | } | 
 | 2717 |  | 
 | 2718 | void IntrinsicLocationsBuilderX86::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { | 
 | 2719 |   CreateLeadingZeroLocations(arena_, invoke, /* is_long */ true); | 
 | 2720 | } | 
 | 2721 |  | 
 | 2722 | void IntrinsicCodeGeneratorX86::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { | 
| Aart Bik | a19616e | 2016-02-01 18:57:58 -0800 | [diff] [blame] | 2723 |   GenLeadingZeros(GetAssembler(), codegen_, invoke, /* is_long */ true); | 
| Mark Mendell | d589767 | 2015-08-12 21:16:41 -0400 | [diff] [blame] | 2724 | } | 
 | 2725 |  | 
| Mark Mendell | 2d55479 | 2015-09-15 21:45:18 -0400 | [diff] [blame] | 2726 | static void CreateTrailingZeroLocations(ArenaAllocator* arena, HInvoke* invoke, bool is_long) { | 
 | 2727 |   LocationSummary* locations = new (arena) LocationSummary(invoke, | 
 | 2728 |                                                            LocationSummary::kNoCall, | 
 | 2729 |                                                            kIntrinsified); | 
 | 2730 |   if (is_long) { | 
 | 2731 |     locations->SetInAt(0, Location::RequiresRegister()); | 
 | 2732 |   } else { | 
 | 2733 |     locations->SetInAt(0, Location::Any()); | 
 | 2734 |   } | 
 | 2735 |   locations->SetOut(Location::RequiresRegister()); | 
 | 2736 | } | 
 | 2737 |  | 
| Aart Bik | a19616e | 2016-02-01 18:57:58 -0800 | [diff] [blame] | 2738 | static void GenTrailingZeros(X86Assembler* assembler, | 
 | 2739 |                              CodeGeneratorX86* codegen, | 
 | 2740 |                              HInvoke* invoke, bool is_long) { | 
| Mark Mendell | 2d55479 | 2015-09-15 21:45:18 -0400 | [diff] [blame] | 2741 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 2742 |   Location src = locations->InAt(0); | 
 | 2743 |   Register out = locations->Out().AsRegister<Register>(); | 
 | 2744 |  | 
 | 2745 |   if (invoke->InputAt(0)->IsConstant()) { | 
 | 2746 |     // Evaluate this at compile time. | 
 | 2747 |     int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant()); | 
 | 2748 |     if (value == 0) { | 
 | 2749 |       value = is_long ? 64 : 32; | 
 | 2750 |     } else { | 
 | 2751 |       value = is_long ? CTZ(static_cast<uint64_t>(value)) : CTZ(static_cast<uint32_t>(value)); | 
 | 2752 |     } | 
| Aart Bik | a19616e | 2016-02-01 18:57:58 -0800 | [diff] [blame] | 2753 |     codegen->Load32BitValue(out, value); | 
| Mark Mendell | 2d55479 | 2015-09-15 21:45:18 -0400 | [diff] [blame] | 2754 |     return; | 
 | 2755 |   } | 
 | 2756 |  | 
 | 2757 |   // Handle the non-constant cases. | 
 | 2758 |   if (!is_long) { | 
 | 2759 |     if (src.IsRegister()) { | 
 | 2760 |       __ bsfl(out, src.AsRegister<Register>()); | 
 | 2761 |     } else { | 
 | 2762 |       DCHECK(src.IsStackSlot()); | 
 | 2763 |       __ bsfl(out, Address(ESP, src.GetStackIndex())); | 
 | 2764 |     } | 
 | 2765 |  | 
 | 2766 |     // BSF sets ZF if the input was zero, and the output is undefined. | 
 | 2767 |     NearLabel done; | 
 | 2768 |     __ j(kNotEqual, &done); | 
 | 2769 |  | 
 | 2770 |     // Fix the zero case with the expected result. | 
 | 2771 |     __ movl(out, Immediate(32)); | 
 | 2772 |  | 
 | 2773 |     __ Bind(&done); | 
 | 2774 |     return; | 
 | 2775 |   } | 
 | 2776 |  | 
 | 2777 |   // 64 bit case needs to worry about both parts of the register. | 
 | 2778 |   DCHECK(src.IsRegisterPair()); | 
 | 2779 |   Register src_lo = src.AsRegisterPairLow<Register>(); | 
 | 2780 |   Register src_hi = src.AsRegisterPairHigh<Register>(); | 
 | 2781 |   NearLabel done, all_zeroes; | 
 | 2782 |  | 
 | 2783 |   // If the low word is zero, then ZF will be set.  If not, we have the answer. | 
 | 2784 |   __ bsfl(out, src_lo); | 
 | 2785 |   __ j(kNotEqual, &done); | 
 | 2786 |  | 
 | 2787 |   // Low word was zero.  We have to compute the high word count and add 32. | 
 | 2788 |   __ bsfl(out, src_hi); | 
 | 2789 |   __ j(kEqual, &all_zeroes); | 
 | 2790 |  | 
 | 2791 |   // We had a valid result.  Add 32 to account for the low word being zero. | 
 | 2792 |   __ addl(out, Immediate(32)); | 
 | 2793 |   __ jmp(&done); | 
 | 2794 |  | 
 | 2795 |   // All zero case. | 
 | 2796 |   __ Bind(&all_zeroes); | 
 | 2797 |   __ movl(out, Immediate(64)); | 
 | 2798 |  | 
 | 2799 |   __ Bind(&done); | 
 | 2800 | } | 
 | 2801 |  | 
 | 2802 | void IntrinsicLocationsBuilderX86::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) { | 
 | 2803 |   CreateTrailingZeroLocations(arena_, invoke, /* is_long */ false); | 
 | 2804 | } | 
 | 2805 |  | 
 | 2806 | void IntrinsicCodeGeneratorX86::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) { | 
| Aart Bik | a19616e | 2016-02-01 18:57:58 -0800 | [diff] [blame] | 2807 |   GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long */ false); | 
| Mark Mendell | 2d55479 | 2015-09-15 21:45:18 -0400 | [diff] [blame] | 2808 | } | 
 | 2809 |  | 
 | 2810 | void IntrinsicLocationsBuilderX86::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { | 
 | 2811 |   CreateTrailingZeroLocations(arena_, invoke, /* is_long */ true); | 
 | 2812 | } | 
 | 2813 |  | 
 | 2814 | void IntrinsicCodeGeneratorX86::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { | 
| Aart Bik | a19616e | 2016-02-01 18:57:58 -0800 | [diff] [blame] | 2815 |   GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long */ true); | 
| Mark Mendell | 2d55479 | 2015-09-15 21:45:18 -0400 | [diff] [blame] | 2816 | } | 
 | 2817 |  | 
| Serguei Katkov | 288c7a8 | 2016-05-16 11:53:15 +0600 | [diff] [blame] | 2818 | void IntrinsicLocationsBuilderX86::VisitReferenceGetReferent(HInvoke* invoke) { | 
 | 2819 |   if (kEmitCompilerReadBarrier) { | 
 | 2820 |     // Do not intrinsify this call with the read barrier configuration. | 
 | 2821 |     return; | 
 | 2822 |   } | 
 | 2823 |   LocationSummary* locations = new (arena_) LocationSummary(invoke, | 
 | 2824 |                                                             LocationSummary::kCallOnSlowPath, | 
 | 2825 |                                                             kIntrinsified); | 
 | 2826 |   locations->SetInAt(0, Location::RequiresRegister()); | 
 | 2827 |   locations->SetOut(Location::SameAsFirstInput()); | 
 | 2828 |   locations->AddTemp(Location::RequiresRegister()); | 
 | 2829 | } | 
 | 2830 |  | 
 | 2831 | void IntrinsicCodeGeneratorX86::VisitReferenceGetReferent(HInvoke* invoke) { | 
 | 2832 |   DCHECK(!kEmitCompilerReadBarrier); | 
 | 2833 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 2834 |   X86Assembler* assembler = GetAssembler(); | 
 | 2835 |  | 
 | 2836 |   Register obj = locations->InAt(0).AsRegister<Register>(); | 
 | 2837 |   Register out = locations->Out().AsRegister<Register>(); | 
 | 2838 |  | 
 | 2839 |   SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke); | 
 | 2840 |   codegen_->AddSlowPath(slow_path); | 
 | 2841 |  | 
 | 2842 |   // Load ArtMethod first. | 
 | 2843 |   HInvokeStaticOrDirect* invoke_direct = invoke->AsInvokeStaticOrDirect(); | 
 | 2844 |   DCHECK(invoke_direct != nullptr); | 
 | 2845 |   Location temp_loc = codegen_->GenerateCalleeMethodStaticOrDirectCall( | 
 | 2846 |       invoke_direct, locations->GetTemp(0)); | 
 | 2847 |   DCHECK(temp_loc.Equals(locations->GetTemp(0))); | 
 | 2848 |   Register temp = temp_loc.AsRegister<Register>(); | 
 | 2849 |  | 
 | 2850 |   // Now get declaring class. | 
 | 2851 |   __ movl(temp, Address(temp, ArtMethod::DeclaringClassOffset().Int32Value())); | 
 | 2852 |  | 
 | 2853 |   uint32_t slow_path_flag_offset = codegen_->GetReferenceSlowFlagOffset(); | 
 | 2854 |   uint32_t disable_flag_offset = codegen_->GetReferenceDisableFlagOffset(); | 
 | 2855 |   DCHECK_NE(slow_path_flag_offset, 0u); | 
 | 2856 |   DCHECK_NE(disable_flag_offset, 0u); | 
 | 2857 |   DCHECK_NE(slow_path_flag_offset, disable_flag_offset); | 
 | 2858 |  | 
 | 2859 |   // Check static flags preventing us for using intrinsic. | 
 | 2860 |   if (slow_path_flag_offset == disable_flag_offset + 1) { | 
 | 2861 |     __ cmpw(Address(temp, disable_flag_offset), Immediate(0)); | 
 | 2862 |     __ j(kNotEqual, slow_path->GetEntryLabel()); | 
 | 2863 |   } else { | 
 | 2864 |     __ cmpb(Address(temp, disable_flag_offset), Immediate(0)); | 
 | 2865 |     __ j(kNotEqual, slow_path->GetEntryLabel()); | 
 | 2866 |     __ cmpb(Address(temp, slow_path_flag_offset), Immediate(0)); | 
 | 2867 |     __ j(kNotEqual, slow_path->GetEntryLabel()); | 
 | 2868 |   } | 
 | 2869 |  | 
 | 2870 |   // Fast path. | 
 | 2871 |   __ movl(out, Address(obj, mirror::Reference::ReferentOffset().Int32Value())); | 
 | 2872 |   codegen_->MaybeRecordImplicitNullCheck(invoke); | 
 | 2873 |   __ MaybeUnpoisonHeapReference(out); | 
 | 2874 |   __ Bind(slow_path->GetExitLabel()); | 
 | 2875 | } | 
 | 2876 |  | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 2877 | static bool IsSameInput(HInstruction* instruction, size_t input0, size_t input1) { | 
 | 2878 |   return instruction->InputAt(input0) == instruction->InputAt(input1); | 
 | 2879 | } | 
 | 2880 |  | 
 | 2881 | void IntrinsicLocationsBuilderX86::VisitSystemArrayCopy(HInvoke* invoke) { | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 2882 |   // The only read barrier implementation supporting the | 
 | 2883 |   // SystemArrayCopy intrinsic is the Baker-style read barriers. | 
 | 2884 |   if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) { | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 2885 |     return; | 
 | 2886 |   } | 
 | 2887 |  | 
 | 2888 |   CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke); | 
 | 2889 |   if (invoke->GetLocations() != nullptr) { | 
 | 2890 |     // Need a byte register for marking. | 
 | 2891 |     invoke->GetLocations()->SetTempAt(1, Location::RegisterLocation(ECX)); | 
 | 2892 |  | 
 | 2893 |     static constexpr size_t kSrc = 0; | 
 | 2894 |     static constexpr size_t kSrcPos = 1; | 
 | 2895 |     static constexpr size_t kDest = 2; | 
 | 2896 |     static constexpr size_t kDestPos = 3; | 
 | 2897 |     static constexpr size_t kLength = 4; | 
 | 2898 |  | 
 | 2899 |     if (!invoke->InputAt(kSrcPos)->IsIntConstant() && | 
 | 2900 |         !invoke->InputAt(kDestPos)->IsIntConstant() && | 
 | 2901 |         !invoke->InputAt(kLength)->IsIntConstant()) { | 
 | 2902 |       if (!IsSameInput(invoke, kSrcPos, kDestPos) && | 
 | 2903 |           !IsSameInput(invoke, kSrcPos, kLength) && | 
 | 2904 |           !IsSameInput(invoke, kDestPos, kLength) && | 
 | 2905 |           !IsSameInput(invoke, kSrc, kDest)) { | 
 | 2906 |         // Not enough registers, make the length also take a stack slot. | 
 | 2907 |         invoke->GetLocations()->SetInAt(kLength, Location::Any()); | 
 | 2908 |       } | 
 | 2909 |     } | 
 | 2910 |   } | 
 | 2911 | } | 
 | 2912 |  | 
 | 2913 | void IntrinsicCodeGeneratorX86::VisitSystemArrayCopy(HInvoke* invoke) { | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 2914 |   // The only read barrier implementation supporting the | 
 | 2915 |   // SystemArrayCopy intrinsic is the Baker-style read barriers. | 
 | 2916 |   DCHECK(!kEmitCompilerReadBarrier || kUseBakerReadBarrier); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 2917 |  | 
 | 2918 |   X86Assembler* assembler = GetAssembler(); | 
 | 2919 |   LocationSummary* locations = invoke->GetLocations(); | 
 | 2920 |  | 
 | 2921 |   uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); | 
 | 2922 |   uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); | 
 | 2923 |   uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); | 
 | 2924 |   uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 2925 |   uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value(); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 2926 |  | 
 | 2927 |   Register src = locations->InAt(0).AsRegister<Register>(); | 
 | 2928 |   Location src_pos = locations->InAt(1); | 
 | 2929 |   Register dest = locations->InAt(2).AsRegister<Register>(); | 
 | 2930 |   Location dest_pos = locations->InAt(3); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 2931 |   Location length_arg = locations->InAt(4); | 
 | 2932 |   Location length = length_arg; | 
 | 2933 |   Location temp1_loc = locations->GetTemp(0); | 
 | 2934 |   Register temp1 = temp1_loc.AsRegister<Register>(); | 
 | 2935 |   Location temp2_loc = locations->GetTemp(1); | 
 | 2936 |   Register temp2 = temp2_loc.AsRegister<Register>(); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 2937 |  | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 2938 |   SlowPathCode* intrinsic_slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke); | 
 | 2939 |   codegen_->AddSlowPath(intrinsic_slow_path); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 2940 |  | 
 | 2941 |   NearLabel conditions_on_positions_validated; | 
 | 2942 |   SystemArrayCopyOptimizations optimizations(invoke); | 
 | 2943 |  | 
 | 2944 |   // If source and destination are the same, we go to slow path if we need to do | 
 | 2945 |   // forward copying. | 
 | 2946 |   if (src_pos.IsConstant()) { | 
 | 2947 |     int32_t src_pos_constant = src_pos.GetConstant()->AsIntConstant()->GetValue(); | 
 | 2948 |     if (dest_pos.IsConstant()) { | 
 | 2949 |       int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue(); | 
 | 2950 |       if (optimizations.GetDestinationIsSource()) { | 
 | 2951 |         // Checked when building locations. | 
 | 2952 |         DCHECK_GE(src_pos_constant, dest_pos_constant); | 
 | 2953 |       } else if (src_pos_constant < dest_pos_constant) { | 
 | 2954 |         __ cmpl(src, dest); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 2955 |         __ j(kEqual, intrinsic_slow_path->GetEntryLabel()); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 2956 |       } | 
 | 2957 |     } else { | 
 | 2958 |       if (!optimizations.GetDestinationIsSource()) { | 
 | 2959 |         __ cmpl(src, dest); | 
 | 2960 |         __ j(kNotEqual, &conditions_on_positions_validated); | 
 | 2961 |       } | 
 | 2962 |       __ cmpl(dest_pos.AsRegister<Register>(), Immediate(src_pos_constant)); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 2963 |       __ j(kGreater, intrinsic_slow_path->GetEntryLabel()); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 2964 |     } | 
 | 2965 |   } else { | 
 | 2966 |     if (!optimizations.GetDestinationIsSource()) { | 
 | 2967 |       __ cmpl(src, dest); | 
 | 2968 |       __ j(kNotEqual, &conditions_on_positions_validated); | 
 | 2969 |     } | 
 | 2970 |     if (dest_pos.IsConstant()) { | 
 | 2971 |       int32_t dest_pos_constant = dest_pos.GetConstant()->AsIntConstant()->GetValue(); | 
 | 2972 |       __ cmpl(src_pos.AsRegister<Register>(), Immediate(dest_pos_constant)); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 2973 |       __ j(kLess, intrinsic_slow_path->GetEntryLabel()); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 2974 |     } else { | 
 | 2975 |       __ cmpl(src_pos.AsRegister<Register>(), dest_pos.AsRegister<Register>()); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 2976 |       __ j(kLess, intrinsic_slow_path->GetEntryLabel()); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 2977 |     } | 
 | 2978 |   } | 
 | 2979 |  | 
 | 2980 |   __ Bind(&conditions_on_positions_validated); | 
 | 2981 |  | 
 | 2982 |   if (!optimizations.GetSourceIsNotNull()) { | 
 | 2983 |     // Bail out if the source is null. | 
 | 2984 |     __ testl(src, src); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 2985 |     __ j(kEqual, intrinsic_slow_path->GetEntryLabel()); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 2986 |   } | 
 | 2987 |  | 
 | 2988 |   if (!optimizations.GetDestinationIsNotNull() && !optimizations.GetDestinationIsSource()) { | 
 | 2989 |     // Bail out if the destination is null. | 
 | 2990 |     __ testl(dest, dest); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 2991 |     __ j(kEqual, intrinsic_slow_path->GetEntryLabel()); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 2992 |   } | 
 | 2993 |  | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 2994 |   Location temp3_loc = locations->GetTemp(2); | 
 | 2995 |   Register temp3 = temp3_loc.AsRegister<Register>(); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 2996 |   if (length.IsStackSlot()) { | 
 | 2997 |     __ movl(temp3, Address(ESP, length.GetStackIndex())); | 
 | 2998 |     length = Location::RegisterLocation(temp3); | 
 | 2999 |   } | 
 | 3000 |  | 
 | 3001 |   // If the length is negative, bail out. | 
 | 3002 |   // We have already checked in the LocationsBuilder for the constant case. | 
 | 3003 |   if (!length.IsConstant() && | 
 | 3004 |       !optimizations.GetCountIsSourceLength() && | 
 | 3005 |       !optimizations.GetCountIsDestinationLength()) { | 
 | 3006 |     __ testl(length.AsRegister<Register>(), length.AsRegister<Register>()); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3007 |     __ j(kLess, intrinsic_slow_path->GetEntryLabel()); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3008 |   } | 
 | 3009 |  | 
 | 3010 |   // Validity checks: source. | 
 | 3011 |   CheckPosition(assembler, | 
 | 3012 |                 src_pos, | 
 | 3013 |                 src, | 
 | 3014 |                 length, | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3015 |                 intrinsic_slow_path, | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3016 |                 temp1, | 
 | 3017 |                 optimizations.GetCountIsSourceLength()); | 
 | 3018 |  | 
 | 3019 |   // Validity checks: dest. | 
 | 3020 |   CheckPosition(assembler, | 
 | 3021 |                 dest_pos, | 
 | 3022 |                 dest, | 
 | 3023 |                 length, | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3024 |                 intrinsic_slow_path, | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3025 |                 temp1, | 
 | 3026 |                 optimizations.GetCountIsDestinationLength()); | 
 | 3027 |  | 
 | 3028 |   if (!optimizations.GetDoesNotNeedTypeCheck()) { | 
 | 3029 |     // Check whether all elements of the source array are assignable to the component | 
 | 3030 |     // type of the destination array. We do two checks: the classes are the same, | 
 | 3031 |     // or the destination is Object[]. If none of these checks succeed, we go to the | 
 | 3032 |     // slow path. | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3033 |  | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3034 |     if (!optimizations.GetSourceIsNonPrimitiveArray()) { | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3035 |       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { | 
 | 3036 |         // /* HeapReference<Class> */ temp1 = src->klass_ | 
 | 3037 |         codegen_->GenerateFieldLoadWithBakerReadBarrier( | 
| Vladimir Marko | 953437b | 2016-08-24 08:30:46 +0000 | [diff] [blame] | 3038 |             invoke, temp1_loc, src, class_offset, /* needs_null_check */ false); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3039 |         // Bail out if the source is not a non primitive array. | 
 | 3040 |         // /* HeapReference<Class> */ temp1 = temp1->component_type_ | 
 | 3041 |         codegen_->GenerateFieldLoadWithBakerReadBarrier( | 
| Vladimir Marko | 953437b | 2016-08-24 08:30:46 +0000 | [diff] [blame] | 3042 |             invoke, temp1_loc, temp1, component_offset, /* needs_null_check */ false); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3043 |         __ testl(temp1, temp1); | 
 | 3044 |         __ j(kEqual, intrinsic_slow_path->GetEntryLabel()); | 
 | 3045 |         // If heap poisoning is enabled, `temp1` has been unpoisoned | 
 | 3046 |         // by the the previous call to GenerateFieldLoadWithBakerReadBarrier. | 
 | 3047 |       } else { | 
 | 3048 |         // /* HeapReference<Class> */ temp1 = src->klass_ | 
 | 3049 |         __ movl(temp1, Address(src, class_offset)); | 
 | 3050 |         __ MaybeUnpoisonHeapReference(temp1); | 
 | 3051 |         // Bail out if the source is not a non primitive array. | 
 | 3052 |         // /* HeapReference<Class> */ temp1 = temp1->component_type_ | 
 | 3053 |         __ movl(temp1, Address(temp1, component_offset)); | 
 | 3054 |         __ testl(temp1, temp1); | 
 | 3055 |         __ j(kEqual, intrinsic_slow_path->GetEntryLabel()); | 
 | 3056 |         __ MaybeUnpoisonHeapReference(temp1); | 
 | 3057 |       } | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3058 |       __ cmpw(Address(temp1, primitive_offset), Immediate(Primitive::kPrimNot)); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3059 |       __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel()); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3060 |     } | 
 | 3061 |  | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3062 |     if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { | 
 | 3063 |       if (length.Equals(Location::RegisterLocation(temp3))) { | 
 | 3064 |         // When Baker read barriers are enabled, register `temp3`, | 
 | 3065 |         // which in the present case contains the `length` parameter, | 
 | 3066 |         // will be overwritten below.  Make the `length` location | 
 | 3067 |         // reference the original stack location; it will be moved | 
 | 3068 |         // back to `temp3` later if necessary. | 
 | 3069 |         DCHECK(length_arg.IsStackSlot()); | 
 | 3070 |         length = length_arg; | 
 | 3071 |       } | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3072 |  | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3073 |       // /* HeapReference<Class> */ temp1 = dest->klass_ | 
 | 3074 |       codegen_->GenerateFieldLoadWithBakerReadBarrier( | 
| Vladimir Marko | 953437b | 2016-08-24 08:30:46 +0000 | [diff] [blame] | 3075 |           invoke, temp1_loc, dest, class_offset, /* needs_null_check */ false); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3076 |  | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3077 |       if (!optimizations.GetDestinationIsNonPrimitiveArray()) { | 
 | 3078 |         // Bail out if the destination is not a non primitive array. | 
 | 3079 |         // | 
 | 3080 |         // Register `temp1` is not trashed by the read barrier emitted | 
 | 3081 |         // by GenerateFieldLoadWithBakerReadBarrier below, as that | 
 | 3082 |         // method produces a call to a ReadBarrierMarkRegX entry point, | 
 | 3083 |         // which saves all potentially live registers, including | 
 | 3084 |         // temporaries such a `temp1`. | 
 | 3085 |         // /* HeapReference<Class> */ temp2 = temp1->component_type_ | 
 | 3086 |         codegen_->GenerateFieldLoadWithBakerReadBarrier( | 
| Vladimir Marko | 953437b | 2016-08-24 08:30:46 +0000 | [diff] [blame] | 3087 |             invoke, temp2_loc, temp1, component_offset, /* needs_null_check */ false); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3088 |         __ testl(temp2, temp2); | 
 | 3089 |         __ j(kEqual, intrinsic_slow_path->GetEntryLabel()); | 
 | 3090 |         // If heap poisoning is enabled, `temp2` has been unpoisoned | 
 | 3091 |         // by the the previous call to GenerateFieldLoadWithBakerReadBarrier. | 
 | 3092 |         __ cmpw(Address(temp2, primitive_offset), Immediate(Primitive::kPrimNot)); | 
 | 3093 |         __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel()); | 
 | 3094 |       } | 
 | 3095 |  | 
 | 3096 |       // For the same reason given earlier, `temp1` is not trashed by the | 
 | 3097 |       // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below. | 
 | 3098 |       // /* HeapReference<Class> */ temp2 = src->klass_ | 
 | 3099 |       codegen_->GenerateFieldLoadWithBakerReadBarrier( | 
| Vladimir Marko | 953437b | 2016-08-24 08:30:46 +0000 | [diff] [blame] | 3100 |           invoke, temp2_loc, src, class_offset, /* needs_null_check */ false); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3101 |       // Note: if heap poisoning is on, we are comparing two unpoisoned references here. | 
 | 3102 |       __ cmpl(temp1, temp2); | 
 | 3103 |  | 
 | 3104 |       if (optimizations.GetDestinationIsTypedObjectArray()) { | 
 | 3105 |         NearLabel do_copy; | 
 | 3106 |         __ j(kEqual, &do_copy); | 
 | 3107 |         // /* HeapReference<Class> */ temp1 = temp1->component_type_ | 
 | 3108 |         codegen_->GenerateFieldLoadWithBakerReadBarrier( | 
| Vladimir Marko | 953437b | 2016-08-24 08:30:46 +0000 | [diff] [blame] | 3109 |             invoke, temp1_loc, temp1, component_offset, /* needs_null_check */ false); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3110 |         // We do not need to emit a read barrier for the following | 
 | 3111 |         // heap reference load, as `temp1` is only used in a | 
 | 3112 |         // comparison with null below, and this reference is not | 
 | 3113 |         // kept afterwards. | 
 | 3114 |         __ cmpl(Address(temp1, super_offset), Immediate(0)); | 
 | 3115 |         __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel()); | 
 | 3116 |         __ Bind(&do_copy); | 
 | 3117 |       } else { | 
 | 3118 |         __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel()); | 
 | 3119 |       } | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3120 |     } else { | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3121 |       // Non read barrier code. | 
 | 3122 |  | 
 | 3123 |       // /* HeapReference<Class> */ temp1 = dest->klass_ | 
 | 3124 |       __ movl(temp1, Address(dest, class_offset)); | 
 | 3125 |       if (!optimizations.GetDestinationIsNonPrimitiveArray()) { | 
 | 3126 |         __ MaybeUnpoisonHeapReference(temp1); | 
 | 3127 |         // Bail out if the destination is not a non primitive array. | 
 | 3128 |         // /* HeapReference<Class> */ temp2 = temp1->component_type_ | 
 | 3129 |         __ movl(temp2, Address(temp1, component_offset)); | 
 | 3130 |         __ testl(temp2, temp2); | 
 | 3131 |         __ j(kEqual, intrinsic_slow_path->GetEntryLabel()); | 
 | 3132 |         __ MaybeUnpoisonHeapReference(temp2); | 
 | 3133 |         __ cmpw(Address(temp2, primitive_offset), Immediate(Primitive::kPrimNot)); | 
 | 3134 |         __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel()); | 
 | 3135 |         // Re-poison the heap reference to make the compare instruction below | 
 | 3136 |         // compare two poisoned references. | 
 | 3137 |         __ PoisonHeapReference(temp1); | 
 | 3138 |       } | 
 | 3139 |  | 
 | 3140 |       // Note: if heap poisoning is on, we are comparing two poisoned references here. | 
 | 3141 |       __ cmpl(temp1, Address(src, class_offset)); | 
 | 3142 |  | 
 | 3143 |       if (optimizations.GetDestinationIsTypedObjectArray()) { | 
 | 3144 |         NearLabel do_copy; | 
 | 3145 |         __ j(kEqual, &do_copy); | 
 | 3146 |         __ MaybeUnpoisonHeapReference(temp1); | 
 | 3147 |         // /* HeapReference<Class> */ temp1 = temp1->component_type_ | 
 | 3148 |         __ movl(temp1, Address(temp1, component_offset)); | 
 | 3149 |         __ MaybeUnpoisonHeapReference(temp1); | 
 | 3150 |         __ cmpl(Address(temp1, super_offset), Immediate(0)); | 
 | 3151 |         __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel()); | 
 | 3152 |         __ Bind(&do_copy); | 
 | 3153 |       } else { | 
 | 3154 |         __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel()); | 
 | 3155 |       } | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3156 |     } | 
 | 3157 |   } else if (!optimizations.GetSourceIsNonPrimitiveArray()) { | 
 | 3158 |     DCHECK(optimizations.GetDestinationIsNonPrimitiveArray()); | 
 | 3159 |     // Bail out if the source is not a non primitive array. | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3160 |     if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { | 
 | 3161 |       // /* HeapReference<Class> */ temp1 = src->klass_ | 
 | 3162 |       codegen_->GenerateFieldLoadWithBakerReadBarrier( | 
| Vladimir Marko | 953437b | 2016-08-24 08:30:46 +0000 | [diff] [blame] | 3163 |           invoke, temp1_loc, src, class_offset, /* needs_null_check */ false); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3164 |       // /* HeapReference<Class> */ temp1 = temp1->component_type_ | 
 | 3165 |       codegen_->GenerateFieldLoadWithBakerReadBarrier( | 
| Vladimir Marko | 953437b | 2016-08-24 08:30:46 +0000 | [diff] [blame] | 3166 |           invoke, temp1_loc, temp1, component_offset, /* needs_null_check */ false); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3167 |       __ testl(temp1, temp1); | 
 | 3168 |       __ j(kEqual, intrinsic_slow_path->GetEntryLabel()); | 
 | 3169 |       // If heap poisoning is enabled, `temp1` has been unpoisoned | 
 | 3170 |       // by the the previous call to GenerateFieldLoadWithBakerReadBarrier. | 
 | 3171 |     } else { | 
 | 3172 |       // /* HeapReference<Class> */ temp1 = src->klass_ | 
 | 3173 |       __ movl(temp1, Address(src, class_offset)); | 
 | 3174 |       __ MaybeUnpoisonHeapReference(temp1); | 
 | 3175 |       // /* HeapReference<Class> */ temp1 = temp1->component_type_ | 
 | 3176 |       __ movl(temp1, Address(temp1, component_offset)); | 
 | 3177 |       __ testl(temp1, temp1); | 
 | 3178 |       __ j(kEqual, intrinsic_slow_path->GetEntryLabel()); | 
 | 3179 |       __ MaybeUnpoisonHeapReference(temp1); | 
 | 3180 |     } | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3181 |     __ cmpw(Address(temp1, primitive_offset), Immediate(Primitive::kPrimNot)); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3182 |     __ j(kNotEqual, intrinsic_slow_path->GetEntryLabel()); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3183 |   } | 
 | 3184 |  | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3185 |   // Compute the base source address in `temp1`. | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3186 |   int32_t element_size = Primitive::ComponentSize(Primitive::kPrimNot); | 
 | 3187 |   DCHECK_EQ(element_size, 4); | 
 | 3188 |   uint32_t offset = mirror::Array::DataOffset(element_size).Uint32Value(); | 
 | 3189 |   if (src_pos.IsConstant()) { | 
 | 3190 |     int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue(); | 
 | 3191 |     __ leal(temp1, Address(src, element_size * constant + offset)); | 
 | 3192 |   } else { | 
 | 3193 |     __ leal(temp1, Address(src, src_pos.AsRegister<Register>(), ScaleFactor::TIMES_4, offset)); | 
 | 3194 |   } | 
 | 3195 |  | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3196 |   if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { | 
 | 3197 |     // If it is needed (in the case of the fast-path loop), the base | 
 | 3198 |     // destination address is computed later, as `temp2` is used for | 
 | 3199 |     // intermediate computations. | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3200 |  | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3201 |     // Compute the end source address in `temp3`. | 
 | 3202 |     if (length.IsConstant()) { | 
 | 3203 |       int32_t constant = length.GetConstant()->AsIntConstant()->GetValue(); | 
 | 3204 |       __ leal(temp3, Address(temp1, element_size * constant)); | 
 | 3205 |     } else { | 
 | 3206 |       if (length.IsStackSlot()) { | 
 | 3207 |         // Location `length` is again pointing at a stack slot, as | 
 | 3208 |         // register `temp3` (which was containing the length parameter | 
 | 3209 |         // earlier) has been overwritten; restore it now | 
 | 3210 |         DCHECK(length.Equals(length_arg)); | 
 | 3211 |         __ movl(temp3, Address(ESP, length.GetStackIndex())); | 
 | 3212 |         length = Location::RegisterLocation(temp3); | 
 | 3213 |       } | 
 | 3214 |       __ leal(temp3, Address(temp1, length.AsRegister<Register>(), ScaleFactor::TIMES_4, 0)); | 
 | 3215 |     } | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3216 |  | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3217 |     // SystemArrayCopy implementation for Baker read barriers (see | 
 | 3218 |     // also CodeGeneratorX86::GenerateReferenceLoadWithBakerReadBarrier): | 
 | 3219 |     // | 
 | 3220 |     //   if (src_ptr != end_ptr) { | 
 | 3221 |     //     uint32_t rb_state = Lockword(src->monitor_).ReadBarrierState(); | 
 | 3222 |     //     lfence;  // Load fence or artificial data dependency to prevent load-load reordering | 
| Hiroshi Yamauchi | 12b58b2 | 2016-11-01 11:55:29 -0700 | [diff] [blame] | 3223 |     //     bool is_gray = (rb_state == ReadBarrier::GrayState()); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3224 |     //     if (is_gray) { | 
 | 3225 |     //       // Slow-path copy. | 
 | 3226 |     //       for (size_t i = 0; i != length; ++i) { | 
 | 3227 |     //         dest_array[dest_pos + i] = | 
 | 3228 |     //             MaybePoison(ReadBarrier::Mark(MaybeUnpoison(src_array[src_pos + i]))); | 
 | 3229 |     //       } | 
 | 3230 |     //     } else { | 
 | 3231 |     //       // Fast-path copy. | 
 | 3232 |     //       do { | 
 | 3233 |     //         *dest_ptr++ = *src_ptr++; | 
 | 3234 |     //       } while (src_ptr != end_ptr) | 
 | 3235 |     //     } | 
 | 3236 |     //   } | 
 | 3237 |  | 
 | 3238 |     NearLabel loop, done; | 
 | 3239 |  | 
 | 3240 |     // Don't enter copy loop if `length == 0`. | 
 | 3241 |     __ cmpl(temp1, temp3); | 
 | 3242 |     __ j(kEqual, &done); | 
 | 3243 |  | 
| Vladimir Marko | 953437b | 2016-08-24 08:30:46 +0000 | [diff] [blame] | 3244 |     // Given the numeric representation, it's enough to check the low bit of the rb_state. | 
| Hiroshi Yamauchi | 12b58b2 | 2016-11-01 11:55:29 -0700 | [diff] [blame] | 3245 |     static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); | 
 | 3246 |     static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); | 
| Vladimir Marko | 953437b | 2016-08-24 08:30:46 +0000 | [diff] [blame] | 3247 |     constexpr uint32_t gray_byte_position = LockWord::kReadBarrierStateShift / kBitsPerByte; | 
 | 3248 |     constexpr uint32_t gray_bit_position = LockWord::kReadBarrierStateShift % kBitsPerByte; | 
 | 3249 |     constexpr int32_t test_value = static_cast<int8_t>(1 << gray_bit_position); | 
 | 3250 |  | 
| Hiroshi Yamauchi | 12b58b2 | 2016-11-01 11:55:29 -0700 | [diff] [blame] | 3251 |     // if (rb_state == ReadBarrier::GrayState()) | 
| Vladimir Marko | 953437b | 2016-08-24 08:30:46 +0000 | [diff] [blame] | 3252 |     //   goto slow_path; | 
 | 3253 |     // At this point, just do the "if" and make sure that flags are preserved until the branch. | 
 | 3254 |     __ testb(Address(src, monitor_offset + gray_byte_position), Immediate(test_value)); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3255 |  | 
 | 3256 |     // Load fence to prevent load-load reordering. | 
 | 3257 |     // Note that this is a no-op, thanks to the x86 memory model. | 
 | 3258 |     codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); | 
 | 3259 |  | 
 | 3260 |     // Slow path used to copy array when `src` is gray. | 
 | 3261 |     SlowPathCode* read_barrier_slow_path = | 
 | 3262 |         new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathX86(invoke); | 
 | 3263 |     codegen_->AddSlowPath(read_barrier_slow_path); | 
 | 3264 |  | 
| Vladimir Marko | 953437b | 2016-08-24 08:30:46 +0000 | [diff] [blame] | 3265 |     // We have done the "if" of the gray bit check above, now branch based on the flags. | 
 | 3266 |     __ j(kNotZero, read_barrier_slow_path->GetEntryLabel()); | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3267 |  | 
 | 3268 |     // Fast-path copy. | 
 | 3269 |  | 
 | 3270 |     // Set the base destination address in `temp2`. | 
 | 3271 |     if (dest_pos.IsConstant()) { | 
 | 3272 |       int32_t constant = dest_pos.GetConstant()->AsIntConstant()->GetValue(); | 
 | 3273 |       __ leal(temp2, Address(dest, element_size * constant + offset)); | 
 | 3274 |     } else { | 
 | 3275 |       __ leal(temp2, Address(dest, dest_pos.AsRegister<Register>(), ScaleFactor::TIMES_4, offset)); | 
 | 3276 |     } | 
 | 3277 |  | 
 | 3278 |     // Iterate over the arrays and do a raw copy of the objects. We don't need to | 
 | 3279 |     // poison/unpoison. | 
 | 3280 |     __ Bind(&loop); | 
 | 3281 |     __ pushl(Address(temp1, 0)); | 
 | 3282 |     __ cfi().AdjustCFAOffset(4); | 
 | 3283 |     __ popl(Address(temp2, 0)); | 
 | 3284 |     __ cfi().AdjustCFAOffset(-4); | 
 | 3285 |     __ addl(temp1, Immediate(element_size)); | 
 | 3286 |     __ addl(temp2, Immediate(element_size)); | 
 | 3287 |     __ cmpl(temp1, temp3); | 
 | 3288 |     __ j(kNotEqual, &loop); | 
 | 3289 |  | 
 | 3290 |     __ Bind(read_barrier_slow_path->GetExitLabel()); | 
 | 3291 |     __ Bind(&done); | 
 | 3292 |   } else { | 
 | 3293 |     // Non read barrier code. | 
 | 3294 |  | 
 | 3295 |     // Compute the base destination address in `temp2`. | 
 | 3296 |     if (dest_pos.IsConstant()) { | 
 | 3297 |       int32_t constant = dest_pos.GetConstant()->AsIntConstant()->GetValue(); | 
 | 3298 |       __ leal(temp2, Address(dest, element_size * constant + offset)); | 
 | 3299 |     } else { | 
 | 3300 |       __ leal(temp2, Address(dest, dest_pos.AsRegister<Register>(), ScaleFactor::TIMES_4, offset)); | 
 | 3301 |     } | 
 | 3302 |  | 
 | 3303 |     // Compute the end source address in `temp3`. | 
 | 3304 |     if (length.IsConstant()) { | 
 | 3305 |       int32_t constant = length.GetConstant()->AsIntConstant()->GetValue(); | 
 | 3306 |       __ leal(temp3, Address(temp1, element_size * constant)); | 
 | 3307 |     } else { | 
 | 3308 |       __ leal(temp3, Address(temp1, length.AsRegister<Register>(), ScaleFactor::TIMES_4, 0)); | 
 | 3309 |     } | 
 | 3310 |  | 
 | 3311 |     // Iterate over the arrays and do a raw copy of the objects. We don't need to | 
 | 3312 |     // poison/unpoison. | 
 | 3313 |     NearLabel loop, done; | 
 | 3314 |     __ cmpl(temp1, temp3); | 
 | 3315 |     __ j(kEqual, &done); | 
 | 3316 |     __ Bind(&loop); | 
 | 3317 |     __ pushl(Address(temp1, 0)); | 
 | 3318 |     __ cfi().AdjustCFAOffset(4); | 
 | 3319 |     __ popl(Address(temp2, 0)); | 
 | 3320 |     __ cfi().AdjustCFAOffset(-4); | 
 | 3321 |     __ addl(temp1, Immediate(element_size)); | 
 | 3322 |     __ addl(temp2, Immediate(element_size)); | 
 | 3323 |     __ cmpl(temp1, temp3); | 
 | 3324 |     __ j(kNotEqual, &loop); | 
 | 3325 |     __ Bind(&done); | 
 | 3326 |   } | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3327 |  | 
 | 3328 |   // We only need one card marking on the destination array. | 
 | 3329 |   codegen_->MarkGCCard(temp1, | 
 | 3330 |                        temp2, | 
 | 3331 |                        dest, | 
 | 3332 |                        Register(kNoRegister), | 
 | 3333 |                        /* value_can_be_null */ false); | 
 | 3334 |  | 
| Roland Levillain | 0b671c0 | 2016-08-19 12:02:34 +0100 | [diff] [blame] | 3335 |   __ Bind(intrinsic_slow_path->GetExitLabel()); | 
| Nicolas Geoffray | fea1abd | 2016-07-06 12:09:12 +0100 | [diff] [blame] | 3336 | } | 
 | 3337 |  | 
| Aart Bik | 2f9fcc9 | 2016-03-01 15:16:54 -0800 | [diff] [blame] | 3338 | UNIMPLEMENTED_INTRINSIC(X86, MathRoundDouble) | 
| Aart Bik | 2f9fcc9 | 2016-03-01 15:16:54 -0800 | [diff] [blame] | 3339 | UNIMPLEMENTED_INTRINSIC(X86, FloatIsInfinite) | 
 | 3340 | UNIMPLEMENTED_INTRINSIC(X86, DoubleIsInfinite) | 
 | 3341 | UNIMPLEMENTED_INTRINSIC(X86, IntegerHighestOneBit) | 
 | 3342 | UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit) | 
 | 3343 | UNIMPLEMENTED_INTRINSIC(X86, IntegerLowestOneBit) | 
 | 3344 | UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit) | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 3345 |  | 
| Aart Bik | ff7d89c | 2016-11-07 08:49:28 -0800 | [diff] [blame] | 3346 | UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOf); | 
 | 3347 | UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOfAfter); | 
| Aart Bik | 71bf7b4 | 2016-11-16 10:17:46 -0800 | [diff] [blame] | 3348 | UNIMPLEMENTED_INTRINSIC(X86, StringBufferAppend); | 
 | 3349 | UNIMPLEMENTED_INTRINSIC(X86, StringBufferLength); | 
 | 3350 | UNIMPLEMENTED_INTRINSIC(X86, StringBufferToString); | 
 | 3351 | UNIMPLEMENTED_INTRINSIC(X86, StringBuilderAppend); | 
 | 3352 | UNIMPLEMENTED_INTRINSIC(X86, StringBuilderLength); | 
 | 3353 | UNIMPLEMENTED_INTRINSIC(X86, StringBuilderToString); | 
| Aart Bik | ff7d89c | 2016-11-07 08:49:28 -0800 | [diff] [blame] | 3354 |  | 
| Aart Bik | 0e54c01 | 2016-03-04 12:08:31 -0800 | [diff] [blame] | 3355 | // 1.8. | 
 | 3356 | UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddInt) | 
 | 3357 | UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddLong) | 
 | 3358 | UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetInt) | 
 | 3359 | UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetLong) | 
 | 3360 | UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetObject) | 
| Aart Bik | 0e54c01 | 2016-03-04 12:08:31 -0800 | [diff] [blame] | 3361 |  | 
| Aart Bik | 2f9fcc9 | 2016-03-01 15:16:54 -0800 | [diff] [blame] | 3362 | UNREACHABLE_INTRINSICS(X86) | 
| Roland Levillain | 4d02711 | 2015-07-01 15:41:14 +0100 | [diff] [blame] | 3363 |  | 
 | 3364 | #undef __ | 
 | 3365 |  | 
| Mark Mendell | 09ed1a3 | 2015-03-25 08:30:06 -0400 | [diff] [blame] | 3366 | }  // namespace x86 | 
 | 3367 | }  // namespace art |