| 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 __ bx(LR, CC); |
| 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 __ bx(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 __ bx(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 __ bx(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 __ bx(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 __ bx(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 __ bx(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 __ bx(LR, EQ); // left is 0? Return 0. |
| 681 __ cmp(left, ShifterOperand(right)); |
| 682 __ mov(R0, ShifterOperand(0), EQ); |
| 683 __ bx(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 __ bx(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 |
| 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 // Check to see if we have integer division |
| 715 if (!CPUFeatures::integer_division_supported()) |
| 716 return false; |
| 717 |
| 718 Label fall_through, subtract; |
| 719 TestBothArgumentsSmis(assembler, &fall_through); |
| 720 // R1: Tagged left (dividend). |
| 721 // R0: Tagged right (divisor). |
| 722 // Check if modulo by zero -> exception thrown in main function. |
| 723 __ cmp(R0, ShifterOperand(0)); |
| 724 __ b(&fall_through, EQ); |
| 725 EmitRemainderOperation(assembler); |
| 726 // Untagged right in R0. Untagged remainder result in R1. |
| 727 |
| 728 __ cmp(R1, ShifterOperand(0)); |
| 729 __ mov(R0, ShifterOperand(R1, LSL, 1), GE); // Tag and move result to R0. |
| 730 __ bx(LR, GE); |
| 731 |
| 732 // Result is negative, adjust it. |
| 733 __ cmp(R0, ShifterOperand(0)); |
| 734 __ sub(R0, R1, ShifterOperand(R0), LT); |
| 735 __ add(R0, R1, ShifterOperand(R0), GE); |
| 736 __ SmiTag(R0); |
| 737 __ Ret(); |
| 738 |
| 739 __ Bind(&fall_through); |
| 130 return false; | 740 return false; |
| 131 } | 741 } |
| 132 | 742 |
| 133 | 743 |
| 134 bool Intrinsifier::Integer_remainder(Assembler* assembler) { | 744 bool Intrinsifier::Integer_remainder(Assembler* assembler) { |
| 745 // Check to see if we have integer division |
| 746 if (!CPUFeatures::integer_division_supported()) |
| 747 return false; |
| 748 |
| 749 Label fall_through; |
| 750 TestBothArgumentsSmis(assembler, &fall_through); |
| 751 // R1: Tagged left (dividend). |
| 752 // R0: Tagged right (divisor). |
| 753 // Check if modulo by zero -> exception thrown in main function. |
| 754 __ cmp(R0, ShifterOperand(0)); |
| 755 __ b(&fall_through, EQ); |
| 756 EmitRemainderOperation(assembler); |
| 757 // Untagged remainder result in R1. |
| 758 __ mov(R0, ShifterOperand(R1, LSL, 1)); // Tag result and return. |
| 759 __ Ret(); |
| 760 |
| 761 __ Bind(&fall_through); |
| 135 return false; | 762 return false; |
| 136 } | 763 } |
| 137 | 764 |
| 138 | 765 |
| 139 bool Intrinsifier::Integer_truncDivide(Assembler* assembler) { | 766 bool Intrinsifier::Integer_truncDivide(Assembler* assembler) { |
| 767 // Check to see if we have integer division |
| 768 if (!CPUFeatures::integer_division_supported()) |
| 769 return false; |
| 770 |
| 771 Label fall_through; |
| 772 |
| 773 TestBothArgumentsSmis(assembler, &fall_through); |
| 774 __ cmp(R0, ShifterOperand(0)); |
| 775 __ b(&fall_through, EQ); // If b is 0, fall through. |
| 776 |
| 777 __ SmiUntag(R0); |
| 778 __ SmiUntag(R1); |
| 779 __ sdiv(R0, R1, R0); |
| 780 // Check the corner case of dividing the 'MIN_SMI' with -1, in which case we |
| 781 // cannot tag the result. |
| 782 __ CompareImmediate(R0, 0x40000000); |
| 783 __ SmiTag(R0, NE); // Not equal. Okay to tag and return. |
| 784 __ bx(LR, NE); // Return. |
| 785 __ Bind(&fall_through); |
| 140 return false; | 786 return false; |
| 141 } | 787 } |
| 142 | 788 |
| 143 | 789 |
| 144 bool Intrinsifier::Integer_negate(Assembler* assembler) { | 790 bool Intrinsifier::Integer_negate(Assembler* assembler) { |
| 791 __ ldr(R0, Address(SP, + 0 * kWordSize)); // Grab first argument. |
| 792 __ tst(R0, ShifterOperand(kSmiTagMask)); // Test for Smi. |
| 793 __ rsb(R0, R0, ShifterOperand(0), EQ); // R0 is a Smi. R0 <- 0 - R0. |
| 794 __ bx(LR, EQ); // Return. |
| 795 // R0 is not a Smi. Fall through. |
| 145 return false; | 796 return false; |
| 146 } | 797 } |
| 147 | 798 |
| 148 | 799 |
| 149 bool Intrinsifier::Integer_bitAndFromInteger(Assembler* assembler) { | 800 bool Intrinsifier::Integer_bitAndFromInteger(Assembler* assembler) { |
| 801 Label fall_through; |
| 802 |
| 803 TestBothArgumentsSmis(assembler, &fall_through); // checks two smis |
| 804 __ and_(R0, R0, ShifterOperand(R1)); |
| 805 |
| 806 __ Ret(); |
| 807 __ Bind(&fall_through); |
| 150 return false; | 808 return false; |
| 151 } | 809 } |
| 152 | 810 |
| 153 | 811 |
| 154 bool Intrinsifier::Integer_bitAnd(Assembler* assembler) { | 812 bool Intrinsifier::Integer_bitAnd(Assembler* assembler) { |
| 155 return false; | 813 return Integer_bitAndFromInteger(assembler); |
| 156 } | 814 } |
| 157 | 815 |
| 158 | 816 |
| 159 bool Intrinsifier::Integer_bitOrFromInteger(Assembler* assembler) { | 817 bool Intrinsifier::Integer_bitOrFromInteger(Assembler* assembler) { |
| 818 Label fall_through; |
| 819 |
| 820 TestBothArgumentsSmis(assembler, &fall_through); // checks two smis |
| 821 __ orr(R0, R0, ShifterOperand(R1)); |
| 822 |
| 823 __ Ret(); |
| 824 __ Bind(&fall_through); |
| 160 return false; | 825 return false; |
| 161 } | 826 } |
| 162 | 827 |
| 163 | 828 |
| 164 bool Intrinsifier::Integer_bitOr(Assembler* assembler) { | 829 bool Intrinsifier::Integer_bitOr(Assembler* assembler) { |
| 165 return false; | 830 return Integer_bitOrFromInteger(assembler); |
| 166 } | 831 } |
| 167 | 832 |
| 168 | 833 |
| 169 bool Intrinsifier::Integer_bitXorFromInteger(Assembler* assembler) { | 834 bool Intrinsifier::Integer_bitXorFromInteger(Assembler* assembler) { |
| 835 Label fall_through; |
| 836 __ Untested("Intrinsifier::Integer_bitXorFromInteger"); |
| 837 |
| 838 TestBothArgumentsSmis(assembler, &fall_through); // checks two smis |
| 839 __ eor(R0, R0, ShifterOperand(R1)); |
| 840 |
| 841 __ Ret(); |
| 842 __ Bind(&fall_through); |
| 170 return false; | 843 return false; |
| 171 } | 844 } |
| 172 | 845 |
| 173 | 846 |
| 174 bool Intrinsifier::Integer_bitXor(Assembler* assembler) { | 847 bool Intrinsifier::Integer_bitXor(Assembler* assembler) { |
| 175 return false; | 848 return Integer_bitXorFromInteger(assembler); |
| 176 } | 849 } |
| 177 | 850 |
| 178 | 851 |
| 179 bool Intrinsifier::Integer_shl(Assembler* assembler) { | 852 bool Intrinsifier::Integer_shl(Assembler* assembler) { |
| 853 ASSERT(kSmiTagShift == 1); |
| 854 ASSERT(kSmiTag == 0); |
| 855 Label fall_through; |
| 856 |
| 857 TestBothArgumentsSmis(assembler, &fall_through); |
| 858 __ CompareImmediate(R0, Smi::RawValue(Smi::kBits)); |
| 859 __ b(&fall_through, HI); |
| 860 |
| 861 __ SmiUntag(R0); |
| 862 |
| 863 // Check for overflow by shifting left and shifting back arithmetically. |
| 864 // If the result is different from the original, there was overflow. |
| 865 __ mov(IP, ShifterOperand(R1, LSL, R0)); |
| 866 __ cmp(R1, ShifterOperand(IP, ASR, R0)); |
| 867 |
| 868 // No overflow, result in R0. |
| 869 __ mov(R0, ShifterOperand(R1, LSL, R0), EQ); |
| 870 __ bx(LR, EQ); |
| 871 |
| 872 // Arguments are Smi but the shift produced an overflow to Mint. |
| 873 __ CompareImmediate(R6, 0); |
| 874 __ b(&fall_through, LT); |
| 875 __ SmiUntag(R6); |
| 876 |
| 877 // Pull off high bits that will be shifted off of R6 by making a mask |
| 878 // ((1 << R0) - 1), shifting it to the left, masking R6, then shifting back. |
| 879 // high bits = (((1 << R0) - 1) << (32 - R0)) & R6) >> (32 - R0) |
| 880 // lo bits = R6 << R0 |
| 881 __ LoadImmediate(R7, 1); |
| 882 __ mov(R7, ShifterOperand(R7, LSL, R0)); // R7 <- 1 << R0 |
| 883 __ sub(R7, R7, ShifterOperand(1)); // R7 <- R7 - 1 |
| 884 __ rsb(R8, R0, ShifterOperand(32)); // R8 <- 32 - R0 |
| 885 __ mov(R7, ShifterOperand(R7, LSL, R8)); // R7 <- R7 << R8 |
| 886 __ and_(R7, R6, ShifterOperand(R7)); // R7 <- R7 & R6 |
| 887 __ mov(R7, ShifterOperand(R7, LSR, R8)); // R7 <- R7 >> R8 |
| 888 // Now R7 has the bits that fall off of R6 on a left shift. |
| 889 __ mov(R1, ShifterOperand(R6, LSL, R0)); // R1 gets the low bits. |
| 890 |
| 891 const Class& mint_class = Class::Handle( |
| 892 Isolate::Current()->object_store()->mint_class()); |
| 893 __ TryAllocate(mint_class, &fall_through, R0); |
| 894 |
| 895 |
| 896 __ str(R1, FieldAddress(R0, Mint::value_offset())); |
| 897 __ str(R7, FieldAddress(R0, Mint::value_offset() + kWordSize)); |
| 898 __ Ret(); |
| 899 __ Bind(&fall_through); |
| 900 return false; |
| 901 } |
| 902 |
| 903 |
| 904 static void Get64SmiOrMint(Assembler* assembler, |
| 905 Register res_hi, |
| 906 Register res_lo, |
| 907 Register reg, |
| 908 Label* not_smi_or_mint) { |
| 909 Label not_smi, done; |
| 910 __ tst(reg, ShifterOperand(kSmiTagMask)); |
| 911 __ b(¬_smi, NE); |
| 912 __ SmiUntag(reg); |
| 913 |
| 914 // Sign extend to 64 bit |
| 915 __ mov(res_lo, ShifterOperand(reg)); |
| 916 __ mov(res_hi, ShifterOperand(res_lo, ASR, 31)); |
| 917 __ b(&done); |
| 918 |
| 919 __ Bind(¬_smi); |
| 920 __ CompareClassId(reg, kMintCid, res_lo); |
| 921 __ b(not_smi_or_mint, NE); |
| 922 |
| 923 // Mint. |
| 924 __ ldr(res_lo, FieldAddress(reg, Mint::value_offset())); |
| 925 __ ldr(res_hi, FieldAddress(reg, Mint::value_offset() + kWordSize)); |
| 926 __ Bind(&done); |
| 927 return; |
| 928 } |
| 929 |
| 930 |
| 931 static bool CompareIntegers(Assembler* assembler, Condition true_condition) { |
| 932 Label try_mint_smi, is_true, is_false, drop_two_fall_through, fall_through; |
| 933 TestBothArgumentsSmis(assembler, &try_mint_smi); |
| 934 // R0 contains the right argument. R1 contains left argument |
| 935 |
| 936 __ cmp(R1, ShifterOperand(R0)); |
| 937 __ b(&is_true, true_condition); |
| 938 __ Bind(&is_false); |
| 939 __ LoadObject(R0, Bool::False()); |
| 940 __ Ret(); |
| 941 __ Bind(&is_true); |
| 942 __ LoadObject(R0, Bool::True()); |
| 943 __ Ret(); |
| 944 |
| 945 // 64-bit comparison |
| 946 Condition hi_true_cond, hi_false_cond, lo_false_cond; |
| 947 switch (true_condition) { |
| 948 case LT: |
| 949 case LE: |
| 950 hi_true_cond = LT; |
| 951 hi_false_cond = GT; |
| 952 lo_false_cond = (true_condition == LT) ? CS : HI; |
| 953 break; |
| 954 case GT: |
| 955 case GE: |
| 956 hi_true_cond = GT; |
| 957 hi_false_cond = LT; |
| 958 lo_false_cond = (true_condition == GT) ? LS : CC; |
| 959 break; |
| 960 default: |
| 961 UNREACHABLE(); |
| 962 hi_true_cond = hi_false_cond = lo_false_cond = VS; |
| 963 } |
| 964 |
| 965 __ Bind(&try_mint_smi); |
| 966 // Get left as 64 bit integer. |
| 967 Get64SmiOrMint(assembler, R3, R2, R1, &fall_through); |
| 968 // Get right as 64 bit integer. |
| 969 Get64SmiOrMint(assembler, R7, R6, R0, &fall_through); |
| 970 // R3: left high. |
| 971 // R2: left low. |
| 972 // R7: right high. |
| 973 // R6: right low. |
| 974 |
| 975 __ cmp(R3, ShifterOperand(R7)); // Compare left hi, right high. |
| 976 __ b(&is_false, hi_false_cond); |
| 977 __ b(&is_true, hi_true_cond); |
| 978 __ cmp(R2, ShifterOperand(R6)); // Compare left lo, right lo. |
| 979 __ b(&is_false, lo_false_cond); |
| 980 // Else is true. |
| 981 __ b(&is_true); |
| 982 |
| 983 __ Bind(&fall_through); |
| 180 return false; | 984 return false; |
| 181 } | 985 } |
| 182 | 986 |
| 183 | 987 |
| 184 bool Intrinsifier::Integer_greaterThanFromInt(Assembler* assembler) { | 988 bool Intrinsifier::Integer_greaterThanFromInt(Assembler* assembler) { |
| 185 return false; | 989 return CompareIntegers(assembler, LT); |
| 186 } | 990 } |
| 187 | 991 |
| 188 | 992 |
| 189 bool Intrinsifier::Integer_lessThan(Assembler* assembler) { | 993 bool Intrinsifier::Integer_lessThan(Assembler* assembler) { |
| 190 return false; | 994 return Integer_greaterThanFromInt(assembler); |
| 191 } | 995 } |
| 192 | 996 |
| 193 | 997 |
| 194 bool Intrinsifier::Integer_greaterThan(Assembler* assembler) { | 998 bool Intrinsifier::Integer_greaterThan(Assembler* assembler) { |
| 195 return false; | 999 return CompareIntegers(assembler, GT); |
| 196 } | 1000 } |
| 197 | 1001 |
| 198 | 1002 |
| 199 bool Intrinsifier::Integer_lessEqualThan(Assembler* assembler) { | 1003 bool Intrinsifier::Integer_lessEqualThan(Assembler* assembler) { |
| 200 return false; | 1004 return CompareIntegers(assembler, LE); |
| 201 } | 1005 } |
| 202 | 1006 |
| 203 | 1007 |
| 204 bool Intrinsifier::Integer_greaterEqualThan(Assembler* assembler) { | 1008 bool Intrinsifier::Integer_greaterEqualThan(Assembler* assembler) { |
| 205 return false; | 1009 return CompareIntegers(assembler, GE); |
| 206 } | 1010 } |
| 207 | 1011 |
| 208 | 1012 |
| 1013 // This is called for Smi, Mint and Bigint receivers. The right argument |
| 1014 // can be Smi, Mint, Bigint or double. |
| 209 bool Intrinsifier::Integer_equalToInteger(Assembler* assembler) { | 1015 bool Intrinsifier::Integer_equalToInteger(Assembler* assembler) { |
| 1016 Label fall_through, true_label, check_for_mint; |
| 1017 // For integer receiver '===' check first. |
| 1018 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 1019 __ ldr(R1, Address(SP, 1 * kWordSize)); |
| 1020 __ cmp(R0, ShifterOperand(R1)); |
| 1021 __ b(&true_label, EQ); |
| 1022 |
| 1023 __ orr(R2, R0, ShifterOperand(R1)); |
| 1024 __ tst(R2, ShifterOperand(kSmiTagMask)); |
| 1025 __ b(&check_for_mint, NE); // If R0 or R1 is not a smi do Mint checks. |
| 1026 |
| 1027 // Both arguments are smi, '===' is good enough. |
| 1028 __ LoadObject(R0, Bool::False()); |
| 1029 __ Ret(); |
| 1030 __ Bind(&true_label); |
| 1031 __ LoadObject(R0, Bool::True()); |
| 1032 __ Ret(); |
| 1033 |
| 1034 // At least one of the arguments was not Smi. |
| 1035 Label receiver_not_smi; |
| 1036 __ Bind(&check_for_mint); |
| 1037 |
| 1038 __ tst(R1, ShifterOperand(kSmiTagMask)); // Check receiver. |
| 1039 __ b(&receiver_not_smi, NE); |
| 1040 |
| 1041 // Left (receiver) is Smi, return false if right is not Double. |
| 1042 // Note that an instance of Mint or Bigint never contains a value that can be |
| 1043 // represented by Smi. |
| 1044 |
| 1045 __ CompareClassId(R0, kDoubleCid, R2); |
| 1046 __ b(&fall_through, EQ); |
| 1047 __ LoadObject(R0, Bool::False()); // Smi == Mint -> false. |
| 1048 __ Ret(); |
| 1049 |
| 1050 __ Bind(&receiver_not_smi); |
| 1051 // R1:: receiver. |
| 1052 |
| 1053 __ CompareClassId(R1, kMintCid, R2); |
| 1054 __ b(&fall_through, NE); |
| 1055 // Receiver is Mint, return false if right is Smi. |
| 1056 __ tst(R0, ShifterOperand(kSmiTagMask)); |
| 1057 __ b(&fall_through, NE); |
| 1058 __ LoadObject(R0, Bool::False()); |
| 1059 __ Ret(); |
| 1060 // TODO(srdjan): Implement Mint == Mint comparison. |
| 1061 |
| 1062 __ Bind(&fall_through); |
| 210 return false; | 1063 return false; |
| 211 } | 1064 } |
| 212 | 1065 |
| 213 | 1066 |
| 214 bool Intrinsifier::Integer_equal(Assembler* assembler) { | 1067 bool Intrinsifier::Integer_equal(Assembler* assembler) { |
| 215 return false; | 1068 return Integer_equalToInteger(assembler); |
| 216 } | 1069 } |
| 217 | 1070 |
| 218 | 1071 |
| 219 bool Intrinsifier::Integer_sar(Assembler* assembler) { | 1072 bool Intrinsifier::Integer_sar(Assembler* assembler) { |
| 1073 Label fall_through; |
| 1074 |
| 1075 TestBothArgumentsSmis(assembler, &fall_through); |
| 1076 // Shift amount in R0. Value to shift in R1. |
| 1077 |
| 1078 // Fall through if shift amount is negative. |
| 1079 __ SmiUntag(R0); |
| 1080 __ CompareImmediate(R0, 0); |
| 1081 __ b(&fall_through, LT); |
| 1082 |
| 1083 // If shift amount is bigger than 31, set to 31. |
| 1084 __ CompareImmediate(R0, 0x1F); |
| 1085 __ LoadImmediate(R0, 0x1F, GT); |
| 1086 __ SmiUntag(R1); |
| 1087 __ mov(R0, ShifterOperand(R1, ASR, R0)); |
| 1088 __ SmiTag(R0); |
| 1089 __ Ret(); |
| 1090 __ Bind(&fall_through); |
| 220 return false; | 1091 return false; |
| 221 } | 1092 } |
| 222 | 1093 |
| 223 | 1094 |
| 224 bool Intrinsifier::Smi_bitNegate(Assembler* assembler) { | 1095 bool Intrinsifier::Smi_bitNegate(Assembler* assembler) { |
| 1096 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 1097 __ mvn(R0, ShifterOperand(R0)); |
| 1098 __ bic(R0, R0, ShifterOperand(kSmiTagMask)); // Remove inverted smi-tag. |
| 1099 __ Ret(); |
| 1100 return false; |
| 1101 } |
| 1102 |
| 1103 |
| 1104 // Check if the last argument is a double, jump to label 'is_smi' if smi |
| 1105 // (easy to convert to double), otherwise jump to label 'not_double_smi', |
| 1106 // Returns the last argument in R0. |
| 1107 static void TestLastArgumentIsDouble(Assembler* assembler, |
| 1108 Label* is_smi, |
| 1109 Label* not_double_smi) { |
| 1110 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 1111 __ tst(R0, ShifterOperand(kSmiTagMask)); |
| 1112 __ b(is_smi, EQ); |
| 1113 __ CompareClassId(R0, kDoubleCid, R1); |
| 1114 __ b(not_double_smi, NE); |
| 1115 // Fall through with Double in R0. |
| 1116 } |
| 1117 |
| 1118 |
| 1119 // Both arguments on stack, arg0 (left) is a double, arg1 (right) is of unknown |
| 1120 // type. Return true or false object in the register R0. Any NaN argument |
| 1121 // returns false. Any non-double arg1 causes control flow to fall through to the |
| 1122 // slow case (compiled method body). |
| 1123 static bool CompareDoubles(Assembler* assembler, Condition true_condition) { |
| 1124 Label fall_through, is_smi, double_op; |
| 1125 |
| 1126 TestLastArgumentIsDouble(assembler, &is_smi, &fall_through); |
| 1127 // Both arguments are double, right operand is in R0. |
| 1128 |
| 1129 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); |
| 1130 __ vldrd(D1, Address(R0)); |
| 1131 __ Bind(&double_op); |
| 1132 __ ldr(R0, Address(SP, 1 * kWordSize)); // Left argument. |
| 1133 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); |
| 1134 __ vldrd(D0, Address(R0)); |
| 1135 |
| 1136 __ vcmpd(D0, D1); |
| 1137 __ vmstat(); |
| 1138 __ LoadObject(R0, Bool::False()); |
| 1139 // Return false if D0 or D1 was NaN before checking true condition. |
| 1140 __ bx(LR, VS); |
| 1141 __ LoadObject(R0, Bool::True(), true_condition); |
| 1142 __ Ret(); |
| 1143 |
| 1144 __ Bind(&is_smi); // Convert R0 to a double. |
| 1145 __ SmiUntag(R0); |
| 1146 __ vmovsr(S0, R0); |
| 1147 __ vcvtdi(D1, S0); |
| 1148 __ b(&double_op); // Then do the comparison. |
| 1149 __ Bind(&fall_through); |
| 225 return false; | 1150 return false; |
| 226 } | 1151 } |
| 227 | 1152 |
| 228 | 1153 |
| 229 bool Intrinsifier::Double_greaterThan(Assembler* assembler) { | 1154 bool Intrinsifier::Double_greaterThan(Assembler* assembler) { |
| 230 return false; | 1155 return CompareDoubles(assembler, HI); |
| 231 } | 1156 } |
| 232 | 1157 |
| 233 | 1158 |
| 234 bool Intrinsifier::Double_greaterEqualThan(Assembler* assembler) { | 1159 bool Intrinsifier::Double_greaterEqualThan(Assembler* assembler) { |
| 235 return false; | 1160 return CompareDoubles(assembler, CS); |
| 236 } | 1161 } |
| 237 | 1162 |
| 238 | 1163 |
| 239 bool Intrinsifier::Double_lessThan(Assembler* assembler) { | 1164 bool Intrinsifier::Double_lessThan(Assembler* assembler) { |
| 240 return false; | 1165 return CompareDoubles(assembler, CC); |
| 241 } | 1166 } |
| 242 | 1167 |
| 243 | 1168 |
| 244 bool Intrinsifier::Double_equal(Assembler* assembler) { | 1169 bool Intrinsifier::Double_equal(Assembler* assembler) { |
| 245 return false; | 1170 return CompareDoubles(assembler, EQ); |
| 246 } | 1171 } |
| 247 | 1172 |
| 248 | 1173 |
| 249 bool Intrinsifier::Double_lessEqualThan(Assembler* assembler) { | 1174 bool Intrinsifier::Double_lessEqualThan(Assembler* assembler) { |
| 1175 return CompareDoubles(assembler, LS); |
| 1176 } |
| 1177 |
| 1178 |
| 1179 // Expects left argument to be double (receiver). Right argument is unknown. |
| 1180 // Both arguments are on stack. |
| 1181 static bool DoubleArithmeticOperations(Assembler* assembler, Token::Kind kind) { |
| 1182 Label fall_through; |
| 1183 |
| 1184 TestLastArgumentIsDouble(assembler, &fall_through, &fall_through); |
| 1185 // Both arguments are double, right operand is in R0. |
| 1186 // Can't use FieldAddress here. R0 is heap-object-tagged, so the offset will |
| 1187 // not be 4-byte aligned. |
| 1188 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); |
| 1189 __ vldrd(D1, Address(R0)); |
| 1190 __ ldr(R0, Address(SP, 1 * kWordSize)); // Left argument. |
| 1191 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); |
| 1192 __ vldrd(D0, Address(R0)); |
| 1193 switch (kind) { |
| 1194 case Token::kADD: __ vaddd(D0, D0, D1); break; |
| 1195 case Token::kSUB: __ vsubd(D0, D0, D1); break; |
| 1196 case Token::kMUL: __ vmuld(D0, D0, D1); break; |
| 1197 case Token::kDIV: __ vdivd(D0, D0, D1); break; |
| 1198 default: UNREACHABLE(); |
| 1199 } |
| 1200 const Class& double_class = Class::Handle( |
| 1201 Isolate::Current()->object_store()->double_class()); |
| 1202 __ TryAllocate(double_class, &fall_through, R0); // Result register. |
| 1203 __ AddImmediate(R1, R0, Double::value_offset() - kHeapObjectTag); |
| 1204 __ vstrd(D0, Address(R1)); |
| 1205 __ Ret(); |
| 1206 __ Bind(&fall_through); |
| 250 return false; | 1207 return false; |
| 251 } | 1208 } |
| 252 | 1209 |
| 253 | 1210 |
| 254 bool Intrinsifier::Double_add(Assembler* assembler) { | 1211 bool Intrinsifier::Double_add(Assembler* assembler) { |
| 255 return false; | 1212 return DoubleArithmeticOperations(assembler, Token::kADD); |
| 256 } | 1213 } |
| 257 | 1214 |
| 258 | 1215 |
| 259 bool Intrinsifier::Double_mul(Assembler* assembler) { | 1216 bool Intrinsifier::Double_mul(Assembler* assembler) { |
| 260 return false; | 1217 return DoubleArithmeticOperations(assembler, Token::kMUL); |
| 261 } | 1218 } |
| 262 | 1219 |
| 263 | 1220 |
| 264 bool Intrinsifier::Double_sub(Assembler* assembler) { | 1221 bool Intrinsifier::Double_sub(Assembler* assembler) { |
| 265 return false; | 1222 return DoubleArithmeticOperations(assembler, Token::kSUB); |
| 266 } | 1223 } |
| 267 | 1224 |
| 268 | 1225 |
| 269 bool Intrinsifier::Double_div(Assembler* assembler) { | 1226 bool Intrinsifier::Double_div(Assembler* assembler) { |
| 270 return false; | 1227 return DoubleArithmeticOperations(assembler, Token::kDIV); |
| 271 } | 1228 } |
| 272 | 1229 |
| 273 | 1230 |
| 1231 // Left is double right is integer (Bigint, Mint or Smi) |
| 274 bool Intrinsifier::Double_mulFromInteger(Assembler* assembler) { | 1232 bool Intrinsifier::Double_mulFromInteger(Assembler* assembler) { |
| 1233 Label fall_through; |
| 1234 __ Untested("Intrinsifier::Double_mulFromInteger"); |
| 1235 // Only Smi-s allowed. |
| 1236 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 1237 __ tst(R0, ShifterOperand(kSmiTagMask)); |
| 1238 __ b(&fall_through, NE); |
| 1239 // Is Smi. |
| 1240 __ SmiUntag(R0); |
| 1241 __ vmovsr(S0, R0); |
| 1242 __ vcvtdi(D1, S0); |
| 1243 __ ldr(R0, Address(SP, 1 * kWordSize)); |
| 1244 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); |
| 1245 __ vldrd(D0, Address(R0)); |
| 1246 __ vmuld(D0, D0, D1); |
| 1247 const Class& double_class = Class::Handle( |
| 1248 Isolate::Current()->object_store()->double_class()); |
| 1249 __ TryAllocate(double_class, &fall_through, R0); // Result register. |
| 1250 __ AddImmediate(R1, R0, Double::value_offset() - kHeapObjectTag); |
| 1251 __ vstrd(D0, Address(R1)); |
| 1252 __ Ret(); |
| 1253 __ Bind(&fall_through); |
| 275 return false; | 1254 return false; |
| 276 } | 1255 } |
| 277 | 1256 |
| 278 | 1257 |
| 279 bool Intrinsifier::Double_fromInteger(Assembler* assembler) { | 1258 bool Intrinsifier::Double_fromInteger(Assembler* assembler) { |
| 1259 Label fall_through; |
| 1260 |
| 1261 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 1262 __ tst(R0, ShifterOperand(kSmiTagMask)); |
| 1263 __ b(&fall_through, NE); |
| 1264 // Is Smi. |
| 1265 __ SmiUntag(R0); |
| 1266 __ vmovsr(S0, R0); |
| 1267 __ vcvtdi(D0, S0); |
| 1268 const Class& double_class = Class::Handle( |
| 1269 Isolate::Current()->object_store()->double_class()); |
| 1270 __ TryAllocate(double_class, &fall_through, R0); // Result register. |
| 1271 __ AddImmediate(R1, R0, Double::value_offset() - kHeapObjectTag); |
| 1272 __ vstrd(D0, Address(R1)); |
| 1273 __ Ret(); |
| 1274 __ Bind(&fall_through); |
| 280 return false; | 1275 return false; |
| 281 } | 1276 } |
| 282 | 1277 |
| 283 | 1278 |
| 284 bool Intrinsifier::Double_getIsNaN(Assembler* assembler) { | 1279 bool Intrinsifier::Double_getIsNaN(Assembler* assembler) { |
| 285 return false; | 1280 Label is_true; |
| 1281 __ Untested("Intrinsifier::Double_getIsNaN"); |
| 1282 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 1283 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); |
| 1284 __ vldrd(D0, Address(R0)); |
| 1285 __ vcmpd(D0, D0); |
| 1286 __ vmstat(); |
| 1287 __ LoadObject(R0, Bool::False(), VS); |
| 1288 __ LoadObject(R0, Bool::True(), VC); |
| 1289 __ Ret(); |
| 1290 return true; |
| 286 } | 1291 } |
| 287 | 1292 |
| 288 | 1293 |
| 289 bool Intrinsifier::Double_getIsNegative(Assembler* assembler) { | 1294 bool Intrinsifier::Double_getIsNegative(Assembler* assembler) { |
| 290 return false; | 1295 Label is_false, is_true, is_zero; |
| 1296 __ Untested("Intrinsifier::Double_getIsNegative"); |
| 1297 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 1298 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); |
| 1299 __ vldrd(D0, Address(R0)); |
| 1300 __ LoadDImmediate(D1, 0.0, R1); |
| 1301 __ vcmpd(D0, D1); |
| 1302 __ vmstat(); |
| 1303 __ b(&is_false, VS); // NaN -> false. |
| 1304 __ b(&is_zero, EQ); // Check for negative zero. |
| 1305 __ b(&is_false, CS); // >= 0 -> false. |
| 1306 |
| 1307 __ Bind(&is_true); |
| 1308 __ LoadObject(R0, Bool::True()); |
| 1309 __ Ret(); |
| 1310 |
| 1311 __ Bind(&is_false); |
| 1312 __ LoadObject(R0, Bool::False()); |
| 1313 __ Ret(); |
| 1314 |
| 1315 __ Bind(&is_zero); |
| 1316 // Check for negative zero by looking at the sign bit. |
| 1317 __ vmovrrd(R0, R1, D0); // R1:R0 <- D0, so sign bit is in bit 31 of R1. |
| 1318 __ mov(R1, ShifterOperand(R1, LSR, 31)); |
| 1319 __ tst(R1, ShifterOperand(1)); |
| 1320 __ b(&is_true, NE); // Sign bit set. |
| 1321 __ b(&is_false); |
| 1322 return true; |
| 291 } | 1323 } |
| 292 | 1324 |
| 293 | 1325 |
| 294 bool Intrinsifier::Double_toInt(Assembler* assembler) { | 1326 bool Intrinsifier::Double_toInt(Assembler* assembler) { |
| 1327 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 1328 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); |
| 1329 __ vldrd(D0, Address(R0)); |
| 1330 __ vcvtid(S0, D0); |
| 1331 __ vmovrs(R0, S0); |
| 1332 // Overflow is signaled with minint. |
| 1333 Label fall_through; |
| 1334 // Check for overflow and that it fits into Smi. |
| 1335 __ CompareImmediate(R0, 0xC0000000); |
| 1336 __ b(&fall_through, MI); |
| 1337 __ SmiTag(R0); |
| 1338 __ Ret(); |
| 1339 __ Bind(&fall_through); |
| 295 return false; | 1340 return false; |
| 296 } | 1341 } |
| 297 | 1342 |
| 298 | 1343 |
| 299 bool Intrinsifier::Math_sqrt(Assembler* assembler) { | 1344 bool Intrinsifier::Math_sqrt(Assembler* assembler) { |
| 300 return false; | 1345 Label fall_through, is_smi, double_op; |
| 301 } | 1346 __ Untested("Intrinsifier::Math_sqrt"); |
| 302 | 1347 TestLastArgumentIsDouble(assembler, &is_smi, &fall_through); |
| 1348 // Argument is double and is in R0. |
| 1349 __ AddImmediate(R0, Double::value_offset() - kHeapObjectTag); |
| 1350 __ vldrd(D1, Address(R0)); |
| 1351 __ Bind(&double_op); |
| 1352 __ vsqrtd(D0, D1); |
| 1353 const Class& double_class = Class::Handle( |
| 1354 Isolate::Current()->object_store()->double_class()); |
| 1355 __ TryAllocate(double_class, &fall_through, R0); // Result register. |
| 1356 __ AddImmediate(R1, R0, Double::value_offset() - kHeapObjectTag); |
| 1357 __ vstrd(D0, Address(R1)); |
| 1358 __ Ret(); |
| 1359 __ Bind(&is_smi); |
| 1360 __ SmiUntag(R0); |
| 1361 __ vmovsr(S0, R0); |
| 1362 __ vcvtdi(D1, S0); |
| 1363 __ b(&double_op); |
| 1364 __ Bind(&fall_through); |
| 1365 return false; |
| 1366 } |
| 1367 |
| 303 | 1368 |
| 304 bool Intrinsifier::Math_sin(Assembler* assembler) { | 1369 bool Intrinsifier::Math_sin(Assembler* assembler) { |
| 305 return false; | 1370 return false; |
| 306 } | 1371 } |
| 307 | 1372 |
| 308 | 1373 |
| 309 bool Intrinsifier::Math_cos(Assembler* assembler) { | 1374 bool Intrinsifier::Math_cos(Assembler* assembler) { |
| 310 return false; | 1375 return false; |
| 311 } | 1376 } |
| 312 | 1377 |
| 313 | 1378 |
| 1379 // var state = ((_A * (_state[kSTATE_LO])) + _state[kSTATE_HI]) & _MASK_64; |
| 1380 // _state[kSTATE_LO] = state & _MASK_32; |
| 1381 // _state[kSTATE_HI] = state >> 32; |
| 314 bool Intrinsifier::Random_nextState(Assembler* assembler) { | 1382 bool Intrinsifier::Random_nextState(Assembler* assembler) { |
| 315 return false; | 1383 const Library& math_lib = Library::Handle(Library::MathLibrary()); |
| 1384 ASSERT(!math_lib.IsNull()); |
| 1385 const Class& random_class = |
| 1386 Class::Handle(math_lib.LookupClassAllowPrivate(Symbols::_Random())); |
| 1387 ASSERT(!random_class.IsNull()); |
| 1388 const Field& state_field = Field::ZoneHandle( |
| 1389 random_class.LookupInstanceField(Symbols::_state())); |
| 1390 ASSERT(!state_field.IsNull()); |
| 1391 const Field& random_A_field = Field::ZoneHandle( |
| 1392 random_class.LookupStaticField(Symbols::_A())); |
| 1393 ASSERT(!random_A_field.IsNull()); |
| 1394 ASSERT(random_A_field.is_const()); |
| 1395 const Instance& a_value = Instance::Handle(random_A_field.value()); |
| 1396 const int64_t a_int_value = Integer::Cast(a_value).AsInt64Value(); |
| 1397 // 'a_int_value' is a mask. |
| 1398 ASSERT(Utils::IsUint(32, a_int_value)); |
| 1399 int32_t a_int32_value = static_cast<int32_t>(a_int_value); |
| 1400 |
| 1401 __ Untested("Random_nextState"); |
| 1402 |
| 1403 __ ldr(R0, Address(SP, 0 * kWordSize)); // Receiver. |
| 1404 __ ldr(R1, FieldAddress(R0, state_field.Offset())); // Field '_state'. |
| 1405 // Addresses of _state[0] and _state[1]. |
| 1406 |
| 1407 const int64_t disp_0 = |
| 1408 FlowGraphCompiler::DataOffsetFor(kTypedDataUint32ArrayCid); |
| 1409 |
| 1410 const int64_t disp_1 = |
| 1411 FlowGraphCompiler::ElementSizeFor(kTypedDataUint32ArrayCid) + |
| 1412 FlowGraphCompiler::DataOffsetFor(kTypedDataUint32ArrayCid); |
| 1413 |
| 1414 __ LoadImmediate(R0, a_int32_value); |
| 1415 __ LoadFromOffset(kLoadWord, R2, R1, disp_0 - 1); |
| 1416 __ LoadFromOffset(kLoadWord, R3, R1, disp_1 - 1); |
| 1417 __ mov(R6, ShifterOperand(R3, ASR, 31)); // Sign extend into R6. |
| 1418 // 64-bit multiply and accumulate into R6:R3. |
| 1419 __ smlal(R3, R6, R0, R2); // R6:R3 <- R6:R3 + R0 * R2. |
| 1420 __ StoreToOffset(kStoreWord, R3, R1, disp_0 - 1); |
| 1421 __ StoreToOffset(kStoreWord, R6, R1, disp_1 - 1); |
| 1422 __ Ret(); |
| 1423 return true; |
| 316 } | 1424 } |
| 317 | 1425 |
| 318 | 1426 |
| 319 bool Intrinsifier::Object_equal(Assembler* assembler) { | 1427 bool Intrinsifier::Object_equal(Assembler* assembler) { |
| 320 return false; | 1428 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 1429 __ ldr(R1, Address(SP, 1 * kWordSize)); |
| 1430 __ cmp(R0, ShifterOperand(R1)); |
| 1431 __ LoadObject(R0, Bool::False(), NE); |
| 1432 __ LoadObject(R0, Bool::True(), EQ); |
| 1433 __ Ret(); |
| 1434 return true; |
| 321 } | 1435 } |
| 322 | 1436 |
| 323 | 1437 |
| 324 bool Intrinsifier::String_getHashCode(Assembler* assembler) { | 1438 bool Intrinsifier::String_getHashCode(Assembler* assembler) { |
| 1439 __ Untested("Intrinsifier::String_getHashCode"); |
| 1440 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 1441 __ ldr(R0, FieldAddress(R0, String::hash_offset())); |
| 1442 __ cmp(R0, ShifterOperand(0)); |
| 1443 __ bx(LR, NE); // Hash not yet computed. |
| 325 return false; | 1444 return false; |
| 326 } | 1445 } |
| 327 | 1446 |
| 328 | 1447 |
| 329 bool Intrinsifier::String_getLength(Assembler* assembler) { | 1448 bool Intrinsifier::String_getLength(Assembler* assembler) { |
| 330 return false; | 1449 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 331 } | 1450 __ ldr(R0, FieldAddress(R0, String::length_offset())); |
| 332 | 1451 __ Ret(); |
| 333 | 1452 return true; |
| 1453 } |
| 1454 |
| 1455 |
| 1456 // TODO(srdjan): Implement for two and four byte strings as well. |
| 334 bool Intrinsifier::String_codeUnitAt(Assembler* assembler) { | 1457 bool Intrinsifier::String_codeUnitAt(Assembler* assembler) { |
| 1458 Label fall_through; |
| 1459 |
| 1460 __ ldr(R1, Address(SP, 0 * kWordSize)); // Index. |
| 1461 __ ldr(R0, Address(SP, 1 * kWordSize)); // String. |
| 1462 __ tst(R1, ShifterOperand(kSmiTagMask)); |
| 1463 __ b(&fall_through, NE); // Index is not a Smi. |
| 1464 // Range check. |
| 1465 __ ldr(R2, FieldAddress(R0, String::length_offset())); |
| 1466 __ cmp(R1, ShifterOperand(R2)); |
| 1467 __ b(&fall_through, CS); // Runtime throws exception. |
| 1468 __ CompareClassId(R0, kOneByteStringCid, R3); |
| 1469 __ b(&fall_through, NE); |
| 1470 __ SmiUntag(R1); |
| 1471 __ AddImmediate(R0, OneByteString::data_offset() - kHeapObjectTag); |
| 1472 __ ldrb(R0, Address(R0, R1)); |
| 1473 __ SmiTag(R0); |
| 1474 __ Ret(); |
| 1475 __ Bind(&fall_through); |
| 335 return false; | 1476 return false; |
| 336 } | 1477 } |
| 337 | 1478 |
| 338 | 1479 |
| 339 bool Intrinsifier::String_getIsEmpty(Assembler* assembler) { | 1480 bool Intrinsifier::String_getIsEmpty(Assembler* assembler) { |
| 340 return false; | 1481 __ ldr(R0, Address(SP, 0 * kWordSize)); |
| 1482 __ ldr(R0, FieldAddress(R0, String::length_offset())); |
| 1483 __ cmp(R0, ShifterOperand(Smi::RawValue(0))); |
| 1484 __ LoadObject(R0, Bool::True(), EQ); |
| 1485 __ LoadObject(R0, Bool::False(), NE); |
| 1486 __ Ret(); |
| 1487 return true; |
| 341 } | 1488 } |
| 342 | 1489 |
| 343 | 1490 |
| 344 bool Intrinsifier::OneByteString_getHashCode(Assembler* assembler) { | 1491 bool Intrinsifier::OneByteString_getHashCode(Assembler* assembler) { |
| 345 return false; | 1492 __ ldr(R1, Address(SP, 0 * kWordSize)); |
| 346 } | 1493 __ ldr(R0, FieldAddress(R1, String::hash_offset())); |
| 347 | 1494 __ cmp(R0, ShifterOperand(0)); |
| 348 | 1495 __ bx(LR, NE); // Return if already computed. |
| 1496 |
| 1497 __ ldr(R2, FieldAddress(R1, String::length_offset())); |
| 1498 |
| 1499 Label done; |
| 1500 // If the string is empty, set the hash to 1, and return. |
| 1501 __ cmp(R2, ShifterOperand(Smi::RawValue(0))); |
| 1502 __ b(&done, EQ); |
| 1503 |
| 1504 __ SmiUntag(R2); |
| 1505 __ mov(R3, ShifterOperand(0)); |
| 1506 __ AddImmediate(R6, R1, OneByteString::data_offset() - kHeapObjectTag); |
| 1507 // R1: Instance of OneByteString. |
| 1508 // R2: String length, untagged integer. |
| 1509 // R3: Loop counter, untagged integer. |
| 1510 // R6: String data. |
| 1511 // R0: Hash code, untagged integer. |
| 1512 |
| 1513 Label loop; |
| 1514 // Add to hash code: (hash_ is uint32) |
| 1515 // hash_ += ch; |
| 1516 // hash_ += hash_ << 10; |
| 1517 // hash_ ^= hash_ >> 6; |
| 1518 // Get one characters (ch). |
| 1519 __ Bind(&loop); |
| 1520 __ ldrb(R7, Address(R6, 0)); |
| 1521 // R7: ch. |
| 1522 __ add(R3, R3, ShifterOperand(1)); |
| 1523 __ add(R6, R6, ShifterOperand(1)); |
| 1524 __ add(R0, R0, ShifterOperand(R7)); |
| 1525 __ add(R0, R0, ShifterOperand(R0, LSL, 10)); |
| 1526 __ eor(R0, R0, ShifterOperand(R0, LSR, 6)); |
| 1527 __ cmp(R3, ShifterOperand(R2)); |
| 1528 __ b(&loop, NE); |
| 1529 |
| 1530 // Finalize. |
| 1531 // hash_ += hash_ << 3; |
| 1532 // hash_ ^= hash_ >> 11; |
| 1533 // hash_ += hash_ << 15; |
| 1534 __ add(R0, R0, ShifterOperand(R0, LSL, 3)); |
| 1535 __ eor(R0, R0, ShifterOperand(R0, LSR, 11)); |
| 1536 __ add(R0, R0, ShifterOperand(R0, LSL, 15)); |
| 1537 // hash_ = hash_ & ((static_cast<intptr_t>(1) << bits) - 1); |
| 1538 __ LoadImmediate(R2, (static_cast<intptr_t>(1) << String::kHashBits) - 1); |
| 1539 __ and_(R0, R0, ShifterOperand(R2)); |
| 1540 __ cmp(R0, ShifterOperand(0)); |
| 1541 // return hash_ == 0 ? 1 : hash_; |
| 1542 __ Bind(&done); |
| 1543 __ mov(R0, ShifterOperand(1), EQ); |
| 1544 __ SmiTag(R0); |
| 1545 __ str(R0, FieldAddress(R1, String::hash_offset())); |
| 1546 __ Ret(); |
| 1547 return false; |
| 1548 } |
| 1549 |
| 1550 |
| 1551 // Allocates one-byte string of length 'end - start'. The content is not |
| 1552 // initialized. |
| 1553 // 'length-reg' (R2) contains tagged length. |
| 1554 // Returns new string as tagged pointer in R0. |
| 1555 static void TryAllocateOnebyteString(Assembler* assembler, |
| 1556 Label* ok, |
| 1557 Label* failure) { |
| 1558 const Register length_reg = R2; |
| 1559 Label fail; |
| 1560 |
| 1561 __ mov(R6, ShifterOperand(length_reg)); // Save the length register. |
| 1562 __ SmiUntag(length_reg); |
| 1563 const intptr_t fixed_size = sizeof(RawString) + kObjectAlignment - 1; |
| 1564 __ AddImmediate(length_reg, fixed_size); |
| 1565 __ bic(length_reg, length_reg, ShifterOperand(kObjectAlignment - 1)); |
| 1566 |
| 1567 Isolate* isolate = Isolate::Current(); |
| 1568 Heap* heap = isolate->heap(); |
| 1569 |
| 1570 __ LoadImmediate(R3, heap->TopAddress()); |
| 1571 __ ldr(R0, Address(R3, 0)); |
| 1572 |
| 1573 // length_reg: allocation size. |
| 1574 __ adds(R1, R0, ShifterOperand(length_reg)); |
| 1575 __ b(&fail, VS); // Fail on overflow. |
| 1576 |
| 1577 // Check if the allocation fits into the remaining space. |
| 1578 // R0: potential new object start. |
| 1579 // R1: potential next object start. |
| 1580 // R2: allocation size. |
| 1581 // R3: heap->Top->Address(). |
| 1582 __ LoadImmediate(R7, heap->EndAddress()); |
| 1583 __ ldr(R7, Address(R7, 0)); |
| 1584 __ cmp(R1, ShifterOperand(R7)); |
| 1585 __ b(&fail, CS); |
| 1586 |
| 1587 // Successfully allocated the object(s), now update top to point to |
| 1588 // next object start and initialize the object. |
| 1589 __ str(R1, Address(R3, 0)); |
| 1590 __ AddImmediate(R0, kHeapObjectTag); |
| 1591 |
| 1592 // Initialize the tags. |
| 1593 // R0: new object start as a tagged pointer. |
| 1594 // R1: new object end address. |
| 1595 // R2: allocation size. |
| 1596 { |
| 1597 const intptr_t shift = RawObject::kSizeTagBit - kObjectAlignmentLog2; |
| 1598 const Class& cls = |
| 1599 Class::Handle(isolate->object_store()->one_byte_string_class()); |
| 1600 |
| 1601 __ CompareImmediate(R2, RawObject::SizeTag::kMaxSizeTag); |
| 1602 __ mov(R2, ShifterOperand(R2, LSL, shift), LS); |
| 1603 __ mov(R2, ShifterOperand(0), HI); |
| 1604 |
| 1605 // Get the class index and insert it into the tags. |
| 1606 // R2: size and bit tags. |
| 1607 __ LoadImmediate(TMP, RawObject::ClassIdTag::encode(cls.id())); |
| 1608 __ orr(R2, R2, ShifterOperand(TMP)); |
| 1609 __ str(R2, FieldAddress(R0, String::tags_offset())); // Store tags. |
| 1610 } |
| 1611 |
| 1612 // Set the length field using the saved length (R6). |
| 1613 __ StoreIntoObjectNoBarrier(R0, |
| 1614 FieldAddress(R0, String::length_offset()), |
| 1615 R6); |
| 1616 // Clear hash. |
| 1617 __ LoadImmediate(TMP, 0); |
| 1618 __ str(TMP, FieldAddress(R0, String::hash_offset())); |
| 1619 __ b(ok); |
| 1620 |
| 1621 __ Bind(&fail); |
| 1622 __ b(failure); |
| 1623 } |
| 1624 |
| 1625 |
| 1626 // Arg0: Onebyte String |
| 1627 // Arg1: Start index as Smi. |
| 1628 // Arg2: End index as Smi. |
| 1629 // The indexes must be valid. |
| 349 bool Intrinsifier::OneByteString_substringUnchecked(Assembler* assembler) { | 1630 bool Intrinsifier::OneByteString_substringUnchecked(Assembler* assembler) { |
| 1631 const intptr_t kStringOffset = 2 * kWordSize; |
| 1632 const intptr_t kStartIndexOffset = 1 * kWordSize; |
| 1633 const intptr_t kEndIndexOffset = 0 * kWordSize; |
| 1634 Label fall_through, ok; |
| 1635 |
| 1636 __ ldr(R2, Address(SP, kEndIndexOffset)); |
| 1637 __ ldr(TMP, Address(SP, kStartIndexOffset)); |
| 1638 __ sub(R2, R2, ShifterOperand(TMP)); |
| 1639 TryAllocateOnebyteString(assembler, &ok, &fall_through); |
| 1640 __ Bind(&ok); |
| 1641 // R0: new string as tagged pointer. |
| 1642 // Copy string. |
| 1643 __ ldr(R3, Address(SP, kStringOffset)); |
| 1644 __ ldr(R1, Address(SP, kStartIndexOffset)); |
| 1645 __ SmiUntag(R1); |
| 1646 __ add(R3, R3, ShifterOperand(R1)); |
| 1647 // Calculate start address and untag (- 1). |
| 1648 __ AddImmediate(R3, OneByteString::data_offset() - 1); |
| 1649 |
| 1650 // R3: Start address to copy from (untagged). |
| 1651 // R1: Untagged start index. |
| 1652 __ ldr(R2, Address(SP, kEndIndexOffset)); |
| 1653 __ SmiUntag(R2); |
| 1654 __ sub(R2, R2, ShifterOperand(R1)); |
| 1655 |
| 1656 // R3: Start address to copy from (untagged). |
| 1657 // R2: Untagged number of bytes to copy. |
| 1658 // R0: Tagged result string. |
| 1659 // R6: Pointer into R3. |
| 1660 // R7: Pointer into R0. |
| 1661 // R1: Scratch register. |
| 1662 Label loop, done; |
| 1663 __ cmp(R2, ShifterOperand(0)); |
| 1664 __ b(&done, LE); |
| 1665 __ mov(R6, ShifterOperand(R3)); |
| 1666 __ mov(R7, ShifterOperand(R0)); |
| 1667 __ Bind(&loop); |
| 1668 __ ldrb(R1, Address(R6, 0)); |
| 1669 __ AddImmediate(R6, 1); |
| 1670 __ sub(R2, R2, ShifterOperand(1)); |
| 1671 __ cmp(R2, ShifterOperand(0)); |
| 1672 __ strb(R1, FieldAddress(R7, OneByteString::data_offset())); |
| 1673 __ AddImmediate(R7, 1); |
| 1674 __ b(&loop, GT); |
| 1675 |
| 1676 __ Bind(&done); |
| 1677 __ Ret(); |
| 1678 __ Bind(&fall_through); |
| 350 return false; | 1679 return false; |
| 351 } | 1680 } |
| 352 | 1681 |
| 353 | 1682 |
| 354 bool Intrinsifier::OneByteString_setAt(Assembler* assembler) { | 1683 bool Intrinsifier::OneByteString_setAt(Assembler* assembler) { |
| 355 return false; | 1684 __ ldr(R2, Address(SP, 0 * kWordSize)); // Value. |
| 1685 __ ldr(R1, Address(SP, 1 * kWordSize)); // Index. |
| 1686 __ ldr(R0, Address(SP, 2 * kWordSize)); // OneByteString. |
| 1687 __ SmiUntag(R1); |
| 1688 __ SmiUntag(R2); |
| 1689 __ AddImmediate(R3, R0, OneByteString::data_offset() - kHeapObjectTag); |
| 1690 __ strb(R2, Address(R3, R1)); |
| 1691 __ Ret(); |
| 1692 return true; |
| 356 } | 1693 } |
| 357 | 1694 |
| 358 | 1695 |
| 359 bool Intrinsifier::OneByteString_allocate(Assembler* assembler) { | 1696 bool Intrinsifier::OneByteString_allocate(Assembler* assembler) { |
| 360 return false; | 1697 __ ldr(R2, Address(SP, 0 * kWordSize)); // Length. |
| 361 } | 1698 Label fall_through, ok; |
| 362 | 1699 TryAllocateOnebyteString(assembler, &ok, &fall_through); |
| 1700 |
| 1701 __ Bind(&ok); |
| 1702 __ Ret(); |
| 1703 |
| 1704 __ Bind(&fall_through); |
| 1705 return false; |
| 1706 } |
| 1707 |
| 363 } // namespace dart | 1708 } // namespace dart |
| 364 | 1709 |
| 365 #endif // defined TARGET_ARCH_ARM | 1710 #endif // defined TARGET_ARCH_ARM |
| OLD | NEW |