1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "assembler_arm64.h" 18 #include "base/logging.h" 19 #include "entrypoints/quick/quick_entrypoints.h" 20 #include "offsets.h" 21 #include "thread.h" 22 #include "utils.h" 23 24 namespace art { 25 namespace arm64 { 26 27 #ifdef ___ 28 #error "ARM64 Assembler macro already defined." 29 #else 30 #define ___ vixl_masm_-> 31 #endif 32 33 void Arm64Assembler::EmitSlowPaths() { 34 if (!exception_blocks_.empty()) { 35 for (size_t i = 0; i < exception_blocks_.size(); i++) { 36 EmitExceptionPoll(exception_blocks_.at(i)); 37 } 38 } 39 ___ FinalizeCode(); 40 } 41 42 size_t Arm64Assembler::CodeSize() const { 43 return ___ SizeOfCodeGenerated(); 44 } 45 46 void Arm64Assembler::FinalizeInstructions(const MemoryRegion& region) { 47 // Copy the instructions from the buffer. 48 MemoryRegion from(reinterpret_cast<void*>(vixl_buf_), CodeSize()); 49 region.CopyFrom(0, from); 50 } 51 52 void Arm64Assembler::GetCurrentThread(ManagedRegister tr) { 53 ___ Mov(reg_x(tr.AsArm64().AsCoreRegister()), reg_x(ETR)); 54 } 55 56 void Arm64Assembler::GetCurrentThread(FrameOffset offset, ManagedRegister /* scratch */) { 57 StoreToOffset(ETR, SP, offset.Int32Value()); 58 } 59 60 // See Arm64 PCS Section 5.2.2.1. 61 void Arm64Assembler::IncreaseFrameSize(size_t adjust) { 62 CHECK_ALIGNED(adjust, kStackAlignment); 63 AddConstant(SP, -adjust); 64 } 65 66 // See Arm64 PCS Section 5.2.2.1. 67 void Arm64Assembler::DecreaseFrameSize(size_t adjust) { 68 CHECK_ALIGNED(adjust, kStackAlignment); 69 AddConstant(SP, adjust); 70 } 71 72 void Arm64Assembler::AddConstant(Register rd, int32_t value, Condition cond) { 73 AddConstant(rd, rd, value, cond); 74 } 75 76 void Arm64Assembler::AddConstant(Register rd, Register rn, int32_t value, 77 Condition cond) { 78 if ((cond == AL) || (cond == NV)) { 79 // VIXL macro-assembler handles all variants. 80 ___ Add(reg_x(rd), reg_x(rn), value); 81 } else { 82 // temp = rd + value 83 // rd = cond ? temp : rn 84 vixl::UseScratchRegisterScope temps(vixl_masm_); 85 temps.Exclude(reg_x(rd), reg_x(rn)); 86 vixl::Register temp = temps.AcquireX(); 87 ___ Add(temp, reg_x(rn), value); 88 ___ Csel(reg_x(rd), temp, reg_x(rd), COND_OP(cond)); 89 } 90 } 91 92 void Arm64Assembler::StoreWToOffset(StoreOperandType type, WRegister source, 93 Register base, int32_t offset) { 94 switch (type) { 95 case kStoreByte: 96 ___ Strb(reg_w(source), MEM_OP(reg_x(base), offset)); 97 break; 98 case kStoreHalfword: 99 ___ Strh(reg_w(source), MEM_OP(reg_x(base), offset)); 100 break; 101 case kStoreWord: 102 ___ Str(reg_w(source), MEM_OP(reg_x(base), offset)); 103 break; 104 default: 105 LOG(FATAL) << "UNREACHABLE"; 106 } 107 } 108 109 void Arm64Assembler::StoreToOffset(Register source, Register base, int32_t offset) { 110 CHECK_NE(source, SP); 111 ___ Str(reg_x(source), MEM_OP(reg_x(base), offset)); 112 } 113 114 void Arm64Assembler::StoreSToOffset(SRegister source, Register base, int32_t offset) { 115 ___ Str(reg_s(source), MEM_OP(reg_x(base), offset)); 116 } 117 118 void Arm64Assembler::StoreDToOffset(DRegister source, Register base, int32_t offset) { 119 ___ Str(reg_d(source), MEM_OP(reg_x(base), offset)); 120 } 121 122 void Arm64Assembler::Store(FrameOffset offs, ManagedRegister m_src, size_t size) { 123 Arm64ManagedRegister src = m_src.AsArm64(); 124 if (src.IsNoRegister()) { 125 CHECK_EQ(0u, size); 126 } else if (src.IsWRegister()) { 127 CHECK_EQ(4u, size); 128 StoreWToOffset(kStoreWord, src.AsWRegister(), SP, offs.Int32Value()); 129 } else if (src.IsCoreRegister()) { 130 CHECK_EQ(8u, size); 131 StoreToOffset(src.AsCoreRegister(), SP, offs.Int32Value()); 132 } else if (src.IsSRegister()) { 133 StoreSToOffset(src.AsSRegister(), SP, offs.Int32Value()); 134 } else { 135 CHECK(src.IsDRegister()) << src; 136 StoreDToOffset(src.AsDRegister(), SP, offs.Int32Value()); 137 } 138 } 139 140 void Arm64Assembler::StoreRef(FrameOffset offs, ManagedRegister m_src) { 141 Arm64ManagedRegister src = m_src.AsArm64(); 142 CHECK(src.IsCoreRegister()) << src; 143 StoreWToOffset(kStoreWord, src.AsOverlappingCoreRegisterLow(), SP, 144 offs.Int32Value()); 145 } 146 147 void Arm64Assembler::StoreRawPtr(FrameOffset offs, ManagedRegister m_src) { 148 Arm64ManagedRegister src = m_src.AsArm64(); 149 CHECK(src.IsCoreRegister()) << src; 150 StoreToOffset(src.AsCoreRegister(), SP, offs.Int32Value()); 151 } 152 153 void Arm64Assembler::StoreImmediateToFrame(FrameOffset offs, uint32_t imm, 154 ManagedRegister m_scratch) { 155 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 156 CHECK(scratch.IsCoreRegister()) << scratch; 157 LoadImmediate(scratch.AsCoreRegister(), imm); 158 StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), SP, 159 offs.Int32Value()); 160 } 161 162 void Arm64Assembler::StoreImmediateToThread64(ThreadOffset<8> offs, uint32_t imm, 163 ManagedRegister m_scratch) { 164 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 165 CHECK(scratch.IsCoreRegister()) << scratch; 166 LoadImmediate(scratch.AsCoreRegister(), imm); 167 StoreToOffset(scratch.AsCoreRegister(), ETR, offs.Int32Value()); 168 } 169 170 void Arm64Assembler::StoreStackOffsetToThread64(ThreadOffset<8> tr_offs, 171 FrameOffset fr_offs, 172 ManagedRegister m_scratch) { 173 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 174 CHECK(scratch.IsCoreRegister()) << scratch; 175 AddConstant(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); 176 StoreToOffset(scratch.AsCoreRegister(), ETR, tr_offs.Int32Value()); 177 } 178 179 void Arm64Assembler::StoreStackPointerToThread64(ThreadOffset<8> tr_offs) { 180 vixl::UseScratchRegisterScope temps(vixl_masm_); 181 vixl::Register temp = temps.AcquireX(); 182 ___ Mov(temp, reg_x(SP)); 183 ___ Str(temp, MEM_OP(reg_x(ETR), tr_offs.Int32Value())); 184 } 185 186 void Arm64Assembler::StoreSpanning(FrameOffset dest_off, ManagedRegister m_source, 187 FrameOffset in_off, ManagedRegister m_scratch) { 188 Arm64ManagedRegister source = m_source.AsArm64(); 189 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 190 StoreToOffset(source.AsCoreRegister(), SP, dest_off.Int32Value()); 191 LoadFromOffset(scratch.AsCoreRegister(), SP, in_off.Int32Value()); 192 StoreToOffset(scratch.AsCoreRegister(), SP, dest_off.Int32Value() + 8); 193 } 194 195 // Load routines. 196 void Arm64Assembler::LoadImmediate(Register dest, int32_t value, 197 Condition cond) { 198 if ((cond == AL) || (cond == NV)) { 199 ___ Mov(reg_x(dest), value); 200 } else { 201 // temp = value 202 // rd = cond ? temp : rd 203 if (value != 0) { 204 vixl::UseScratchRegisterScope temps(vixl_masm_); 205 temps.Exclude(reg_x(dest)); 206 vixl::Register temp = temps.AcquireX(); 207 ___ Mov(temp, value); 208 ___ Csel(reg_x(dest), temp, reg_x(dest), COND_OP(cond)); 209 } else { 210 ___ Csel(reg_x(dest), reg_x(XZR), reg_x(dest), COND_OP(cond)); 211 } 212 } 213 } 214 215 void Arm64Assembler::LoadWFromOffset(LoadOperandType type, WRegister dest, 216 Register base, int32_t offset) { 217 switch (type) { 218 case kLoadSignedByte: 219 ___ Ldrsb(reg_w(dest), MEM_OP(reg_x(base), offset)); 220 break; 221 case kLoadSignedHalfword: 222 ___ Ldrsh(reg_w(dest), MEM_OP(reg_x(base), offset)); 223 break; 224 case kLoadUnsignedByte: 225 ___ Ldrb(reg_w(dest), MEM_OP(reg_x(base), offset)); 226 break; 227 case kLoadUnsignedHalfword: 228 ___ Ldrh(reg_w(dest), MEM_OP(reg_x(base), offset)); 229 break; 230 case kLoadWord: 231 ___ Ldr(reg_w(dest), MEM_OP(reg_x(base), offset)); 232 break; 233 default: 234 LOG(FATAL) << "UNREACHABLE"; 235 } 236 } 237 238 // Note: We can extend this member by adding load type info - see 239 // sign extended A64 load variants. 240 void Arm64Assembler::LoadFromOffset(Register dest, Register base, 241 int32_t offset) { 242 CHECK_NE(dest, SP); 243 ___ Ldr(reg_x(dest), MEM_OP(reg_x(base), offset)); 244 } 245 246 void Arm64Assembler::LoadSFromOffset(SRegister dest, Register base, 247 int32_t offset) { 248 ___ Ldr(reg_s(dest), MEM_OP(reg_x(base), offset)); 249 } 250 251 void Arm64Assembler::LoadDFromOffset(DRegister dest, Register base, 252 int32_t offset) { 253 ___ Ldr(reg_d(dest), MEM_OP(reg_x(base), offset)); 254 } 255 256 void Arm64Assembler::Load(Arm64ManagedRegister dest, Register base, 257 int32_t offset, size_t size) { 258 if (dest.IsNoRegister()) { 259 CHECK_EQ(0u, size) << dest; 260 } else if (dest.IsWRegister()) { 261 CHECK_EQ(4u, size) << dest; 262 ___ Ldr(reg_w(dest.AsWRegister()), MEM_OP(reg_x(base), offset)); 263 } else if (dest.IsCoreRegister()) { 264 CHECK_NE(dest.AsCoreRegister(), SP) << dest; 265 if (size == 4u) { 266 ___ Ldr(reg_w(dest.AsOverlappingCoreRegisterLow()), MEM_OP(reg_x(base), offset)); 267 } else { 268 CHECK_EQ(8u, size) << dest; 269 ___ Ldr(reg_x(dest.AsCoreRegister()), MEM_OP(reg_x(base), offset)); 270 } 271 } else if (dest.IsSRegister()) { 272 ___ Ldr(reg_s(dest.AsSRegister()), MEM_OP(reg_x(base), offset)); 273 } else { 274 CHECK(dest.IsDRegister()) << dest; 275 ___ Ldr(reg_d(dest.AsDRegister()), MEM_OP(reg_x(base), offset)); 276 } 277 } 278 279 void Arm64Assembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) { 280 return Load(m_dst.AsArm64(), SP, src.Int32Value(), size); 281 } 282 283 void Arm64Assembler::LoadFromThread64(ManagedRegister m_dst, ThreadOffset<8> src, size_t size) { 284 return Load(m_dst.AsArm64(), ETR, src.Int32Value(), size); 285 } 286 287 void Arm64Assembler::LoadRef(ManagedRegister m_dst, FrameOffset offs) { 288 Arm64ManagedRegister dst = m_dst.AsArm64(); 289 CHECK(dst.IsCoreRegister()) << dst; 290 LoadWFromOffset(kLoadWord, dst.AsOverlappingCoreRegisterLow(), SP, offs.Int32Value()); 291 } 292 293 void Arm64Assembler::LoadRef(ManagedRegister m_dst, ManagedRegister m_base, 294 MemberOffset offs) { 295 Arm64ManagedRegister dst = m_dst.AsArm64(); 296 Arm64ManagedRegister base = m_base.AsArm64(); 297 CHECK(dst.IsCoreRegister() && base.IsCoreRegister()); 298 LoadWFromOffset(kLoadWord, dst.AsOverlappingCoreRegisterLow(), base.AsCoreRegister(), 299 offs.Int32Value()); 300 } 301 302 void Arm64Assembler::LoadRawPtr(ManagedRegister m_dst, ManagedRegister m_base, Offset offs) { 303 Arm64ManagedRegister dst = m_dst.AsArm64(); 304 Arm64ManagedRegister base = m_base.AsArm64(); 305 CHECK(dst.IsCoreRegister() && base.IsCoreRegister()); 306 // Remove dst and base form the temp list - higher level API uses IP1, IP0. 307 vixl::UseScratchRegisterScope temps(vixl_masm_); 308 temps.Exclude(reg_x(dst.AsCoreRegister()), reg_x(base.AsCoreRegister())); 309 ___ Ldr(reg_x(dst.AsCoreRegister()), MEM_OP(reg_x(base.AsCoreRegister()), offs.Int32Value())); 310 } 311 312 void Arm64Assembler::LoadRawPtrFromThread64(ManagedRegister m_dst, ThreadOffset<8> offs) { 313 Arm64ManagedRegister dst = m_dst.AsArm64(); 314 CHECK(dst.IsCoreRegister()) << dst; 315 LoadFromOffset(dst.AsCoreRegister(), ETR, offs.Int32Value()); 316 } 317 318 // Copying routines. 319 void Arm64Assembler::Move(ManagedRegister m_dst, ManagedRegister m_src, size_t size) { 320 Arm64ManagedRegister dst = m_dst.AsArm64(); 321 Arm64ManagedRegister src = m_src.AsArm64(); 322 if (!dst.Equals(src)) { 323 if (dst.IsCoreRegister()) { 324 if (size == 4) { 325 CHECK(src.IsWRegister()); 326 ___ Mov(reg_x(dst.AsCoreRegister()), reg_w(src.AsWRegister())); 327 } else { 328 if (src.IsCoreRegister()) { 329 ___ Mov(reg_x(dst.AsCoreRegister()), reg_x(src.AsCoreRegister())); 330 } else { 331 ___ Mov(reg_x(dst.AsCoreRegister()), reg_w(src.AsWRegister())); 332 } 333 } 334 } else if (dst.IsWRegister()) { 335 CHECK(src.IsWRegister()) << src; 336 ___ Mov(reg_w(dst.AsWRegister()), reg_w(src.AsWRegister())); 337 } else if (dst.IsSRegister()) { 338 CHECK(src.IsSRegister()) << src; 339 ___ Fmov(reg_s(dst.AsSRegister()), reg_s(src.AsSRegister())); 340 } else { 341 CHECK(dst.IsDRegister()) << dst; 342 CHECK(src.IsDRegister()) << src; 343 ___ Fmov(reg_d(dst.AsDRegister()), reg_d(src.AsDRegister())); 344 } 345 } 346 } 347 348 void Arm64Assembler::CopyRawPtrFromThread64(FrameOffset fr_offs, 349 ThreadOffset<8> tr_offs, 350 ManagedRegister m_scratch) { 351 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 352 CHECK(scratch.IsCoreRegister()) << scratch; 353 LoadFromOffset(scratch.AsCoreRegister(), ETR, tr_offs.Int32Value()); 354 StoreToOffset(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); 355 } 356 357 void Arm64Assembler::CopyRawPtrToThread64(ThreadOffset<8> tr_offs, 358 FrameOffset fr_offs, 359 ManagedRegister m_scratch) { 360 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 361 CHECK(scratch.IsCoreRegister()) << scratch; 362 LoadFromOffset(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); 363 StoreToOffset(scratch.AsCoreRegister(), ETR, tr_offs.Int32Value()); 364 } 365 366 void Arm64Assembler::CopyRef(FrameOffset dest, FrameOffset src, 367 ManagedRegister m_scratch) { 368 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 369 CHECK(scratch.IsCoreRegister()) << scratch; 370 LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), 371 SP, src.Int32Value()); 372 StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), 373 SP, dest.Int32Value()); 374 } 375 376 void Arm64Assembler::Copy(FrameOffset dest, FrameOffset src, 377 ManagedRegister m_scratch, size_t size) { 378 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 379 CHECK(scratch.IsCoreRegister()) << scratch; 380 CHECK(size == 4 || size == 8) << size; 381 if (size == 4) { 382 LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), SP, src.Int32Value()); 383 StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), SP, dest.Int32Value()); 384 } else if (size == 8) { 385 LoadFromOffset(scratch.AsCoreRegister(), SP, src.Int32Value()); 386 StoreToOffset(scratch.AsCoreRegister(), SP, dest.Int32Value()); 387 } else { 388 UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8"; 389 } 390 } 391 392 void Arm64Assembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, 393 ManagedRegister m_scratch, size_t size) { 394 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 395 Arm64ManagedRegister base = src_base.AsArm64(); 396 CHECK(base.IsCoreRegister()) << base; 397 CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch; 398 CHECK(size == 4 || size == 8) << size; 399 if (size == 4) { 400 LoadWFromOffset(kLoadWord, scratch.AsWRegister(), base.AsCoreRegister(), 401 src_offset.Int32Value()); 402 StoreWToOffset(kStoreWord, scratch.AsWRegister(), SP, dest.Int32Value()); 403 } else if (size == 8) { 404 LoadFromOffset(scratch.AsCoreRegister(), base.AsCoreRegister(), src_offset.Int32Value()); 405 StoreToOffset(scratch.AsCoreRegister(), SP, dest.Int32Value()); 406 } else { 407 UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8"; 408 } 409 } 410 411 void Arm64Assembler::Copy(ManagedRegister m_dest_base, Offset dest_offs, FrameOffset src, 412 ManagedRegister m_scratch, size_t size) { 413 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 414 Arm64ManagedRegister base = m_dest_base.AsArm64(); 415 CHECK(base.IsCoreRegister()) << base; 416 CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch; 417 CHECK(size == 4 || size == 8) << size; 418 if (size == 4) { 419 LoadWFromOffset(kLoadWord, scratch.AsWRegister(), SP, src.Int32Value()); 420 StoreWToOffset(kStoreWord, scratch.AsWRegister(), base.AsCoreRegister(), 421 dest_offs.Int32Value()); 422 } else if (size == 8) { 423 LoadFromOffset(scratch.AsCoreRegister(), SP, src.Int32Value()); 424 StoreToOffset(scratch.AsCoreRegister(), base.AsCoreRegister(), dest_offs.Int32Value()); 425 } else { 426 UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8"; 427 } 428 } 429 430 void Arm64Assembler::Copy(FrameOffset /*dst*/, FrameOffset /*src_base*/, Offset /*src_offset*/, 431 ManagedRegister /*mscratch*/, size_t /*size*/) { 432 UNIMPLEMENTED(FATAL) << "Unimplemented Copy() variant"; 433 } 434 435 void Arm64Assembler::Copy(ManagedRegister m_dest, Offset dest_offset, 436 ManagedRegister m_src, Offset src_offset, 437 ManagedRegister m_scratch, size_t size) { 438 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 439 Arm64ManagedRegister src = m_src.AsArm64(); 440 Arm64ManagedRegister dest = m_dest.AsArm64(); 441 CHECK(dest.IsCoreRegister()) << dest; 442 CHECK(src.IsCoreRegister()) << src; 443 CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch; 444 CHECK(size == 4 || size == 8) << size; 445 if (size == 4) { 446 if (scratch.IsWRegister()) { 447 LoadWFromOffset(kLoadWord, scratch.AsWRegister(), src.AsCoreRegister(), 448 src_offset.Int32Value()); 449 StoreWToOffset(kStoreWord, scratch.AsWRegister(), dest.AsCoreRegister(), 450 dest_offset.Int32Value()); 451 } else { 452 LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), src.AsCoreRegister(), 453 src_offset.Int32Value()); 454 StoreWToOffset(kStoreWord, scratch.AsOverlappingCoreRegisterLow(), dest.AsCoreRegister(), 455 dest_offset.Int32Value()); 456 } 457 } else if (size == 8) { 458 LoadFromOffset(scratch.AsCoreRegister(), src.AsCoreRegister(), src_offset.Int32Value()); 459 StoreToOffset(scratch.AsCoreRegister(), dest.AsCoreRegister(), dest_offset.Int32Value()); 460 } else { 461 UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8"; 462 } 463 } 464 465 void Arm64Assembler::Copy(FrameOffset /*dst*/, Offset /*dest_offset*/, 466 FrameOffset /*src*/, Offset /*src_offset*/, 467 ManagedRegister /*scratch*/, size_t /*size*/) { 468 UNIMPLEMENTED(FATAL) << "Unimplemented Copy() variant"; 469 } 470 471 void Arm64Assembler::MemoryBarrier(ManagedRegister m_scratch) { 472 // TODO: Should we check that m_scratch is IP? - see arm. 473 #if ANDROID_SMP != 0 474 ___ Dmb(vixl::InnerShareable, vixl::BarrierAll); 475 #endif 476 } 477 478 void Arm64Assembler::SignExtend(ManagedRegister mreg, size_t size) { 479 Arm64ManagedRegister reg = mreg.AsArm64(); 480 CHECK(size == 1 || size == 2) << size; 481 CHECK(reg.IsWRegister()) << reg; 482 if (size == 1) { 483 ___ sxtb(reg_w(reg.AsWRegister()), reg_w(reg.AsWRegister())); 484 } else { 485 ___ sxth(reg_w(reg.AsWRegister()), reg_w(reg.AsWRegister())); 486 } 487 } 488 489 void Arm64Assembler::ZeroExtend(ManagedRegister mreg, size_t size) { 490 Arm64ManagedRegister reg = mreg.AsArm64(); 491 CHECK(size == 1 || size == 2) << size; 492 CHECK(reg.IsWRegister()) << reg; 493 if (size == 1) { 494 ___ uxtb(reg_w(reg.AsWRegister()), reg_w(reg.AsWRegister())); 495 } else { 496 ___ uxth(reg_w(reg.AsWRegister()), reg_w(reg.AsWRegister())); 497 } 498 } 499 500 void Arm64Assembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) { 501 // TODO: not validating references. 502 } 503 504 void Arm64Assembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) { 505 // TODO: not validating references. 506 } 507 508 void Arm64Assembler::Call(ManagedRegister m_base, Offset offs, ManagedRegister m_scratch) { 509 Arm64ManagedRegister base = m_base.AsArm64(); 510 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 511 CHECK(base.IsCoreRegister()) << base; 512 CHECK(scratch.IsCoreRegister()) << scratch; 513 LoadFromOffset(scratch.AsCoreRegister(), base.AsCoreRegister(), offs.Int32Value()); 514 ___ Blr(reg_x(scratch.AsCoreRegister())); 515 } 516 517 void Arm64Assembler::JumpTo(ManagedRegister m_base, Offset offs, ManagedRegister m_scratch) { 518 Arm64ManagedRegister base = m_base.AsArm64(); 519 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 520 CHECK(base.IsCoreRegister()) << base; 521 CHECK(scratch.IsCoreRegister()) << scratch; 522 // Remove base and scratch form the temp list - higher level API uses IP1, IP0. 523 vixl::UseScratchRegisterScope temps(vixl_masm_); 524 temps.Exclude(reg_x(base.AsCoreRegister()), reg_x(scratch.AsCoreRegister())); 525 ___ Ldr(reg_x(scratch.AsCoreRegister()), MEM_OP(reg_x(base.AsCoreRegister()), offs.Int32Value())); 526 ___ Br(reg_x(scratch.AsCoreRegister())); 527 } 528 529 void Arm64Assembler::Call(FrameOffset base, Offset offs, ManagedRegister m_scratch) { 530 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 531 CHECK(scratch.IsCoreRegister()) << scratch; 532 // Call *(*(SP + base) + offset) 533 LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), SP, base.Int32Value()); 534 LoadFromOffset(scratch.AsCoreRegister(), scratch.AsCoreRegister(), offs.Int32Value()); 535 ___ Blr(reg_x(scratch.AsCoreRegister())); 536 } 537 538 void Arm64Assembler::CallFromThread64(ThreadOffset<8> /*offset*/, ManagedRegister /*scratch*/) { 539 UNIMPLEMENTED(FATAL) << "Unimplemented Call() variant"; 540 } 541 542 void Arm64Assembler::CreateHandleScopeEntry(ManagedRegister m_out_reg, FrameOffset handle_scope_offs, 543 ManagedRegister m_in_reg, bool null_allowed) { 544 Arm64ManagedRegister out_reg = m_out_reg.AsArm64(); 545 Arm64ManagedRegister in_reg = m_in_reg.AsArm64(); 546 // For now we only hold stale handle scope entries in x registers. 547 CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg; 548 CHECK(out_reg.IsCoreRegister()) << out_reg; 549 if (null_allowed) { 550 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is 551 // the address in the handle scope holding the reference. 552 // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset) 553 if (in_reg.IsNoRegister()) { 554 LoadWFromOffset(kLoadWord, out_reg.AsOverlappingCoreRegisterLow(), SP, 555 handle_scope_offs.Int32Value()); 556 in_reg = out_reg; 557 } 558 ___ Cmp(reg_w(in_reg.AsOverlappingCoreRegisterLow()), 0); 559 if (!out_reg.Equals(in_reg)) { 560 LoadImmediate(out_reg.AsCoreRegister(), 0, EQ); 561 } 562 AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), NE); 563 } else { 564 AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), AL); 565 } 566 } 567 568 void Arm64Assembler::CreateHandleScopeEntry(FrameOffset out_off, FrameOffset handle_scope_offset, 569 ManagedRegister m_scratch, bool null_allowed) { 570 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 571 CHECK(scratch.IsCoreRegister()) << scratch; 572 if (null_allowed) { 573 LoadWFromOffset(kLoadWord, scratch.AsOverlappingCoreRegisterLow(), SP, 574 handle_scope_offset.Int32Value()); 575 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is 576 // the address in the handle scope holding the reference. 577 // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset) 578 ___ Cmp(reg_w(scratch.AsOverlappingCoreRegisterLow()), 0); 579 // Move this logic in add constants with flags. 580 AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), NE); 581 } else { 582 AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), AL); 583 } 584 StoreToOffset(scratch.AsCoreRegister(), SP, out_off.Int32Value()); 585 } 586 587 void Arm64Assembler::LoadReferenceFromHandleScope(ManagedRegister m_out_reg, 588 ManagedRegister m_in_reg) { 589 Arm64ManagedRegister out_reg = m_out_reg.AsArm64(); 590 Arm64ManagedRegister in_reg = m_in_reg.AsArm64(); 591 CHECK(out_reg.IsCoreRegister()) << out_reg; 592 CHECK(in_reg.IsCoreRegister()) << in_reg; 593 vixl::Label exit; 594 if (!out_reg.Equals(in_reg)) { 595 // FIXME: Who sets the flags here? 596 LoadImmediate(out_reg.AsCoreRegister(), 0, EQ); 597 } 598 ___ Cbz(reg_x(in_reg.AsCoreRegister()), &exit); 599 LoadFromOffset(out_reg.AsCoreRegister(), in_reg.AsCoreRegister(), 0); 600 ___ Bind(&exit); 601 } 602 603 void Arm64Assembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjust) { 604 CHECK_ALIGNED(stack_adjust, kStackAlignment); 605 Arm64ManagedRegister scratch = m_scratch.AsArm64(); 606 Arm64Exception *current_exception = new Arm64Exception(scratch, stack_adjust); 607 exception_blocks_.push_back(current_exception); 608 LoadFromOffset(scratch.AsCoreRegister(), ETR, Thread::ExceptionOffset<8>().Int32Value()); 609 ___ Cbnz(reg_x(scratch.AsCoreRegister()), current_exception->Entry()); 610 } 611 612 void Arm64Assembler::EmitExceptionPoll(Arm64Exception *exception) { 613 vixl::UseScratchRegisterScope temps(vixl_masm_); 614 temps.Exclude(reg_x(exception->scratch_.AsCoreRegister())); 615 vixl::Register temp = temps.AcquireX(); 616 617 // Bind exception poll entry. 618 ___ Bind(exception->Entry()); 619 if (exception->stack_adjust_ != 0) { // Fix up the frame. 620 DecreaseFrameSize(exception->stack_adjust_); 621 } 622 // Pass exception object as argument. 623 // Don't care about preserving X0 as this won't return. 624 ___ Mov(reg_x(X0), reg_x(exception->scratch_.AsCoreRegister())); 625 ___ Ldr(temp, MEM_OP(reg_x(ETR), QUICK_ENTRYPOINT_OFFSET(8, pDeliverException).Int32Value())); 626 627 // Move ETR(Callee saved) back to TR(Caller saved) reg. We use ETR on calls 628 // to external functions that might trash TR. We do not need the original 629 // ETR(X21) saved in BuildFrame(). 630 ___ Mov(reg_x(TR), reg_x(ETR)); 631 632 ___ Blr(temp); 633 // Call should never return. 634 ___ Brk(); 635 } 636 637 constexpr size_t kFramePointerSize = 8; 638 639 void Arm64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, 640 const std::vector<ManagedRegister>& callee_save_regs, 641 const ManagedRegisterEntrySpills& entry_spills) { 642 CHECK_ALIGNED(frame_size, kStackAlignment); 643 CHECK(X0 == method_reg.AsArm64().AsCoreRegister()); 644 645 // TODO: *create APCS FP - end of FP chain; 646 // *add support for saving a different set of callee regs. 647 // For now we check that the size of callee regs vector is 11. 648 CHECK_EQ(callee_save_regs.size(), kJniRefSpillRegsSize); 649 // Increase frame to required size - must be at least space to push StackReference<Method>. 650 CHECK_GT(frame_size, kJniRefSpillRegsSize * kFramePointerSize); 651 IncreaseFrameSize(frame_size); 652 653 // TODO: Ugly hard code... 654 // Should generate these according to the spill mask automatically. 655 // TUNING: Use stp. 656 // Note: Must match Arm64JniCallingConvention::CoreSpillMask(). 657 size_t reg_offset = frame_size; 658 reg_offset -= 8; 659 StoreToOffset(LR, SP, reg_offset); 660 reg_offset -= 8; 661 StoreToOffset(X29, SP, reg_offset); 662 reg_offset -= 8; 663 StoreToOffset(X28, SP, reg_offset); 664 reg_offset -= 8; 665 StoreToOffset(X27, SP, reg_offset); 666 reg_offset -= 8; 667 StoreToOffset(X26, SP, reg_offset); 668 reg_offset -= 8; 669 StoreToOffset(X25, SP, reg_offset); 670 reg_offset -= 8; 671 StoreToOffset(X24, SP, reg_offset); 672 reg_offset -= 8; 673 StoreToOffset(X23, SP, reg_offset); 674 reg_offset -= 8; 675 StoreToOffset(X22, SP, reg_offset); 676 reg_offset -= 8; 677 StoreToOffset(X21, SP, reg_offset); 678 reg_offset -= 8; 679 StoreToOffset(X20, SP, reg_offset); 680 681 // Move TR(Caller saved) to ETR(Callee saved). The original (ETR)X21 has been saved on stack. 682 // This way we make sure that TR is not trashed by native code. 683 ___ Mov(reg_x(ETR), reg_x(TR)); 684 685 // Write StackReference<Method>. 686 DCHECK_EQ(4U, sizeof(StackReference<mirror::ArtMethod>)); 687 StoreWToOffset(StoreOperandType::kStoreWord, W0, SP, 0); 688 689 // Write out entry spills 690 int32_t offset = frame_size + sizeof(StackReference<mirror::ArtMethod>); 691 for (size_t i = 0; i < entry_spills.size(); ++i) { 692 Arm64ManagedRegister reg = entry_spills.at(i).AsArm64(); 693 if (reg.IsNoRegister()) { 694 // only increment stack offset. 695 ManagedRegisterSpill spill = entry_spills.at(i); 696 offset += spill.getSize(); 697 } else if (reg.IsCoreRegister()) { 698 StoreToOffset(reg.AsCoreRegister(), SP, offset); 699 offset += 8; 700 } else if (reg.IsWRegister()) { 701 StoreWToOffset(kStoreWord, reg.AsWRegister(), SP, offset); 702 offset += 4; 703 } else if (reg.IsDRegister()) { 704 StoreDToOffset(reg.AsDRegister(), SP, offset); 705 offset += 8; 706 } else if (reg.IsSRegister()) { 707 StoreSToOffset(reg.AsSRegister(), SP, offset); 708 offset += 4; 709 } 710 } 711 } 712 713 void Arm64Assembler::RemoveFrame(size_t frame_size, const std::vector<ManagedRegister>& callee_save_regs) { 714 CHECK_ALIGNED(frame_size, kStackAlignment); 715 716 // For now we only check that the size of the frame is greater than the spill size. 717 CHECK_EQ(callee_save_regs.size(), kJniRefSpillRegsSize); 718 CHECK_GT(frame_size, kJniRefSpillRegsSize * kFramePointerSize); 719 720 // We move ETR(aapcs64 callee saved) back to TR(aapcs64 caller saved) which might have 721 // been trashed in the native call. The original ETR(X21) is restored from stack. 722 ___ Mov(reg_x(TR), reg_x(ETR)); 723 724 // TODO: Ugly hard code... 725 // Should generate these according to the spill mask automatically. 726 // TUNING: Use ldp. 727 // Note: Must match Arm64JniCallingConvention::CoreSpillMask(). 728 size_t reg_offset = frame_size; 729 reg_offset -= 8; 730 LoadFromOffset(LR, SP, reg_offset); 731 reg_offset -= 8; 732 LoadFromOffset(X29, SP, reg_offset); 733 reg_offset -= 8; 734 LoadFromOffset(X28, SP, reg_offset); 735 reg_offset -= 8; 736 LoadFromOffset(X27, SP, reg_offset); 737 reg_offset -= 8; 738 LoadFromOffset(X26, SP, reg_offset); 739 reg_offset -= 8; 740 LoadFromOffset(X25, SP, reg_offset); 741 reg_offset -= 8; 742 LoadFromOffset(X24, SP, reg_offset); 743 reg_offset -= 8; 744 LoadFromOffset(X23, SP, reg_offset); 745 reg_offset -= 8; 746 LoadFromOffset(X22, SP, reg_offset); 747 reg_offset -= 8; 748 LoadFromOffset(X21, SP, reg_offset); 749 reg_offset -= 8; 750 LoadFromOffset(X20, SP, reg_offset); 751 752 // Decrease frame size to start of callee saved regs. 753 DecreaseFrameSize(frame_size); 754 755 // Pop callee saved and return to LR. 756 ___ Ret(); 757 } 758 759 } // namespace arm64 760 } // namespace art 761