CodeGenerator-mips64.cpp (13784B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "jit/mips64/CodeGenerator-mips64.h" 8 9 #include "jit/CodeGenerator.h" 10 #include "jit/MIR-wasm.h" 11 #include "jit/MIR.h" 12 #include "jit/MIRGraph.h" 13 #include "js/Conversions.h" 14 #include "vm/Shape.h" 15 16 #include "jit/MacroAssembler-inl.h" 17 #include "jit/shared/CodeGenerator-shared-inl.h" 18 19 using namespace js; 20 using namespace js::jit; 21 22 void CodeGenerator::visitBox(LBox* box) { 23 const LAllocation* in = box->payload(); 24 ValueOperand result = ToOutValue(box); 25 26 masm.moveValue(TypedOrValueRegister(box->type(), ToAnyRegister(in)), result); 27 } 28 29 void CodeGenerator::visitUnbox(LUnbox* unbox) { 30 MUnbox* mir = unbox->mir(); 31 32 Register result = ToRegister(unbox->output()); 33 34 if (mir->fallible()) { 35 ValueOperand value = ToValue(unbox->input()); 36 Label bail; 37 switch (mir->type()) { 38 case MIRType::Int32: 39 masm.fallibleUnboxInt32(value, result, &bail); 40 break; 41 case MIRType::Boolean: 42 masm.fallibleUnboxBoolean(value, result, &bail); 43 break; 44 case MIRType::Object: 45 masm.fallibleUnboxObject(value, result, &bail); 46 break; 47 case MIRType::String: 48 masm.fallibleUnboxString(value, result, &bail); 49 break; 50 case MIRType::Symbol: 51 masm.fallibleUnboxSymbol(value, result, &bail); 52 break; 53 case MIRType::BigInt: 54 masm.fallibleUnboxBigInt(value, result, &bail); 55 break; 56 default: 57 MOZ_CRASH("Given MIRType cannot be unboxed."); 58 } 59 bailoutFrom(&bail, unbox->snapshot()); 60 return; 61 } 62 63 LAllocation* input = unbox->getOperand(LUnbox::Input); 64 if (input->isGeneralReg()) { 65 Register inputReg = ToRegister(input); 66 switch (mir->type()) { 67 case MIRType::Int32: 68 masm.unboxInt32(inputReg, result); 69 break; 70 case MIRType::Boolean: 71 masm.unboxBoolean(inputReg, result); 72 break; 73 case MIRType::Object: 74 masm.unboxObject(inputReg, result); 75 break; 76 case MIRType::String: 77 masm.unboxString(inputReg, result); 78 break; 79 case MIRType::Symbol: 80 masm.unboxSymbol(inputReg, result); 81 break; 82 case MIRType::BigInt: 83 masm.unboxBigInt(inputReg, result); 84 break; 85 default: 86 MOZ_CRASH("Given MIRType cannot be unboxed."); 87 } 88 return; 89 } 90 91 Address inputAddr = ToAddress(input); 92 switch (mir->type()) { 93 case MIRType::Int32: 94 masm.unboxInt32(inputAddr, result); 95 break; 96 case MIRType::Boolean: 97 masm.unboxBoolean(inputAddr, result); 98 break; 99 case MIRType::Object: 100 masm.unboxObject(inputAddr, result); 101 break; 102 case MIRType::String: 103 masm.unboxString(inputAddr, result); 104 break; 105 case MIRType::Symbol: 106 masm.unboxSymbol(inputAddr, result); 107 break; 108 case MIRType::BigInt: 109 masm.unboxBigInt(inputAddr, result); 110 break; 111 default: 112 MOZ_CRASH("Given MIRType cannot be unboxed."); 113 } 114 } 115 116 void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) { 117 Register lhs = ToRegister(lir->lhs()); 118 Register rhs = ToRegister(lir->rhs()); 119 Register output = ToRegister(lir->output()); 120 121 Label done; 122 123 // Handle divide by zero. 124 if (lir->canBeDivideByZero()) { 125 Label nonZero; 126 masm.ma_b(rhs, rhs, &nonZero, Assembler::NonZero); 127 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, lir->trapSiteDesc()); 128 masm.bind(&nonZero); 129 } 130 131 // Handle an integer overflow exception from INT64_MIN / -1. 132 if (lir->canBeNegativeOverflow()) { 133 Label notOverflow; 134 masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(INT64_MIN), ¬Overflow); 135 masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), ¬Overflow); 136 if (lir->mir()->isMod()) { 137 masm.ma_xor(output, output); 138 } else { 139 masm.wasmTrap(wasm::Trap::IntegerOverflow, lir->trapSiteDesc()); 140 } 141 masm.jump(&done); 142 masm.bind(¬Overflow); 143 } 144 145 #ifdef MIPSR6 146 if (lir->mir()->isMod()) { 147 masm.as_dmod(output, lhs, rhs); 148 } else { 149 masm.as_ddiv(output, lhs, rhs); 150 } 151 #else 152 masm.as_ddiv(lhs, rhs); 153 if (lir->mir()->isMod()) { 154 masm.as_mfhi(output); 155 } else { 156 masm.as_mflo(output); 157 } 158 #endif 159 masm.bind(&done); 160 } 161 162 void CodeGenerator::visitUDivOrModI64(LUDivOrModI64* lir) { 163 Register lhs = ToRegister(lir->lhs()); 164 Register rhs = ToRegister(lir->rhs()); 165 Register output = ToRegister(lir->output()); 166 167 Label done; 168 169 // Prevent divide by zero. 170 if (lir->canBeDivideByZero()) { 171 Label nonZero; 172 masm.ma_b(rhs, rhs, &nonZero, Assembler::NonZero); 173 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, lir->trapSiteDesc()); 174 masm.bind(&nonZero); 175 } 176 177 #ifdef MIPSR6 178 if (lir->mir()->isMod()) { 179 masm.as_dmodu(output, lhs, rhs); 180 } else { 181 masm.as_ddivu(output, lhs, rhs); 182 } 183 #else 184 masm.as_ddivu(lhs, rhs); 185 if (lir->mir()->isMod()) { 186 masm.as_mfhi(output); 187 } else { 188 masm.as_mflo(output); 189 } 190 #endif 191 masm.bind(&done); 192 } 193 194 template <typename T> 195 void CodeGeneratorMIPS64::emitWasmLoadI64(T* lir) { 196 const MWasmLoad* mir = lir->mir(); 197 198 Register memoryBase = ToRegister(lir->memoryBase()); 199 Register ptrScratch = ToTempRegisterOrInvalid(lir->temp0()); 200 201 Register ptrReg = ToRegister(lir->ptr()); 202 if (mir->base()->type() == MIRType::Int32) { 203 // See comment in visitWasmLoad re the type of 'base'. 204 masm.move32ZeroExtendToPtr(ptrReg, ptrReg); 205 } 206 207 if constexpr (std::is_same_v<T, LWasmUnalignedLoadI64>) { 208 MOZ_ASSERT(IsUnaligned(mir->access())); 209 masm.wasmUnalignedLoadI64(mir->access(), memoryBase, ptrReg, ptrScratch, 210 ToOutRegister64(lir), ToRegister(lir->temp1())); 211 } else { 212 MOZ_ASSERT(!IsUnaligned(mir->access())); 213 masm.wasmLoadI64(mir->access(), memoryBase, ptrReg, ptrScratch, 214 ToOutRegister64(lir)); 215 } 216 } 217 218 void CodeGenerator::visitWasmLoadI64(LWasmLoadI64* lir) { 219 emitWasmLoadI64(lir); 220 } 221 222 void CodeGenerator::visitWasmUnalignedLoadI64(LWasmUnalignedLoadI64* lir) { 223 emitWasmLoadI64(lir); 224 } 225 226 template <typename T> 227 void CodeGeneratorMIPS64::emitWasmStoreI64(T* lir) { 228 const MWasmStore* mir = lir->mir(); 229 230 Register memoryBase = ToRegister(lir->memoryBase()); 231 Register ptrScratch = ToTempRegisterOrInvalid(lir->temp0()); 232 233 Register ptrReg = ToRegister(lir->ptr()); 234 if (mir->base()->type() == MIRType::Int32) { 235 // See comment in visitWasmLoad re the type of 'base'. 236 masm.move32ZeroExtendToPtr(ptrReg, ptrReg); 237 } 238 239 if constexpr (std::is_same_v<T, LWasmUnalignedStoreI64>) { 240 MOZ_ASSERT(IsUnaligned(mir->access())); 241 masm.wasmUnalignedStoreI64(mir->access(), ToRegister64(lir->value()), 242 memoryBase, ptrReg, ptrScratch, 243 ToRegister(lir->temp1())); 244 } else { 245 MOZ_ASSERT(!IsUnaligned(mir->access())); 246 masm.wasmStoreI64(mir->access(), ToRegister64(lir->value()), memoryBase, 247 ptrReg, ptrScratch); 248 } 249 } 250 251 void CodeGenerator::visitWasmStoreI64(LWasmStoreI64* lir) { 252 emitWasmStoreI64(lir); 253 } 254 255 void CodeGenerator::visitWasmUnalignedStoreI64(LWasmUnalignedStoreI64* lir) { 256 emitWasmStoreI64(lir); 257 } 258 259 void CodeGenerator::visitWasmSelectI64(LWasmSelectI64* lir) { 260 MOZ_ASSERT(lir->mir()->type() == MIRType::Int64); 261 262 Register cond = ToRegister(lir->condExpr()); 263 LInt64Allocation falseExpr = lir->falseExpr(); 264 265 Register64 out = ToOutRegister64(lir); 266 MOZ_ASSERT(ToRegister64(lir->trueExpr()) == out, 267 "true expr is reused for input"); 268 269 if (falseExpr.value().isGeneralReg()) { 270 masm.as_movz(out.reg, ToRegister(falseExpr.value()), cond); 271 } else { 272 Label done; 273 masm.ma_b(cond, cond, &done, Assembler::NonZero, ShortJump); 274 masm.loadPtr(ToAddress(falseExpr.value()), out.reg); 275 masm.bind(&done); 276 } 277 } 278 279 void CodeGenerator::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir) { 280 const LAllocation* input = lir->input(); 281 Register output = ToRegister(lir->output()); 282 283 if (lir->mir()->isUnsigned()) { 284 masm.ma_dext(output, ToRegister(input), Imm32(0), Imm32(32)); 285 } else { 286 masm.ma_sll(output, ToRegister(input), Imm32(0)); 287 } 288 } 289 290 void CodeGenerator::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir) { 291 LInt64Allocation input = lir->input(); 292 Register output = ToRegister(lir->output()); 293 294 if (lir->mir()->bottomHalf()) { 295 if (input.value().isMemory()) { 296 masm.load32(ToAddress(input), output); 297 } else { 298 masm.move64To32(ToRegister64(input), output); 299 } 300 } else { 301 MOZ_CRASH("Not implemented."); 302 } 303 } 304 305 void CodeGenerator::visitSignExtendInt64(LSignExtendInt64* lir) { 306 Register64 input = ToRegister64(lir->input()); 307 Register64 output = ToOutRegister64(lir); 308 switch (lir->mir()->mode()) { 309 case MSignExtendInt64::Byte: 310 masm.move32To64SignExtend(input.reg, output); 311 masm.move8SignExtend(output.reg, output.reg); 312 break; 313 case MSignExtendInt64::Half: 314 masm.move32To64SignExtend(input.reg, output); 315 masm.move16SignExtend(output.reg, output.reg); 316 break; 317 case MSignExtendInt64::Word: 318 masm.move32To64SignExtend(input.reg, output); 319 break; 320 } 321 } 322 323 void CodeGenerator::visitWasmExtendU32Index(LWasmExtendU32Index* lir) { 324 Register input = ToRegister(lir->input()); 325 Register output = ToRegister(lir->output()); 326 MOZ_ASSERT(input == output); 327 masm.move32To64ZeroExtend(input, Register64(output)); 328 } 329 330 void CodeGenerator::visitWasmWrapU32Index(LWasmWrapU32Index* lir) { 331 Register input = ToRegister(lir->input()); 332 Register output = ToRegister(lir->output()); 333 MOZ_ASSERT(input == output); 334 masm.move64To32(Register64(input), output); 335 } 336 337 void CodeGenerator::visitBitNotI64(LBitNotI64* ins) { 338 Register input = ToRegister64(ins->input()).reg; 339 Register output = ToOutRegister64(ins).reg; 340 masm.ma_not(output, input); 341 } 342 343 void CodeGenerator::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir) { 344 FloatRegister input = ToFloatRegister(lir->input()); 345 Register64 output = ToOutRegister64(lir); 346 347 MWasmTruncateToInt64* mir = lir->mir(); 348 MIRType fromType = mir->input()->type(); 349 350 MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32); 351 352 auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output); 353 addOutOfLineCode(ool, mir); 354 355 Label* oolEntry = ool->entry(); 356 Label* oolRejoin = ool->rejoin(); 357 bool isSaturating = mir->isSaturating(); 358 359 if (fromType == MIRType::Double) { 360 if (mir->isUnsigned()) { 361 masm.wasmTruncateDoubleToUInt64(input, output, isSaturating, oolEntry, 362 oolRejoin, InvalidFloatReg); 363 } else { 364 masm.wasmTruncateDoubleToInt64(input, output, isSaturating, oolEntry, 365 oolRejoin, InvalidFloatReg); 366 } 367 } else { 368 if (mir->isUnsigned()) { 369 masm.wasmTruncateFloat32ToUInt64(input, output, isSaturating, oolEntry, 370 oolRejoin, InvalidFloatReg); 371 } else { 372 masm.wasmTruncateFloat32ToInt64(input, output, isSaturating, oolEntry, 373 oolRejoin, InvalidFloatReg); 374 } 375 } 376 } 377 378 void CodeGenerator::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir) { 379 Register64 input = ToRegister64(lir->input()); 380 FloatRegister output = ToFloatRegister(lir->output()); 381 382 MIRType outputType = lir->mir()->type(); 383 MOZ_ASSERT(outputType == MIRType::Double || outputType == MIRType::Float32); 384 385 if (outputType == MIRType::Double) { 386 if (lir->mir()->isUnsigned()) { 387 masm.convertUInt64ToDouble(input, output, Register::Invalid()); 388 } else { 389 masm.convertInt64ToDouble(input, output); 390 } 391 } else { 392 if (lir->mir()->isUnsigned()) { 393 masm.convertUInt64ToFloat32(input, output, Register::Invalid()); 394 } else { 395 masm.convertInt64ToFloat32(input, output); 396 } 397 } 398 } 399 400 void CodeGenerator::visitAtomicLoad64(LAtomicLoad64* lir) { 401 Register elements = ToRegister(lir->elements()); 402 Register64 out = ToOutRegister64(lir); 403 404 Scalar::Type storageType = lir->mir()->storageType(); 405 406 auto source = ToAddressOrBaseIndex(elements, lir->index(), storageType); 407 408 auto sync = Synchronization::Load(); 409 masm.memoryBarrierBefore(sync); 410 source.match([&](const auto& source) { masm.load64(source, out); }); 411 masm.memoryBarrierAfter(sync); 412 } 413 414 void CodeGenerator::visitAtomicStore64(LAtomicStore64* lir) { 415 Register elements = ToRegister(lir->elements()); 416 Register64 value = ToRegister64(lir->value()); 417 418 Scalar::Type writeType = lir->mir()->writeType(); 419 420 auto dest = ToAddressOrBaseIndex(elements, lir->index(), writeType); 421 422 auto sync = Synchronization::Store(); 423 masm.memoryBarrierBefore(sync); 424 dest.match([&](const auto& dest) { masm.store64(value, dest); }); 425 masm.memoryBarrierAfter(sync); 426 } 427 428 void CodeGeneratorMIPS64::emitBigIntPtrDiv(LBigIntPtrDiv* ins, 429 Register dividend, Register divisor, 430 Register output) { 431 // Callers handle division by zero and integer overflow. 432 #ifdef MIPSR6 433 masm.as_ddiv(/* result= */ output, dividend, divisor); 434 #else 435 masm.as_ddiv(dividend, divisor); 436 masm.as_mflo(/* result= */ output); 437 #endif 438 } 439 440 void CodeGeneratorMIPS64::emitBigIntPtrMod(LBigIntPtrMod* ins, 441 Register dividend, Register divisor, 442 Register output) { 443 // Callers handle division by zero and integer overflow. 444 #ifdef MIPSR6 445 masm.as_dmod(/* result= */ output, dividend, divisor); 446 #else 447 masm.as_ddiv(dividend, divisor); 448 masm.as_mfhi(/* result= */ output); 449 #endif 450 }