Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_ARM. | 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_ARM. |
| 6 #if defined(TARGET_ARCH_ARM) | 6 #if defined(TARGET_ARCH_ARM) |
| 7 | 7 |
| 8 #include "vm/intrinsifier.h" | 8 #include "vm/intrinsifier.h" |
| 9 | |
| 10 #include "vm/assembler.h" | |
| 11 #include "vm/flow_graph_compiler.h" | |
| 9 #include "vm/object.h" | 12 #include "vm/object.h" |
| 13 #include "vm/object_store.h" | |
| 14 #include "vm/symbols.h" | |
| 10 | 15 |
| 11 namespace dart { | 16 namespace dart { |
| 12 | 17 |
| 18 DECLARE_FLAG(bool, enable_type_checks); | |
| 19 | |
| 20 | |
| 21 #define __ assembler-> | |
| 22 | |
| 13 bool Intrinsifier::ObjectArray_Allocate(Assembler* assembler) { | 23 bool Intrinsifier::ObjectArray_Allocate(Assembler* assembler) { |
| 24 const intptr_t kTypeArgumentsOffset = 1 * kWordSize; | |
| 25 const intptr_t kArrayLengthOffset = 0 * kWordSize; | |
| 26 Label fall_through; | |
| 27 | |
| 28 // Compute the size to be allocated, it is based on the array length | |
| 29 // and is computed as: | |
| 30 // RoundedAllocationSize((array_length * kwordSize) + sizeof(RawArray)). | |
| 31 __ ldr(R3, Address(SP, kArrayLengthOffset)); // Array length. | |
| 32 | |
| 33 // Check that length is a positive Smi. | |
| 34 __ tst(R3, ShifterOperand(kSmiTagMask)); | |
| 35 __ b(&fall_through, NE); | |
| 36 __ cmp(R3, ShifterOperand(0)); | |
| 37 __ b(&fall_through, LT); | |
| 38 | |
| 39 // Check for maximum allowed length. | |
| 40 const intptr_t max_len = | |
| 41 reinterpret_cast<int32_t>(Smi::New(Array::kMaxElements)); | |
| 42 __ CompareImmediate(R3, max_len); | |
| 43 __ b(&fall_through, GT); | |
| 44 | |
| 45 const intptr_t fixed_size = sizeof(RawArray) + kObjectAlignment - 1; | |
| 46 __ LoadImmediate(R2, fixed_size); | |
| 47 __ add(R2, R2, ShifterOperand(R3, LSL, 1)); // R3 is a Smi. | |
| 48 ASSERT(kSmiTagShift == 1); | |
| 49 __ bic(R2, R2, ShifterOperand(kObjectAlignment - 1)); | |
| 50 | |
| 51 // R2: Allocation size. | |
| 52 | |
| 53 Isolate* isolate = Isolate::Current(); | |
| 54 Heap* heap = isolate->heap(); | |
| 55 | |
| 56 __ LoadImmediate(R6, heap->TopAddress()); | |
| 57 __ ldr(R0, Address(R6, 0)); // Potential new object start. | |
| 58 __ adds(R1, R0, ShifterOperand(R2)); // Potential next object start. | |
| 59 __ b(&fall_through, VS); | |
| 60 | |
| 61 // Check if the allocation fits into the remaining space. | |
| 62 // R0: potential new object start. | |
| 63 // R1: potential next object start. | |
| 64 // R2: allocation size. | |
| 65 __ LoadImmediate(R3, heap->EndAddress()); | |
| 66 __ ldr(R3, Address(R3, 0)); | |
| 67 __ cmp(R1, ShifterOperand(R3)); | |
| 68 __ b(&fall_through, CS); | |
| 69 | |
| 70 // Successfully allocated the object(s), now update top to point to | |
| 71 // next object start and initialize the object. | |
| 72 __ str(R1, Address(R6, 0)); | |
| 73 __ add(R0, R0, ShifterOperand(kHeapObjectTag)); | |
| 74 | |
| 75 // Initialize the tags. | |
| 76 // R0: new object start as a tagged pointer. | |
| 77 // R1: new object end address. | |
| 78 // R2: allocation size. | |
| 79 { | |
| 80 const intptr_t shift = RawObject::kSizeTagBit - kObjectAlignmentLog2; | |
| 81 const Class& cls = Class::Handle(isolate->object_store()->array_class()); | |
| 82 | |
| 83 __ CompareImmediate(R2, RawObject::SizeTag::kMaxSizeTag); | |
| 84 __ mov(R2, ShifterOperand(R2, LSL, shift), LS); | |
| 85 __ mov(R2, ShifterOperand(0), HI); | |
| 86 | |
| 87 // Get the class index and insert it into the tags. | |
| 88 // R2: size and bit tags. | |
| 89 __ LoadImmediate(TMP, RawObject::ClassIdTag::encode(cls.id())); | |
| 90 __ orr(R2, R2, ShifterOperand(TMP)); | |
| 91 __ str(R2, FieldAddress(R0, Array::tags_offset())); // Store tags. | |
| 92 } | |
| 93 | |
| 94 // R0: new object start as a tagged pointer. | |
| 95 // R1: new object end address. | |
| 96 // Store the type argument field. | |
| 97 __ ldr(R2, Address(SP, kTypeArgumentsOffset)); // Type argument. | |
| 98 __ StoreIntoObjectNoBarrier(R0, | |
| 99 FieldAddress(R0, Array::type_arguments_offset()), | |
| 100 R2); | |
| 101 | |
| 102 // Set the length field. | |
| 103 __ ldr(R2, Address(SP, kArrayLengthOffset)); // Array Length. | |
| 104 __ StoreIntoObjectNoBarrier(R0, | |
| 105 FieldAddress(R0, Array::length_offset()), | |
| 106 R2); | |
| 107 | |
| 108 // Initialize all array elements to raw_null. | |
| 109 // R0: new object start as a tagged pointer. | |
| 110 // R1: new object end address. | |
| 111 // R2: iterator which initially points to the start of the variable | |
| 112 // data area to be initialized. | |
| 113 // R3: null | |
| 114 __ LoadImmediate(R3, reinterpret_cast<intptr_t>(Object::null())); | |
| 115 __ AddImmediate(R2, R0, sizeof(RawArray) - kHeapObjectTag); | |
| 116 | |
| 117 Label init_loop; | |
| 118 __ Bind(&init_loop); | |
| 119 __ cmp(R2, ShifterOperand(R1)); | |
| 120 __ str(R3, Address(R2, 0), CC); | |
| 121 __ AddImmediate(R2, kWordSize, CC); | |
| 122 __ b(&init_loop, CC); | |
| 123 | |
| 124 __ Ret(); // Returns the newly allocated object in R0. | |
| 125 __ Bind(&fall_through); | |
| 14 return false; | 126 return false; |
| 15 } | 127 } |
| 16 | 128 |
| 17 | 129 |
| 18 bool Intrinsifier::Array_getLength(Assembler* assembler) { | 130 bool Intrinsifier::Array_getLength(Assembler* assembler) { |
| 19 return false; | 131 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 132 __ ldr(R0, FieldAddress(R0, Array::length_offset())); | |
| 133 __ Ret(); | |
| 134 return true; | |
| 20 } | 135 } |
| 21 | 136 |
| 22 | 137 |
| 23 bool Intrinsifier::ImmutableArray_getLength(Assembler* assembler) { | 138 bool Intrinsifier::ImmutableArray_getLength(Assembler* assembler) { |
| 24 return false; | 139 return Array_getLength(assembler); |
| 25 } | 140 } |
| 26 | 141 |
| 27 | 142 |
| 28 bool Intrinsifier::Array_getIndexed(Assembler* assembler) { | 143 bool Intrinsifier::Array_getIndexed(Assembler* assembler) { |
| 144 Label fall_through; | |
| 145 | |
| 146 __ ldr(R0, Address(SP, + 0 * kWordSize)); // Index | |
| 147 __ ldr(R1, Address(SP, + 1 * kWordSize)); // Array | |
| 148 | |
| 149 __ tst(R0, ShifterOperand(kSmiTagMask)); | |
| 150 __ b(&fall_through, NE); // Index is not an smi, fall through | |
| 151 | |
| 152 // range check | |
| 153 __ ldr(R6, FieldAddress(R1, Array::length_offset())); | |
| 154 __ cmp(R0, ShifterOperand(R6)); | |
| 155 | |
| 156 ASSERT(kSmiTagShift == 1); | |
| 157 // array element at R1 + R0*2 + Array::data_offset - 1 | |
| 158 __ add(R6, R1, ShifterOperand(R0, LSL, 1), CC); | |
| 159 __ ldr(R0, FieldAddress(R6, Array::data_offset()), CC); | |
| 160 __ mov(PC, ShifterOperand(LR), CC); | |
|
regis
2013/05/28 22:01:44
The use of mov to pc to return is deprecated. You
zra
2013/05/28 22:49:31
Done.
| |
| 161 __ Bind(&fall_through); | |
| 29 return false; | 162 return false; |
| 30 } | 163 } |
| 31 | 164 |
| 32 | 165 |
| 33 bool Intrinsifier::ImmutableArray_getIndexed(Assembler* assembler) { | 166 bool Intrinsifier::ImmutableArray_getIndexed(Assembler* assembler) { |
| 34 return false; | 167 return Array_getIndexed(assembler); |
| 35 } | 168 } |
| 36 | 169 |
| 37 | 170 |
| 171 static intptr_t ComputeObjectArrayTypeArgumentsOffset() { | |
| 172 const Library& core_lib = Library::Handle(Library::CoreLibrary()); | |
| 173 const Class& cls = | |
| 174 Class::Handle(core_lib.LookupClassAllowPrivate(Symbols::ObjectArray())); | |
| 175 ASSERT(!cls.IsNull()); | |
| 176 ASSERT(cls.HasTypeArguments()); | |
| 177 ASSERT(cls.NumTypeArguments() == 1); | |
| 178 const intptr_t field_offset = cls.type_arguments_field_offset(); | |
| 179 ASSERT(field_offset != Class::kNoTypeArguments); | |
| 180 return field_offset; | |
| 181 } | |
| 182 | |
| 183 | |
| 184 // Intrinsify only for Smi value and index. Non-smi values need a store buffer | |
| 185 // update. Array length is always a Smi. | |
| 38 bool Intrinsifier::Array_setIndexed(Assembler* assembler) { | 186 bool Intrinsifier::Array_setIndexed(Assembler* assembler) { |
| 39 return false; | 187 Label fall_through; |
| 40 } | 188 |
| 41 | 189 if (FLAG_enable_type_checks) { |
| 42 | 190 const intptr_t type_args_field_offset = |
| 191 ComputeObjectArrayTypeArgumentsOffset(); | |
| 192 // Inline simple tests (Smi, null), fallthrough if not positive. | |
| 193 const int32_t raw_null = reinterpret_cast<intptr_t>(Object::null()); | |
| 194 Label checked_ok; | |
| 195 __ ldr(R2, Address(SP, 0 * kWordSize)); // Value. | |
| 196 | |
| 197 // Null value is valid for any type. | |
| 198 __ CompareImmediate(R2, raw_null); | |
| 199 __ b(&checked_ok, EQ); | |
| 200 | |
| 201 __ ldr(R1, Address(SP, 2 * kWordSize)); // Array. | |
| 202 __ ldr(R1, FieldAddress(R1, type_args_field_offset)); | |
| 203 | |
| 204 // R1: Type arguments of array. | |
| 205 __ CompareImmediate(R1, raw_null); | |
| 206 __ b(&checked_ok, EQ); | |
| 207 | |
| 208 // Check if it's dynamic. | |
| 209 // For now handle only TypeArguments and bail out if InstantiatedTypeArgs. | |
| 210 __ CompareClassId(R1, kTypeArgumentsCid, R0); | |
| 211 __ b(&fall_through, NE); | |
| 212 // Get type at index 0. | |
| 213 __ ldr(R0, FieldAddress(R1, TypeArguments::type_at_offset(0))); | |
| 214 __ CompareObject(R0, Type::ZoneHandle(Type::DynamicType())); | |
| 215 __ b(&checked_ok, EQ); | |
| 216 | |
| 217 // Check for int and num. | |
| 218 __ tst(R2, ShifterOperand(kSmiTagMask)); // Value is Smi? | |
| 219 __ b(&fall_through, NE); // Non-smi value. | |
| 220 __ CompareObject(R0, Type::ZoneHandle(Type::IntType())); | |
| 221 __ b(&checked_ok, EQ); | |
| 222 __ CompareObject(R0, Type::ZoneHandle(Type::Number())); | |
| 223 __ b(&fall_through, NE); | |
| 224 __ Bind(&checked_ok); | |
| 225 } | |
| 226 __ ldr(R1, Address(SP, 1 * kWordSize)); // Index. | |
| 227 __ tst(R1, ShifterOperand(kSmiTagMask)); | |
| 228 // Index not Smi. | |
| 229 __ b(&fall_through, NE); | |
| 230 __ ldr(R0, Address(SP, 2 * kWordSize)); // Array. | |
| 231 | |
| 232 // Range check. | |
| 233 __ ldr(R3, FieldAddress(R0, Array::length_offset())); // Array length. | |
| 234 __ cmp(R1, ShifterOperand(R3)); | |
| 235 // Runtime throws exception. | |
| 236 __ b(&fall_through, CS); | |
| 237 | |
| 238 // Note that R1 is Smi, i.e, times 2. | |
| 239 ASSERT(kSmiTagShift == 1); | |
| 240 // Destroy R2 as we will not continue in the function. | |
| 241 __ ldr(R2, Address(SP, 0 * kWordSize)); // Value. | |
| 242 __ add(R1, R0, ShifterOperand(R1, LSL, 1)); // R1 is Smi. | |
| 243 __ StoreIntoObject(R0, | |
| 244 FieldAddress(R1, Array::data_offset()), | |
| 245 R2); | |
| 246 // Caller is responsible of preserving the value if necessary. | |
| 247 __ Ret(); | |
| 248 __ Bind(&fall_through); | |
| 249 return false; | |
| 250 } | |
| 251 | |
| 252 | |
| 253 // Allocate a GrowableObjectArray using the backing array specified. | |
| 254 // On stack: type argument (+1), data (+0). | |
| 43 bool Intrinsifier::GrowableArray_Allocate(Assembler* assembler) { | 255 bool Intrinsifier::GrowableArray_Allocate(Assembler* assembler) { |
| 256 // The newly allocated object is returned in R0. | |
| 257 const intptr_t kTypeArgumentsOffset = 1 * kWordSize; | |
| 258 const intptr_t kArrayOffset = 0 * kWordSize; | |
| 259 Label fall_through; | |
| 260 | |
| 261 // Compute the size to be allocated, it is based on the array length | |
| 262 // and is computed as: | |
| 263 // RoundedAllocationSize(sizeof(RawGrowableObjectArray)) + | |
| 264 intptr_t fixed_size = GrowableObjectArray::InstanceSize(); | |
| 265 | |
| 266 Isolate* isolate = Isolate::Current(); | |
| 267 Heap* heap = isolate->heap(); | |
| 268 | |
| 269 __ LoadImmediate(R2, heap->TopAddress()); | |
| 270 __ ldr(R0, Address(R2, 0)); | |
| 271 __ AddImmediate(R1, R0, fixed_size); | |
| 272 | |
| 273 // Check if the allocation fits into the remaining space. | |
| 274 // R0: potential new backing array object start. | |
| 275 // R1: potential next object start. | |
| 276 __ LoadImmediate(R3, heap->EndAddress()); | |
| 277 __ ldr(R3, Address(R3, 0)); | |
| 278 __ cmp(R1, ShifterOperand(R3)); | |
| 279 __ b(&fall_through, CS); | |
| 280 | |
| 281 // Successfully allocated the object(s), now update top to point to | |
| 282 // next object start and initialize the object. | |
| 283 __ str(R1, Address(R2, 0)); | |
| 284 __ AddImmediate(R0, kHeapObjectTag); | |
| 285 | |
| 286 // Initialize the tags. | |
| 287 // R0: new growable array object start as a tagged pointer. | |
| 288 const Class& cls = Class::Handle( | |
| 289 isolate->object_store()->growable_object_array_class()); | |
| 290 uword tags = 0; | |
| 291 tags = RawObject::SizeTag::update(fixed_size, tags); | |
| 292 tags = RawObject::ClassIdTag::update(cls.id(), tags); | |
| 293 __ LoadImmediate(R1, tags); | |
| 294 __ str(R1, FieldAddress(R0, GrowableObjectArray::tags_offset())); | |
| 295 | |
| 296 // Store backing array object in growable array object. | |
| 297 __ ldr(R1, Address(SP, kArrayOffset)); // Data argument. | |
| 298 // R0 is new, no barrier needed. | |
| 299 __ StoreIntoObjectNoBarrier( | |
| 300 R0, | |
| 301 FieldAddress(R0, GrowableObjectArray::data_offset()), | |
| 302 R1); | |
| 303 | |
| 304 // R0: new growable array object start as a tagged pointer. | |
| 305 // Store the type argument field in the growable array object. | |
| 306 __ ldr(R1, Address(SP, kTypeArgumentsOffset)); // Type argument. | |
| 307 __ StoreIntoObjectNoBarrier( | |
| 308 R0, | |
| 309 FieldAddress(R0, GrowableObjectArray::type_arguments_offset()), | |
| 310 R1); | |
| 311 | |
| 312 // Set the length field in the growable array object to 0. | |
| 313 __ LoadImmediate(R1, 0); | |
| 314 __ str(R1, FieldAddress(R0, GrowableObjectArray::length_offset())); | |
| 315 __ Ret(); // Returns the newly allocated object in R0. | |
| 316 | |
| 317 __ Bind(&fall_through); | |
| 44 return false; | 318 return false; |
| 45 } | 319 } |
| 46 | 320 |
| 47 | 321 |
| 48 bool Intrinsifier::GrowableArray_getLength(Assembler* assembler) { | 322 bool Intrinsifier::GrowableArray_getLength(Assembler* assembler) { |
| 49 return false; | 323 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 324 __ ldr(R0, FieldAddress(R0, GrowableObjectArray::length_offset())); | |
| 325 __ Ret(); | |
| 326 return true; | |
| 50 } | 327 } |
| 51 | 328 |
| 52 | 329 |
| 53 bool Intrinsifier::GrowableArray_getCapacity(Assembler* assembler) { | 330 bool Intrinsifier::GrowableArray_getCapacity(Assembler* assembler) { |
| 54 return false; | 331 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 332 __ ldr(R0, FieldAddress(R0, GrowableObjectArray::data_offset())); | |
| 333 __ ldr(R0, FieldAddress(R0, Array::length_offset())); | |
| 334 __ Ret(); | |
| 335 return true; | |
| 55 } | 336 } |
| 56 | 337 |
| 57 | 338 |
| 58 bool Intrinsifier::GrowableArray_getIndexed(Assembler* assembler) { | 339 bool Intrinsifier::GrowableArray_getIndexed(Assembler* assembler) { |
| 59 return false; | 340 Label fall_through; |
| 60 } | 341 |
| 61 | 342 __ ldr(R0, Address(SP, + 0 * kWordSize)); // Index |
| 62 | 343 __ ldr(R1, Address(SP, + 1 * kWordSize)); // Array |
| 344 | |
| 345 __ tst(R0, ShifterOperand(kSmiTagMask)); | |
| 346 __ b(&fall_through, NE); // Index is not an smi, fall through | |
| 347 | |
| 348 // range check | |
| 349 __ ldr(R6, FieldAddress(R1, GrowableObjectArray::length_offset())); | |
| 350 __ cmp(R0, ShifterOperand(R6)); | |
| 351 | |
| 352 ASSERT(kSmiTagShift == 1); | |
| 353 // array element at R6 + R0 * 2 + Array::data_offset - 1 | |
| 354 __ ldr(R6, FieldAddress(R1, GrowableObjectArray::data_offset()), CC); // data | |
| 355 __ add(R6, R6, ShifterOperand(R0, LSL, 1), CC); | |
| 356 __ ldr(R0, FieldAddress(R6, Array::data_offset()), CC); | |
| 357 __ mov(PC, ShifterOperand(LR), CC); | |
| 358 __ Bind(&fall_through); | |
| 359 return false; | |
| 360 } | |
| 361 | |
| 362 | |
| 363 // Set value into growable object array at specified index. | |
| 364 // On stack: growable array (+2), index (+1), value (+0). | |
| 63 bool Intrinsifier::GrowableArray_setIndexed(Assembler* assembler) { | 365 bool Intrinsifier::GrowableArray_setIndexed(Assembler* assembler) { |
| 64 return false; | 366 if (FLAG_enable_type_checks) { |
| 65 } | 367 return false; |
| 66 | 368 } |
| 67 | 369 Label fall_through; |
| 370 __ ldr(R1, Address(SP, 1 * kWordSize)); // Index. | |
| 371 __ ldr(R0, Address(SP, 2 * kWordSize)); // GrowableArray. | |
| 372 __ tst(R1, ShifterOperand(kSmiTagMask)); | |
| 373 __ b(&fall_through, NE); // Non-smi index. | |
| 374 // Range check using _length field. | |
| 375 __ ldr(R2, FieldAddress(R0, GrowableObjectArray::length_offset())); | |
| 376 __ cmp(R1, ShifterOperand(R2)); | |
| 377 // Runtime throws exception. | |
| 378 __ b(&fall_through, CS); | |
| 379 __ ldr(R0, FieldAddress(R0, GrowableObjectArray::data_offset())); // data. | |
| 380 __ ldr(R2, Address(SP, 0 * kWordSize)); // Value. | |
| 381 // Note that R1 is Smi, i.e, times 2. | |
| 382 ASSERT(kSmiTagShift == 1); | |
| 383 __ add(R1, R0, ShifterOperand(R1, LSL, 1)); | |
| 384 __ StoreIntoObject(R0, | |
| 385 FieldAddress(R1, Array::data_offset()), | |
| 386 R2); | |
| 387 __ Ret(); | |
| 388 __ Bind(&fall_through); | |
| 389 return false; | |
| 390 } | |
| 391 | |
| 392 | |
| 393 // Set length of growable object array. The length cannot | |
| 394 // be greater than the length of the data container. | |
| 395 // On stack: growable array (+1), length (+0). | |
| 68 bool Intrinsifier::GrowableArray_setLength(Assembler* assembler) { | 396 bool Intrinsifier::GrowableArray_setLength(Assembler* assembler) { |
| 69 return false; | 397 __ ldr(R0, Address(SP, 1 * kWordSize)); // Growable array. |
| 70 } | 398 __ ldr(R1, Address(SP, 0 * kWordSize)); // Length value. |
| 71 | 399 __ tst(R1, ShifterOperand(kSmiTagMask)); // Check for Smi. |
| 72 | 400 __ str(R1, FieldAddress(R0, GrowableObjectArray::length_offset()), EQ); |
| 401 __ mov(PC, ShifterOperand(LR), EQ); | |
| 402 // Fall through on non-Smi. | |
| 403 return false; | |
| 404 } | |
| 405 | |
| 406 | |
| 407 // Set data of growable object array. | |
| 408 // On stack: growable array (+1), data (+0). | |
| 73 bool Intrinsifier::GrowableArray_setData(Assembler* assembler) { | 409 bool Intrinsifier::GrowableArray_setData(Assembler* assembler) { |
| 74 return false; | 410 if (FLAG_enable_type_checks) { |
| 75 } | 411 return false; |
| 76 | 412 } |
| 77 | 413 Label fall_through; |
| 414 __ ldr(R1, Address(SP, 0 * kWordSize)); // Data. | |
| 415 // Check that data is an ObjectArray. | |
| 416 __ tst(R1, ShifterOperand(kSmiTagMask)); | |
| 417 __ b(&fall_through, EQ); // Data is Smi. | |
| 418 __ CompareClassId(R1, kArrayCid, R0); | |
| 419 __ b(&fall_through, NE); | |
| 420 __ ldr(R0, Address(SP, 1 * kWordSize)); // Growable array. | |
| 421 __ StoreIntoObject(R0, | |
| 422 FieldAddress(R0, GrowableObjectArray::data_offset()), | |
| 423 R1); | |
| 424 __ Ret(); | |
| 425 __ Bind(&fall_through); | |
| 426 return false; | |
| 427 } | |
| 428 | |
| 429 | |
| 430 // Add an element to growable array if it doesn't need to grow, otherwise | |
| 431 // call into regular code. | |
| 432 // On stack: growable array (+1), value (+0). | |
| 78 bool Intrinsifier::GrowableArray_add(Assembler* assembler) { | 433 bool Intrinsifier::GrowableArray_add(Assembler* assembler) { |
| 79 return false; | 434 // In checked mode we need to type-check the incoming argument. |
| 80 } | 435 if (FLAG_enable_type_checks) return false; |
| 81 | 436 Label fall_through; |
| 82 | 437 // R0: Array. |
| 438 __ ldr(R0, Address(SP, 1 * kWordSize)); | |
| 439 // R1: length. | |
| 440 __ ldr(R1, FieldAddress(R0, GrowableObjectArray::length_offset())); | |
| 441 // R2: data. | |
| 442 __ ldr(R2, FieldAddress(R0, GrowableObjectArray::data_offset())); | |
| 443 // R3: capacity. | |
| 444 __ ldr(R3, FieldAddress(R2, Array::length_offset())); | |
| 445 // Compare length with capacity. | |
| 446 __ cmp(R1, ShifterOperand(R3)); | |
| 447 __ b(&fall_through, EQ); // Must grow data. | |
| 448 const int32_t value_one = reinterpret_cast<int32_t>(Smi::New(1)); | |
| 449 // len = len + 1; | |
| 450 __ add(R3, R1, ShifterOperand(value_one)); | |
| 451 __ str(R3, FieldAddress(R0, GrowableObjectArray::length_offset())); | |
| 452 __ ldr(R0, Address(SP, 0 * kWordSize)); // Value. | |
| 453 ASSERT(kSmiTagShift == 1); | |
| 454 __ add(R1, R2, ShifterOperand(R1, LSL, 1)); | |
| 455 __ StoreIntoObject(R2, | |
| 456 FieldAddress(R1, Array::data_offset()), | |
| 457 R0); | |
| 458 const int32_t raw_null = reinterpret_cast<int32_t>(Object::null()); | |
| 459 __ LoadImmediate(R0, raw_null); | |
| 460 __ Ret(); | |
| 461 __ Bind(&fall_through); | |
| 462 return false; | |
| 463 } | |
| 464 | |
| 465 | |
| 466 #define TYPED_ARRAY_ALLOCATION(type_name, cid, max_len, scale_shift) \ | |
| 467 Label fall_through; \ | |
| 468 const intptr_t kArrayLengthStackOffset = 0 * kWordSize; \ | |
| 469 __ ldr(R2, Address(SP, kArrayLengthStackOffset)); /* Array length. */ \ | |
| 470 /* Check that length is a positive Smi. */ \ | |
| 471 /* R2: requested array length argument. */ \ | |
| 472 __ tst(R2, ShifterOperand(kSmiTagMask)); \ | |
| 473 __ b(&fall_through, NE); \ | |
| 474 __ CompareImmediate(R2, 0); \ | |
| 475 __ b(&fall_through, LT); \ | |
| 476 __ SmiUntag(R2); \ | |
| 477 /* Check for maximum allowed length. */ \ | |
| 478 /* R2: untagged array length. */ \ | |
| 479 __ CompareImmediate(R2, max_len); \ | |
| 480 __ b(&fall_through, GT); \ | |
| 481 __ mov(R2, ShifterOperand(R2, LSL, scale_shift)); \ | |
| 482 const intptr_t fixed_size = sizeof(Raw##type_name) + kObjectAlignment - 1; \ | |
| 483 __ AddImmediate(R2, fixed_size); \ | |
| 484 __ bic(R2, R2, ShifterOperand(kObjectAlignment - 1)); \ | |
| 485 Heap* heap = Isolate::Current()->heap(); \ | |
| 486 \ | |
| 487 __ LoadImmediate(R0, heap->TopAddress()); \ | |
| 488 __ ldr(R0, Address(R0, 0)); \ | |
| 489 \ | |
| 490 /* R2: allocation size. */ \ | |
| 491 __ add(R1, R0, ShifterOperand(R2)); \ | |
| 492 __ b(&fall_through, VS); \ | |
| 493 \ | |
| 494 /* Check if the allocation fits into the remaining space. */ \ | |
| 495 /* R0: potential new object start. */ \ | |
| 496 /* R1: potential next object start. */ \ | |
| 497 /* R2: allocation size. */ \ | |
| 498 __ LoadImmediate(R3, heap->EndAddress()); \ | |
| 499 __ ldr(R3, Address(R3, 0)); \ | |
| 500 __ cmp(R1, ShifterOperand(R3)); \ | |
| 501 __ b(&fall_through, CS); \ | |
| 502 \ | |
| 503 /* Successfully allocated the object(s), now update top to point to */ \ | |
| 504 /* next object start and initialize the object. */ \ | |
| 505 __ LoadImmediate(R3, heap->TopAddress()); \ | |
| 506 __ str(R1, Address(R3, 0)); \ | |
| 507 __ AddImmediate(R0, kHeapObjectTag); \ | |
| 508 \ | |
| 509 /* Initialize the tags. */ \ | |
| 510 /* R0: new object start as a tagged pointer. */ \ | |
| 511 /* R1: new object end address. */ \ | |
| 512 /* R2: allocation size. */ \ | |
| 513 { \ | |
| 514 __ CompareImmediate(R2, RawObject::SizeTag::kMaxSizeTag); \ | |
| 515 __ mov(R2, ShifterOperand(R2, LSL, \ | |
| 516 RawObject::kSizeTagBit - kObjectAlignmentLog2), LS); \ | |
| 517 __ mov(R2, ShifterOperand(0), HI); \ | |
| 518 \ | |
| 519 /* Get the class index and insert it into the tags. */ \ | |
| 520 __ LoadImmediate(TMP, RawObject::ClassIdTag::encode(cid)); \ | |
| 521 __ orr(R2, R2, ShifterOperand(TMP)); \ | |
| 522 __ str(R2, FieldAddress(R0, type_name::tags_offset())); /* Tags. */ \ | |
| 523 } \ | |
| 524 /* Set the length field. */ \ | |
| 525 /* R0: new object start as a tagged pointer. */ \ | |
| 526 /* R1: new object end address. */ \ | |
| 527 __ ldr(R2, Address(SP, kArrayLengthStackOffset)); /* Array length. */ \ | |
| 528 __ StoreIntoObjectNoBarrier(R0, \ | |
| 529 FieldAddress(R0, type_name::length_offset()), \ | |
| 530 R2); \ | |
| 531 /* Initialize all array elements to 0. */ \ | |
| 532 /* R0: new object start as a tagged pointer. */ \ | |
| 533 /* R1: new object end address. */ \ | |
| 534 /* R2: iterator which initially points to the start of the variable */ \ | |
| 535 /* R3: scratch register. */ \ | |
| 536 /* data area to be initialized. */ \ | |
| 537 __ LoadImmediate(R3, 0); \ | |
| 538 __ AddImmediate(R2, R0, sizeof(Raw##type_name) - 1); \ | |
| 539 Label init_loop; \ | |
| 540 __ Bind(&init_loop); \ | |
| 541 __ cmp(R2, ShifterOperand(R1)); \ | |
| 542 __ str(R3, Address(R2, 0), CC); \ | |
| 543 __ add(R2, R2, ShifterOperand(kWordSize), CC); \ | |
| 544 __ b(&init_loop, CC); \ | |
| 545 \ | |
| 546 __ Ret(); \ | |
| 547 __ Bind(&fall_through); \ | |
| 548 | |
| 549 | |
| 550 // Gets the length of a TypedData. | |
| 83 bool Intrinsifier::TypedData_getLength(Assembler* assembler) { | 551 bool Intrinsifier::TypedData_getLength(Assembler* assembler) { |
| 84 return false; | 552 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 85 } | 553 __ ldr(R0, FieldAddress(R0, TypedData::length_offset())); |
| 554 __ Ret(); | |
| 555 return true; | |
| 556 } | |
| 557 | |
| 558 | |
| 559 static int GetScaleFactor(intptr_t size) { | |
| 560 switch (size) { | |
| 561 case 1: return 0; | |
| 562 case 2: return 1; | |
| 563 case 4: return 2; | |
| 564 case 8: return 3; | |
| 565 case 16: return 4; | |
| 566 } | |
| 567 UNREACHABLE(); | |
| 568 return -1; | |
| 569 }; | |
| 86 | 570 |
| 87 | 571 |
| 88 #define TYPED_DATA_ALLOCATOR(clazz) \ | 572 #define TYPED_DATA_ALLOCATOR(clazz) \ |
| 89 bool Intrinsifier::TypedData_##clazz##_new(Assembler* assembler) { \ | 573 bool Intrinsifier::TypedData_##clazz##_new(Assembler* assembler) { \ |
| 574 intptr_t size = TypedData::ElementSizeInBytes(kTypedData##clazz##Cid); \ | |
| 575 intptr_t max_len = TypedData::MaxElements(kTypedData##clazz##Cid); \ | |
| 576 int shift = GetScaleFactor(size); \ | |
| 577 TYPED_ARRAY_ALLOCATION(TypedData, kTypedData##clazz##Cid, max_len, shift); \ | |
| 90 return false; \ | 578 return false; \ |
| 91 } \ | 579 } \ |
| 92 bool Intrinsifier::TypedData_##clazz##_factory(Assembler* assembler) { \ | 580 bool Intrinsifier::TypedData_##clazz##_factory(Assembler* assembler) { \ |
| 581 intptr_t size = TypedData::ElementSizeInBytes(kTypedData##clazz##Cid); \ | |
| 582 intptr_t max_len = TypedData::MaxElements(kTypedData##clazz##Cid); \ | |
| 583 int shift = GetScaleFactor(size); \ | |
| 584 TYPED_ARRAY_ALLOCATION(TypedData, kTypedData##clazz##Cid, max_len, shift); \ | |
| 93 return false; \ | 585 return false; \ |
| 94 } | 586 } |
| 95 CLASS_LIST_TYPED_DATA(TYPED_DATA_ALLOCATOR) | 587 CLASS_LIST_TYPED_DATA(TYPED_DATA_ALLOCATOR) |
| 96 #undef TYPED_DATA_ALLOCATOR | 588 #undef TYPED_DATA_ALLOCATOR |
| 97 | 589 |
| 98 | 590 |
| 591 // Loads args from stack into R0 and R1 | |
| 592 // Tests if they are smis, jumps to label not_smi if not. | |
| 593 static void TestBothArgumentsSmis(Assembler* assembler, Label* not_smi) { | |
| 594 __ ldr(R0, Address(SP, + 0 * kWordSize)); | |
| 595 __ ldr(R1, Address(SP, + 1 * kWordSize)); | |
| 596 __ orr(TMP, R0, ShifterOperand(R1)); | |
| 597 __ tst(TMP, ShifterOperand(kSmiTagMask)); | |
| 598 __ b(not_smi, NE); | |
| 599 return; | |
| 600 } | |
| 601 | |
| 602 | |
| 99 bool Intrinsifier::Integer_addFromInteger(Assembler* assembler) { | 603 bool Intrinsifier::Integer_addFromInteger(Assembler* assembler) { |
| 604 Label fall_through; | |
| 605 TestBothArgumentsSmis(assembler, &fall_through); // Checks two smis. | |
| 606 __ adds(R0, R0, ShifterOperand(R1)); // Adds. | |
| 607 __ mov(PC, ShifterOperand(LR), VC); // Return if no overflow. | |
| 608 // Otherwise fall through. | |
| 609 __ Bind(&fall_through); | |
| 100 return false; | 610 return false; |
| 101 } | 611 } |
| 102 | 612 |
| 103 | 613 |
| 104 bool Intrinsifier::Integer_add(Assembler* assembler) { | 614 bool Intrinsifier::Integer_add(Assembler* assembler) { |
| 105 return false; | 615 return Integer_addFromInteger(assembler); |
| 106 } | 616 } |
| 107 | 617 |
| 108 | 618 |
| 109 bool Intrinsifier::Integer_subFromInteger(Assembler* assembler) { | 619 bool Intrinsifier::Integer_subFromInteger(Assembler* assembler) { |
| 620 Label fall_through; | |
| 621 TestBothArgumentsSmis(assembler, &fall_through); | |
| 622 __ subs(R0, R0, ShifterOperand(R1)); // Subtract. | |
| 623 __ mov(PC, ShifterOperand(LR), VC); // Return if no overflow. | |
| 624 // Otherwise fall through. | |
| 625 __ Bind(&fall_through); | |
| 110 return false; | 626 return false; |
| 111 } | 627 } |
| 112 | 628 |
| 113 | 629 |
| 114 bool Intrinsifier::Integer_sub(Assembler* assembler) { | 630 bool Intrinsifier::Integer_sub(Assembler* assembler) { |
| 631 Label fall_through; | |
| 632 TestBothArgumentsSmis(assembler, &fall_through); | |
| 633 __ subs(R0, R1, ShifterOperand(R0)); // Subtract. | |
| 634 __ mov(PC, ShifterOperand(LR), VC); // Return if no overflow. | |
| 635 // Otherwise fall through. | |
| 636 __ Bind(&fall_through); | |
| 115 return false; | 637 return false; |
| 116 } | 638 } |
| 117 | 639 |
| 118 | 640 |
| 119 bool Intrinsifier::Integer_mulFromInteger(Assembler* assembler) { | 641 bool Intrinsifier::Integer_mulFromInteger(Assembler* assembler) { |
| 642 Label fall_through; | |
| 643 | |
| 644 TestBothArgumentsSmis(assembler, &fall_through); // checks two smis | |
| 645 __ SmiUntag(R0); // untags R6. only want result shifted by one | |
| 646 | |
| 647 __ smull(R0, IP, R0, R1); // IP:R0 <- R0 * R1. | |
| 648 __ cmp(IP, ShifterOperand(R0, ASR, 31)); | |
| 649 __ mov(PC, ShifterOperand(LR), EQ); | |
| 650 __ Bind(&fall_through); // Fall through on overflow. | |
| 120 return false; | 651 return false; |
| 121 } | 652 } |
| 122 | 653 |
| 123 | 654 |
| 124 bool Intrinsifier::Integer_mul(Assembler* assembler) { | 655 bool Intrinsifier::Integer_mul(Assembler* assembler) { |
| 125 return false; | 656 return Integer_mulFromInteger(assembler); |
| 126 } | 657 } |
| 127 | 658 |
| 128 | 659 |
| 660 // Optimizations: | |
| 661 // - result is 0 if: | |
| 662 // - left is 0 | |
| 663 // - left equals right | |
| 664 // - result is left if | |
| 665 // - left > 0 && left < right | |
| 666 // R1: Tagged left (dividend). | |
| 667 // R0: Tagged right (divisor). | |
| 668 // Returns with result in R0, OR: | |
| 669 // R1: Untagged result (remainder). | |
| 670 static void EmitRemainderOperation(Assembler* assembler) { | |
| 671 Label modulo; | |
| 672 const Register left = R1; | |
| 673 const Register right = R0; | |
| 674 const Register result = R1; | |
| 675 ASSERT(left == result); | |
| 676 | |
| 677 // Check for quick zero results. | |
| 678 __ cmp(left, ShifterOperand(0)); | |
| 679 __ mov(R0, ShifterOperand(0), EQ); | |
| 680 __ mov(PC, ShifterOperand(LR), EQ); // left is 0? Return 0. | |
| 681 __ cmp(left, ShifterOperand(right)); | |
| 682 __ mov(R0, ShifterOperand(0), EQ); | |
| 683 __ mov(PC, ShifterOperand(LR), EQ); // left == right? Return 0. | |
| 684 | |
| 685 // Check if result should be left. | |
| 686 __ cmp(left, ShifterOperand(0)); | |
| 687 __ b(&modulo, LT); | |
| 688 // left is positive. | |
| 689 __ cmp(left, ShifterOperand(right)); | |
| 690 // left is less than right, result is left. | |
| 691 __ mov(R0, ShifterOperand(left), LT); | |
| 692 __ mov(PC, ShifterOperand(LR), LT); | |
| 693 | |
| 694 __ Bind(&modulo); | |
| 695 // result <- left - right * (left / right) | |
| 696 __ SmiUntag(left); | |
| 697 __ SmiUntag(right); | |
| 698 __ sdiv(TMP, left, right); // TMP <- left / right | |
|
regis
2013/05/28 22:01:44
Shouldn't you verify in the caller that integer di
zra
2013/05/28 22:49:31
Yes. Done.
| |
| 699 __ mls(result, right, TMP, left); // result <- left - right * TMP | |
| 700 return; | |
| 701 } | |
| 702 | |
| 703 | |
| 704 // Implementation: | |
| 705 // res = left % right; | |
| 706 // if (res < 0) { | |
| 707 // if (right < 0) { | |
| 708 // res = res - right; | |
| 709 // } else { | |
| 710 // res = res + right; | |
| 711 // } | |
| 712 // } | |
| 129 bool Intrinsifier::Integer_modulo(Assembler* assembler) { | 713 bool Intrinsifier::Integer_modulo(Assembler* assembler) { |
| 714 Label fall_through, subtract; | |
| 715 TestBothArgumentsSmis(assembler, &fall_through); | |
| 716 // R1: Tagged left (dividend). | |
| 717 // R0: Tagged right (divisor). | |
| 718 // Check if modulo by zero -> exception thrown in main function. | |
| 719 __ cmp(R0, ShifterOperand(0)); | |
| 720 __ b(&fall_through, EQ); | |
| 721 EmitRemainderOperation(assembler); | |
| 722 // Untagged right in R0. Untagged remainder result in R1. | |
| 723 | |
| 724 __ cmp(R1, ShifterOperand(0)); | |
| 725 __ mov(R0, ShifterOperand(R1, LSL, 1), GE); // Tag and move result to R0. | |
| 726 __ mov(PC, ShifterOperand(LR), GE); | |
| 727 | |
| 728 // Result is negative, adjust it. | |
| 729 __ cmp(R0, ShifterOperand(0)); | |
| 730 __ sub(R0, R1, ShifterOperand(R0), LT); | |
| 731 __ add(R0, R1, ShifterOperand(R0), GE); | |
| 732 __ SmiTag(R0); | |
| 733 __ Ret(); | |
| 734 | |
| 735 __ Bind(&fall_through); | |
| 130 return false; | 736 return false; |
| 131 } | 737 } |
| 132 | 738 |
| 133 | 739 |
| 134 bool Intrinsifier::Integer_remainder(Assembler* assembler) { | 740 bool Intrinsifier::Integer_remainder(Assembler* assembler) { |
| 741 Label fall_through; | |
| 742 TestBothArgumentsSmis(assembler, &fall_through); | |
| 743 // R1: Tagged left (dividend). | |
| 744 // R0: Tagged right (divisor). | |
| 745 // Check if modulo by zero -> exception thrown in main function. | |
| 746 __ cmp(R0, ShifterOperand(0)); | |
| 747 __ b(&fall_through, EQ); | |
| 748 EmitRemainderOperation(assembler); | |
| 749 // Untagged remainder result in R1. | |
| 750 __ mov(R0, ShifterOperand(R1, LSL, 1)); // Tag result and return. | |
| 751 __ Ret(); | |
| 752 | |
| 753 __ Bind(&fall_through); | |
| 135 return false; | 754 return false; |
| 136 } | 755 } |
| 137 | 756 |
| 138 | 757 |
| 139 bool Intrinsifier::Integer_truncDivide(Assembler* assembler) { | 758 bool Intrinsifier::Integer_truncDivide(Assembler* assembler) { |
| 759 // Check to see if we have integer division | |
| 760 if (!CPUFeatures::integer_division_supported()) | |
| 761 return false; | |
| 762 | |
| 763 Label fall_through; | |
| 764 | |
| 765 TestBothArgumentsSmis(assembler, &fall_through); | |
| 766 __ cmp(R0, ShifterOperand(0)); | |
| 767 __ b(&fall_through, EQ); // If b is 0, fall through. | |
| 768 | |
| 769 __ SmiUntag(R0); | |
| 770 __ SmiUntag(R1); | |
| 771 __ sdiv(R0, R1, R0); | |
| 772 // Check the corner case of dividing the 'MIN_SMI' with -1, in which case we | |
| 773 // cannot tag the result. | |
| 774 __ CompareImmediate(R0, 0x40000000); | |
| 775 __ SmiTag(R0, NE); // Not equal. Okay to tag and return. | |
| 776 __ mov(PC, ShifterOperand(LR), NE); // Return. | |
| 777 __ Bind(&fall_through); | |
| 140 return false; | 778 return false; |
| 141 } | 779 } |
| 142 | 780 |
| 143 | 781 |
| 144 bool Intrinsifier::Integer_negate(Assembler* assembler) { | 782 bool Intrinsifier::Integer_negate(Assembler* assembler) { |
| 783 __ ldr(R0, Address(SP, + 0 * kWordSize)); // Grab first argument. | |
| 784 __ tst(R0, ShifterOperand(kSmiTagMask)); // Test for Smi. | |
| 785 __ rsb(R0, R0, ShifterOperand(0), EQ); // R0 is a Smi. R0 <- 0 - R0. | |
| 786 __ mov(PC, ShifterOperand(LR), EQ); // Return. | |
| 787 // R0 is not a Smi. Fall through. | |
| 145 return false; | 788 return false; |
| 146 } | 789 } |
| 147 | 790 |
| 148 | 791 |
| 149 bool Intrinsifier::Integer_bitAndFromInteger(Assembler* assembler) { | 792 bool Intrinsifier::Integer_bitAndFromInteger(Assembler* assembler) { |
| 793 Label fall_through; | |
| 794 | |
| 795 TestBothArgumentsSmis(assembler, &fall_through); // checks two smis | |
| 796 __ and_(R0, R0, ShifterOperand(R1)); | |
| 797 | |
| 798 __ Ret(); | |
| 799 __ Bind(&fall_through); | |
| 150 return false; | 800 return false; |
| 151 } | 801 } |
| 152 | 802 |
| 153 | 803 |
| 154 bool Intrinsifier::Integer_bitAnd(Assembler* assembler) { | 804 bool Intrinsifier::Integer_bitAnd(Assembler* assembler) { |
| 155 return false; | 805 return Integer_bitAndFromInteger(assembler); |
| 156 } | 806 } |
| 157 | 807 |
| 158 | 808 |
| 159 bool Intrinsifier::Integer_bitOrFromInteger(Assembler* assembler) { | 809 bool Intrinsifier::Integer_bitOrFromInteger(Assembler* assembler) { |
| 810 Label fall_through; | |
| 811 | |
| 812 TestBothArgumentsSmis(assembler, &fall_through); // checks two smis | |
| 813 __ orr(R0, R0, ShifterOperand(R1)); | |
| 814 | |
| 815 __ Ret(); | |
| 816 __ Bind(&fall_through); | |
| 160 return false; | 817 return false; |
| 161 } | 818 } |
| 162 | 819 |
| 163 | 820 |
| 164 bool Intrinsifier::Integer_bitOr(Assembler* assembler) { | 821 bool Intrinsifier::Integer_bitOr(Assembler* assembler) { |
| 165 return false; | 822 return Integer_bitOrFromInteger(assembler); |
| 166 } | 823 } |
| 167 | 824 |
| 168 | 825 |
| 169 bool Intrinsifier::Integer_bitXorFromInteger(Assembler* assembler) { | 826 bool Intrinsifier::Integer_bitXorFromInteger(Assembler* assembler) { |
| 827 Label fall_through; | |
| 828 __ Untested("Intrinsifier::Integer_bitXorFromInteger"); | |
| 829 | |
| 830 TestBothArgumentsSmis(assembler, &fall_through); // checks two smis | |
| 831 __ eor(R0, R0, ShifterOperand(R1)); | |
| 832 | |
| 833 __ Ret(); | |
| 834 __ Bind(&fall_through); | |
| 170 return false; | 835 return false; |
| 171 } | 836 } |
| 172 | 837 |
| 173 | 838 |
| 174 bool Intrinsifier::Integer_bitXor(Assembler* assembler) { | 839 bool Intrinsifier::Integer_bitXor(Assembler* assembler) { |
| 175 return false; | 840 return Integer_bitXorFromInteger(assembler); |
| 176 } | 841 } |
| 177 | 842 |
| 178 | 843 |
| 179 bool Intrinsifier::Integer_shl(Assembler* assembler) { | 844 bool Intrinsifier::Integer_shl(Assembler* assembler) { |
| 845 ASSERT(kSmiTagShift == 1); | |
| 846 ASSERT(kSmiTag == 0); | |
| 847 Label fall_through; | |
| 848 | |
| 849 TestBothArgumentsSmis(assembler, &fall_through); | |
| 850 __ CompareImmediate(R0, Smi::RawValue(Smi::kBits)); | |
| 851 __ b(&fall_through, HI); | |
| 852 | |
| 853 __ SmiUntag(R0); | |
| 854 | |
| 855 // Check for overflow by shifting left and shifting back arithmetically. | |
| 856 // If the result is different from the original, there was overflow. | |
| 857 __ mov(R6, ShifterOperand(R1)); | |
| 858 __ mov(R1, ShifterOperand(R1, LSL, R0)); | |
| 859 __ mov(R1, ShifterOperand(R1, ASR, R0)); | |
| 860 __ cmp(R1, ShifterOperand(R6)); | |
|
regis
2013/05/28 22:01:44
You can make this sequence shorter as I did in my
zra
2013/05/28 22:49:31
Done.
| |
| 861 | |
| 862 // No overflow, result in R0. | |
| 863 __ mov(R0, ShifterOperand(R1, LSL, R0), EQ); | |
| 864 __ mov(PC, ShifterOperand(LR), EQ); | |
| 865 | |
| 866 // Arguments are Smi but the shift produced an overflow to Mint. | |
| 867 __ CompareImmediate(R6, 0); | |
| 868 __ b(&fall_through, LT); | |
| 869 __ SmiUntag(R6); | |
| 870 | |
| 871 // Pull off high bits that will be shifted off of R6 by making a mask | |
| 872 // ((1 << R0) - 1), shifting it to the left, masking R6, then shifting back. | |
| 873 // high bits = (((1 << R0) - 1) << (32 - R0)) & R6) >> (32 - R0) | |
| 874 // lo bits = R6 << R0 | |
| 875 __ LoadImmediate(R7, 1); | |
| 876 __ mov(R7, ShifterOperand(R7, LSL, R0)); // R7 <- 1 << R0 | |
| 877 __ sub(R7, R7, ShifterOperand(1)); // R7 <- R7 - 1 | |
| 878 __ rsb(R8, R0, ShifterOperand(32)); // R8 <- 32 - R0 | |
| 879 __ mov(R7, ShifterOperand(R7, LSL, R8)); // R7 <- R7 << R8 | |
| 880 __ and_(R7, R6, ShifterOperand(R7)); // R7 <- R7 & R6 | |
| 881 __ mov(R7, ShifterOperand(R7, LSR, R8)); // R7 <- R7 >> R8 | |
| 882 // Now R7 has the bits that fall off of R6 on a left shift. | |
| 883 __ mov(R1, ShifterOperand(R6, LSL, R0)); // R1 gets the low bits. | |
| 884 | |
| 885 const Class& mint_class = Class::Handle( | |
| 886 Isolate::Current()->object_store()->mint_class()); | |
| 887 __ TryAllocate(mint_class, &fall_through, R0); | |
| 888 | |
| 889 | |
| 890 __ str(R1, FieldAddress(R0, Mint::value_offset())); | |
| 891 __ str(R7, FieldAddress(R0, Mint::value_offset() + kWordSize)); | |
| 892 __ Ret(); | |
| 893 __ Bind(&fall_through); | |
| 894 return false; | |
| 895 } | |
| 896 | |
| 897 | |
| 898 static void Get64SmiOrMint(Assembler* assembler, | |
| 899 Register res_hi, | |
| 900 Register res_lo, | |
| 901 Register reg, | |
| 902 Label* not_smi_or_mint) { | |
| 903 Label not_smi, done; | |
| 904 __ tst(reg, ShifterOperand(kSmiTagMask)); | |
| 905 __ b(¬_smi, NE); | |
| 906 __ SmiUntag(reg); | |
| 907 | |
| 908 // Sign extend to 64 bit | |
| 909 __ mov(res_lo, ShifterOperand(reg)); | |
| 910 __ mov(res_hi, ShifterOperand(res_lo, ASR, 31)); | |
| 911 __ b(&done); | |
| 912 | |
| 913 __ Bind(¬_smi); | |
| 914 __ CompareClassId(reg, kMintCid, res_lo); | |
| 915 __ b(not_smi_or_mint, NE); | |
| 916 | |
| 917 // Mint. | |
| 918 __ ldr(res_lo, FieldAddress(reg, Mint::value_offset())); | |
| 919 __ ldr(res_hi, FieldAddress(reg, Mint::value_offset() + kWordSize)); | |
| 920 __ Bind(&done); | |
| 921 return; | |
| 922 } | |
| 923 | |
| 924 | |
| 925 static bool CompareIntegers(Assembler* assembler, Condition true_condition) { | |
| 926 Label try_mint_smi, is_true, is_false, drop_two_fall_through, fall_through; | |
| 927 TestBothArgumentsSmis(assembler, &try_mint_smi); | |
| 928 // R0 contains the right argument. R1 contains left argument | |
| 929 | |
| 930 __ cmp(R1, ShifterOperand(R0)); | |
| 931 __ b(&is_true, true_condition); | |
| 932 __ Bind(&is_false); | |
| 933 __ LoadObject(R0, Bool::False()); | |
| 934 __ Ret(); | |
| 935 __ Bind(&is_true); | |
| 936 __ LoadObject(R0, Bool::True()); | |
| 937 __ Ret(); | |
| 938 | |
| 939 // 64-bit comparison | |
| 940 Condition hi_true_cond, hi_false_cond, lo_false_cond; | |
| 941 switch (true_condition) { | |
| 942 case LT: | |
| 943 case LE: | |
| 944 hi_true_cond = LT; | |
| 945 hi_false_cond = GT; | |
| 946 lo_false_cond = (true_condition == LT) ? CS : HI; | |
| 947 break; | |
| 948 case GT: | |
| 949 case GE: | |
| 950 hi_true_cond = GT; | |
| 951 hi_false_cond = LT; | |
| 952 lo_false_cond = (true_condition == GT) ? LS : CC; | |
| 953 break; | |
| 954 default: | |
| 955 UNREACHABLE(); | |
| 956 hi_true_cond = hi_false_cond = lo_false_cond = VS; | |
| 957 } | |
| 958 | |
| 959 __ Bind(&try_mint_smi); | |
| 960 // Get left as 64 bit integer. | |
| 961 Get64SmiOrMint(assembler, R3, R2, R1, &fall_through); | |
| 962 // Get right as 64 bit integer. | |
| 963 Get64SmiOrMint(assembler, R7, R6, R0, &fall_through); | |
| 964 // R3: left high. | |
| 965 // R2: left low. | |
| 966 // R7: right high. | |
| 967 // R6: right low. | |
| 968 | |
| 969 __ cmp(R3, ShifterOperand(R7)); // Compare left hi, right high. | |
| 970 __ b(&is_false, hi_false_cond); | |
| 971 __ b(&is_true, hi_true_cond); | |
| 972 __ cmp(R2, ShifterOperand(R6)); // Compare left lo, right lo. | |
| 973 __ b(&is_false, lo_false_cond); | |
| 974 // Else is true. | |
| 975 __ b(&is_true); | |
| 976 | |
| 977 __ Bind(&fall_through); | |
| 180 return false; | 978 return false; |
| 181 } | 979 } |
| 182 | 980 |
| 183 | 981 |
| 184 bool Intrinsifier::Integer_greaterThanFromInt(Assembler* assembler) { | 982 bool Intrinsifier::Integer_greaterThanFromInt(Assembler* assembler) { |
| 185 return false; | 983 return CompareIntegers(assembler, LT); |
| 186 } | 984 } |
| 187 | 985 |
| 188 | 986 |
| 189 bool Intrinsifier::Integer_lessThan(Assembler* assembler) { | 987 bool Intrinsifier::Integer_lessThan(Assembler* assembler) { |
| 190 return false; | 988 return Integer_greaterThanFromInt(assembler); |
| 191 } | 989 } |
| 192 | 990 |
| 193 | 991 |
| 194 bool Intrinsifier::Integer_greaterThan(Assembler* assembler) { | 992 bool Intrinsifier::Integer_greaterThan(Assembler* assembler) { |
| 195 return false; | 993 return CompareIntegers(assembler, GT); |
| 196 } | 994 } |
| 197 | 995 |
| 198 | 996 |
| 199 bool Intrinsifier::Integer_lessEqualThan(Assembler* assembler) { | 997 bool Intrinsifier::Integer_lessEqualThan(Assembler* assembler) { |
| 200 return false; | 998 return CompareIntegers(assembler, LE); |
| 201 } | 999 } |
| 202 | 1000 |
| 203 | 1001 |
| 204 bool Intrinsifier::Integer_greaterEqualThan(Assembler* assembler) { | 1002 bool Intrinsifier::Integer_greaterEqualThan(Assembler* assembler) { |
| 205 return false; | 1003 return CompareIntegers(assembler, GE); |
| 206 } | 1004 } |
| 207 | 1005 |
| 208 | 1006 |
| 1007 // This is called for Smi, Mint and Bigint receivers. The right argument | |
| 1008 // can be Smi, Mint, Bigint or double. | |
| 209 bool Intrinsifier::Integer_equalToInteger(Assembler* assembler) { | 1009 bool Intrinsifier::Integer_equalToInteger(Assembler* assembler) { |
| 1010 Label fall_through, true_label, check_for_mint; | |
| 1011 // For integer receiver '===' check first. | |
| 1012 __ ldr(R0, Address(SP, 0 * kWordSize)); | |
| 1013 __ ldr(R1, Address(SP, 1 * kWordSize)); | |
| 1014 __ cmp(R0, ShifterOperand(R1)); | |
| 1015 __ b(&true_label, EQ); | |
| 1016 | |
| 1017 __ orr(R2, R0, ShifterOperand(R1)); | |
| 1018 __ tst(R2, ShifterOperand(kSmiTagMask)); | |
| 1019 __ b(&check_for_mint, NE); // If R0 or R1 is not a smi do Mint checks. | |
| 1020 | |
| 1021 // Both arguments are smi, '===' is good enough. | |
| 1022 __ LoadObject(R0, Bool::False()); | |
| 1023 __ Ret(); | |
| 1024 __ Bind(&true_label); | |
| 1025 __ LoadObject(R0, Bool::True()); | |
| 1026 __ Ret(); | |
| 1027 | |
| 1028 // At least one of the arguments was not Smi. | |
| 1029 Label receiver_not_smi; | |
| 1030 __ Bind(&check_for_mint); | |
| 1031 | |
| 1032 __ tst(R1, ShifterOperand(kSmiTagMask)); // Check receiver. | |
| 1033 __ b(&receiver_not_smi, NE); | |
| 1034 | |
| 1035 // Left (receiver) is Smi, return false if right is not Double. | |
| 1036 // Note that an instance of Mint or Bigint never contains a value that can be | |
| 1037 // represented by Smi. | |
| 1038 | |
| 1039 __ CompareClassId(R0, kDoubleCid, R2); | |
| 1040 __ b(&fall_through, EQ); | |
| 1041 __ LoadObject(R0, Bool::False()); // Smi == Mint -> false. | |
| 1042 __ Ret(); | |
| 1043 | |
| 1044 __ Bind(&receiver_not_smi); | |
| 1045 // R1:: receiver. | |
| 1046 | |
| 1047 __ CompareClassId(R1, kMintCid, R2); | |
| 1048 __ b(&fall_through, NE); | |
| 1049 // Receiver is Mint, return false if right is Smi. | |
| 1050 __ tst(R0, ShifterOperand(kSmiTagMask)); | |
| 1051 __ b(&fall_through, NE); | |
| 1052 __ LoadObject(R0, Bool::False()); | |
| 1053 __ Ret(); | |
| 1054 // TODO(srdjan): Implement Mint == Mint comparison. | |
| 1055 | |
| 1056 __ Bind(&fall_through); | |
| 210 return false; | 1057 return false; |
| 211 } | 1058 } |
| 212 | 1059 |
| 213 | 1060 |
| 214 bool Intrinsifier::Integer_equal(Assembler* assembler) { | 1061 bool Intrinsifier::Integer_equal(Assembler* assembler) { |
| 215 return false; | 1062 return Integer_equalToInteger(assembler); |
| 216 } | 1063 } |
| 217 | 1064 |
| 218 | 1065 |
| 219 bool Intrinsifier::Integer_sar(Assembler* assembler) { | 1066 bool Intrinsifier::Integer_sar(Assembler* assembler) { |
| 1067 Label fall_through; | |
| 1068 | |
| 1069 TestBothArgumentsSmis(assembler, &fall_through); | |
| 1070 // Shift amount in R0. Value to shift in R1. | |
| 1071 | |
| 1072 // Fall through if shift amount is negative. | |
| 1073 __ SmiUntag(R0); | |
| 1074 __ CompareImmediate(R0, 0); | |
| 1075 __ b(&fall_through, LT); | |
| 1076 | |
| 1077 // If shift amount is bigger than 31, set to 31. | |
| 1078 __ CompareImmediate(R0, 0x1F); | |
| 1079 __ LoadImmediate(R0, 0x1F, GT); | |
| 1080 __ SmiUntag(R1); | |
| 1081 __ mov(R0, ShifterOperand(R1, ASR, R0)); | |
| 1082 __ SmiTag(R0); | |
| 1083 __ Ret(); | |
| 1084 __ Bind(&fall_through); | |
| 220 return false; | 1085 return false; |
| 221 } | 1086 } |
| 222 | 1087 |
| 223 | 1088 |
| 224 bool Intrinsifier::Smi_bitNegate(Assembler* assembler) { | 1089 bool Intrinsifier::Smi_bitNegate(Assembler* assembler) { |
| 1090 __ ldr(R0, Address(SP, 0 * kWordSize)); | |
| 1091 __ mvn(R0, ShifterOperand(R0)); | |
| 1092 __ bic(R0, R0, ShifterOperand(kSmiTagMask)); // Remove inverted smi-tag. | |
| 1093 __ Ret(); | |
| 1094 return false; | |
| 1095 } | |
| 1096 | |
| 1097 | |
| 1098 // Check if the last argument is a double, jump to label 'is_smi' if smi | |
| 1099 // (easy to convert to double), otherwise jump to label 'not_double_smi', | |
| 1100 // Returns the last argument in R0. | |
| 1101 static void TestLastArgumentIsDouble(Assembler* assembler, | |
| 1102 Label* is_smi, | |
| 1103 Label* not_double_smi) { | |
| 1104 __ ldr(R0, Address(SP, 0 * kWordSize)); | |
| 1105 __ tst(R0, ShifterOperand(kSmiTagMask)); | |
| 1106 __ b(is_smi, EQ); | |
| 1107 __ CompareClassId(R0, kDoubleCid, R1); | |
| 1108 __ b(not_double_smi, NE); | |
| 1109 // Fall through with Double in R0. | |
| 1110 } | |
| 1111 | |
| 1112 | |
| 1113 // Both arguments on stack, arg0 (left) is a double, arg1 (right) is of unknown | |
| 1114 // type. Return true or false object in the register R0. Any NaN argument | |
| 1115 // returns false. Any non-double arg1 causes control flow to fall through to the | |
| 1116 // slow case (compiled method body). | |
| 1117 static bool CompareDoubles(Assembler* assembler, Condition true_condition) { | |
| 1118 Label fall_through, is_smi, double_op; | |
| 1119 | |
| 1120 TestLastArgumentIsDouble(assembler, &is_smi, &fall_through); | |
| 1121 // Both arguments are double, right operand is in R0. | |
| 1122 | |
| 1123 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); | |
| 1124 __ vldrd(D1, Address(R0)); | |
| 1125 __ Bind(&double_op); | |
| 1126 __ ldr(R0, Address(SP, 1 * kWordSize)); // Left argument. | |
| 1127 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); | |
| 1128 __ vldrd(D0, Address(R0)); | |
| 1129 | |
| 1130 __ vcmpd(D0, D1); | |
| 1131 __ vmstat(); | |
| 1132 __ LoadObject(R0, Bool::False()); | |
| 1133 // Return false if D0 or D1 was NaN before checking true condition. | |
| 1134 __ mov(PC, ShifterOperand(LR), VS); | |
| 1135 __ LoadObject(R0, Bool::True(), true_condition); | |
| 1136 __ Ret(); | |
| 1137 | |
| 1138 __ Bind(&is_smi); // Convert R0 to a double. | |
| 1139 __ SmiUntag(R0); | |
| 1140 __ vmovsr(S0, R0); | |
| 1141 __ vcvtdi(D1, S0); | |
| 1142 __ b(&double_op); // Then do the comparison. | |
| 1143 __ Bind(&fall_through); | |
| 225 return false; | 1144 return false; |
| 226 } | 1145 } |
| 227 | 1146 |
| 228 | 1147 |
| 229 bool Intrinsifier::Double_greaterThan(Assembler* assembler) { | 1148 bool Intrinsifier::Double_greaterThan(Assembler* assembler) { |
| 230 return false; | 1149 return CompareDoubles(assembler, HI); |
| 231 } | 1150 } |
| 232 | 1151 |
| 233 | 1152 |
| 234 bool Intrinsifier::Double_greaterEqualThan(Assembler* assembler) { | 1153 bool Intrinsifier::Double_greaterEqualThan(Assembler* assembler) { |
| 235 return false; | 1154 return CompareDoubles(assembler, CS); |
| 236 } | 1155 } |
| 237 | 1156 |
| 238 | 1157 |
| 239 bool Intrinsifier::Double_lessThan(Assembler* assembler) { | 1158 bool Intrinsifier::Double_lessThan(Assembler* assembler) { |
| 240 return false; | 1159 return CompareDoubles(assembler, CC); |
| 241 } | 1160 } |
| 242 | 1161 |
| 243 | 1162 |
| 244 bool Intrinsifier::Double_equal(Assembler* assembler) { | 1163 bool Intrinsifier::Double_equal(Assembler* assembler) { |
| 245 return false; | 1164 return CompareDoubles(assembler, EQ); |
| 246 } | 1165 } |
| 247 | 1166 |
| 248 | 1167 |
| 249 bool Intrinsifier::Double_lessEqualThan(Assembler* assembler) { | 1168 bool Intrinsifier::Double_lessEqualThan(Assembler* assembler) { |
| 1169 return CompareDoubles(assembler, LS); | |
| 1170 } | |
| 1171 | |
| 1172 | |
| 1173 // Expects left argument to be double (receiver). Right argument is unknown. | |
| 1174 // Both arguments are on stack. | |
| 1175 static bool DoubleArithmeticOperations(Assembler* assembler, Token::Kind kind) { | |
| 1176 Label fall_through; | |
| 1177 | |
| 1178 TestLastArgumentIsDouble(assembler, &fall_through, &fall_through); | |
| 1179 // Both arguments are double, right operand is in R0. | |
| 1180 // Can't use FieldAddress here. R0 is heap-object-tagged, so the offset will | |
| 1181 // not be 4-byte aligned. | |
| 1182 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); | |
| 1183 __ vldrd(D1, Address(R0)); | |
| 1184 __ ldr(R0, Address(SP, 1 * kWordSize)); // Left argument. | |
| 1185 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); | |
| 1186 __ vldrd(D0, Address(R0)); | |
| 1187 switch (kind) { | |
| 1188 case Token::kADD: __ vaddd(D0, D0, D1); break; | |
| 1189 case Token::kSUB: __ vsubd(D0, D0, D1); break; | |
| 1190 case Token::kMUL: __ vmuld(D0, D0, D1); break; | |
| 1191 case Token::kDIV: __ vdivd(D0, D0, D1); break; | |
| 1192 default: UNREACHABLE(); | |
| 1193 } | |
| 1194 const Class& double_class = Class::Handle( | |
| 1195 Isolate::Current()->object_store()->double_class()); | |
| 1196 __ TryAllocate(double_class, &fall_through, R0); // Result register. | |
| 1197 __ AddImmediate(R1, R0, Double::value_offset() - kHeapObjectTag); | |
| 1198 __ vstrd(D0, Address(R1)); | |
| 1199 __ Ret(); | |
| 1200 __ Bind(&fall_through); | |
| 250 return false; | 1201 return false; |
| 251 } | 1202 } |
| 252 | 1203 |
| 253 | 1204 |
| 254 bool Intrinsifier::Double_add(Assembler* assembler) { | 1205 bool Intrinsifier::Double_add(Assembler* assembler) { |
| 255 return false; | 1206 return DoubleArithmeticOperations(assembler, Token::kADD); |
| 256 } | 1207 } |
| 257 | 1208 |
| 258 | 1209 |
| 259 bool Intrinsifier::Double_mul(Assembler* assembler) { | 1210 bool Intrinsifier::Double_mul(Assembler* assembler) { |
| 260 return false; | 1211 return DoubleArithmeticOperations(assembler, Token::kMUL); |
| 261 } | 1212 } |
| 262 | 1213 |
| 263 | 1214 |
| 264 bool Intrinsifier::Double_sub(Assembler* assembler) { | 1215 bool Intrinsifier::Double_sub(Assembler* assembler) { |
| 265 return false; | 1216 return DoubleArithmeticOperations(assembler, Token::kSUB); |
| 266 } | 1217 } |
| 267 | 1218 |
| 268 | 1219 |
| 269 bool Intrinsifier::Double_div(Assembler* assembler) { | 1220 bool Intrinsifier::Double_div(Assembler* assembler) { |
| 270 return false; | 1221 return DoubleArithmeticOperations(assembler, Token::kDIV); |
| 271 } | 1222 } |
| 272 | 1223 |
| 273 | 1224 |
| 1225 // Left is double right is integer (Bigint, Mint or Smi) | |
| 274 bool Intrinsifier::Double_mulFromInteger(Assembler* assembler) { | 1226 bool Intrinsifier::Double_mulFromInteger(Assembler* assembler) { |
| 1227 Label fall_through; | |
| 1228 __ Untested("Intrinsifier::Double_mulFromInteger"); | |
| 1229 // Only Smi-s allowed. | |
| 1230 __ ldr(R0, Address(SP, 0 * kWordSize)); | |
| 1231 __ tst(R0, ShifterOperand(kSmiTagMask)); | |
| 1232 __ b(&fall_through, NE); | |
| 1233 // Is Smi. | |
| 1234 __ SmiUntag(R0); | |
| 1235 __ vmovsr(S0, R0); | |
| 1236 __ vcvtdi(D1, S0); | |
| 1237 __ ldr(R0, Address(SP, 1 * kWordSize)); | |
| 1238 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); | |
| 1239 __ vldrd(D0, Address(R0)); | |
| 1240 __ vmuld(D0, D0, D1); | |
| 1241 const Class& double_class = Class::Handle( | |
| 1242 Isolate::Current()->object_store()->double_class()); | |
| 1243 __ TryAllocate(double_class, &fall_through, R0); // Result register. | |
| 1244 __ AddImmediate(R1, R0, Double::value_offset() - kHeapObjectTag); | |
| 1245 __ vstrd(D0, Address(R1)); | |
| 1246 __ Ret(); | |
| 1247 __ Bind(&fall_through); | |
| 275 return false; | 1248 return false; |
| 276 } | 1249 } |
| 277 | 1250 |
| 278 | 1251 |
| 279 bool Intrinsifier::Double_fromInteger(Assembler* assembler) { | 1252 bool Intrinsifier::Double_fromInteger(Assembler* assembler) { |
| 1253 Label fall_through; | |
| 1254 | |
| 1255 __ ldr(R0, Address(SP, 0 * kWordSize)); | |
| 1256 __ tst(R0, ShifterOperand(kSmiTagMask)); | |
| 1257 __ b(&fall_through, NE); | |
| 1258 // Is Smi. | |
| 1259 __ SmiUntag(R0); | |
| 1260 __ vmovsr(S0, R0); | |
| 1261 __ vcvtdi(D0, S0); | |
| 1262 const Class& double_class = Class::Handle( | |
| 1263 Isolate::Current()->object_store()->double_class()); | |
| 1264 __ TryAllocate(double_class, &fall_through, R0); // Result register. | |
| 1265 __ AddImmediate(R1, R0, Double::value_offset() - kHeapObjectTag); | |
| 1266 __ vstrd(D0, Address(R1)); | |
| 1267 __ Ret(); | |
| 1268 __ Bind(&fall_through); | |
| 280 return false; | 1269 return false; |
| 281 } | 1270 } |
| 282 | 1271 |
| 283 | 1272 |
| 284 bool Intrinsifier::Double_getIsNaN(Assembler* assembler) { | 1273 bool Intrinsifier::Double_getIsNaN(Assembler* assembler) { |
| 285 return false; | 1274 Label is_true; |
| 1275 __ Untested("Intrinsifier::Double_getIsNaN"); | |
| 1276 __ ldr(R0, Address(SP, 0 * kWordSize)); | |
| 1277 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); | |
| 1278 __ vldrd(D0, Address(R0)); | |
| 1279 __ vcmpd(D0, D0); | |
| 1280 __ vmstat(); | |
| 1281 __ LoadObject(R0, Bool::False(), VS); | |
| 1282 __ LoadObject(R0, Bool::True(), VC); | |
| 1283 __ Ret(); | |
| 1284 return true; | |
| 286 } | 1285 } |
| 287 | 1286 |
| 288 | 1287 |
| 289 bool Intrinsifier::Double_getIsNegative(Assembler* assembler) { | 1288 bool Intrinsifier::Double_getIsNegative(Assembler* assembler) { |
| 290 return false; | 1289 Label is_false, is_true, is_zero; |
| 1290 __ Untested("Intrinsifier::Double_getIsNegative"); | |
| 1291 __ ldr(R0, Address(SP, 0 * kWordSize)); | |
| 1292 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); | |
| 1293 __ vldrd(D0, Address(R0)); | |
| 1294 __ LoadDImmediate(D1, 0.0, R1); | |
| 1295 __ vcmpd(D0, D1); | |
| 1296 __ vmstat(); | |
| 1297 __ b(&is_false, VS); // NaN -> false. | |
| 1298 __ b(&is_zero, EQ); // Check for negative zero. | |
| 1299 __ b(&is_false, CS); // >= 0 -> false. | |
| 1300 | |
| 1301 __ Bind(&is_true); | |
| 1302 __ LoadObject(R0, Bool::True()); | |
| 1303 __ Ret(); | |
| 1304 | |
| 1305 __ Bind(&is_false); | |
| 1306 __ LoadObject(R0, Bool::False()); | |
| 1307 __ Ret(); | |
| 1308 | |
| 1309 __ Bind(&is_zero); | |
| 1310 // Check for negative zero by looking at the sign bit. | |
| 1311 __ vmovrrd(R0, R1, D0); // R1:R0 <- D0, so sign bit is in bit 31 of R1. | |
| 1312 __ mov(R1, ShifterOperand(R1, LSR, 31)); | |
| 1313 __ tst(R1, ShifterOperand(1)); | |
| 1314 __ b(&is_true, NE); // Sign bit set. | |
| 1315 __ b(&is_false); | |
| 1316 return true; | |
| 291 } | 1317 } |
| 292 | 1318 |
| 293 | 1319 |
| 294 bool Intrinsifier::Double_toInt(Assembler* assembler) { | 1320 bool Intrinsifier::Double_toInt(Assembler* assembler) { |
| 1321 __ ldr(R0, Address(SP, 0 * kWordSize)); | |
| 1322 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); | |
| 1323 __ vldrd(D0, Address(R0)); | |
| 1324 __ vcvtid(S0, D0); | |
| 1325 __ vmovrs(R0, S0); | |
| 1326 // Overflow is signaled with minint. | |
| 1327 Label fall_through; | |
| 1328 // Check for overflow and that it fits into Smi. | |
| 1329 __ CompareImmediate(R0, 0xC0000000); | |
| 1330 __ b(&fall_through, MI); | |
| 1331 __ SmiTag(R0); | |
| 1332 __ Ret(); | |
| 1333 __ Bind(&fall_through); | |
| 295 return false; | 1334 return false; |
| 296 } | 1335 } |
| 297 | 1336 |
| 298 | 1337 |
| 299 bool Intrinsifier::Math_sqrt(Assembler* assembler) { | 1338 bool Intrinsifier::Math_sqrt(Assembler* assembler) { |
| 300 return false; | 1339 Label fall_through, is_smi, double_op; |
| 301 } | 1340 __ Untested("Intrinsifier::Math_sqrt"); |
| 302 | 1341 TestLastArgumentIsDouble(assembler, &is_smi, &fall_through); |
| 1342 // Argument is double and is in R0. | |
| 1343 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); | |
| 1344 __ vldrd(D1, Address(R0)); | |
| 1345 __ Bind(&double_op); | |
| 1346 __ vsqrtd(D0, D1); | |
| 1347 const Class& double_class = Class::Handle( | |
| 1348 Isolate::Current()->object_store()->double_class()); | |
| 1349 __ TryAllocate(double_class, &fall_through, R0); // Result register. | |
| 1350 __ AddImmediate(R1, R0, Double::value_offset() - kHeapObjectTag); | |
| 1351 __ vstrd(D0, Address(R1)); | |
| 1352 __ Ret(); | |
| 1353 __ Bind(&is_smi); | |
| 1354 __ SmiUntag(R0); | |
| 1355 __ vmovsr(S0, R0); | |
| 1356 __ vcvtdi(D1, S0); | |
| 1357 __ b(&double_op); | |
| 1358 __ Bind(&fall_through); | |
| 1359 return false; | |
| 1360 } | |
| 1361 | |
| 303 | 1362 |
| 304 bool Intrinsifier::Math_sin(Assembler* assembler) { | 1363 bool Intrinsifier::Math_sin(Assembler* assembler) { |
| 305 return false; | 1364 return false; |
| 306 } | 1365 } |
| 307 | 1366 |
| 308 | 1367 |
| 309 bool Intrinsifier::Math_cos(Assembler* assembler) { | 1368 bool Intrinsifier::Math_cos(Assembler* assembler) { |
| 310 return false; | 1369 return false; |
| 311 } | 1370 } |
| 312 | 1371 |
| 313 | 1372 |
| 1373 // var state = ((_A * (_state[kSTATE_LO])) + _state[kSTATE_HI]) & _MASK_64; | |
| 1374 // _state[kSTATE_LO] = state & _MASK_32; | |
| 1375 // _state[kSTATE_HI] = state >> 32; | |
| 314 bool Intrinsifier::Random_nextState(Assembler* assembler) { | 1376 bool Intrinsifier::Random_nextState(Assembler* assembler) { |
| 315 return false; | 1377 const Library& math_lib = Library::Handle(Library::MathLibrary()); |
| 1378 ASSERT(!math_lib.IsNull()); | |
| 1379 const Class& random_class = | |
| 1380 Class::Handle(math_lib.LookupClassAllowPrivate(Symbols::_Random())); | |
| 1381 ASSERT(!random_class.IsNull()); | |
| 1382 const Field& state_field = Field::ZoneHandle( | |
| 1383 random_class.LookupInstanceField(Symbols::_state())); | |
| 1384 ASSERT(!state_field.IsNull()); | |
| 1385 const Field& random_A_field = Field::ZoneHandle( | |
| 1386 random_class.LookupStaticField(Symbols::_A())); | |
| 1387 ASSERT(!random_A_field.IsNull()); | |
| 1388 ASSERT(random_A_field.is_const()); | |
| 1389 const Instance& a_value = Instance::Handle(random_A_field.value()); | |
| 1390 const int64_t a_int_value = Integer::Cast(a_value).AsInt64Value(); | |
| 1391 // 'a_int_value' is a mask. | |
| 1392 ASSERT(Utils::IsUint(32, a_int_value)); | |
| 1393 int32_t a_int32_value = static_cast<int32_t>(a_int_value); | |
| 1394 | |
| 1395 __ Untested("Random_nextState"); | |
| 1396 | |
| 1397 __ ldr(R0, Address(SP, 0 * kWordSize)); // Receiver. | |
| 1398 __ ldr(R1, FieldAddress(R0, state_field.Offset())); // Field '_state'. | |
| 1399 // Addresses of _state[0] and _state[1]. | |
| 1400 Address addr_0 = FlowGraphCompiler::ElementAddressForIntIndex( | |
| 1401 kTypedDataUint32ArrayCid, | |
| 1402 FlowGraphCompiler::ElementSizeFor(kTypedDataUint32ArrayCid), | |
| 1403 R1, | |
| 1404 0); | |
| 1405 Address addr_1 = FlowGraphCompiler::ElementAddressForIntIndex( | |
| 1406 kTypedDataUint32ArrayCid, | |
| 1407 FlowGraphCompiler::ElementSizeFor(kTypedDataUint32ArrayCid), | |
| 1408 R1, | |
| 1409 1); | |
| 1410 __ LoadImmediate(R0, a_int32_value); | |
| 1411 __ ldr(R2, addr_0); | |
| 1412 __ ldr(R3, addr_1); | |
| 1413 __ mov(R6, ShifterOperand(R3, ASR, 31)); // Sign extend into R6. | |
| 1414 // 64-bit multiply and accumulate into R6:R3. | |
| 1415 __ smlal(R3, R6, R0, R2); // R6:R3 <- R6:R3 + R0 * R2. | |
| 1416 __ str(R3, addr_0); | |
| 1417 __ str(R6, addr_1); | |
| 1418 __ Ret(); | |
| 1419 return true; | |
| 316 } | 1420 } |
| 317 | 1421 |
| 318 | 1422 |
| 319 bool Intrinsifier::Object_equal(Assembler* assembler) { | 1423 bool Intrinsifier::Object_equal(Assembler* assembler) { |
| 320 return false; | 1424 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 1425 __ ldr(R1, Address(SP, 1 * kWordSize)); | |
| 1426 __ cmp(R0, ShifterOperand(R1)); | |
| 1427 __ LoadObject(R0, Bool::False(), NE); | |
| 1428 __ LoadObject(R0, Bool::True(), EQ); | |
| 1429 __ Ret(); | |
| 1430 return true; | |
| 321 } | 1431 } |
| 322 | 1432 |
| 323 | 1433 |
| 324 bool Intrinsifier::String_getHashCode(Assembler* assembler) { | 1434 bool Intrinsifier::String_getHashCode(Assembler* assembler) { |
| 1435 __ Untested("Intrinsifier::String_getHashCode"); | |
| 1436 __ ldr(R0, Address(SP, 0 * kWordSize)); | |
| 1437 __ ldr(R0, FieldAddress(R0, String::hash_offset())); | |
| 1438 __ cmp(R0, ShifterOperand(0)); | |
| 1439 __ mov(PC, ShifterOperand(LR), NE); // Hash not yet computed. | |
| 325 return false; | 1440 return false; |
| 326 } | 1441 } |
| 327 | 1442 |
| 328 | 1443 |
| 329 bool Intrinsifier::String_getLength(Assembler* assembler) { | 1444 bool Intrinsifier::String_getLength(Assembler* assembler) { |
| 330 return false; | 1445 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 331 } | 1446 __ ldr(R0, FieldAddress(R0, String::length_offset())); |
| 332 | 1447 __ Ret(); |
| 333 | 1448 return true; |
| 1449 } | |
| 1450 | |
| 1451 | |
| 1452 // TODO(srdjan): Implement for two and four byte strings as well. | |
| 334 bool Intrinsifier::String_codeUnitAt(Assembler* assembler) { | 1453 bool Intrinsifier::String_codeUnitAt(Assembler* assembler) { |
| 1454 Label fall_through; | |
| 1455 | |
| 1456 __ ldr(R1, Address(SP, 0 * kWordSize)); // Index. | |
| 1457 __ ldr(R0, Address(SP, 1 * kWordSize)); // String. | |
| 1458 __ tst(R1, ShifterOperand(kSmiTagMask)); | |
| 1459 __ b(&fall_through, NE); // Index is not a Smi. | |
| 1460 // Range check. | |
| 1461 __ ldr(R2, FieldAddress(R0, String::length_offset())); | |
| 1462 __ cmp(R1, ShifterOperand(R2)); | |
| 1463 __ b(&fall_through, CS); // Runtime throws exception. | |
| 1464 __ CompareClassId(R0, kOneByteStringCid, R3); | |
| 1465 __ b(&fall_through, NE); | |
| 1466 __ SmiUntag(R1); | |
| 1467 __ AddImmediate(R0, OneByteString::data_offset() - kHeapObjectTag); | |
| 1468 __ ldrb(R0, Address(R0, R1)); | |
| 1469 __ SmiTag(R0); | |
| 1470 __ Ret(); | |
| 1471 __ Bind(&fall_through); | |
| 335 return false; | 1472 return false; |
| 336 } | 1473 } |
| 337 | 1474 |
| 338 | 1475 |
| 339 bool Intrinsifier::String_getIsEmpty(Assembler* assembler) { | 1476 bool Intrinsifier::String_getIsEmpty(Assembler* assembler) { |
| 340 return false; | 1477 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 1478 __ ldr(R0, FieldAddress(R0, String::length_offset())); | |
| 1479 __ cmp(R0, ShifterOperand(Smi::RawValue(0))); | |
| 1480 __ LoadObject(R0, Bool::True(), EQ); | |
| 1481 __ LoadObject(R0, Bool::False(), NE); | |
| 1482 __ Ret(); | |
| 1483 return true; | |
| 341 } | 1484 } |
| 342 | 1485 |
| 343 | 1486 |
| 344 bool Intrinsifier::OneByteString_getHashCode(Assembler* assembler) { | 1487 bool Intrinsifier::OneByteString_getHashCode(Assembler* assembler) { |
| 345 return false; | 1488 __ ldr(R1, Address(SP, 0 * kWordSize)); |
| 346 } | 1489 __ ldr(R0, FieldAddress(R1, String::hash_offset())); |
| 347 | 1490 __ cmp(R0, ShifterOperand(0)); |
| 348 | 1491 __ mov(PC, ShifterOperand(LR), NE); // Return if already computed. |
| 1492 | |
| 1493 __ ldr(R2, FieldAddress(R1, String::length_offset())); | |
| 1494 | |
| 1495 Label done; | |
| 1496 // If the string is empty, set the hash to 1, and return. | |
| 1497 __ cmp(R2, ShifterOperand(Smi::RawValue(0))); | |
| 1498 __ b(&done, EQ); | |
| 1499 | |
| 1500 __ SmiUntag(R2); | |
| 1501 __ mov(R3, ShifterOperand(0)); | |
| 1502 __ AddImmediate(R6, R1, OneByteString::data_offset() - kHeapObjectTag); | |
| 1503 // R1: Instance of OneByteString. | |
| 1504 // R2: String length, untagged integer. | |
| 1505 // R3: Loop counter, untagged integer. | |
| 1506 // R6: String data. | |
| 1507 // R0: Hash code, untagged integer. | |
| 1508 | |
| 1509 Label loop; | |
| 1510 // Add to hash code: (hash_ is uint32) | |
| 1511 // hash_ += ch; | |
| 1512 // hash_ += hash_ << 10; | |
| 1513 // hash_ ^= hash_ >> 6; | |
| 1514 // Get one characters (ch). | |
| 1515 __ Bind(&loop); | |
| 1516 __ ldrb(R7, Address(R6, 0)); | |
| 1517 // R7: ch. | |
| 1518 __ add(R3, R3, ShifterOperand(1)); | |
| 1519 __ add(R6, R6, ShifterOperand(1)); | |
| 1520 __ add(R0, R0, ShifterOperand(R7)); | |
| 1521 __ add(R0, R0, ShifterOperand(R0, LSL, 10)); | |
| 1522 __ eor(R0, R0, ShifterOperand(R0, LSR, 6)); | |
| 1523 __ cmp(R3, ShifterOperand(R2)); | |
| 1524 __ b(&loop, NE); | |
| 1525 | |
| 1526 // Finalize. | |
| 1527 // hash_ += hash_ << 3; | |
| 1528 // hash_ ^= hash_ >> 11; | |
| 1529 // hash_ += hash_ << 15; | |
| 1530 __ add(R0, R0, ShifterOperand(R0, LSL, 3)); | |
| 1531 __ eor(R0, R0, ShifterOperand(R0, LSR, 11)); | |
| 1532 __ add(R0, R0, ShifterOperand(R0, LSL, 15)); | |
| 1533 // hash_ = hash_ & ((static_cast<intptr_t>(1) << bits) - 1); | |
| 1534 __ LoadImmediate(R2, (static_cast<intptr_t>(1) << String::kHashBits) - 1); | |
| 1535 __ and_(R0, R0, ShifterOperand(R2)); | |
| 1536 __ cmp(R0, ShifterOperand(0)); | |
| 1537 // return hash_ == 0 ? 1 : hash_; | |
| 1538 __ Bind(&done); | |
| 1539 __ mov(R0, ShifterOperand(1), EQ); | |
| 1540 __ SmiTag(R0); | |
| 1541 __ str(R0, FieldAddress(R1, String::hash_offset())); | |
| 1542 __ Ret(); | |
| 1543 return false; | |
| 1544 } | |
| 1545 | |
| 1546 | |
| 1547 // Allocates one-byte string of length 'end - start'. The content is not | |
| 1548 // initialized. | |
| 1549 // 'length-reg' (R2) contains tagged length. | |
| 1550 // Returns new string as tagged pointer in R0. | |
| 1551 static void TryAllocateOnebyteString(Assembler* assembler, | |
| 1552 Label* ok, | |
| 1553 Label* failure) { | |
| 1554 const Register length_reg = R2; | |
| 1555 Label fail; | |
| 1556 | |
| 1557 __ mov(R6, ShifterOperand(length_reg)); // Save the length register. | |
| 1558 __ SmiUntag(length_reg); | |
| 1559 const intptr_t fixed_size = sizeof(RawString) + kObjectAlignment - 1; | |
| 1560 __ AddImmediate(length_reg, fixed_size); | |
| 1561 __ bic(length_reg, length_reg, ShifterOperand(kObjectAlignment - 1)); | |
| 1562 | |
| 1563 Isolate* isolate = Isolate::Current(); | |
| 1564 Heap* heap = isolate->heap(); | |
| 1565 | |
| 1566 __ LoadImmediate(R3, heap->TopAddress()); | |
| 1567 __ ldr(R0, Address(R3, 0)); | |
| 1568 | |
| 1569 // length_reg: allocation size. | |
| 1570 __ adds(R1, R0, ShifterOperand(length_reg)); | |
| 1571 __ b(&fail, VS); // Fail on overflow. | |
| 1572 | |
| 1573 // Check if the allocation fits into the remaining space. | |
| 1574 // R0: potential new object start. | |
| 1575 // R1: potential next object start. | |
| 1576 // R2: allocation size. | |
| 1577 // R3: heap->Top->Address(). | |
| 1578 __ LoadImmediate(R7, heap->EndAddress()); | |
| 1579 __ ldr(R7, Address(R7, 0)); | |
| 1580 __ cmp(R1, ShifterOperand(R7)); | |
| 1581 __ b(&fail, CS); | |
| 1582 | |
| 1583 // Successfully allocated the object(s), now update top to point to | |
| 1584 // next object start and initialize the object. | |
| 1585 __ str(R1, Address(R3, 0)); | |
| 1586 __ AddImmediate(R0, kHeapObjectTag); | |
| 1587 | |
| 1588 // Initialize the tags. | |
| 1589 // R0: new object start as a tagged pointer. | |
| 1590 // R1: new object end address. | |
| 1591 // R2: allocation size. | |
| 1592 { | |
| 1593 const intptr_t shift = RawObject::kSizeTagBit - kObjectAlignmentLog2; | |
| 1594 const Class& cls = | |
| 1595 Class::Handle(isolate->object_store()->one_byte_string_class()); | |
| 1596 | |
| 1597 __ CompareImmediate(R2, RawObject::SizeTag::kMaxSizeTag); | |
| 1598 __ mov(R2, ShifterOperand(R2, LSL, shift), LS); | |
| 1599 __ mov(R2, ShifterOperand(0), HI); | |
| 1600 | |
| 1601 // Get the class index and insert it into the tags. | |
| 1602 // R2: size and bit tags. | |
| 1603 __ LoadImmediate(TMP, RawObject::ClassIdTag::encode(cls.id())); | |
| 1604 __ orr(R2, R2, ShifterOperand(TMP)); | |
| 1605 __ str(R2, FieldAddress(R0, String::tags_offset())); // Store tags. | |
| 1606 } | |
| 1607 | |
| 1608 // Set the length field using the saved length (R6). | |
| 1609 __ StoreIntoObjectNoBarrier(R0, | |
| 1610 FieldAddress(R0, String::length_offset()), | |
| 1611 R6); | |
| 1612 // Clear hash. | |
| 1613 __ LoadImmediate(TMP, 0); | |
| 1614 __ str(TMP, FieldAddress(R0, String::hash_offset())); | |
| 1615 __ b(ok); | |
| 1616 | |
| 1617 __ Bind(&fail); | |
| 1618 __ b(failure); | |
| 1619 } | |
| 1620 | |
| 1621 | |
| 1622 // Arg0: Onebyte String | |
| 1623 // Arg1: Start index as Smi. | |
| 1624 // Arg2: End index as Smi. | |
| 1625 // The indexes must be valid. | |
| 349 bool Intrinsifier::OneByteString_substringUnchecked(Assembler* assembler) { | 1626 bool Intrinsifier::OneByteString_substringUnchecked(Assembler* assembler) { |
| 1627 const intptr_t kStringOffset = 2 * kWordSize; | |
| 1628 const intptr_t kStartIndexOffset = 1 * kWordSize; | |
| 1629 const intptr_t kEndIndexOffset = 0 * kWordSize; | |
| 1630 Label fall_through, ok; | |
| 1631 | |
| 1632 __ ldr(R2, Address(SP, kEndIndexOffset)); | |
| 1633 __ ldr(TMP, Address(SP, kStartIndexOffset)); | |
| 1634 __ sub(R2, R2, ShifterOperand(TMP)); | |
| 1635 TryAllocateOnebyteString(assembler, &ok, &fall_through); | |
| 1636 __ Bind(&ok); | |
| 1637 // R0: new string as tagged pointer. | |
| 1638 // Copy string. | |
| 1639 __ ldr(R3, Address(SP, kStringOffset)); | |
| 1640 __ ldr(R1, Address(SP, kStartIndexOffset)); | |
| 1641 __ SmiUntag(R1); | |
| 1642 __ add(R3, R3, ShifterOperand(R1)); | |
| 1643 // Calculate start address and untag (- 1). | |
| 1644 __ AddImmediate(R3, OneByteString::data_offset() - 1); | |
| 1645 | |
| 1646 // R3: Start address to copy from (untagged). | |
| 1647 // R1: Untagged start index. | |
| 1648 __ ldr(R2, Address(SP, kEndIndexOffset)); | |
| 1649 __ SmiUntag(R2); | |
| 1650 __ sub(R2, R2, ShifterOperand(R1)); | |
| 1651 | |
| 1652 // R3: Start address to copy from (untagged). | |
| 1653 // R2: Untagged number of bytes to copy. | |
| 1654 // R0: Tagged result string. | |
| 1655 // R6: Pointer into R3. | |
| 1656 // R7: Pointer into R0. | |
| 1657 // R1: Scratch register. | |
| 1658 Label loop, done; | |
| 1659 __ cmp(R2, ShifterOperand(0)); | |
| 1660 __ b(&done, LE); | |
| 1661 __ mov(R6, ShifterOperand(R3)); | |
| 1662 __ mov(R7, ShifterOperand(R0)); | |
| 1663 __ Bind(&loop); | |
| 1664 __ ldrb(R1, Address(R6, 0)); | |
| 1665 __ AddImmediate(R6, 1); | |
| 1666 __ sub(R2, R2, ShifterOperand(1)); | |
| 1667 __ cmp(R2, ShifterOperand(0)); | |
| 1668 __ strb(R1, FieldAddress(R7, OneByteString::data_offset())); | |
| 1669 __ AddImmediate(R7, 1); | |
| 1670 __ b(&loop, GT); | |
| 1671 | |
| 1672 __ Bind(&done); | |
| 1673 __ Ret(); | |
| 1674 __ Bind(&fall_through); | |
| 350 return false; | 1675 return false; |
| 351 } | 1676 } |
| 352 | 1677 |
| 353 | 1678 |
| 354 bool Intrinsifier::OneByteString_setAt(Assembler* assembler) { | 1679 bool Intrinsifier::OneByteString_setAt(Assembler* assembler) { |
| 355 return false; | 1680 __ ldr(R2, Address(SP, 0 * kWordSize)); // Value. |
| 1681 __ ldr(R1, Address(SP, 1 * kWordSize)); // Index. | |
| 1682 __ ldr(R0, Address(SP, 2 * kWordSize)); // OneByteString. | |
| 1683 __ SmiUntag(R1); | |
| 1684 __ SmiUntag(R2); | |
| 1685 __ AddImmediate(R3, R0, OneByteString::data_offset() - kHeapObjectTag); | |
| 1686 __ strb(R2, Address(R3, R1)); | |
| 1687 __ Ret(); | |
| 1688 return true; | |
| 356 } | 1689 } |
| 357 | 1690 |
| 358 | 1691 |
| 359 bool Intrinsifier::OneByteString_allocate(Assembler* assembler) { | 1692 bool Intrinsifier::OneByteString_allocate(Assembler* assembler) { |
| 360 return false; | 1693 __ ldr(R2, Address(SP, 0 * kWordSize)); // Length. |
| 361 } | 1694 Label fall_through, ok; |
| 362 | 1695 TryAllocateOnebyteString(assembler, &ok, &fall_through); |
| 1696 | |
| 1697 __ Bind(&ok); | |
| 1698 __ Ret(); | |
| 1699 | |
| 1700 __ Bind(&fall_through); | |
| 1701 return false; | |
| 1702 } | |
| 1703 | |
| 363 } // namespace dart | 1704 } // namespace dart |
| 364 | 1705 |
| 365 #endif // defined TARGET_ARCH_ARM | 1706 #endif // defined TARGET_ARCH_ARM |
| OLD | NEW |