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