js/src/jit/CodeGenerator.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "jit/CodeGenerator.h"
michael@0 8
michael@0 9 #include "mozilla/Assertions.h"
michael@0 10 #include "mozilla/Attributes.h"
michael@0 11 #include "mozilla/DebugOnly.h"
michael@0 12
michael@0 13 #include "jslibmath.h"
michael@0 14 #include "jsmath.h"
michael@0 15 #include "jsnum.h"
michael@0 16 #include "jsprf.h"
michael@0 17
michael@0 18 #include "builtin/Eval.h"
michael@0 19 #include "builtin/TypedObject.h"
michael@0 20 #ifdef JSGC_GENERATIONAL
michael@0 21 # include "gc/Nursery.h"
michael@0 22 #endif
michael@0 23 #include "jit/IonCaches.h"
michael@0 24 #include "jit/IonLinker.h"
michael@0 25 #include "jit/IonOptimizationLevels.h"
michael@0 26 #include "jit/IonSpewer.h"
michael@0 27 #include "jit/MIRGenerator.h"
michael@0 28 #include "jit/MoveEmitter.h"
michael@0 29 #include "jit/ParallelFunctions.h"
michael@0 30 #include "jit/ParallelSafetyAnalysis.h"
michael@0 31 #include "jit/RangeAnalysis.h"
michael@0 32 #include "vm/ForkJoin.h"
michael@0 33 #include "vm/TraceLogging.h"
michael@0 34
michael@0 35 #include "jsboolinlines.h"
michael@0 36
michael@0 37 #include "jit/ExecutionMode-inl.h"
michael@0 38 #include "jit/shared/CodeGenerator-shared-inl.h"
michael@0 39 #include "vm/Interpreter-inl.h"
michael@0 40
michael@0 41 using namespace js;
michael@0 42 using namespace js::jit;
michael@0 43
michael@0 44 using mozilla::DebugOnly;
michael@0 45 using mozilla::FloatingPoint;
michael@0 46 using mozilla::Maybe;
michael@0 47 using mozilla::NegativeInfinity;
michael@0 48 using mozilla::PositiveInfinity;
michael@0 49 using JS::GenericNaN;
michael@0 50
michael@0 51 namespace js {
michael@0 52 namespace jit {
michael@0 53
michael@0 54 // This out-of-line cache is used to do a double dispatch including it-self and
michael@0 55 // the wrapped IonCache.
michael@0 56 class OutOfLineUpdateCache :
michael@0 57 public OutOfLineCodeBase<CodeGenerator>,
michael@0 58 public IonCacheVisitor
michael@0 59 {
michael@0 60 private:
michael@0 61 LInstruction *lir_;
michael@0 62 size_t cacheIndex_;
michael@0 63 AddCacheState state_;
michael@0 64
michael@0 65 public:
michael@0 66 OutOfLineUpdateCache(LInstruction *lir, size_t cacheIndex)
michael@0 67 : lir_(lir),
michael@0 68 cacheIndex_(cacheIndex)
michael@0 69 { }
michael@0 70
michael@0 71 void bind(MacroAssembler *masm) {
michael@0 72 // The binding of the initial jump is done in
michael@0 73 // CodeGenerator::visitOutOfLineCache.
michael@0 74 }
michael@0 75
michael@0 76 size_t getCacheIndex() const {
michael@0 77 return cacheIndex_;
michael@0 78 }
michael@0 79 LInstruction *lir() const {
michael@0 80 return lir_;
michael@0 81 }
michael@0 82 AddCacheState &state() {
michael@0 83 return state_;
michael@0 84 }
michael@0 85
michael@0 86 bool accept(CodeGenerator *codegen) {
michael@0 87 return codegen->visitOutOfLineCache(this);
michael@0 88 }
michael@0 89
michael@0 90 // ICs' visit functions delegating the work to the CodeGen visit funtions.
michael@0 91 #define VISIT_CACHE_FUNCTION(op) \
michael@0 92 bool visit##op##IC(CodeGenerator *codegen) { \
michael@0 93 CodeGenerator::DataPtr<op##IC> ic(codegen, getCacheIndex()); \
michael@0 94 return codegen->visit##op##IC(this, ic); \
michael@0 95 }
michael@0 96
michael@0 97 IONCACHE_KIND_LIST(VISIT_CACHE_FUNCTION)
michael@0 98 #undef VISIT_CACHE_FUNCTION
michael@0 99 };
michael@0 100
michael@0 101 // This function is declared here because it needs to instantiate an
michael@0 102 // OutOfLineUpdateCache, but we want to keep it visible inside the
michael@0 103 // CodeGeneratorShared such as we can specialize inline caches in function of
michael@0 104 // the architecture.
michael@0 105 bool
michael@0 106 CodeGeneratorShared::addCache(LInstruction *lir, size_t cacheIndex)
michael@0 107 {
michael@0 108 if (cacheIndex == SIZE_MAX)
michael@0 109 return false;
michael@0 110
michael@0 111 DataPtr<IonCache> cache(this, cacheIndex);
michael@0 112 MInstruction *mir = lir->mirRaw()->toInstruction();
michael@0 113 if (mir->resumePoint())
michael@0 114 cache->setScriptedLocation(mir->block()->info().script(),
michael@0 115 mir->resumePoint()->pc());
michael@0 116 else
michael@0 117 cache->setIdempotent();
michael@0 118
michael@0 119 OutOfLineUpdateCache *ool = new(alloc()) OutOfLineUpdateCache(lir, cacheIndex);
michael@0 120 if (!addOutOfLineCode(ool))
michael@0 121 return false;
michael@0 122
michael@0 123 // OOL-specific state depends on the type of cache.
michael@0 124 cache->initializeAddCacheState(lir, &ool->state());
michael@0 125
michael@0 126 cache->emitInitialJump(masm, ool->state());
michael@0 127 masm.bind(ool->rejoin());
michael@0 128
michael@0 129 return true;
michael@0 130 }
michael@0 131
michael@0 132 bool
michael@0 133 CodeGenerator::visitOutOfLineCache(OutOfLineUpdateCache *ool)
michael@0 134 {
michael@0 135 DataPtr<IonCache> cache(this, ool->getCacheIndex());
michael@0 136
michael@0 137 // Register the location of the OOL path in the IC.
michael@0 138 cache->setFallbackLabel(masm.labelForPatch());
michael@0 139 cache->bindInitialJump(masm, ool->state());
michael@0 140
michael@0 141 // Dispatch to ICs' accept functions.
michael@0 142 return cache->accept(this, ool);
michael@0 143 }
michael@0 144
michael@0 145 StringObject *
michael@0 146 MNewStringObject::templateObj() const {
michael@0 147 return &templateObj_->as<StringObject>();
michael@0 148 }
michael@0 149
michael@0 150 CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
michael@0 151 : CodeGeneratorSpecific(gen, graph, masm)
michael@0 152 , ionScriptLabels_(gen->alloc())
michael@0 153 {
michael@0 154 }
michael@0 155
michael@0 156 CodeGenerator::~CodeGenerator()
michael@0 157 {
michael@0 158 JS_ASSERT_IF(!gen->compilingAsmJS(), masm.numAsmJSAbsoluteLinks() == 0);
michael@0 159 }
michael@0 160
michael@0 161 typedef bool (*StringToNumberFn)(ThreadSafeContext *, JSString *, double *);
michael@0 162 typedef bool (*StringToNumberParFn)(ForkJoinContext *, JSString *, double *);
michael@0 163 static const VMFunctionsModal StringToNumberInfo = VMFunctionsModal(
michael@0 164 FunctionInfo<StringToNumberFn>(StringToNumber),
michael@0 165 FunctionInfo<StringToNumberParFn>(StringToNumberPar));
michael@0 166
michael@0 167 bool
michael@0 168 CodeGenerator::visitValueToInt32(LValueToInt32 *lir)
michael@0 169 {
michael@0 170 ValueOperand operand = ToValue(lir, LValueToInt32::Input);
michael@0 171 Register output = ToRegister(lir->output());
michael@0 172 FloatRegister temp = ToFloatRegister(lir->tempFloat());
michael@0 173
michael@0 174 MDefinition *input;
michael@0 175 if (lir->mode() == LValueToInt32::NORMAL)
michael@0 176 input = lir->mirNormal()->input();
michael@0 177 else
michael@0 178 input = lir->mirTruncate()->input();
michael@0 179
michael@0 180 Label fails;
michael@0 181 if (lir->mode() == LValueToInt32::TRUNCATE) {
michael@0 182 OutOfLineCode *oolDouble = oolTruncateDouble(temp, output);
michael@0 183 if (!oolDouble)
michael@0 184 return false;
michael@0 185
michael@0 186 // We can only handle strings in truncation contexts, like bitwise
michael@0 187 // operations.
michael@0 188 Label *stringEntry, *stringRejoin;
michael@0 189 Register stringReg;
michael@0 190 if (input->mightBeType(MIRType_String)) {
michael@0 191 stringReg = ToRegister(lir->temp());
michael@0 192 OutOfLineCode *oolString = oolCallVM(StringToNumberInfo, lir, (ArgList(), stringReg),
michael@0 193 StoreFloatRegisterTo(temp));
michael@0 194 if (!oolString)
michael@0 195 return false;
michael@0 196 stringEntry = oolString->entry();
michael@0 197 stringRejoin = oolString->rejoin();
michael@0 198 } else {
michael@0 199 stringReg = InvalidReg;
michael@0 200 stringEntry = nullptr;
michael@0 201 stringRejoin = nullptr;
michael@0 202 }
michael@0 203
michael@0 204 masm.truncateValueToInt32(operand, input, stringEntry, stringRejoin, oolDouble->entry(),
michael@0 205 stringReg, temp, output, &fails);
michael@0 206 masm.bind(oolDouble->rejoin());
michael@0 207 } else {
michael@0 208 masm.convertValueToInt32(operand, input, temp, output, &fails,
michael@0 209 lir->mirNormal()->canBeNegativeZero(),
michael@0 210 lir->mirNormal()->conversion());
michael@0 211 }
michael@0 212
michael@0 213 return bailoutFrom(&fails, lir->snapshot());
michael@0 214 }
michael@0 215
michael@0 216 bool
michael@0 217 CodeGenerator::visitValueToDouble(LValueToDouble *lir)
michael@0 218 {
michael@0 219 MToDouble *mir = lir->mir();
michael@0 220 ValueOperand operand = ToValue(lir, LValueToDouble::Input);
michael@0 221 FloatRegister output = ToFloatRegister(lir->output());
michael@0 222
michael@0 223 Register tag = masm.splitTagForTest(operand);
michael@0 224
michael@0 225 Label isDouble, isInt32, isBool, isNull, isUndefined, done;
michael@0 226 bool hasBoolean = false, hasNull = false, hasUndefined = false;
michael@0 227
michael@0 228 masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
michael@0 229 masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
michael@0 230
michael@0 231 if (mir->conversion() != MToDouble::NumbersOnly) {
michael@0 232 masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
michael@0 233 masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
michael@0 234 hasBoolean = true;
michael@0 235 hasUndefined = true;
michael@0 236 if (mir->conversion() != MToDouble::NonNullNonStringPrimitives) {
michael@0 237 masm.branchTestNull(Assembler::Equal, tag, &isNull);
michael@0 238 hasNull = true;
michael@0 239 }
michael@0 240 }
michael@0 241
michael@0 242 if (!bailout(lir->snapshot()))
michael@0 243 return false;
michael@0 244
michael@0 245 if (hasNull) {
michael@0 246 masm.bind(&isNull);
michael@0 247 masm.loadConstantDouble(0.0, output);
michael@0 248 masm.jump(&done);
michael@0 249 }
michael@0 250
michael@0 251 if (hasUndefined) {
michael@0 252 masm.bind(&isUndefined);
michael@0 253 masm.loadConstantDouble(GenericNaN(), output);
michael@0 254 masm.jump(&done);
michael@0 255 }
michael@0 256
michael@0 257 if (hasBoolean) {
michael@0 258 masm.bind(&isBool);
michael@0 259 masm.boolValueToDouble(operand, output);
michael@0 260 masm.jump(&done);
michael@0 261 }
michael@0 262
michael@0 263 masm.bind(&isInt32);
michael@0 264 masm.int32ValueToDouble(operand, output);
michael@0 265 masm.jump(&done);
michael@0 266
michael@0 267 masm.bind(&isDouble);
michael@0 268 masm.unboxDouble(operand, output);
michael@0 269 masm.bind(&done);
michael@0 270
michael@0 271 return true;
michael@0 272 }
michael@0 273
michael@0 274 bool
michael@0 275 CodeGenerator::visitValueToFloat32(LValueToFloat32 *lir)
michael@0 276 {
michael@0 277 MToFloat32 *mir = lir->mir();
michael@0 278 ValueOperand operand = ToValue(lir, LValueToFloat32::Input);
michael@0 279 FloatRegister output = ToFloatRegister(lir->output());
michael@0 280
michael@0 281 Register tag = masm.splitTagForTest(operand);
michael@0 282
michael@0 283 Label isDouble, isInt32, isBool, isNull, isUndefined, done;
michael@0 284 bool hasBoolean = false, hasNull = false, hasUndefined = false;
michael@0 285
michael@0 286 masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
michael@0 287 masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
michael@0 288
michael@0 289 if (mir->conversion() != MToFloat32::NumbersOnly) {
michael@0 290 masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
michael@0 291 masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
michael@0 292 hasBoolean = true;
michael@0 293 hasUndefined = true;
michael@0 294 if (mir->conversion() != MToFloat32::NonNullNonStringPrimitives) {
michael@0 295 masm.branchTestNull(Assembler::Equal, tag, &isNull);
michael@0 296 hasNull = true;
michael@0 297 }
michael@0 298 }
michael@0 299
michael@0 300 if (!bailout(lir->snapshot()))
michael@0 301 return false;
michael@0 302
michael@0 303 if (hasNull) {
michael@0 304 masm.bind(&isNull);
michael@0 305 masm.loadConstantFloat32(0.0f, output);
michael@0 306 masm.jump(&done);
michael@0 307 }
michael@0 308
michael@0 309 if (hasUndefined) {
michael@0 310 masm.bind(&isUndefined);
michael@0 311 masm.loadConstantFloat32(float(GenericNaN()), output);
michael@0 312 masm.jump(&done);
michael@0 313 }
michael@0 314
michael@0 315 if (hasBoolean) {
michael@0 316 masm.bind(&isBool);
michael@0 317 masm.boolValueToFloat32(operand, output);
michael@0 318 masm.jump(&done);
michael@0 319 }
michael@0 320
michael@0 321 masm.bind(&isInt32);
michael@0 322 masm.int32ValueToFloat32(operand, output);
michael@0 323 masm.jump(&done);
michael@0 324
michael@0 325 masm.bind(&isDouble);
michael@0 326 masm.unboxDouble(operand, output);
michael@0 327 masm.convertDoubleToFloat32(output, output);
michael@0 328 masm.bind(&done);
michael@0 329
michael@0 330 return true;
michael@0 331 }
michael@0 332
michael@0 333 bool
michael@0 334 CodeGenerator::visitInt32ToDouble(LInt32ToDouble *lir)
michael@0 335 {
michael@0 336 masm.convertInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
michael@0 337 return true;
michael@0 338 }
michael@0 339
michael@0 340 bool
michael@0 341 CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble *lir)
michael@0 342 {
michael@0 343 masm.convertFloat32ToDouble(ToFloatRegister(lir->input()), ToFloatRegister(lir->output()));
michael@0 344 return true;
michael@0 345 }
michael@0 346
michael@0 347 bool
michael@0 348 CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32 *lir)
michael@0 349 {
michael@0 350 masm.convertDoubleToFloat32(ToFloatRegister(lir->input()), ToFloatRegister(lir->output()));
michael@0 351 return true;
michael@0 352 }
michael@0 353
michael@0 354 bool
michael@0 355 CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32 *lir)
michael@0 356 {
michael@0 357 masm.convertInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output()));
michael@0 358 return true;
michael@0 359 }
michael@0 360
michael@0 361 bool
michael@0 362 CodeGenerator::visitDoubleToInt32(LDoubleToInt32 *lir)
michael@0 363 {
michael@0 364 Label fail;
michael@0 365 FloatRegister input = ToFloatRegister(lir->input());
michael@0 366 Register output = ToRegister(lir->output());
michael@0 367 masm.convertDoubleToInt32(input, output, &fail, lir->mir()->canBeNegativeZero());
michael@0 368 if (!bailoutFrom(&fail, lir->snapshot()))
michael@0 369 return false;
michael@0 370 return true;
michael@0 371 }
michael@0 372
michael@0 373 bool
michael@0 374 CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32 *lir)
michael@0 375 {
michael@0 376 Label fail;
michael@0 377 FloatRegister input = ToFloatRegister(lir->input());
michael@0 378 Register output = ToRegister(lir->output());
michael@0 379 masm.convertFloat32ToInt32(input, output, &fail, lir->mir()->canBeNegativeZero());
michael@0 380 if (!bailoutFrom(&fail, lir->snapshot()))
michael@0 381 return false;
michael@0 382 return true;
michael@0 383 }
michael@0 384
michael@0 385 void
michael@0 386 CodeGenerator::emitOOLTestObject(Register objreg,
michael@0 387 Label *ifEmulatesUndefined,
michael@0 388 Label *ifDoesntEmulateUndefined,
michael@0 389 Register scratch)
michael@0 390 {
michael@0 391 saveVolatile(scratch);
michael@0 392 masm.setupUnalignedABICall(1, scratch);
michael@0 393 masm.passABIArg(objreg);
michael@0 394 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::EmulatesUndefined));
michael@0 395 masm.storeCallResult(scratch);
michael@0 396 restoreVolatile(scratch);
michael@0 397
michael@0 398 masm.branchIfTrueBool(scratch, ifEmulatesUndefined);
michael@0 399 masm.jump(ifDoesntEmulateUndefined);
michael@0 400 }
michael@0 401
michael@0 402 // Base out-of-line code generator for all tests of the truthiness of an
michael@0 403 // object, where the object might not be truthy. (Recall that per spec all
michael@0 404 // objects are truthy, but we implement the JSCLASS_EMULATES_UNDEFINED class
michael@0 405 // flag to permit objects to look like |undefined| in certain contexts,
michael@0 406 // including in object truthiness testing.) We check truthiness inline except
michael@0 407 // when we're testing it on a proxy (or if TI guarantees us that the specified
michael@0 408 // object will never emulate |undefined|), in which case out-of-line code will
michael@0 409 // call EmulatesUndefined for a conclusive answer.
michael@0 410 class OutOfLineTestObject : public OutOfLineCodeBase<CodeGenerator>
michael@0 411 {
michael@0 412 Register objreg_;
michael@0 413 Register scratch_;
michael@0 414
michael@0 415 Label *ifEmulatesUndefined_;
michael@0 416 Label *ifDoesntEmulateUndefined_;
michael@0 417
michael@0 418 #ifdef DEBUG
michael@0 419 bool initialized() { return ifEmulatesUndefined_ != nullptr; }
michael@0 420 #endif
michael@0 421
michael@0 422 public:
michael@0 423 OutOfLineTestObject()
michael@0 424 #ifdef DEBUG
michael@0 425 : ifEmulatesUndefined_(nullptr), ifDoesntEmulateUndefined_(nullptr)
michael@0 426 #endif
michael@0 427 { }
michael@0 428
michael@0 429 bool accept(CodeGenerator *codegen) MOZ_FINAL MOZ_OVERRIDE {
michael@0 430 MOZ_ASSERT(initialized());
michael@0 431 codegen->emitOOLTestObject(objreg_, ifEmulatesUndefined_, ifDoesntEmulateUndefined_,
michael@0 432 scratch_);
michael@0 433 return true;
michael@0 434 }
michael@0 435
michael@0 436 // Specify the register where the object to be tested is found, labels to
michael@0 437 // jump to if the object is truthy or falsy, and a scratch register for
michael@0 438 // use in the out-of-line path.
michael@0 439 void setInputAndTargets(Register objreg, Label *ifEmulatesUndefined, Label *ifDoesntEmulateUndefined,
michael@0 440 Register scratch)
michael@0 441 {
michael@0 442 MOZ_ASSERT(!initialized());
michael@0 443 MOZ_ASSERT(ifEmulatesUndefined);
michael@0 444 objreg_ = objreg;
michael@0 445 scratch_ = scratch;
michael@0 446 ifEmulatesUndefined_ = ifEmulatesUndefined;
michael@0 447 ifDoesntEmulateUndefined_ = ifDoesntEmulateUndefined;
michael@0 448 }
michael@0 449 };
michael@0 450
michael@0 451 // A subclass of OutOfLineTestObject containing two extra labels, for use when
michael@0 452 // the ifTruthy/ifFalsy labels are needed in inline code as well as out-of-line
michael@0 453 // code. The user should bind these labels in inline code, and specify them as
michael@0 454 // targets via setInputAndTargets, as appropriate.
michael@0 455 class OutOfLineTestObjectWithLabels : public OutOfLineTestObject
michael@0 456 {
michael@0 457 Label label1_;
michael@0 458 Label label2_;
michael@0 459
michael@0 460 public:
michael@0 461 OutOfLineTestObjectWithLabels() { }
michael@0 462
michael@0 463 Label *label1() { return &label1_; }
michael@0 464 Label *label2() { return &label2_; }
michael@0 465 };
michael@0 466
michael@0 467 void
michael@0 468 CodeGenerator::testObjectEmulatesUndefinedKernel(Register objreg,
michael@0 469 Label *ifEmulatesUndefined,
michael@0 470 Label *ifDoesntEmulateUndefined,
michael@0 471 Register scratch, OutOfLineTestObject *ool)
michael@0 472 {
michael@0 473 ool->setInputAndTargets(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined, scratch);
michael@0 474
michael@0 475 // Perform a fast-path check of the object's class flags if the object's
michael@0 476 // not a proxy. Let out-of-line code handle the slow cases that require
michael@0 477 // saving registers, making a function call, and restoring registers.
michael@0 478 masm.branchTestObjectTruthy(false, objreg, scratch, ool->entry(), ifEmulatesUndefined);
michael@0 479 }
michael@0 480
michael@0 481 void
michael@0 482 CodeGenerator::branchTestObjectEmulatesUndefined(Register objreg,
michael@0 483 Label *ifEmulatesUndefined,
michael@0 484 Label *ifDoesntEmulateUndefined,
michael@0 485 Register scratch, OutOfLineTestObject *ool)
michael@0 486 {
michael@0 487 MOZ_ASSERT(!ifDoesntEmulateUndefined->bound(),
michael@0 488 "ifDoesntEmulateUndefined will be bound to the fallthrough path");
michael@0 489
michael@0 490 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined,
michael@0 491 scratch, ool);
michael@0 492 masm.bind(ifDoesntEmulateUndefined);
michael@0 493 }
michael@0 494
michael@0 495 void
michael@0 496 CodeGenerator::testObjectEmulatesUndefined(Register objreg,
michael@0 497 Label *ifEmulatesUndefined,
michael@0 498 Label *ifDoesntEmulateUndefined,
michael@0 499 Register scratch, OutOfLineTestObject *ool)
michael@0 500 {
michael@0 501 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined,
michael@0 502 scratch, ool);
michael@0 503 masm.jump(ifDoesntEmulateUndefined);
michael@0 504 }
michael@0 505
michael@0 506 void
michael@0 507 CodeGenerator::testValueTruthyKernel(const ValueOperand &value,
michael@0 508 const LDefinition *scratch1, const LDefinition *scratch2,
michael@0 509 FloatRegister fr,
michael@0 510 Label *ifTruthy, Label *ifFalsy,
michael@0 511 OutOfLineTestObject *ool)
michael@0 512 {
michael@0 513 Register tag = masm.splitTagForTest(value);
michael@0 514
michael@0 515 // Eventually we will want some sort of type filter here. For now, just
michael@0 516 // emit all easy cases. For speed we use the cached tag for all comparison,
michael@0 517 // except for doubles, which we test last (as the operation can clobber the
michael@0 518 // tag, which may be in ScratchReg).
michael@0 519 masm.branchTestUndefined(Assembler::Equal, tag, ifFalsy);
michael@0 520 masm.branchTestNull(Assembler::Equal, tag, ifFalsy);
michael@0 521
michael@0 522 Label notBoolean;
michael@0 523 masm.branchTestBoolean(Assembler::NotEqual, tag, &notBoolean);
michael@0 524 masm.branchTestBooleanTruthy(false, value, ifFalsy);
michael@0 525 masm.jump(ifTruthy);
michael@0 526 masm.bind(&notBoolean);
michael@0 527
michael@0 528 Label notInt32;
michael@0 529 masm.branchTestInt32(Assembler::NotEqual, tag, &notInt32);
michael@0 530 masm.branchTestInt32Truthy(false, value, ifFalsy);
michael@0 531 masm.jump(ifTruthy);
michael@0 532 masm.bind(&notInt32);
michael@0 533
michael@0 534 if (ool) {
michael@0 535 Label notObject;
michael@0 536
michael@0 537 masm.branchTestObject(Assembler::NotEqual, tag, &notObject);
michael@0 538
michael@0 539 Register objreg = masm.extractObject(value, ToRegister(scratch1));
michael@0 540 testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, ToRegister(scratch2), ool);
michael@0 541
michael@0 542 masm.bind(&notObject);
michael@0 543 } else {
michael@0 544 masm.branchTestObject(Assembler::Equal, tag, ifTruthy);
michael@0 545 }
michael@0 546
michael@0 547 // Test if a string is non-empty.
michael@0 548 Label notString;
michael@0 549 masm.branchTestString(Assembler::NotEqual, tag, &notString);
michael@0 550 masm.branchTestStringTruthy(false, value, ifFalsy);
michael@0 551 masm.jump(ifTruthy);
michael@0 552 masm.bind(&notString);
michael@0 553
michael@0 554 // If we reach here the value is a double.
michael@0 555 masm.unboxDouble(value, fr);
michael@0 556 masm.branchTestDoubleTruthy(false, fr, ifFalsy);
michael@0 557
michael@0 558 // Fall through for truthy.
michael@0 559 }
michael@0 560
michael@0 561 void
michael@0 562 CodeGenerator::testValueTruthy(const ValueOperand &value,
michael@0 563 const LDefinition *scratch1, const LDefinition *scratch2,
michael@0 564 FloatRegister fr,
michael@0 565 Label *ifTruthy, Label *ifFalsy,
michael@0 566 OutOfLineTestObject *ool)
michael@0 567 {
michael@0 568 testValueTruthyKernel(value, scratch1, scratch2, fr, ifTruthy, ifFalsy, ool);
michael@0 569 masm.jump(ifTruthy);
michael@0 570 }
michael@0 571
michael@0 572 Label *
michael@0 573 CodeGenerator::getJumpLabelForBranch(MBasicBlock *block)
michael@0 574 {
michael@0 575 if (!labelForBackedgeWithImplicitCheck(block))
michael@0 576 return block->lir()->label();
michael@0 577
michael@0 578 // We need to use a patchable jump for this backedge, but want to treat
michael@0 579 // this as a normal label target to simplify codegen. Efficiency isn't so
michael@0 580 // important here as these tests are extremely unlikely to be used in loop
michael@0 581 // backedges, so emit inline code for the patchable jump. Heap allocating
michael@0 582 // the label allows it to be used by out of line blocks.
michael@0 583 Label *res = GetIonContext()->temp->lifoAlloc()->new_<Label>();
michael@0 584 Label after;
michael@0 585 masm.jump(&after);
michael@0 586 masm.bind(res);
michael@0 587 jumpToBlock(block);
michael@0 588 masm.bind(&after);
michael@0 589 return res;
michael@0 590 }
michael@0 591
michael@0 592 bool
michael@0 593 CodeGenerator::visitTestOAndBranch(LTestOAndBranch *lir)
michael@0 594 {
michael@0 595 MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(),
michael@0 596 "Objects which can't emulate undefined should have been constant-folded");
michael@0 597
michael@0 598 OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject();
michael@0 599 if (!addOutOfLineCode(ool))
michael@0 600 return false;
michael@0 601
michael@0 602 Label *truthy = getJumpLabelForBranch(lir->ifTruthy());
michael@0 603 Label *falsy = getJumpLabelForBranch(lir->ifFalsy());
michael@0 604
michael@0 605 testObjectEmulatesUndefined(ToRegister(lir->input()), falsy, truthy,
michael@0 606 ToRegister(lir->temp()), ool);
michael@0 607 return true;
michael@0 608
michael@0 609 }
michael@0 610
michael@0 611 bool
michael@0 612 CodeGenerator::visitTestVAndBranch(LTestVAndBranch *lir)
michael@0 613 {
michael@0 614 OutOfLineTestObject *ool = nullptr;
michael@0 615 if (lir->mir()->operandMightEmulateUndefined()) {
michael@0 616 ool = new(alloc()) OutOfLineTestObject();
michael@0 617 if (!addOutOfLineCode(ool))
michael@0 618 return false;
michael@0 619 }
michael@0 620
michael@0 621 Label *truthy = getJumpLabelForBranch(lir->ifTruthy());
michael@0 622 Label *falsy = getJumpLabelForBranch(lir->ifFalsy());
michael@0 623
michael@0 624 testValueTruthy(ToValue(lir, LTestVAndBranch::Input),
michael@0 625 lir->temp1(), lir->temp2(),
michael@0 626 ToFloatRegister(lir->tempFloat()),
michael@0 627 truthy, falsy, ool);
michael@0 628 return true;
michael@0 629 }
michael@0 630
michael@0 631 bool
michael@0 632 CodeGenerator::visitFunctionDispatch(LFunctionDispatch *lir)
michael@0 633 {
michael@0 634 MFunctionDispatch *mir = lir->mir();
michael@0 635 Register input = ToRegister(lir->input());
michael@0 636 Label *lastLabel;
michael@0 637 size_t casesWithFallback;
michael@0 638
michael@0 639 // Determine if the last case is fallback or an ordinary case.
michael@0 640 if (!mir->hasFallback()) {
michael@0 641 JS_ASSERT(mir->numCases() > 0);
michael@0 642 casesWithFallback = mir->numCases();
michael@0 643 lastLabel = mir->getCaseBlock(mir->numCases() - 1)->lir()->label();
michael@0 644 } else {
michael@0 645 casesWithFallback = mir->numCases() + 1;
michael@0 646 lastLabel = mir->getFallback()->lir()->label();
michael@0 647 }
michael@0 648
michael@0 649 // Compare function pointers, except for the last case.
michael@0 650 for (size_t i = 0; i < casesWithFallback - 1; i++) {
michael@0 651 JS_ASSERT(i < mir->numCases());
michael@0 652 JSFunction *func = mir->getCase(i);
michael@0 653 LBlock *target = mir->getCaseBlock(i)->lir();
michael@0 654 masm.branchPtr(Assembler::Equal, input, ImmGCPtr(func), target->label());
michael@0 655 }
michael@0 656
michael@0 657 // Jump to the last case.
michael@0 658 masm.jump(lastLabel);
michael@0 659
michael@0 660 return true;
michael@0 661 }
michael@0 662
michael@0 663 bool
michael@0 664 CodeGenerator::visitTypeObjectDispatch(LTypeObjectDispatch *lir)
michael@0 665 {
michael@0 666 MTypeObjectDispatch *mir = lir->mir();
michael@0 667 Register input = ToRegister(lir->input());
michael@0 668 Register temp = ToRegister(lir->temp());
michael@0 669
michael@0 670 // Hold the incoming TypeObject.
michael@0 671 masm.loadPtr(Address(input, JSObject::offsetOfType()), temp);
michael@0 672
michael@0 673 // Compare TypeObjects.
michael@0 674 InlinePropertyTable *propTable = mir->propTable();
michael@0 675 for (size_t i = 0; i < mir->numCases(); i++) {
michael@0 676 JSFunction *func = mir->getCase(i);
michael@0 677 LBlock *target = mir->getCaseBlock(i)->lir();
michael@0 678
michael@0 679 DebugOnly<bool> found = false;
michael@0 680 for (size_t j = 0; j < propTable->numEntries(); j++) {
michael@0 681 if (propTable->getFunction(j) != func)
michael@0 682 continue;
michael@0 683 types::TypeObject *typeObj = propTable->getTypeObject(j);
michael@0 684 masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(typeObj), target->label());
michael@0 685 found = true;
michael@0 686 }
michael@0 687 JS_ASSERT(found);
michael@0 688 }
michael@0 689
michael@0 690 // Unknown function: jump to fallback block.
michael@0 691 LBlock *fallback = mir->getFallback()->lir();
michael@0 692 masm.jump(fallback->label());
michael@0 693 return true;
michael@0 694 }
michael@0 695
michael@0 696 bool
michael@0 697 CodeGenerator::visitBooleanToString(LBooleanToString *lir)
michael@0 698 {
michael@0 699 Register input = ToRegister(lir->input());
michael@0 700 Register output = ToRegister(lir->output());
michael@0 701 const JSAtomState &names = GetIonContext()->runtime->names();
michael@0 702 Label true_, done;
michael@0 703
michael@0 704 masm.branchTest32(Assembler::NonZero, input, input, &true_);
michael@0 705 masm.movePtr(ImmGCPtr(names.false_), output);
michael@0 706 masm.jump(&done);
michael@0 707
michael@0 708 masm.bind(&true_);
michael@0 709 masm.movePtr(ImmGCPtr(names.true_), output);
michael@0 710
michael@0 711 masm.bind(&done);
michael@0 712
michael@0 713 return true;
michael@0 714 }
michael@0 715
michael@0 716 void
michael@0 717 CodeGenerator::emitIntToString(Register input, Register output, Label *ool)
michael@0 718 {
michael@0 719 masm.branch32(Assembler::AboveOrEqual, input, Imm32(StaticStrings::INT_STATIC_LIMIT), ool);
michael@0 720
michael@0 721 // Fast path for small integers.
michael@0 722 masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings().intStaticTable), output);
michael@0 723 masm.loadPtr(BaseIndex(output, input, ScalePointer), output);
michael@0 724 }
michael@0 725
michael@0 726 typedef JSFlatString *(*IntToStringFn)(ThreadSafeContext *, int);
michael@0 727 typedef JSFlatString *(*IntToStringParFn)(ForkJoinContext *, int);
michael@0 728 static const VMFunctionsModal IntToStringInfo = VMFunctionsModal(
michael@0 729 FunctionInfo<IntToStringFn>(Int32ToString<CanGC>),
michael@0 730 FunctionInfo<IntToStringParFn>(IntToStringPar));
michael@0 731
michael@0 732 bool
michael@0 733 CodeGenerator::visitIntToString(LIntToString *lir)
michael@0 734 {
michael@0 735 Register input = ToRegister(lir->input());
michael@0 736 Register output = ToRegister(lir->output());
michael@0 737
michael@0 738 OutOfLineCode *ool = oolCallVM(IntToStringInfo, lir, (ArgList(), input),
michael@0 739 StoreRegisterTo(output));
michael@0 740 if (!ool)
michael@0 741 return false;
michael@0 742
michael@0 743 emitIntToString(input, output, ool->entry());
michael@0 744
michael@0 745 masm.bind(ool->rejoin());
michael@0 746 return true;
michael@0 747 }
michael@0 748
michael@0 749 typedef JSString *(*DoubleToStringFn)(ThreadSafeContext *, double);
michael@0 750 typedef JSString *(*DoubleToStringParFn)(ForkJoinContext *, double);
michael@0 751 static const VMFunctionsModal DoubleToStringInfo = VMFunctionsModal(
michael@0 752 FunctionInfo<DoubleToStringFn>(NumberToString<CanGC>),
michael@0 753 FunctionInfo<DoubleToStringParFn>(DoubleToStringPar));
michael@0 754
michael@0 755 bool
michael@0 756 CodeGenerator::visitDoubleToString(LDoubleToString *lir)
michael@0 757 {
michael@0 758 FloatRegister input = ToFloatRegister(lir->input());
michael@0 759 Register temp = ToRegister(lir->tempInt());
michael@0 760 Register output = ToRegister(lir->output());
michael@0 761
michael@0 762 OutOfLineCode *ool = oolCallVM(DoubleToStringInfo, lir, (ArgList(), input),
michael@0 763 StoreRegisterTo(output));
michael@0 764 if (!ool)
michael@0 765 return false;
michael@0 766
michael@0 767 // Try double to integer conversion and run integer to string code.
michael@0 768 masm.convertDoubleToInt32(input, temp, ool->entry(), true);
michael@0 769 emitIntToString(temp, output, ool->entry());
michael@0 770
michael@0 771 masm.bind(ool->rejoin());
michael@0 772 return true;
michael@0 773 }
michael@0 774
michael@0 775 typedef JSString *(*PrimitiveToStringFn)(JSContext *, HandleValue);
michael@0 776 typedef JSString *(*PrimitiveToStringParFn)(ForkJoinContext *, HandleValue);
michael@0 777 static const VMFunctionsModal PrimitiveToStringInfo = VMFunctionsModal(
michael@0 778 FunctionInfo<PrimitiveToStringFn>(ToStringSlow),
michael@0 779 FunctionInfo<PrimitiveToStringParFn>(PrimitiveToStringPar));
michael@0 780
michael@0 781 bool
michael@0 782 CodeGenerator::visitPrimitiveToString(LPrimitiveToString *lir)
michael@0 783 {
michael@0 784 ValueOperand input = ToValue(lir, LPrimitiveToString::Input);
michael@0 785 Register output = ToRegister(lir->output());
michael@0 786
michael@0 787 OutOfLineCode *ool = oolCallVM(PrimitiveToStringInfo, lir, (ArgList(), input),
michael@0 788 StoreRegisterTo(output));
michael@0 789 if (!ool)
michael@0 790 return false;
michael@0 791
michael@0 792 Label done;
michael@0 793 Register tag = masm.splitTagForTest(input);
michael@0 794 const JSAtomState &names = GetIonContext()->runtime->names();
michael@0 795
michael@0 796 // String
michael@0 797 if (lir->mir()->input()->mightBeType(MIRType_String)) {
michael@0 798 Label notString;
michael@0 799 masm.branchTestString(Assembler::NotEqual, tag, &notString);
michael@0 800 masm.unboxString(input, output);
michael@0 801 masm.jump(&done);
michael@0 802 masm.bind(&notString);
michael@0 803 }
michael@0 804
michael@0 805 // Integer
michael@0 806 if (lir->mir()->input()->mightBeType(MIRType_Int32)) {
michael@0 807 Label notInteger;
michael@0 808 masm.branchTestInt32(Assembler::NotEqual, tag, &notInteger);
michael@0 809 Register unboxed = ToTempUnboxRegister(lir->tempToUnbox());
michael@0 810 unboxed = masm.extractInt32(input, unboxed);
michael@0 811 emitIntToString(unboxed, output, ool->entry());
michael@0 812 masm.jump(&done);
michael@0 813 masm.bind(&notInteger);
michael@0 814 }
michael@0 815
michael@0 816 // Double
michael@0 817 if (lir->mir()->input()->mightBeType(MIRType_Double)) {
michael@0 818 // Note: no fastpath. Need two extra registers and can only convert doubles
michael@0 819 // that fit integers and are smaller than StaticStrings::INT_STATIC_LIMIT.
michael@0 820 masm.branchTestDouble(Assembler::Equal, tag, ool->entry());
michael@0 821 }
michael@0 822
michael@0 823 // Undefined
michael@0 824 if (lir->mir()->input()->mightBeType(MIRType_Undefined)) {
michael@0 825 Label notUndefined;
michael@0 826 masm.branchTestUndefined(Assembler::NotEqual, tag, &notUndefined);
michael@0 827 masm.movePtr(ImmGCPtr(names.undefined), output);
michael@0 828 masm.jump(&done);
michael@0 829 masm.bind(&notUndefined);
michael@0 830 }
michael@0 831
michael@0 832 // Null
michael@0 833 if (lir->mir()->input()->mightBeType(MIRType_Null)) {
michael@0 834 Label notNull;
michael@0 835 masm.branchTestNull(Assembler::NotEqual, tag, &notNull);
michael@0 836 masm.movePtr(ImmGCPtr(names.null), output);
michael@0 837 masm.jump(&done);
michael@0 838 masm.bind(&notNull);
michael@0 839 }
michael@0 840
michael@0 841 // Boolean
michael@0 842 if (lir->mir()->input()->mightBeType(MIRType_Boolean)) {
michael@0 843 Label notBoolean, true_;
michael@0 844 masm.branchTestBoolean(Assembler::NotEqual, tag, &notBoolean);
michael@0 845 masm.branchTestBooleanTruthy(true, input, &true_);
michael@0 846 masm.movePtr(ImmGCPtr(names.false_), output);
michael@0 847 masm.jump(&done);
michael@0 848 masm.bind(&true_);
michael@0 849 masm.movePtr(ImmGCPtr(names.true_), output);
michael@0 850 masm.jump(&done);
michael@0 851 masm.bind(&notBoolean);
michael@0 852 }
michael@0 853
michael@0 854 #ifdef DEBUG
michael@0 855 // Objects are not supported or we see a type that wasn't accounted for.
michael@0 856 masm.assumeUnreachable("Unexpected type for MPrimitiveToString.");
michael@0 857 #endif
michael@0 858
michael@0 859 masm.bind(&done);
michael@0 860 masm.bind(ool->rejoin());
michael@0 861 return true;
michael@0 862 }
michael@0 863
michael@0 864 typedef JSObject *(*CloneRegExpObjectFn)(JSContext *, JSObject *);
michael@0 865 static const VMFunction CloneRegExpObjectInfo =
michael@0 866 FunctionInfo<CloneRegExpObjectFn>(CloneRegExpObject);
michael@0 867
michael@0 868 bool
michael@0 869 CodeGenerator::visitRegExp(LRegExp *lir)
michael@0 870 {
michael@0 871 pushArg(ImmGCPtr(lir->mir()->source()));
michael@0 872 return callVM(CloneRegExpObjectInfo, lir);
michael@0 873 }
michael@0 874
michael@0 875 typedef bool (*RegExpExecRawFn)(JSContext *cx, HandleObject regexp,
michael@0 876 HandleString input, MutableHandleValue output);
michael@0 877 static const VMFunction RegExpExecRawInfo = FunctionInfo<RegExpExecRawFn>(regexp_exec_raw);
michael@0 878
michael@0 879 bool
michael@0 880 CodeGenerator::visitRegExpExec(LRegExpExec *lir)
michael@0 881 {
michael@0 882 pushArg(ToRegister(lir->string()));
michael@0 883 pushArg(ToRegister(lir->regexp()));
michael@0 884 return callVM(RegExpExecRawInfo, lir);
michael@0 885 }
michael@0 886
michael@0 887 typedef bool (*RegExpTestRawFn)(JSContext *cx, HandleObject regexp,
michael@0 888 HandleString input, bool *result);
michael@0 889 static const VMFunction RegExpTestRawInfo = FunctionInfo<RegExpTestRawFn>(regexp_test_raw);
michael@0 890
michael@0 891 bool
michael@0 892 CodeGenerator::visitRegExpTest(LRegExpTest *lir)
michael@0 893 {
michael@0 894 pushArg(ToRegister(lir->string()));
michael@0 895 pushArg(ToRegister(lir->regexp()));
michael@0 896 return callVM(RegExpTestRawInfo, lir);
michael@0 897 }
michael@0 898
michael@0 899 typedef JSString *(*RegExpReplaceFn)(JSContext *, HandleString, HandleObject, HandleString);
michael@0 900 static const VMFunction RegExpReplaceInfo = FunctionInfo<RegExpReplaceFn>(RegExpReplace);
michael@0 901
michael@0 902 bool
michael@0 903 CodeGenerator::visitRegExpReplace(LRegExpReplace *lir)
michael@0 904 {
michael@0 905 if (lir->replacement()->isConstant())
michael@0 906 pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString()));
michael@0 907 else
michael@0 908 pushArg(ToRegister(lir->replacement()));
michael@0 909
michael@0 910 pushArg(ToRegister(lir->pattern()));
michael@0 911
michael@0 912 if (lir->string()->isConstant())
michael@0 913 pushArg(ImmGCPtr(lir->string()->toConstant()->toString()));
michael@0 914 else
michael@0 915 pushArg(ToRegister(lir->string()));
michael@0 916
michael@0 917 return callVM(RegExpReplaceInfo, lir);
michael@0 918 }
michael@0 919
michael@0 920 typedef JSString *(*StringReplaceFn)(JSContext *, HandleString, HandleString, HandleString);
michael@0 921 static const VMFunction StringReplaceInfo = FunctionInfo<StringReplaceFn>(StringReplace);
michael@0 922
michael@0 923 bool
michael@0 924 CodeGenerator::visitStringReplace(LStringReplace *lir)
michael@0 925 {
michael@0 926 if (lir->replacement()->isConstant())
michael@0 927 pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString()));
michael@0 928 else
michael@0 929 pushArg(ToRegister(lir->replacement()));
michael@0 930
michael@0 931 if (lir->pattern()->isConstant())
michael@0 932 pushArg(ImmGCPtr(lir->pattern()->toConstant()->toString()));
michael@0 933 else
michael@0 934 pushArg(ToRegister(lir->pattern()));
michael@0 935
michael@0 936 if (lir->string()->isConstant())
michael@0 937 pushArg(ImmGCPtr(lir->string()->toConstant()->toString()));
michael@0 938 else
michael@0 939 pushArg(ToRegister(lir->string()));
michael@0 940
michael@0 941 return callVM(StringReplaceInfo, lir);
michael@0 942 }
michael@0 943
michael@0 944
michael@0 945 typedef JSObject *(*LambdaFn)(JSContext *, HandleFunction, HandleObject);
michael@0 946 static const VMFunction LambdaInfo = FunctionInfo<LambdaFn>(js::Lambda);
michael@0 947
michael@0 948 bool
michael@0 949 CodeGenerator::visitLambdaForSingleton(LLambdaForSingleton *lir)
michael@0 950 {
michael@0 951 pushArg(ToRegister(lir->scopeChain()));
michael@0 952 pushArg(ImmGCPtr(lir->mir()->info().fun));
michael@0 953 return callVM(LambdaInfo, lir);
michael@0 954 }
michael@0 955
michael@0 956 bool
michael@0 957 CodeGenerator::visitLambda(LLambda *lir)
michael@0 958 {
michael@0 959 Register scopeChain = ToRegister(lir->scopeChain());
michael@0 960 Register output = ToRegister(lir->output());
michael@0 961 Register tempReg = ToRegister(lir->temp());
michael@0 962 const LambdaFunctionInfo &info = lir->mir()->info();
michael@0 963
michael@0 964 OutOfLineCode *ool = oolCallVM(LambdaInfo, lir, (ArgList(), ImmGCPtr(info.fun), scopeChain),
michael@0 965 StoreRegisterTo(output));
michael@0 966 if (!ool)
michael@0 967 return false;
michael@0 968
michael@0 969 JS_ASSERT(!info.singletonType);
michael@0 970
michael@0 971 masm.newGCThing(output, tempReg, info.fun, ool->entry(), gc::DefaultHeap);
michael@0 972 masm.initGCThing(output, tempReg, info.fun);
michael@0 973
michael@0 974 emitLambdaInit(output, scopeChain, info);
michael@0 975
michael@0 976 masm.bind(ool->rejoin());
michael@0 977 return true;
michael@0 978 }
michael@0 979
michael@0 980 typedef JSObject *(*LambdaArrowFn)(JSContext *, HandleFunction, HandleObject, HandleValue);
michael@0 981 static const VMFunction LambdaArrowInfo = FunctionInfo<LambdaArrowFn>(js::LambdaArrow);
michael@0 982
michael@0 983 bool
michael@0 984 CodeGenerator::visitLambdaArrow(LLambdaArrow *lir)
michael@0 985 {
michael@0 986 Register scopeChain = ToRegister(lir->scopeChain());
michael@0 987 ValueOperand thisv = ToValue(lir, LLambdaArrow::ThisValue);
michael@0 988 Register output = ToRegister(lir->output());
michael@0 989 Register tempReg = ToRegister(lir->temp());
michael@0 990 const LambdaFunctionInfo &info = lir->mir()->info();
michael@0 991
michael@0 992 OutOfLineCode *ool = oolCallVM(LambdaArrowInfo, lir,
michael@0 993 (ArgList(), ImmGCPtr(info.fun), scopeChain, thisv),
michael@0 994 StoreRegisterTo(output));
michael@0 995 if (!ool)
michael@0 996 return false;
michael@0 997
michael@0 998 MOZ_ASSERT(!info.useNewTypeForClone);
michael@0 999
michael@0 1000 if (info.singletonType) {
michael@0 1001 // If the function has a singleton type, this instruction will only be
michael@0 1002 // executed once so we don't bother inlining it.
michael@0 1003 masm.jump(ool->entry());
michael@0 1004 masm.bind(ool->rejoin());
michael@0 1005 return true;
michael@0 1006 }
michael@0 1007
michael@0 1008 masm.newGCThing(output, tempReg, info.fun, ool->entry(), gc::DefaultHeap);
michael@0 1009 masm.initGCThing(output, tempReg, info.fun);
michael@0 1010
michael@0 1011 emitLambdaInit(output, scopeChain, info);
michael@0 1012
michael@0 1013 // Initialize extended slots. Lexical |this| is stored in the first one.
michael@0 1014 MOZ_ASSERT(info.flags & JSFunction::EXTENDED);
michael@0 1015 static_assert(FunctionExtended::NUM_EXTENDED_SLOTS == 2, "All slots must be initialized");
michael@0 1016 static_assert(FunctionExtended::ARROW_THIS_SLOT == 0, "|this| must be stored in first slot");
michael@0 1017 masm.storeValue(thisv, Address(output, FunctionExtended::offsetOfExtendedSlot(0)));
michael@0 1018 masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
michael@0 1019
michael@0 1020 masm.bind(ool->rejoin());
michael@0 1021 return true;
michael@0 1022 }
michael@0 1023
michael@0 1024 void
michael@0 1025 CodeGenerator::emitLambdaInit(Register output, Register scopeChain,
michael@0 1026 const LambdaFunctionInfo &info)
michael@0 1027 {
michael@0 1028 MOZ_ASSERT(!!(info.flags & JSFunction::ARROW) == !!(info.flags & JSFunction::EXTENDED));
michael@0 1029
michael@0 1030 // Initialize nargs and flags. We do this with a single uint32 to avoid
michael@0 1031 // 16-bit writes.
michael@0 1032 union {
michael@0 1033 struct S {
michael@0 1034 uint16_t nargs;
michael@0 1035 uint16_t flags;
michael@0 1036 } s;
michael@0 1037 uint32_t word;
michael@0 1038 } u;
michael@0 1039 u.s.nargs = info.fun->nargs();
michael@0 1040 u.s.flags = info.flags;
michael@0 1041
michael@0 1042 JS_ASSERT(JSFunction::offsetOfFlags() == JSFunction::offsetOfNargs() + 2);
michael@0 1043 masm.store32(Imm32(u.word), Address(output, JSFunction::offsetOfNargs()));
michael@0 1044 masm.storePtr(ImmGCPtr(info.scriptOrLazyScript),
michael@0 1045 Address(output, JSFunction::offsetOfNativeOrScript()));
michael@0 1046 masm.storePtr(scopeChain, Address(output, JSFunction::offsetOfEnvironment()));
michael@0 1047 masm.storePtr(ImmGCPtr(info.fun->displayAtom()), Address(output, JSFunction::offsetOfAtom()));
michael@0 1048 }
michael@0 1049
michael@0 1050 bool
michael@0 1051 CodeGenerator::visitLambdaPar(LLambdaPar *lir)
michael@0 1052 {
michael@0 1053 Register resultReg = ToRegister(lir->output());
michael@0 1054 Register cxReg = ToRegister(lir->forkJoinContext());
michael@0 1055 Register scopeChainReg = ToRegister(lir->scopeChain());
michael@0 1056 Register tempReg1 = ToRegister(lir->getTemp0());
michael@0 1057 Register tempReg2 = ToRegister(lir->getTemp1());
michael@0 1058 const LambdaFunctionInfo &info = lir->mir()->info();
michael@0 1059
michael@0 1060 JS_ASSERT(scopeChainReg != resultReg);
michael@0 1061
michael@0 1062 emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, info.fun);
michael@0 1063 emitLambdaInit(resultReg, scopeChainReg, info);
michael@0 1064 return true;
michael@0 1065 }
michael@0 1066
michael@0 1067 bool
michael@0 1068 CodeGenerator::visitLabel(LLabel *lir)
michael@0 1069 {
michael@0 1070 return true;
michael@0 1071 }
michael@0 1072
michael@0 1073 bool
michael@0 1074 CodeGenerator::visitNop(LNop *lir)
michael@0 1075 {
michael@0 1076 return true;
michael@0 1077 }
michael@0 1078
michael@0 1079 bool
michael@0 1080 CodeGenerator::visitOsiPoint(LOsiPoint *lir)
michael@0 1081 {
michael@0 1082 // Note: markOsiPoint ensures enough space exists between the last
michael@0 1083 // LOsiPoint and this one to patch adjacent call instructions.
michael@0 1084
michael@0 1085 JS_ASSERT(masm.framePushed() == frameSize());
michael@0 1086
michael@0 1087 uint32_t osiCallPointOffset;
michael@0 1088 if (!markOsiPoint(lir, &osiCallPointOffset))
michael@0 1089 return false;
michael@0 1090
michael@0 1091 LSafepoint *safepoint = lir->associatedSafepoint();
michael@0 1092 JS_ASSERT(!safepoint->osiCallPointOffset());
michael@0 1093 safepoint->setOsiCallPointOffset(osiCallPointOffset);
michael@0 1094
michael@0 1095 #ifdef DEBUG
michael@0 1096 // There should be no movegroups or other instructions between
michael@0 1097 // an instruction and its OsiPoint. This is necessary because
michael@0 1098 // we use the OsiPoint's snapshot from within VM calls.
michael@0 1099 for (LInstructionReverseIterator iter(current->rbegin(lir)); iter != current->rend(); iter++) {
michael@0 1100 if (*iter == lir || iter->isNop())
michael@0 1101 continue;
michael@0 1102 JS_ASSERT(!iter->isMoveGroup());
michael@0 1103 JS_ASSERT(iter->safepoint() == safepoint);
michael@0 1104 break;
michael@0 1105 }
michael@0 1106 #endif
michael@0 1107
michael@0 1108 #ifdef CHECK_OSIPOINT_REGISTERS
michael@0 1109 if (shouldVerifyOsiPointRegs(safepoint))
michael@0 1110 verifyOsiPointRegs(safepoint);
michael@0 1111 #endif
michael@0 1112
michael@0 1113 return true;
michael@0 1114 }
michael@0 1115
michael@0 1116 bool
michael@0 1117 CodeGenerator::visitGoto(LGoto *lir)
michael@0 1118 {
michael@0 1119 jumpToBlock(lir->target());
michael@0 1120 return true;
michael@0 1121 }
michael@0 1122
michael@0 1123 // Out-of-line path to execute any move groups between the start of a loop
michael@0 1124 // header and its interrupt check, then invoke the interrupt handler.
michael@0 1125 class OutOfLineInterruptCheckImplicit : public OutOfLineCodeBase<CodeGenerator>
michael@0 1126 {
michael@0 1127 public:
michael@0 1128 LBlock *block;
michael@0 1129 LInterruptCheckImplicit *lir;
michael@0 1130
michael@0 1131 OutOfLineInterruptCheckImplicit(LBlock *block, LInterruptCheckImplicit *lir)
michael@0 1132 : block(block), lir(lir)
michael@0 1133 { }
michael@0 1134
michael@0 1135 bool accept(CodeGenerator *codegen) {
michael@0 1136 return codegen->visitOutOfLineInterruptCheckImplicit(this);
michael@0 1137 }
michael@0 1138 };
michael@0 1139
michael@0 1140 bool
michael@0 1141 CodeGenerator::visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit *ool)
michael@0 1142 {
michael@0 1143 #ifdef CHECK_OSIPOINT_REGISTERS
michael@0 1144 // This is path is entered from the patched back-edge of the loop. This
michael@0 1145 // means that the JitAtivation flags used for checking the validity of the
michael@0 1146 // OSI points are not reseted by the path generated by generateBody, so we
michael@0 1147 // have to reset it here.
michael@0 1148 resetOsiPointRegs(ool->lir->safepoint());
michael@0 1149 #endif
michael@0 1150
michael@0 1151 LInstructionIterator iter = ool->block->begin();
michael@0 1152 for (; iter != ool->block->end(); iter++) {
michael@0 1153 if (iter->isLabel()) {
michael@0 1154 // Nothing to do.
michael@0 1155 } else if (iter->isMoveGroup()) {
michael@0 1156 // Replay this move group that preceds the interrupt check at the
michael@0 1157 // start of the loop header. Any incoming jumps here will be from
michael@0 1158 // the backedge and will skip over the move group emitted inline.
michael@0 1159 visitMoveGroup(iter->toMoveGroup());
michael@0 1160 } else {
michael@0 1161 break;
michael@0 1162 }
michael@0 1163 }
michael@0 1164 JS_ASSERT(*iter == ool->lir);
michael@0 1165
michael@0 1166 saveLive(ool->lir);
michael@0 1167 if (!callVM(InterruptCheckInfo, ool->lir))
michael@0 1168 return false;
michael@0 1169 restoreLive(ool->lir);
michael@0 1170 masm.jump(ool->rejoin());
michael@0 1171
michael@0 1172 return true;
michael@0 1173 }
michael@0 1174
michael@0 1175 bool
michael@0 1176 CodeGenerator::visitInterruptCheckImplicit(LInterruptCheckImplicit *lir)
michael@0 1177 {
michael@0 1178 OutOfLineInterruptCheckImplicit *ool = new(alloc()) OutOfLineInterruptCheckImplicit(current, lir);
michael@0 1179 if (!addOutOfLineCode(ool))
michael@0 1180 return false;
michael@0 1181
michael@0 1182 lir->setOolEntry(ool->entry());
michael@0 1183 masm.bind(ool->rejoin());
michael@0 1184 return true;
michael@0 1185 }
michael@0 1186
michael@0 1187 bool
michael@0 1188 CodeGenerator::visitTableSwitch(LTableSwitch *ins)
michael@0 1189 {
michael@0 1190 MTableSwitch *mir = ins->mir();
michael@0 1191 Label *defaultcase = mir->getDefault()->lir()->label();
michael@0 1192 const LAllocation *temp;
michael@0 1193
michael@0 1194 if (mir->getOperand(0)->type() != MIRType_Int32) {
michael@0 1195 temp = ins->tempInt()->output();
michael@0 1196
michael@0 1197 // The input is a double, so try and convert it to an integer.
michael@0 1198 // If it does not fit in an integer, take the default case.
michael@0 1199 masm.convertDoubleToInt32(ToFloatRegister(ins->index()), ToRegister(temp), defaultcase, false);
michael@0 1200 } else {
michael@0 1201 temp = ins->index();
michael@0 1202 }
michael@0 1203
michael@0 1204 return emitTableSwitchDispatch(mir, ToRegister(temp), ToRegisterOrInvalid(ins->tempPointer()));
michael@0 1205 }
michael@0 1206
michael@0 1207 bool
michael@0 1208 CodeGenerator::visitTableSwitchV(LTableSwitchV *ins)
michael@0 1209 {
michael@0 1210 MTableSwitch *mir = ins->mir();
michael@0 1211 Label *defaultcase = mir->getDefault()->lir()->label();
michael@0 1212
michael@0 1213 Register index = ToRegister(ins->tempInt());
michael@0 1214 ValueOperand value = ToValue(ins, LTableSwitchV::InputValue);
michael@0 1215 Register tag = masm.extractTag(value, index);
michael@0 1216 masm.branchTestNumber(Assembler::NotEqual, tag, defaultcase);
michael@0 1217
michael@0 1218 Label unboxInt, isInt;
michael@0 1219 masm.branchTestInt32(Assembler::Equal, tag, &unboxInt);
michael@0 1220 {
michael@0 1221 FloatRegister floatIndex = ToFloatRegister(ins->tempFloat());
michael@0 1222 masm.unboxDouble(value, floatIndex);
michael@0 1223 masm.convertDoubleToInt32(floatIndex, index, defaultcase, false);
michael@0 1224 masm.jump(&isInt);
michael@0 1225 }
michael@0 1226
michael@0 1227 masm.bind(&unboxInt);
michael@0 1228 masm.unboxInt32(value, index);
michael@0 1229
michael@0 1230 masm.bind(&isInt);
michael@0 1231
michael@0 1232 return emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer()));
michael@0 1233 }
michael@0 1234
michael@0 1235 typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind);
michael@0 1236 static const VMFunction DeepCloneObjectLiteralInfo =
michael@0 1237 FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);
michael@0 1238
michael@0 1239 bool
michael@0 1240 CodeGenerator::visitCloneLiteral(LCloneLiteral *lir)
michael@0 1241 {
michael@0 1242 pushArg(ImmWord(js::MaybeSingletonObject));
michael@0 1243 pushArg(ToRegister(lir->output()));
michael@0 1244 return callVM(DeepCloneObjectLiteralInfo, lir);
michael@0 1245 }
michael@0 1246
michael@0 1247 bool
michael@0 1248 CodeGenerator::visitParameter(LParameter *lir)
michael@0 1249 {
michael@0 1250 return true;
michael@0 1251 }
michael@0 1252
michael@0 1253 bool
michael@0 1254 CodeGenerator::visitCallee(LCallee *lir)
michael@0 1255 {
michael@0 1256 // read number of actual arguments from the JS frame.
michael@0 1257 Register callee = ToRegister(lir->output());
michael@0 1258 Address ptr(StackPointer, frameSize() + IonJSFrameLayout::offsetOfCalleeToken());
michael@0 1259
michael@0 1260 masm.loadPtr(ptr, callee);
michael@0 1261 return true;
michael@0 1262 }
michael@0 1263
michael@0 1264 bool
michael@0 1265 CodeGenerator::visitStart(LStart *lir)
michael@0 1266 {
michael@0 1267 return true;
michael@0 1268 }
michael@0 1269
michael@0 1270 bool
michael@0 1271 CodeGenerator::visitReturn(LReturn *lir)
michael@0 1272 {
michael@0 1273 #if defined(JS_NUNBOX32)
michael@0 1274 DebugOnly<LAllocation *> type = lir->getOperand(TYPE_INDEX);
michael@0 1275 DebugOnly<LAllocation *> payload = lir->getOperand(PAYLOAD_INDEX);
michael@0 1276 JS_ASSERT(ToRegister(type) == JSReturnReg_Type);
michael@0 1277 JS_ASSERT(ToRegister(payload) == JSReturnReg_Data);
michael@0 1278 #elif defined(JS_PUNBOX64)
michael@0 1279 DebugOnly<LAllocation *> result = lir->getOperand(0);
michael@0 1280 JS_ASSERT(ToRegister(result) == JSReturnReg);
michael@0 1281 #endif
michael@0 1282 // Don't emit a jump to the return label if this is the last block.
michael@0 1283 if (current->mir() != *gen->graph().poBegin())
michael@0 1284 masm.jump(&returnLabel_);
michael@0 1285 return true;
michael@0 1286 }
michael@0 1287
michael@0 1288 bool
michael@0 1289 CodeGenerator::visitOsrEntry(LOsrEntry *lir)
michael@0 1290 {
michael@0 1291 // Remember the OSR entry offset into the code buffer.
michael@0 1292 masm.flushBuffer();
michael@0 1293 setOsrEntryOffset(masm.size());
michael@0 1294
michael@0 1295 #ifdef JS_TRACE_LOGGING
michael@0 1296 if (gen->info().executionMode() == SequentialExecution) {
michael@0 1297 if (!emitTracelogStopEvent(TraceLogger::Baseline))
michael@0 1298 return false;
michael@0 1299 if (!emitTracelogStartEvent(TraceLogger::IonMonkey))
michael@0 1300 return false;
michael@0 1301 }
michael@0 1302 #endif
michael@0 1303
michael@0 1304 // Allocate the full frame for this function.
michael@0 1305 uint32_t size = frameSize();
michael@0 1306 if (size != 0)
michael@0 1307 masm.subPtr(Imm32(size), StackPointer);
michael@0 1308 return true;
michael@0 1309 }
michael@0 1310
michael@0 1311 bool
michael@0 1312 CodeGenerator::visitOsrScopeChain(LOsrScopeChain *lir)
michael@0 1313 {
michael@0 1314 const LAllocation *frame = lir->getOperand(0);
michael@0 1315 const LDefinition *object = lir->getDef(0);
michael@0 1316
michael@0 1317 const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfScopeChain();
michael@0 1318
michael@0 1319 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
michael@0 1320 return true;
michael@0 1321 }
michael@0 1322
michael@0 1323 bool
michael@0 1324 CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject *lir)
michael@0 1325 {
michael@0 1326 const LAllocation *frame = lir->getOperand(0);
michael@0 1327 const LDefinition *object = lir->getDef(0);
michael@0 1328
michael@0 1329 const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfArgsObj();
michael@0 1330
michael@0 1331 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
michael@0 1332 return true;
michael@0 1333 }
michael@0 1334
michael@0 1335 bool
michael@0 1336 CodeGenerator::visitOsrValue(LOsrValue *value)
michael@0 1337 {
michael@0 1338 const LAllocation *frame = value->getOperand(0);
michael@0 1339 const ValueOperand out = ToOutValue(value);
michael@0 1340
michael@0 1341 const ptrdiff_t frameOffset = value->mir()->frameOffset();
michael@0 1342
michael@0 1343 masm.loadValue(Address(ToRegister(frame), frameOffset), out);
michael@0 1344 return true;
michael@0 1345 }
michael@0 1346
michael@0 1347 bool
michael@0 1348 CodeGenerator::visitOsrReturnValue(LOsrReturnValue *lir)
michael@0 1349 {
michael@0 1350 const LAllocation *frame = lir->getOperand(0);
michael@0 1351 const ValueOperand out = ToOutValue(lir);
michael@0 1352
michael@0 1353 Address flags = Address(ToRegister(frame), BaselineFrame::reverseOffsetOfFlags());
michael@0 1354 Address retval = Address(ToRegister(frame), BaselineFrame::reverseOffsetOfReturnValue());
michael@0 1355
michael@0 1356 masm.moveValue(UndefinedValue(), out);
michael@0 1357
michael@0 1358 Label done;
michael@0 1359 masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), &done);
michael@0 1360 masm.loadValue(retval, out);
michael@0 1361 masm.bind(&done);
michael@0 1362
michael@0 1363 return true;
michael@0 1364 }
michael@0 1365
michael@0 1366 bool
michael@0 1367 CodeGenerator::visitStackArgT(LStackArgT *lir)
michael@0 1368 {
michael@0 1369 const LAllocation *arg = lir->getArgument();
michael@0 1370 MIRType argType = lir->type();
michael@0 1371 uint32_t argslot = lir->argslot();
michael@0 1372 JS_ASSERT(argslot - 1u < graph.argumentSlotCount());
michael@0 1373
michael@0 1374 int32_t stack_offset = StackOffsetOfPassedArg(argslot);
michael@0 1375 Address dest(StackPointer, stack_offset);
michael@0 1376
michael@0 1377 if (arg->isFloatReg())
michael@0 1378 masm.storeDouble(ToFloatRegister(arg), dest);
michael@0 1379 else if (arg->isRegister())
michael@0 1380 masm.storeValue(ValueTypeFromMIRType(argType), ToRegister(arg), dest);
michael@0 1381 else
michael@0 1382 masm.storeValue(*(arg->toConstant()), dest);
michael@0 1383
michael@0 1384 uint32_t slot = StackOffsetToSlot(stack_offset);
michael@0 1385 JS_ASSERT(slot - 1u < graph.totalSlotCount());
michael@0 1386 return pushedArgumentSlots_.append(slot);
michael@0 1387 }
michael@0 1388
michael@0 1389 bool
michael@0 1390 CodeGenerator::visitStackArgV(LStackArgV *lir)
michael@0 1391 {
michael@0 1392 ValueOperand val = ToValue(lir, 0);
michael@0 1393 uint32_t argslot = lir->argslot();
michael@0 1394 JS_ASSERT(argslot - 1u < graph.argumentSlotCount());
michael@0 1395
michael@0 1396 int32_t stack_offset = StackOffsetOfPassedArg(argslot);
michael@0 1397
michael@0 1398 masm.storeValue(val, Address(StackPointer, stack_offset));
michael@0 1399
michael@0 1400 uint32_t slot = StackOffsetToSlot(stack_offset);
michael@0 1401 JS_ASSERT(slot - 1u < graph.totalSlotCount());
michael@0 1402 return pushedArgumentSlots_.append(slot);
michael@0 1403 }
michael@0 1404
michael@0 1405 bool
michael@0 1406 CodeGenerator::visitMoveGroup(LMoveGroup *group)
michael@0 1407 {
michael@0 1408 if (!group->numMoves())
michael@0 1409 return true;
michael@0 1410
michael@0 1411 MoveResolver &resolver = masm.moveResolver();
michael@0 1412
michael@0 1413 for (size_t i = 0; i < group->numMoves(); i++) {
michael@0 1414 const LMove &move = group->getMove(i);
michael@0 1415
michael@0 1416 const LAllocation *from = move.from();
michael@0 1417 const LAllocation *to = move.to();
michael@0 1418 LDefinition::Type type = move.type();
michael@0 1419
michael@0 1420 // No bogus moves.
michael@0 1421 JS_ASSERT(*from != *to);
michael@0 1422 JS_ASSERT(!from->isConstant());
michael@0 1423
michael@0 1424 MoveOp::Type moveType;
michael@0 1425 switch (type) {
michael@0 1426 case LDefinition::OBJECT:
michael@0 1427 case LDefinition::SLOTS:
michael@0 1428 #ifdef JS_NUNBOX32
michael@0 1429 case LDefinition::TYPE:
michael@0 1430 case LDefinition::PAYLOAD:
michael@0 1431 #else
michael@0 1432 case LDefinition::BOX:
michael@0 1433 #endif
michael@0 1434 case LDefinition::GENERAL: moveType = MoveOp::GENERAL; break;
michael@0 1435 case LDefinition::INT32: moveType = MoveOp::INT32; break;
michael@0 1436 case LDefinition::FLOAT32: moveType = MoveOp::FLOAT32; break;
michael@0 1437 case LDefinition::DOUBLE: moveType = MoveOp::DOUBLE; break;
michael@0 1438 default: MOZ_ASSUME_UNREACHABLE("Unexpected move type");
michael@0 1439 }
michael@0 1440
michael@0 1441 if (!resolver.addMove(toMoveOperand(from), toMoveOperand(to), moveType))
michael@0 1442 return false;
michael@0 1443 }
michael@0 1444
michael@0 1445 if (!resolver.resolve())
michael@0 1446 return false;
michael@0 1447
michael@0 1448 MoveEmitter emitter(masm);
michael@0 1449 emitter.emit(resolver);
michael@0 1450 emitter.finish();
michael@0 1451
michael@0 1452 return true;
michael@0 1453 }
michael@0 1454
michael@0 1455 bool
michael@0 1456 CodeGenerator::visitInteger(LInteger *lir)
michael@0 1457 {
michael@0 1458 masm.move32(Imm32(lir->getValue()), ToRegister(lir->output()));
michael@0 1459 return true;
michael@0 1460 }
michael@0 1461
michael@0 1462 bool
michael@0 1463 CodeGenerator::visitPointer(LPointer *lir)
michael@0 1464 {
michael@0 1465 if (lir->kind() == LPointer::GC_THING)
michael@0 1466 masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output()));
michael@0 1467 else
michael@0 1468 masm.movePtr(ImmPtr(lir->ptr()), ToRegister(lir->output()));
michael@0 1469 return true;
michael@0 1470 }
michael@0 1471
michael@0 1472 bool
michael@0 1473 CodeGenerator::visitSlots(LSlots *lir)
michael@0 1474 {
michael@0 1475 Address slots(ToRegister(lir->object()), JSObject::offsetOfSlots());
michael@0 1476 masm.loadPtr(slots, ToRegister(lir->output()));
michael@0 1477 return true;
michael@0 1478 }
michael@0 1479
michael@0 1480 bool
michael@0 1481 CodeGenerator::visitStoreSlotV(LStoreSlotV *store)
michael@0 1482 {
michael@0 1483 Register base = ToRegister(store->slots());
michael@0 1484 int32_t offset = store->mir()->slot() * sizeof(Value);
michael@0 1485
michael@0 1486 const ValueOperand value = ToValue(store, LStoreSlotV::Value);
michael@0 1487
michael@0 1488 if (store->mir()->needsBarrier())
michael@0 1489 emitPreBarrier(Address(base, offset), MIRType_Value);
michael@0 1490
michael@0 1491 masm.storeValue(value, Address(base, offset));
michael@0 1492 return true;
michael@0 1493 }
michael@0 1494
michael@0 1495 bool
michael@0 1496 CodeGenerator::emitGetPropertyPolymorphic(LInstruction *ins, Register obj, Register scratch,
michael@0 1497 const TypedOrValueRegister &output)
michael@0 1498 {
michael@0 1499 MGetPropertyPolymorphic *mir = ins->mirRaw()->toGetPropertyPolymorphic();
michael@0 1500 JS_ASSERT(mir->numShapes() > 1);
michael@0 1501
michael@0 1502 masm.loadObjShape(obj, scratch);
michael@0 1503
michael@0 1504 Label done;
michael@0 1505 for (size_t i = 0; i < mir->numShapes(); i++) {
michael@0 1506 Label next;
michael@0 1507 masm.branchPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)), &next);
michael@0 1508
michael@0 1509 Shape *shape = mir->shape(i);
michael@0 1510 if (shape->slot() < shape->numFixedSlots()) {
michael@0 1511 // Fixed slot.
michael@0 1512 masm.loadTypedOrValue(Address(obj, JSObject::getFixedSlotOffset(shape->slot())),
michael@0 1513 output);
michael@0 1514 } else {
michael@0 1515 // Dynamic slot.
michael@0 1516 uint32_t offset = (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value);
michael@0 1517 masm.loadPtr(Address(obj, JSObject::offsetOfSlots()), scratch);
michael@0 1518 masm.loadTypedOrValue(Address(scratch, offset), output);
michael@0 1519 }
michael@0 1520
michael@0 1521 masm.jump(&done);
michael@0 1522 masm.bind(&next);
michael@0 1523 }
michael@0 1524
michael@0 1525 // Bailout if no shape matches.
michael@0 1526 if (!bailout(ins->snapshot()))
michael@0 1527 return false;
michael@0 1528
michael@0 1529 masm.bind(&done);
michael@0 1530 return true;
michael@0 1531 }
michael@0 1532
michael@0 1533 bool
michael@0 1534 CodeGenerator::visitGetPropertyPolymorphicV(LGetPropertyPolymorphicV *ins)
michael@0 1535 {
michael@0 1536 Register obj = ToRegister(ins->obj());
michael@0 1537 ValueOperand output = GetValueOutput(ins);
michael@0 1538 return emitGetPropertyPolymorphic(ins, obj, output.scratchReg(), output);
michael@0 1539 }
michael@0 1540
michael@0 1541 bool
michael@0 1542 CodeGenerator::visitGetPropertyPolymorphicT(LGetPropertyPolymorphicT *ins)
michael@0 1543 {
michael@0 1544 Register obj = ToRegister(ins->obj());
michael@0 1545 TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->output()));
michael@0 1546 Register temp = (output.type() == MIRType_Double)
michael@0 1547 ? ToRegister(ins->temp())
michael@0 1548 : output.typedReg().gpr();
michael@0 1549 return emitGetPropertyPolymorphic(ins, obj, temp, output);
michael@0 1550 }
michael@0 1551
michael@0 1552 bool
michael@0 1553 CodeGenerator::emitSetPropertyPolymorphic(LInstruction *ins, Register obj, Register scratch,
michael@0 1554 const ConstantOrRegister &value)
michael@0 1555 {
michael@0 1556 MSetPropertyPolymorphic *mir = ins->mirRaw()->toSetPropertyPolymorphic();
michael@0 1557 JS_ASSERT(mir->numShapes() > 1);
michael@0 1558
michael@0 1559 masm.loadObjShape(obj, scratch);
michael@0 1560
michael@0 1561 Label done;
michael@0 1562 for (size_t i = 0; i < mir->numShapes(); i++) {
michael@0 1563 Label next;
michael@0 1564 masm.branchPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)), &next);
michael@0 1565
michael@0 1566 Shape *shape = mir->shape(i);
michael@0 1567 if (shape->slot() < shape->numFixedSlots()) {
michael@0 1568 // Fixed slot.
michael@0 1569 Address addr(obj, JSObject::getFixedSlotOffset(shape->slot()));
michael@0 1570 if (mir->needsBarrier())
michael@0 1571 emitPreBarrier(addr, MIRType_Value);
michael@0 1572 masm.storeConstantOrRegister(value, addr);
michael@0 1573 } else {
michael@0 1574 // Dynamic slot.
michael@0 1575 masm.loadPtr(Address(obj, JSObject::offsetOfSlots()), scratch);
michael@0 1576 Address addr(scratch, (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value));
michael@0 1577 if (mir->needsBarrier())
michael@0 1578 emitPreBarrier(addr, MIRType_Value);
michael@0 1579 masm.storeConstantOrRegister(value, addr);
michael@0 1580 }
michael@0 1581
michael@0 1582 masm.jump(&done);
michael@0 1583 masm.bind(&next);
michael@0 1584 }
michael@0 1585
michael@0 1586 // Bailout if no shape matches.
michael@0 1587 if (!bailout(ins->snapshot()))
michael@0 1588 return false;
michael@0 1589
michael@0 1590 masm.bind(&done);
michael@0 1591 return true;
michael@0 1592 }
michael@0 1593
michael@0 1594 bool
michael@0 1595 CodeGenerator::visitSetPropertyPolymorphicV(LSetPropertyPolymorphicV *ins)
michael@0 1596 {
michael@0 1597 Register obj = ToRegister(ins->obj());
michael@0 1598 Register temp = ToRegister(ins->temp());
michael@0 1599 ValueOperand value = ToValue(ins, LSetPropertyPolymorphicV::Value);
michael@0 1600 return emitSetPropertyPolymorphic(ins, obj, temp, TypedOrValueRegister(value));
michael@0 1601 }
michael@0 1602
michael@0 1603 bool
michael@0 1604 CodeGenerator::visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT *ins)
michael@0 1605 {
michael@0 1606 Register obj = ToRegister(ins->obj());
michael@0 1607 Register temp = ToRegister(ins->temp());
michael@0 1608
michael@0 1609 ConstantOrRegister value;
michael@0 1610 if (ins->mir()->value()->isConstant())
michael@0 1611 value = ConstantOrRegister(ins->mir()->value()->toConstant()->value());
michael@0 1612 else
michael@0 1613 value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(ins->value()));
michael@0 1614
michael@0 1615 return emitSetPropertyPolymorphic(ins, obj, temp, value);
michael@0 1616 }
michael@0 1617
michael@0 1618 bool
michael@0 1619 CodeGenerator::visitElements(LElements *lir)
michael@0 1620 {
michael@0 1621 Address elements(ToRegister(lir->object()), JSObject::offsetOfElements());
michael@0 1622 masm.loadPtr(elements, ToRegister(lir->output()));
michael@0 1623 return true;
michael@0 1624 }
michael@0 1625
michael@0 1626 typedef bool (*ConvertElementsToDoublesFn)(JSContext *, uintptr_t);
michael@0 1627 static const VMFunction ConvertElementsToDoublesInfo =
michael@0 1628 FunctionInfo<ConvertElementsToDoublesFn>(ObjectElements::ConvertElementsToDoubles);
michael@0 1629
michael@0 1630 bool
michael@0 1631 CodeGenerator::visitConvertElementsToDoubles(LConvertElementsToDoubles *lir)
michael@0 1632 {
michael@0 1633 Register elements = ToRegister(lir->elements());
michael@0 1634
michael@0 1635 OutOfLineCode *ool = oolCallVM(ConvertElementsToDoublesInfo, lir,
michael@0 1636 (ArgList(), elements), StoreNothing());
michael@0 1637 if (!ool)
michael@0 1638 return false;
michael@0 1639
michael@0 1640 Address convertedAddress(elements, ObjectElements::offsetOfFlags());
michael@0 1641 Imm32 bit(ObjectElements::CONVERT_DOUBLE_ELEMENTS);
michael@0 1642 masm.branchTest32(Assembler::Zero, convertedAddress, bit, ool->entry());
michael@0 1643 masm.bind(ool->rejoin());
michael@0 1644 return true;
michael@0 1645 }
michael@0 1646
michael@0 1647 bool
michael@0 1648 CodeGenerator::visitMaybeToDoubleElement(LMaybeToDoubleElement *lir)
michael@0 1649 {
michael@0 1650 Register elements = ToRegister(lir->elements());
michael@0 1651 Register value = ToRegister(lir->value());
michael@0 1652 ValueOperand out = ToOutValue(lir);
michael@0 1653
michael@0 1654 FloatRegister temp = ToFloatRegister(lir->tempFloat());
michael@0 1655 Label convert, done;
michael@0 1656
michael@0 1657 // If the CONVERT_DOUBLE_ELEMENTS flag is set, convert the int32
michael@0 1658 // value to double. Else, just box it.
michael@0 1659 masm.branchTest32(Assembler::NonZero,
michael@0 1660 Address(elements, ObjectElements::offsetOfFlags()),
michael@0 1661 Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),
michael@0 1662 &convert);
michael@0 1663
michael@0 1664 masm.tagValue(JSVAL_TYPE_INT32, value, out);
michael@0 1665 masm.jump(&done);
michael@0 1666
michael@0 1667 masm.bind(&convert);
michael@0 1668 masm.convertInt32ToDouble(value, temp);
michael@0 1669 masm.boxDouble(temp, out);
michael@0 1670
michael@0 1671 masm.bind(&done);
michael@0 1672 return true;
michael@0 1673 }
michael@0 1674
michael@0 1675 bool
michael@0 1676 CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment *lir)
michael@0 1677 {
michael@0 1678 Address environment(ToRegister(lir->function()), JSFunction::offsetOfEnvironment());
michael@0 1679 masm.loadPtr(environment, ToRegister(lir->output()));
michael@0 1680 return true;
michael@0 1681 }
michael@0 1682
michael@0 1683 bool
michael@0 1684 CodeGenerator::visitForkJoinContext(LForkJoinContext *lir)
michael@0 1685 {
michael@0 1686 const Register tempReg = ToRegister(lir->getTempReg());
michael@0 1687
michael@0 1688 masm.setupUnalignedABICall(0, tempReg);
michael@0 1689 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ForkJoinContextPar));
michael@0 1690 JS_ASSERT(ToRegister(lir->output()) == ReturnReg);
michael@0 1691 return true;
michael@0 1692 }
michael@0 1693
michael@0 1694 bool
michael@0 1695 CodeGenerator::visitGuardThreadExclusive(LGuardThreadExclusive *lir)
michael@0 1696 {
michael@0 1697 JS_ASSERT(gen->info().executionMode() == ParallelExecution);
michael@0 1698
michael@0 1699 const Register tempReg = ToRegister(lir->getTempReg());
michael@0 1700 masm.setupUnalignedABICall(2, tempReg);
michael@0 1701 masm.passABIArg(ToRegister(lir->forkJoinContext()));
michael@0 1702 masm.passABIArg(ToRegister(lir->object()));
michael@0 1703 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParallelWriteGuard));
michael@0 1704
michael@0 1705 OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutIllegalWrite, lir);
michael@0 1706 if (!bail)
michael@0 1707 return false;
michael@0 1708
michael@0 1709 // branch to the OOL failure code if false is returned
michael@0 1710 masm.branchIfFalseBool(ReturnReg, bail->entry());
michael@0 1711 return true;
michael@0 1712 }
michael@0 1713
michael@0 1714 bool
michael@0 1715 CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity *guard)
michael@0 1716 {
michael@0 1717 Register obj = ToRegister(guard->input());
michael@0 1718
michael@0 1719 Assembler::Condition cond =
michael@0 1720 guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
michael@0 1721 return bailoutCmpPtr(cond, obj, ImmGCPtr(guard->mir()->singleObject()), guard->snapshot());
michael@0 1722 }
michael@0 1723
michael@0 1724 bool
michael@0 1725 CodeGenerator::visitTypeBarrierV(LTypeBarrierV *lir)
michael@0 1726 {
michael@0 1727 ValueOperand operand = ToValue(lir, LTypeBarrierV::Input);
michael@0 1728 Register scratch = ToTempRegisterOrInvalid(lir->temp());
michael@0 1729
michael@0 1730 Label miss;
michael@0 1731 masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), scratch, &miss);
michael@0 1732 if (!bailoutFrom(&miss, lir->snapshot()))
michael@0 1733 return false;
michael@0 1734 return true;
michael@0 1735 }
michael@0 1736
michael@0 1737 bool
michael@0 1738 CodeGenerator::visitTypeBarrierO(LTypeBarrierO *lir)
michael@0 1739 {
michael@0 1740 Register obj = ToRegister(lir->object());
michael@0 1741 Register scratch = ToTempRegisterOrInvalid(lir->temp());
michael@0 1742
michael@0 1743 Label miss;
michael@0 1744 masm.guardObjectType(obj, lir->mir()->resultTypeSet(), scratch, &miss);
michael@0 1745 if (!bailoutFrom(&miss, lir->snapshot()))
michael@0 1746 return false;
michael@0 1747 return true;
michael@0 1748 }
michael@0 1749
michael@0 1750 bool
michael@0 1751 CodeGenerator::visitMonitorTypes(LMonitorTypes *lir)
michael@0 1752 {
michael@0 1753 ValueOperand operand = ToValue(lir, LMonitorTypes::Input);
michael@0 1754 Register scratch = ToTempUnboxRegister(lir->temp());
michael@0 1755
michael@0 1756 Label matched, miss;
michael@0 1757 masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &miss);
michael@0 1758 if (!bailoutFrom(&miss, lir->snapshot()))
michael@0 1759 return false;
michael@0 1760 return true;
michael@0 1761 }
michael@0 1762
michael@0 1763 #ifdef JSGC_GENERATIONAL
michael@0 1764 // Out-of-line path to update the store buffer.
michael@0 1765 class OutOfLineCallPostWriteBarrier : public OutOfLineCodeBase<CodeGenerator>
michael@0 1766 {
michael@0 1767 LInstruction *lir_;
michael@0 1768 const LAllocation *object_;
michael@0 1769
michael@0 1770 public:
michael@0 1771 OutOfLineCallPostWriteBarrier(LInstruction *lir, const LAllocation *object)
michael@0 1772 : lir_(lir), object_(object)
michael@0 1773 { }
michael@0 1774
michael@0 1775 bool accept(CodeGenerator *codegen) {
michael@0 1776 return codegen->visitOutOfLineCallPostWriteBarrier(this);
michael@0 1777 }
michael@0 1778
michael@0 1779 LInstruction *lir() const {
michael@0 1780 return lir_;
michael@0 1781 }
michael@0 1782 const LAllocation *object() const {
michael@0 1783 return object_;
michael@0 1784 }
michael@0 1785 };
michael@0 1786
michael@0 1787 bool
michael@0 1788 CodeGenerator::visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier *ool)
michael@0 1789 {
michael@0 1790 saveLiveVolatile(ool->lir());
michael@0 1791
michael@0 1792 const LAllocation *obj = ool->object();
michael@0 1793
michael@0 1794 GeneralRegisterSet regs = GeneralRegisterSet::Volatile();
michael@0 1795
michael@0 1796 Register objreg;
michael@0 1797 bool isGlobal = false;
michael@0 1798 if (obj->isConstant()) {
michael@0 1799 JSObject *object = &obj->toConstant()->toObject();
michael@0 1800 isGlobal = object->is<GlobalObject>();
michael@0 1801 objreg = regs.takeAny();
michael@0 1802 masm.movePtr(ImmGCPtr(object), objreg);
michael@0 1803 } else {
michael@0 1804 objreg = ToRegister(obj);
michael@0 1805 regs.takeUnchecked(objreg);
michael@0 1806 }
michael@0 1807
michael@0 1808 Register runtimereg = regs.takeAny();
michael@0 1809 masm.mov(ImmPtr(GetIonContext()->runtime), runtimereg);
michael@0 1810
michael@0 1811 void (*fun)(JSRuntime*, JSObject*) = isGlobal ? PostGlobalWriteBarrier : PostWriteBarrier;
michael@0 1812 masm.setupUnalignedABICall(2, regs.takeAny());
michael@0 1813 masm.passABIArg(runtimereg);
michael@0 1814 masm.passABIArg(objreg);
michael@0 1815 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, fun));
michael@0 1816
michael@0 1817 restoreLiveVolatile(ool->lir());
michael@0 1818
michael@0 1819 masm.jump(ool->rejoin());
michael@0 1820 return true;
michael@0 1821 }
michael@0 1822 #endif
michael@0 1823
michael@0 1824 bool
michael@0 1825 CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO *lir)
michael@0 1826 {
michael@0 1827 #ifdef JSGC_GENERATIONAL
michael@0 1828 OutOfLineCallPostWriteBarrier *ool = new(alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
michael@0 1829 if (!addOutOfLineCode(ool))
michael@0 1830 return false;
michael@0 1831
michael@0 1832 Register temp = ToTempRegisterOrInvalid(lir->temp());
michael@0 1833
michael@0 1834 if (lir->object()->isConstant()) {
michael@0 1835 #ifdef DEBUG
michael@0 1836 const Nursery &nursery = GetIonContext()->runtime->gcNursery();
michael@0 1837 JS_ASSERT(!nursery.isInside(&lir->object()->toConstant()->toObject()));
michael@0 1838 #endif
michael@0 1839 } else {
michael@0 1840 masm.branchPtrInNurseryRange(ToRegister(lir->object()), temp, ool->rejoin());
michael@0 1841 }
michael@0 1842
michael@0 1843 masm.branchPtrInNurseryRange(ToRegister(lir->value()), temp, ool->entry());
michael@0 1844
michael@0 1845 masm.bind(ool->rejoin());
michael@0 1846 #endif
michael@0 1847 return true;
michael@0 1848 }
michael@0 1849
michael@0 1850 bool
michael@0 1851 CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV *lir)
michael@0 1852 {
michael@0 1853 #ifdef JSGC_GENERATIONAL
michael@0 1854 OutOfLineCallPostWriteBarrier *ool = new(alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
michael@0 1855 if (!addOutOfLineCode(ool))
michael@0 1856 return false;
michael@0 1857
michael@0 1858 Register temp = ToTempRegisterOrInvalid(lir->temp());
michael@0 1859
michael@0 1860 if (lir->object()->isConstant()) {
michael@0 1861 #ifdef DEBUG
michael@0 1862 const Nursery &nursery = GetIonContext()->runtime->gcNursery();
michael@0 1863 JS_ASSERT(!nursery.isInside(&lir->object()->toConstant()->toObject()));
michael@0 1864 #endif
michael@0 1865 } else {
michael@0 1866 masm.branchPtrInNurseryRange(ToRegister(lir->object()), temp, ool->rejoin());
michael@0 1867 }
michael@0 1868
michael@0 1869 ValueOperand value = ToValue(lir, LPostWriteBarrierV::Input);
michael@0 1870 masm.branchValueIsNurseryObject(value, temp, ool->entry());
michael@0 1871
michael@0 1872 masm.bind(ool->rejoin());
michael@0 1873 #endif
michael@0 1874 return true;
michael@0 1875 }
michael@0 1876
michael@0 1877 bool
michael@0 1878 CodeGenerator::visitCallNative(LCallNative *call)
michael@0 1879 {
michael@0 1880 JSFunction *target = call->getSingleTarget();
michael@0 1881 JS_ASSERT(target);
michael@0 1882 JS_ASSERT(target->isNative());
michael@0 1883
michael@0 1884 int callargslot = call->argslot();
michael@0 1885 int unusedStack = StackOffsetOfPassedArg(callargslot);
michael@0 1886
michael@0 1887 // Registers used for callWithABI() argument-passing.
michael@0 1888 const Register argContextReg = ToRegister(call->getArgContextReg());
michael@0 1889 const Register argUintNReg = ToRegister(call->getArgUintNReg());
michael@0 1890 const Register argVpReg = ToRegister(call->getArgVpReg());
michael@0 1891
michael@0 1892 // Misc. temporary registers.
michael@0 1893 const Register tempReg = ToRegister(call->getTempReg());
michael@0 1894
michael@0 1895 DebugOnly<uint32_t> initialStack = masm.framePushed();
michael@0 1896
michael@0 1897 masm.checkStackAlignment();
michael@0 1898
michael@0 1899 // Sequential native functions have the signature:
michael@0 1900 // bool (*)(JSContext *, unsigned, Value *vp)
michael@0 1901 // and parallel native functions have the signature:
michael@0 1902 // ParallelResult (*)(ForkJoinContext *, unsigned, Value *vp)
michael@0 1903 // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
michael@0 1904 // are the function arguments.
michael@0 1905
michael@0 1906 // Allocate space for the outparam, moving the StackPointer to what will be &vp[1].
michael@0 1907 masm.adjustStack(unusedStack);
michael@0 1908
michael@0 1909 // Push a Value containing the callee object: natives are allowed to access their callee before
michael@0 1910 // setitng the return value. The StackPointer is moved to &vp[0].
michael@0 1911 masm.Push(ObjectValue(*target));
michael@0 1912
michael@0 1913 // Preload arguments into registers.
michael@0 1914 //
michael@0 1915 // Note that for parallel execution, loadContext does an ABI call, so we
michael@0 1916 // need to do this before we load the other argument registers, otherwise
michael@0 1917 // we'll hose them.
michael@0 1918 ExecutionMode executionMode = gen->info().executionMode();
michael@0 1919 masm.loadContext(argContextReg, tempReg, executionMode);
michael@0 1920 masm.move32(Imm32(call->numStackArgs()), argUintNReg);
michael@0 1921 masm.movePtr(StackPointer, argVpReg);
michael@0 1922
michael@0 1923 masm.Push(argUintNReg);
michael@0 1924
michael@0 1925 // Construct native exit frame.
michael@0 1926 uint32_t safepointOffset;
michael@0 1927 if (!masm.buildFakeExitFrame(tempReg, &safepointOffset))
michael@0 1928 return false;
michael@0 1929 masm.enterFakeExitFrame(argContextReg, tempReg, executionMode);
michael@0 1930
michael@0 1931 if (!markSafepointAt(safepointOffset, call))
michael@0 1932 return false;
michael@0 1933
michael@0 1934 // Construct and execute call.
michael@0 1935 masm.setupUnalignedABICall(3, tempReg);
michael@0 1936 masm.passABIArg(argContextReg);
michael@0 1937 masm.passABIArg(argUintNReg);
michael@0 1938 masm.passABIArg(argVpReg);
michael@0 1939
michael@0 1940 switch (executionMode) {
michael@0 1941 case SequentialExecution:
michael@0 1942 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->native()));
michael@0 1943 break;
michael@0 1944
michael@0 1945 case ParallelExecution:
michael@0 1946 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->parallelNative()));
michael@0 1947 break;
michael@0 1948
michael@0 1949 default:
michael@0 1950 MOZ_ASSUME_UNREACHABLE("No such execution mode");
michael@0 1951 }
michael@0 1952
michael@0 1953 // Test for failure.
michael@0 1954 masm.branchIfFalseBool(ReturnReg, masm.failureLabel(executionMode));
michael@0 1955
michael@0 1956 // Load the outparam vp[0] into output register(s).
michael@0 1957 masm.loadValue(Address(StackPointer, IonNativeExitFrameLayout::offsetOfResult()), JSReturnOperand);
michael@0 1958
michael@0 1959 // The next instruction is removing the footer of the exit frame, so there
michael@0 1960 // is no need for leaveFakeExitFrame.
michael@0 1961
michael@0 1962 // Move the StackPointer back to its original location, unwinding the native exit frame.
michael@0 1963 masm.adjustStack(IonNativeExitFrameLayout::Size() - unusedStack);
michael@0 1964 JS_ASSERT(masm.framePushed() == initialStack);
michael@0 1965
michael@0 1966 dropArguments(call->numStackArgs() + 1);
michael@0 1967 return true;
michael@0 1968 }
michael@0 1969
michael@0 1970 bool
michael@0 1971 CodeGenerator::visitCallDOMNative(LCallDOMNative *call)
michael@0 1972 {
michael@0 1973 JSFunction *target = call->getSingleTarget();
michael@0 1974 JS_ASSERT(target);
michael@0 1975 JS_ASSERT(target->isNative());
michael@0 1976 JS_ASSERT(target->jitInfo());
michael@0 1977 JS_ASSERT(call->mir()->isCallDOMNative());
michael@0 1978
michael@0 1979 int callargslot = call->argslot();
michael@0 1980 int unusedStack = StackOffsetOfPassedArg(callargslot);
michael@0 1981
michael@0 1982 // Registers used for callWithABI() argument-passing.
michael@0 1983 const Register argJSContext = ToRegister(call->getArgJSContext());
michael@0 1984 const Register argObj = ToRegister(call->getArgObj());
michael@0 1985 const Register argPrivate = ToRegister(call->getArgPrivate());
michael@0 1986 const Register argArgs = ToRegister(call->getArgArgs());
michael@0 1987
michael@0 1988 DebugOnly<uint32_t> initialStack = masm.framePushed();
michael@0 1989
michael@0 1990 masm.checkStackAlignment();
michael@0 1991
michael@0 1992 // DOM methods have the signature:
michael@0 1993 // bool (*)(JSContext *, HandleObject, void *private, const JSJitMethodCallArgs& args)
michael@0 1994 // Where args is initialized from an argc and a vp, vp[0] is space for an
michael@0 1995 // outparam and the callee, vp[1] is |this|, and vp[2] onward are the
michael@0 1996 // function arguments. Note that args stores the argv, not the vp, and
michael@0 1997 // argv == vp + 2.
michael@0 1998
michael@0 1999 // Nestle the stack up against the pushed arguments, leaving StackPointer at
michael@0 2000 // &vp[1]
michael@0 2001 masm.adjustStack(unusedStack);
michael@0 2002 // argObj is filled with the extracted object, then returned.
michael@0 2003 Register obj = masm.extractObject(Address(StackPointer, 0), argObj);
michael@0 2004 JS_ASSERT(obj == argObj);
michael@0 2005
michael@0 2006 // Push a Value containing the callee object: natives are allowed to access their callee before
michael@0 2007 // setitng the return value. After this the StackPointer points to &vp[0].
michael@0 2008 masm.Push(ObjectValue(*target));
michael@0 2009
michael@0 2010 // Now compute the argv value. Since StackPointer is pointing to &vp[0] and
michael@0 2011 // argv is &vp[2] we just need to add 2*sizeof(Value) to the current
michael@0 2012 // StackPointer.
michael@0 2013 JS_STATIC_ASSERT(JSJitMethodCallArgsTraits::offsetOfArgv == 0);
michael@0 2014 JS_STATIC_ASSERT(JSJitMethodCallArgsTraits::offsetOfArgc ==
michael@0 2015 IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv);
michael@0 2016 masm.computeEffectiveAddress(Address(StackPointer, 2 * sizeof(Value)), argArgs);
michael@0 2017
michael@0 2018 // GetReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate()
michael@0 2019 masm.loadPrivate(Address(obj, JSObject::getFixedSlotOffset(0)), argPrivate);
michael@0 2020
michael@0 2021 // Push argc from the call instruction into what will become the IonExitFrame
michael@0 2022 masm.Push(Imm32(call->numStackArgs()));
michael@0 2023
michael@0 2024 // Push our argv onto the stack
michael@0 2025 masm.Push(argArgs);
michael@0 2026 // And store our JSJitMethodCallArgs* in argArgs.
michael@0 2027 masm.movePtr(StackPointer, argArgs);
michael@0 2028
michael@0 2029 // Push |this| object for passing HandleObject. We push after argc to
michael@0 2030 // maintain the same sp-relative location of the object pointer with other
michael@0 2031 // DOMExitFrames.
michael@0 2032 masm.Push(argObj);
michael@0 2033 masm.movePtr(StackPointer, argObj);
michael@0 2034
michael@0 2035 // Construct native exit frame.
michael@0 2036 uint32_t safepointOffset;
michael@0 2037 if (!masm.buildFakeExitFrame(argJSContext, &safepointOffset))
michael@0 2038 return false;
michael@0 2039 masm.enterFakeExitFrame(ION_FRAME_DOMMETHOD);
michael@0 2040
michael@0 2041 if (!markSafepointAt(safepointOffset, call))
michael@0 2042 return false;
michael@0 2043
michael@0 2044 // Construct and execute call.
michael@0 2045 masm.setupUnalignedABICall(4, argJSContext);
michael@0 2046
michael@0 2047 masm.loadJSContext(argJSContext);
michael@0 2048
michael@0 2049 masm.passABIArg(argJSContext);
michael@0 2050 masm.passABIArg(argObj);
michael@0 2051 masm.passABIArg(argPrivate);
michael@0 2052 masm.passABIArg(argArgs);
michael@0 2053 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->jitInfo()->method));
michael@0 2054
michael@0 2055 if (target->jitInfo()->isInfallible) {
michael@0 2056 masm.loadValue(Address(StackPointer, IonDOMMethodExitFrameLayout::offsetOfResult()),
michael@0 2057 JSReturnOperand);
michael@0 2058 } else {
michael@0 2059 // Test for failure.
michael@0 2060 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
michael@0 2061
michael@0 2062 // Load the outparam vp[0] into output register(s).
michael@0 2063 masm.loadValue(Address(StackPointer, IonDOMMethodExitFrameLayout::offsetOfResult()),
michael@0 2064 JSReturnOperand);
michael@0 2065 }
michael@0 2066
michael@0 2067 // The next instruction is removing the footer of the exit frame, so there
michael@0 2068 // is no need for leaveFakeExitFrame.
michael@0 2069
michael@0 2070 // Move the StackPointer back to its original location, unwinding the native exit frame.
michael@0 2071 masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack);
michael@0 2072 JS_ASSERT(masm.framePushed() == initialStack);
michael@0 2073
michael@0 2074 dropArguments(call->numStackArgs() + 1);
michael@0 2075 return true;
michael@0 2076 }
michael@0 2077
michael@0 2078 typedef bool (*GetIntrinsicValueFn)(JSContext *cx, HandlePropertyName, MutableHandleValue);
michael@0 2079 static const VMFunction GetIntrinsicValueInfo =
michael@0 2080 FunctionInfo<GetIntrinsicValueFn>(GetIntrinsicValue);
michael@0 2081
michael@0 2082 bool
michael@0 2083 CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir)
michael@0 2084 {
michael@0 2085 pushArg(ImmGCPtr(lir->mir()->name()));
michael@0 2086 return callVM(GetIntrinsicValueInfo, lir);
michael@0 2087 }
michael@0 2088
michael@0 2089 typedef bool (*InvokeFunctionFn)(JSContext *, HandleObject, uint32_t, Value *, Value *);
michael@0 2090 static const VMFunction InvokeFunctionInfo = FunctionInfo<InvokeFunctionFn>(InvokeFunction);
michael@0 2091
michael@0 2092 bool
michael@0 2093 CodeGenerator::emitCallInvokeFunction(LInstruction *call, Register calleereg,
michael@0 2094 uint32_t argc, uint32_t unusedStack)
michael@0 2095 {
michael@0 2096 // Nestle %esp up to the argument vector.
michael@0 2097 // Each path must account for framePushed_ separately, for callVM to be valid.
michael@0 2098 masm.freeStack(unusedStack);
michael@0 2099
michael@0 2100 pushArg(StackPointer); // argv.
michael@0 2101 pushArg(Imm32(argc)); // argc.
michael@0 2102 pushArg(calleereg); // JSFunction *.
michael@0 2103
michael@0 2104 if (!callVM(InvokeFunctionInfo, call))
michael@0 2105 return false;
michael@0 2106
michael@0 2107 // Un-nestle %esp from the argument vector. No prefix was pushed.
michael@0 2108 masm.reserveStack(unusedStack);
michael@0 2109 return true;
michael@0 2110 }
michael@0 2111
michael@0 2112 bool
michael@0 2113 CodeGenerator::visitCallGeneric(LCallGeneric *call)
michael@0 2114 {
michael@0 2115 Register calleereg = ToRegister(call->getFunction());
michael@0 2116 Register objreg = ToRegister(call->getTempObject());
michael@0 2117 Register nargsreg = ToRegister(call->getNargsReg());
michael@0 2118 uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot());
michael@0 2119 ExecutionMode executionMode = gen->info().executionMode();
michael@0 2120 Label invoke, thunk, makeCall, end;
michael@0 2121
michael@0 2122 // Known-target case is handled by LCallKnown.
michael@0 2123 JS_ASSERT(!call->hasSingleTarget());
michael@0 2124
michael@0 2125 // Generate an ArgumentsRectifier.
michael@0 2126 JitCode *argumentsRectifier = gen->jitRuntime()->getArgumentsRectifier(executionMode);
michael@0 2127
michael@0 2128 masm.checkStackAlignment();
michael@0 2129
michael@0 2130 // Guard that calleereg is actually a function object.
michael@0 2131 masm.loadObjClass(calleereg, nargsreg);
michael@0 2132 masm.branchPtr(Assembler::NotEqual, nargsreg, ImmPtr(&JSFunction::class_), &invoke);
michael@0 2133
michael@0 2134 // Guard that calleereg is an interpreted function with a JSScript.
michael@0 2135 // If we are constructing, also ensure the callee is a constructor.
michael@0 2136 if (call->mir()->isConstructing())
michael@0 2137 masm.branchIfNotInterpretedConstructor(calleereg, nargsreg, &invoke);
michael@0 2138 else
michael@0 2139 masm.branchIfFunctionHasNoScript(calleereg, &invoke);
michael@0 2140
michael@0 2141 // Knowing that calleereg is a non-native function, load the JSScript.
michael@0 2142 masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
michael@0 2143
michael@0 2144 // Load script jitcode.
michael@0 2145 masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke);
michael@0 2146
michael@0 2147 // Nestle the StackPointer up to the argument vector.
michael@0 2148 masm.freeStack(unusedStack);
michael@0 2149
michael@0 2150 // Construct the IonFramePrefix.
michael@0 2151 uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS);
michael@0 2152 masm.Push(Imm32(call->numActualArgs()));
michael@0 2153 masm.Push(calleereg);
michael@0 2154 masm.Push(Imm32(descriptor));
michael@0 2155
michael@0 2156 // Check whether the provided arguments satisfy target argc.
michael@0 2157 masm.load16ZeroExtend(Address(calleereg, JSFunction::offsetOfNargs()), nargsreg);
michael@0 2158 masm.branch32(Assembler::Above, nargsreg, Imm32(call->numStackArgs()), &thunk);
michael@0 2159 masm.jump(&makeCall);
michael@0 2160
michael@0 2161 // Argument fixed needed. Load the ArgumentsRectifier.
michael@0 2162 masm.bind(&thunk);
michael@0 2163 {
michael@0 2164 JS_ASSERT(ArgumentsRectifierReg != objreg);
michael@0 2165 masm.movePtr(ImmGCPtr(argumentsRectifier), objreg); // Necessary for GC marking.
michael@0 2166 masm.loadPtr(Address(objreg, JitCode::offsetOfCode()), objreg);
michael@0 2167 masm.move32(Imm32(call->numStackArgs()), ArgumentsRectifierReg);
michael@0 2168 }
michael@0 2169
michael@0 2170 // Finally call the function in objreg.
michael@0 2171 masm.bind(&makeCall);
michael@0 2172 uint32_t callOffset = masm.callIon(objreg);
michael@0 2173 if (!markSafepointAt(callOffset, call))
michael@0 2174 return false;
michael@0 2175
michael@0 2176 // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
michael@0 2177 // The return address has already been removed from the Ion frame.
michael@0 2178 int prefixGarbage = sizeof(IonJSFrameLayout) - sizeof(void *);
michael@0 2179 masm.adjustStack(prefixGarbage - unusedStack);
michael@0 2180 masm.jump(&end);
michael@0 2181
michael@0 2182 // Handle uncompiled or native functions.
michael@0 2183 masm.bind(&invoke);
michael@0 2184 switch (executionMode) {
michael@0 2185 case SequentialExecution:
michael@0 2186 if (!emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack))
michael@0 2187 return false;
michael@0 2188 break;
michael@0 2189
michael@0 2190 case ParallelExecution:
michael@0 2191 if (!emitCallToUncompiledScriptPar(call, calleereg))
michael@0 2192 return false;
michael@0 2193 break;
michael@0 2194
michael@0 2195 default:
michael@0 2196 MOZ_ASSUME_UNREACHABLE("No such execution mode");
michael@0 2197 }
michael@0 2198
michael@0 2199 masm.bind(&end);
michael@0 2200
michael@0 2201 // If the return value of the constructing function is Primitive,
michael@0 2202 // replace the return value with the Object from CreateThis.
michael@0 2203 if (call->mir()->isConstructing()) {
michael@0 2204 Label notPrimitive;
michael@0 2205 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, &notPrimitive);
michael@0 2206 masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
michael@0 2207 masm.bind(&notPrimitive);
michael@0 2208 }
michael@0 2209
michael@0 2210 if (!checkForAbortPar(call))
michael@0 2211 return false;
michael@0 2212
michael@0 2213 dropArguments(call->numStackArgs() + 1);
michael@0 2214 return true;
michael@0 2215 }
michael@0 2216
michael@0 2217 // Generates a call to CallToUncompiledScriptPar() and then bails out.
michael@0 2218 // |calleeReg| should contain the JSFunction*.
michael@0 2219 bool
michael@0 2220 CodeGenerator::emitCallToUncompiledScriptPar(LInstruction *lir, Register calleeReg)
michael@0 2221 {
michael@0 2222 OutOfLineCode *bail = oolAbortPar(ParallelBailoutCalledToUncompiledScript, lir);
michael@0 2223 if (!bail)
michael@0 2224 return false;
michael@0 2225
michael@0 2226 masm.movePtr(calleeReg, CallTempReg0);
michael@0 2227 masm.setupUnalignedABICall(1, CallTempReg1);
michael@0 2228 masm.passABIArg(CallTempReg0);
michael@0 2229 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, CallToUncompiledScriptPar));
michael@0 2230 masm.jump(bail->entry());
michael@0 2231 return true;
michael@0 2232 }
michael@0 2233
michael@0 2234 bool
michael@0 2235 CodeGenerator::visitCallKnown(LCallKnown *call)
michael@0 2236 {
michael@0 2237 Register calleereg = ToRegister(call->getFunction());
michael@0 2238 Register objreg = ToRegister(call->getTempObject());
michael@0 2239 uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot());
michael@0 2240 DebugOnly<JSFunction *> target = call->getSingleTarget();
michael@0 2241 ExecutionMode executionMode = gen->info().executionMode();
michael@0 2242 Label end, uncompiled;
michael@0 2243
michael@0 2244 // Native single targets are handled by LCallNative.
michael@0 2245 JS_ASSERT(!target->isNative());
michael@0 2246 // Missing arguments must have been explicitly appended by the IonBuilder.
michael@0 2247 JS_ASSERT(target->nargs() <= call->numStackArgs());
michael@0 2248
michael@0 2249 JS_ASSERT_IF(call->mir()->isConstructing(), target->isInterpretedConstructor());
michael@0 2250
michael@0 2251 masm.checkStackAlignment();
michael@0 2252
michael@0 2253 // The calleereg is known to be a non-native function, but might point to
michael@0 2254 // a LazyScript instead of a JSScript.
michael@0 2255 masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
michael@0 2256
michael@0 2257 // Knowing that calleereg is a non-native function, load the JSScript.
michael@0 2258 masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
michael@0 2259
michael@0 2260 // Load script jitcode.
michael@0 2261 if (call->mir()->needsArgCheck())
michael@0 2262 masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &uncompiled);
michael@0 2263 else
michael@0 2264 masm.loadBaselineOrIonNoArgCheck(objreg, objreg, executionMode, &uncompiled);
michael@0 2265
michael@0 2266 // Nestle the StackPointer up to the argument vector.
michael@0 2267 masm.freeStack(unusedStack);
michael@0 2268
michael@0 2269 // Construct the IonFramePrefix.
michael@0 2270 uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS);
michael@0 2271 masm.Push(Imm32(call->numActualArgs()));
michael@0 2272 masm.Push(calleereg);
michael@0 2273 masm.Push(Imm32(descriptor));
michael@0 2274
michael@0 2275 // Finally call the function in objreg.
michael@0 2276 uint32_t callOffset = masm.callIon(objreg);
michael@0 2277 if (!markSafepointAt(callOffset, call))
michael@0 2278 return false;
michael@0 2279
michael@0 2280 // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
michael@0 2281 // The return address has already been removed from the Ion frame.
michael@0 2282 int prefixGarbage = sizeof(IonJSFrameLayout) - sizeof(void *);
michael@0 2283 masm.adjustStack(prefixGarbage - unusedStack);
michael@0 2284 masm.jump(&end);
michael@0 2285
michael@0 2286 // Handle uncompiled functions.
michael@0 2287 masm.bind(&uncompiled);
michael@0 2288 switch (executionMode) {
michael@0 2289 case SequentialExecution:
michael@0 2290 if (!emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack))
michael@0 2291 return false;
michael@0 2292 break;
michael@0 2293
michael@0 2294 case ParallelExecution:
michael@0 2295 if (!emitCallToUncompiledScriptPar(call, calleereg))
michael@0 2296 return false;
michael@0 2297 break;
michael@0 2298
michael@0 2299 default:
michael@0 2300 MOZ_ASSUME_UNREACHABLE("No such execution mode");
michael@0 2301 }
michael@0 2302
michael@0 2303 masm.bind(&end);
michael@0 2304
michael@0 2305 if (!checkForAbortPar(call))
michael@0 2306 return false;
michael@0 2307
michael@0 2308 // If the return value of the constructing function is Primitive,
michael@0 2309 // replace the return value with the Object from CreateThis.
michael@0 2310 if (call->mir()->isConstructing()) {
michael@0 2311 Label notPrimitive;
michael@0 2312 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, &notPrimitive);
michael@0 2313 masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
michael@0 2314 masm.bind(&notPrimitive);
michael@0 2315 }
michael@0 2316
michael@0 2317 dropArguments(call->numStackArgs() + 1);
michael@0 2318 return true;
michael@0 2319 }
michael@0 2320
michael@0 2321 bool
michael@0 2322 CodeGenerator::checkForAbortPar(LInstruction *lir)
michael@0 2323 {
michael@0 2324 // In parallel mode, if we call another ion-compiled function and
michael@0 2325 // it returns JS_ION_ERROR, that indicates a bailout that we have
michael@0 2326 // to propagate up the stack.
michael@0 2327 ExecutionMode executionMode = gen->info().executionMode();
michael@0 2328 if (executionMode == ParallelExecution) {
michael@0 2329 OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(lir);
michael@0 2330 if (!bail)
michael@0 2331 return false;
michael@0 2332 masm.branchTestMagic(Assembler::Equal, JSReturnOperand, bail->entry());
michael@0 2333 }
michael@0 2334 return true;
michael@0 2335 }
michael@0 2336
michael@0 2337 bool
michael@0 2338 CodeGenerator::emitCallInvokeFunction(LApplyArgsGeneric *apply, Register extraStackSize)
michael@0 2339 {
michael@0 2340 Register objreg = ToRegister(apply->getTempObject());
michael@0 2341 JS_ASSERT(objreg != extraStackSize);
michael@0 2342
michael@0 2343 // Push the space used by the arguments.
michael@0 2344 masm.movePtr(StackPointer, objreg);
michael@0 2345 masm.Push(extraStackSize);
michael@0 2346
michael@0 2347 pushArg(objreg); // argv.
michael@0 2348 pushArg(ToRegister(apply->getArgc())); // argc.
michael@0 2349 pushArg(ToRegister(apply->getFunction())); // JSFunction *.
michael@0 2350
michael@0 2351 // This specialization og callVM restore the extraStackSize after the call.
michael@0 2352 if (!callVM(InvokeFunctionInfo, apply, &extraStackSize))
michael@0 2353 return false;
michael@0 2354
michael@0 2355 masm.Pop(extraStackSize);
michael@0 2356 return true;
michael@0 2357 }
michael@0 2358
michael@0 2359 // Do not bailout after the execution of this function since the stack no longer
michael@0 2360 // correspond to what is expected by the snapshots.
michael@0 2361 void
michael@0 2362 CodeGenerator::emitPushArguments(LApplyArgsGeneric *apply, Register extraStackSpace)
michael@0 2363 {
michael@0 2364 // Holds the function nargs. Initially undefined.
michael@0 2365 Register argcreg = ToRegister(apply->getArgc());
michael@0 2366
michael@0 2367 Register copyreg = ToRegister(apply->getTempObject());
michael@0 2368 size_t argvOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs();
michael@0 2369 Label end;
michael@0 2370
michael@0 2371 // Initialize the loop counter AND Compute the stack usage (if == 0)
michael@0 2372 masm.movePtr(argcreg, extraStackSpace);
michael@0 2373 masm.branchTestPtr(Assembler::Zero, argcreg, argcreg, &end);
michael@0 2374
michael@0 2375 // Copy arguments.
michael@0 2376 {
michael@0 2377 Register count = extraStackSpace; // <- argcreg
michael@0 2378 Label loop;
michael@0 2379 masm.bind(&loop);
michael@0 2380
michael@0 2381 // We remove sizeof(void*) from argvOffset because withtout it we target
michael@0 2382 // the address after the memory area that we want to copy.
michael@0 2383 BaseIndex disp(StackPointer, argcreg, ScaleFromElemWidth(sizeof(Value)), argvOffset - sizeof(void*));
michael@0 2384
michael@0 2385 // Do not use Push here because other this account to 1 in the framePushed
michael@0 2386 // instead of 0. These push are only counted by argcreg.
michael@0 2387 masm.loadPtr(disp, copyreg);
michael@0 2388 masm.push(copyreg);
michael@0 2389
michael@0 2390 // Handle 32 bits architectures.
michael@0 2391 if (sizeof(Value) == 2 * sizeof(void*)) {
michael@0 2392 masm.loadPtr(disp, copyreg);
michael@0 2393 masm.push(copyreg);
michael@0 2394 }
michael@0 2395
michael@0 2396 masm.decBranchPtr(Assembler::NonZero, count, Imm32(1), &loop);
michael@0 2397 }
michael@0 2398
michael@0 2399 // Compute the stack usage.
michael@0 2400 masm.movePtr(argcreg, extraStackSpace);
michael@0 2401 masm.lshiftPtr(Imm32::ShiftOf(ScaleFromElemWidth(sizeof(Value))), extraStackSpace);
michael@0 2402
michael@0 2403 // Join with all arguments copied and the extra stack usage computed.
michael@0 2404 masm.bind(&end);
michael@0 2405
michael@0 2406 // Push |this|.
michael@0 2407 masm.addPtr(Imm32(sizeof(Value)), extraStackSpace);
michael@0 2408 masm.pushValue(ToValue(apply, LApplyArgsGeneric::ThisIndex));
michael@0 2409 }
michael@0 2410
michael@0 2411 void
michael@0 2412 CodeGenerator::emitPopArguments(LApplyArgsGeneric *apply, Register extraStackSpace)
michael@0 2413 {
michael@0 2414 // Pop |this| and Arguments.
michael@0 2415 masm.freeStack(extraStackSpace);
michael@0 2416 }
michael@0 2417
michael@0 2418 bool
michael@0 2419 CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply)
michael@0 2420 {
michael@0 2421 // Holds the function object.
michael@0 2422 Register calleereg = ToRegister(apply->getFunction());
michael@0 2423
michael@0 2424 // Temporary register for modifying the function object.
michael@0 2425 Register objreg = ToRegister(apply->getTempObject());
michael@0 2426 Register copyreg = ToRegister(apply->getTempCopy());
michael@0 2427
michael@0 2428 // Holds the function nargs. Initially undefined.
michael@0 2429 Register argcreg = ToRegister(apply->getArgc());
michael@0 2430
michael@0 2431 // Unless already known, guard that calleereg is actually a function object.
michael@0 2432 if (!apply->hasSingleTarget()) {
michael@0 2433 masm.loadObjClass(calleereg, objreg);
michael@0 2434
michael@0 2435 ImmPtr ptr = ImmPtr(&JSFunction::class_);
michael@0 2436 if (!bailoutCmpPtr(Assembler::NotEqual, objreg, ptr, apply->snapshot()))
michael@0 2437 return false;
michael@0 2438 }
michael@0 2439
michael@0 2440 // Copy the arguments of the current function.
michael@0 2441 emitPushArguments(apply, copyreg);
michael@0 2442
michael@0 2443 masm.checkStackAlignment();
michael@0 2444
michael@0 2445 // If the function is known to be uncompilable, only emit the call to InvokeFunction.
michael@0 2446 ExecutionMode executionMode = gen->info().executionMode();
michael@0 2447 if (apply->hasSingleTarget()) {
michael@0 2448 JSFunction *target = apply->getSingleTarget();
michael@0 2449 if (target->isNative()) {
michael@0 2450 if (!emitCallInvokeFunction(apply, copyreg))
michael@0 2451 return false;
michael@0 2452 emitPopArguments(apply, copyreg);
michael@0 2453 return true;
michael@0 2454 }
michael@0 2455 }
michael@0 2456
michael@0 2457 Label end, invoke;
michael@0 2458
michael@0 2459 // Guard that calleereg is an interpreted function with a JSScript:
michael@0 2460 if (!apply->hasSingleTarget()) {
michael@0 2461 masm.branchIfFunctionHasNoScript(calleereg, &invoke);
michael@0 2462 } else {
michael@0 2463 // Native single targets are handled by LCallNative.
michael@0 2464 JS_ASSERT(!apply->getSingleTarget()->isNative());
michael@0 2465 }
michael@0 2466
michael@0 2467 // Knowing that calleereg is a non-native function, load the JSScript.
michael@0 2468 masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
michael@0 2469
michael@0 2470 // Load script jitcode.
michael@0 2471 masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke);
michael@0 2472
michael@0 2473 // Call with an Ion frame or a rectifier frame.
michael@0 2474 {
michael@0 2475 // Create the frame descriptor.
michael@0 2476 unsigned pushed = masm.framePushed();
michael@0 2477 masm.addPtr(Imm32(pushed), copyreg);
michael@0 2478 masm.makeFrameDescriptor(copyreg, JitFrame_IonJS);
michael@0 2479
michael@0 2480 masm.Push(argcreg);
michael@0 2481 masm.Push(calleereg);
michael@0 2482 masm.Push(copyreg); // descriptor
michael@0 2483
michael@0 2484 Label underflow, rejoin;
michael@0 2485
michael@0 2486 // Check whether the provided arguments satisfy target argc.
michael@0 2487 if (!apply->hasSingleTarget()) {
michael@0 2488 masm.load16ZeroExtend(Address(calleereg, JSFunction::offsetOfNargs()), copyreg);
michael@0 2489 masm.branch32(Assembler::Below, argcreg, copyreg, &underflow);
michael@0 2490 } else {
michael@0 2491 masm.branch32(Assembler::Below, argcreg, Imm32(apply->getSingleTarget()->nargs()),
michael@0 2492 &underflow);
michael@0 2493 }
michael@0 2494
michael@0 2495 // Skip the construction of the rectifier frame because we have no
michael@0 2496 // underflow.
michael@0 2497 masm.jump(&rejoin);
michael@0 2498
michael@0 2499 // Argument fixup needed. Get ready to call the argumentsRectifier.
michael@0 2500 {
michael@0 2501 masm.bind(&underflow);
michael@0 2502
michael@0 2503 // Hardcode the address of the argumentsRectifier code.
michael@0 2504 JitCode *argumentsRectifier = gen->jitRuntime()->getArgumentsRectifier(executionMode);
michael@0 2505
michael@0 2506 JS_ASSERT(ArgumentsRectifierReg != objreg);
michael@0 2507 masm.movePtr(ImmGCPtr(argumentsRectifier), objreg); // Necessary for GC marking.
michael@0 2508 masm.loadPtr(Address(objreg, JitCode::offsetOfCode()), objreg);
michael@0 2509 masm.movePtr(argcreg, ArgumentsRectifierReg);
michael@0 2510 }
michael@0 2511
michael@0 2512 masm.bind(&rejoin);
michael@0 2513
michael@0 2514 // Finally call the function in objreg, as assigned by one of the paths above.
michael@0 2515 uint32_t callOffset = masm.callIon(objreg);
michael@0 2516 if (!markSafepointAt(callOffset, apply))
michael@0 2517 return false;
michael@0 2518
michael@0 2519 // Recover the number of arguments from the frame descriptor.
michael@0 2520 masm.loadPtr(Address(StackPointer, 0), copyreg);
michael@0 2521 masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), copyreg);
michael@0 2522 masm.subPtr(Imm32(pushed), copyreg);
michael@0 2523
michael@0 2524 // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
michael@0 2525 // The return address has already been removed from the Ion frame.
michael@0 2526 int prefixGarbage = sizeof(IonJSFrameLayout) - sizeof(void *);
michael@0 2527 masm.adjustStack(prefixGarbage);
michael@0 2528 masm.jump(&end);
michael@0 2529 }
michael@0 2530
michael@0 2531 // Handle uncompiled or native functions.
michael@0 2532 {
michael@0 2533 masm.bind(&invoke);
michael@0 2534 if (!emitCallInvokeFunction(apply, copyreg))
michael@0 2535 return false;
michael@0 2536 }
michael@0 2537
michael@0 2538 // Pop arguments and continue.
michael@0 2539 masm.bind(&end);
michael@0 2540 emitPopArguments(apply, copyreg);
michael@0 2541
michael@0 2542 return true;
michael@0 2543 }
michael@0 2544
michael@0 2545 typedef bool (*ArraySpliceDenseFn)(JSContext *, HandleObject, uint32_t, uint32_t);
michael@0 2546 static const VMFunction ArraySpliceDenseInfo = FunctionInfo<ArraySpliceDenseFn>(ArraySpliceDense);
michael@0 2547
michael@0 2548 bool
michael@0 2549 CodeGenerator::visitArraySplice(LArraySplice *lir)
michael@0 2550 {
michael@0 2551 pushArg(ToRegister(lir->getDeleteCount()));
michael@0 2552 pushArg(ToRegister(lir->getStart()));
michael@0 2553 pushArg(ToRegister(lir->getObject()));
michael@0 2554 return callVM(ArraySpliceDenseInfo, lir);
michael@0 2555 }
michael@0 2556
michael@0 2557
michael@0 2558 bool
michael@0 2559 CodeGenerator::visitBail(LBail *lir)
michael@0 2560 {
michael@0 2561 return bailout(lir->snapshot());
michael@0 2562 }
michael@0 2563
michael@0 2564 bool
michael@0 2565 CodeGenerator::visitGetDynamicName(LGetDynamicName *lir)
michael@0 2566 {
michael@0 2567 Register scopeChain = ToRegister(lir->getScopeChain());
michael@0 2568 Register name = ToRegister(lir->getName());
michael@0 2569 Register temp1 = ToRegister(lir->temp1());
michael@0 2570 Register temp2 = ToRegister(lir->temp2());
michael@0 2571 Register temp3 = ToRegister(lir->temp3());
michael@0 2572
michael@0 2573 masm.loadJSContext(temp3);
michael@0 2574
michael@0 2575 /* Make space for the outparam. */
michael@0 2576 masm.adjustStack(-int32_t(sizeof(Value)));
michael@0 2577 masm.movePtr(StackPointer, temp2);
michael@0 2578
michael@0 2579 masm.setupUnalignedABICall(4, temp1);
michael@0 2580 masm.passABIArg(temp3);
michael@0 2581 masm.passABIArg(scopeChain);
michael@0 2582 masm.passABIArg(name);
michael@0 2583 masm.passABIArg(temp2);
michael@0 2584 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, GetDynamicName));
michael@0 2585
michael@0 2586 const ValueOperand out = ToOutValue(lir);
michael@0 2587
michael@0 2588 masm.loadValue(Address(StackPointer, 0), out);
michael@0 2589 masm.adjustStack(sizeof(Value));
michael@0 2590
michael@0 2591 Label undefined;
michael@0 2592 masm.branchTestUndefined(Assembler::Equal, out, &undefined);
michael@0 2593 return bailoutFrom(&undefined, lir->snapshot());
michael@0 2594 }
michael@0 2595
michael@0 2596 bool
michael@0 2597 CodeGenerator::emitFilterArgumentsOrEval(LInstruction *lir, Register string,
michael@0 2598 Register temp1, Register temp2)
michael@0 2599 {
michael@0 2600 masm.loadJSContext(temp2);
michael@0 2601
michael@0 2602 masm.setupUnalignedABICall(2, temp1);
michael@0 2603 masm.passABIArg(temp2);
michael@0 2604 masm.passABIArg(string);
michael@0 2605 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, FilterArgumentsOrEval));
michael@0 2606
michael@0 2607 Label bail;
michael@0 2608 masm.branchIfFalseBool(ReturnReg, &bail);
michael@0 2609 return bailoutFrom(&bail, lir->snapshot());
michael@0 2610 }
michael@0 2611
michael@0 2612 bool
michael@0 2613 CodeGenerator::visitFilterArgumentsOrEvalS(LFilterArgumentsOrEvalS *lir)
michael@0 2614 {
michael@0 2615 return emitFilterArgumentsOrEval(lir, ToRegister(lir->getString()),
michael@0 2616 ToRegister(lir->temp1()),
michael@0 2617 ToRegister(lir->temp2()));
michael@0 2618 }
michael@0 2619
michael@0 2620 bool
michael@0 2621 CodeGenerator::visitFilterArgumentsOrEvalV(LFilterArgumentsOrEvalV *lir)
michael@0 2622 {
michael@0 2623 ValueOperand input = ToValue(lir, LFilterArgumentsOrEvalV::Input);
michael@0 2624
michael@0 2625 // Act as nop on non-strings.
michael@0 2626 Label done;
michael@0 2627 masm.branchTestString(Assembler::NotEqual, input, &done);
michael@0 2628
michael@0 2629 if (!emitFilterArgumentsOrEval(lir, masm.extractString(input, ToRegister(lir->temp3())),
michael@0 2630 ToRegister(lir->temp1()), ToRegister(lir->temp2())))
michael@0 2631 {
michael@0 2632 return false;
michael@0 2633 }
michael@0 2634
michael@0 2635 masm.bind(&done);
michael@0 2636 return true;
michael@0 2637 }
michael@0 2638
michael@0 2639 typedef bool (*DirectEvalSFn)(JSContext *, HandleObject, HandleScript, HandleValue, HandleString,
michael@0 2640 jsbytecode *, MutableHandleValue);
michael@0 2641 static const VMFunction DirectEvalStringInfo = FunctionInfo<DirectEvalSFn>(DirectEvalStringFromIon);
michael@0 2642
michael@0 2643 bool
michael@0 2644 CodeGenerator::visitCallDirectEvalS(LCallDirectEvalS *lir)
michael@0 2645 {
michael@0 2646 Register scopeChain = ToRegister(lir->getScopeChain());
michael@0 2647 Register string = ToRegister(lir->getString());
michael@0 2648
michael@0 2649 pushArg(ImmPtr(lir->mir()->pc()));
michael@0 2650 pushArg(string);
michael@0 2651 pushArg(ToValue(lir, LCallDirectEvalS::ThisValue));
michael@0 2652 pushArg(ImmGCPtr(gen->info().script()));
michael@0 2653 pushArg(scopeChain);
michael@0 2654
michael@0 2655 return callVM(DirectEvalStringInfo, lir);
michael@0 2656 }
michael@0 2657
michael@0 2658 typedef bool (*DirectEvalVFn)(JSContext *, HandleObject, HandleScript, HandleValue, HandleValue,
michael@0 2659 jsbytecode *, MutableHandleValue);
michael@0 2660 static const VMFunction DirectEvalValueInfo = FunctionInfo<DirectEvalVFn>(DirectEvalValueFromIon);
michael@0 2661
michael@0 2662 bool
michael@0 2663 CodeGenerator::visitCallDirectEvalV(LCallDirectEvalV *lir)
michael@0 2664 {
michael@0 2665 Register scopeChain = ToRegister(lir->getScopeChain());
michael@0 2666
michael@0 2667 pushArg(ImmPtr(lir->mir()->pc()));
michael@0 2668 pushArg(ToValue(lir, LCallDirectEvalV::Argument));
michael@0 2669 pushArg(ToValue(lir, LCallDirectEvalV::ThisValue));
michael@0 2670 pushArg(ImmGCPtr(gen->info().script()));
michael@0 2671 pushArg(scopeChain);
michael@0 2672
michael@0 2673 return callVM(DirectEvalValueInfo, lir);
michael@0 2674 }
michael@0 2675
michael@0 2676 // Registers safe for use before generatePrologue().
michael@0 2677 static const uint32_t EntryTempMask = Registers::TempMask & ~(1 << OsrFrameReg.code());
michael@0 2678
michael@0 2679 bool
michael@0 2680 CodeGenerator::generateArgumentsChecks(bool bailout)
michael@0 2681 {
michael@0 2682 // This function can be used the normal way to check the argument types,
michael@0 2683 // before entering the function and bailout when arguments don't match.
michael@0 2684 // For debug purpose, this is can also be used to force/check that the
michael@0 2685 // arguments are correct. Upon fail it will hit a breakpoint.
michael@0 2686
michael@0 2687 MIRGraph &mir = gen->graph();
michael@0 2688 MResumePoint *rp = mir.entryResumePoint();
michael@0 2689
michael@0 2690 // Reserve the amount of stack the actual frame will use. We have to undo
michael@0 2691 // this before falling through to the method proper though, because the
michael@0 2692 // monomorphic call case will bypass this entire path.
michael@0 2693 masm.reserveStack(frameSize());
michael@0 2694
michael@0 2695 // No registers are allocated yet, so it's safe to grab anything.
michael@0 2696 Register temp = GeneralRegisterSet(EntryTempMask).getAny();
michael@0 2697
michael@0 2698 CompileInfo &info = gen->info();
michael@0 2699
michael@0 2700 Label miss;
michael@0 2701 for (uint32_t i = info.startArgSlot(); i < info.endArgSlot(); i++) {
michael@0 2702 // All initial parameters are guaranteed to be MParameters.
michael@0 2703 MParameter *param = rp->getOperand(i)->toParameter();
michael@0 2704 const types::TypeSet *types = param->resultTypeSet();
michael@0 2705 if (!types || types->unknown())
michael@0 2706 continue;
michael@0 2707
michael@0 2708 // Calculate the offset on the stack of the argument.
michael@0 2709 // (i - info.startArgSlot()) - Compute index of arg within arg vector.
michael@0 2710 // ... * sizeof(Value) - Scale by value size.
michael@0 2711 // ArgToStackOffset(...) - Compute displacement within arg vector.
michael@0 2712 int32_t offset = ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value));
michael@0 2713 masm.guardTypeSet(Address(StackPointer, offset), types, temp, &miss);
michael@0 2714 }
michael@0 2715
michael@0 2716 if (miss.used()) {
michael@0 2717 if (bailout) {
michael@0 2718 if (!bailoutFrom(&miss, graph.entrySnapshot()))
michael@0 2719 return false;
michael@0 2720 } else {
michael@0 2721 Label success;
michael@0 2722 masm.jump(&success);
michael@0 2723 masm.bind(&miss);
michael@0 2724 masm.assumeUnreachable("Argument check fail.");
michael@0 2725 masm.bind(&success);
michael@0 2726 }
michael@0 2727 }
michael@0 2728
michael@0 2729 masm.freeStack(frameSize());
michael@0 2730
michael@0 2731 return true;
michael@0 2732 }
michael@0 2733
michael@0 2734 // Out-of-line path to report over-recursed error and fail.
michael@0 2735 class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator>
michael@0 2736 {
michael@0 2737 LCheckOverRecursed *lir_;
michael@0 2738
michael@0 2739 public:
michael@0 2740 CheckOverRecursedFailure(LCheckOverRecursed *lir)
michael@0 2741 : lir_(lir)
michael@0 2742 { }
michael@0 2743
michael@0 2744 bool accept(CodeGenerator *codegen) {
michael@0 2745 return codegen->visitCheckOverRecursedFailure(this);
michael@0 2746 }
michael@0 2747
michael@0 2748 LCheckOverRecursed *lir() const {
michael@0 2749 return lir_;
michael@0 2750 }
michael@0 2751 };
michael@0 2752
michael@0 2753 bool
michael@0 2754 CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed *lir)
michael@0 2755 {
michael@0 2756 // If we don't push anything on the stack, skip the check.
michael@0 2757 if (omitOverRecursedCheck())
michael@0 2758 return true;
michael@0 2759
michael@0 2760 // Ensure that this frame will not cross the stack limit.
michael@0 2761 // This is a weak check, justified by Ion using the C stack: we must always
michael@0 2762 // be some distance away from the actual limit, since if the limit is
michael@0 2763 // crossed, an error must be thrown, which requires more frames.
michael@0 2764 //
michael@0 2765 // It must always be possible to trespass past the stack limit.
michael@0 2766 // Ion may legally place frames very close to the limit. Calling additional
michael@0 2767 // C functions may then violate the limit without any checking.
michael@0 2768
michael@0 2769 // Since Ion frames exist on the C stack, the stack limit may be
michael@0 2770 // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota().
michael@0 2771 const void *limitAddr = GetIonContext()->runtime->addressOfJitStackLimit();
michael@0 2772
michael@0 2773 CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir);
michael@0 2774 if (!addOutOfLineCode(ool))
michael@0 2775 return false;
michael@0 2776
michael@0 2777 // Conditional forward (unlikely) branch to failure.
michael@0 2778 masm.branchPtr(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr), StackPointer, ool->entry());
michael@0 2779 masm.bind(ool->rejoin());
michael@0 2780
michael@0 2781 return true;
michael@0 2782 }
michael@0 2783
michael@0 2784 typedef bool (*DefVarOrConstFn)(JSContext *, HandlePropertyName, unsigned, HandleObject);
michael@0 2785 static const VMFunction DefVarOrConstInfo =
michael@0 2786 FunctionInfo<DefVarOrConstFn>(DefVarOrConst);
michael@0 2787
michael@0 2788 bool
michael@0 2789 CodeGenerator::visitDefVar(LDefVar *lir)
michael@0 2790 {
michael@0 2791 Register scopeChain = ToRegister(lir->scopeChain());
michael@0 2792
michael@0 2793 pushArg(scopeChain); // JSObject *
michael@0 2794 pushArg(Imm32(lir->mir()->attrs())); // unsigned
michael@0 2795 pushArg(ImmGCPtr(lir->mir()->name())); // PropertyName *
michael@0 2796
michael@0 2797 if (!callVM(DefVarOrConstInfo, lir))
michael@0 2798 return false;
michael@0 2799
michael@0 2800 return true;
michael@0 2801 }
michael@0 2802
michael@0 2803 typedef bool (*DefFunOperationFn)(JSContext *, HandleScript, HandleObject, HandleFunction);
michael@0 2804 static const VMFunction DefFunOperationInfo = FunctionInfo<DefFunOperationFn>(DefFunOperation);
michael@0 2805
michael@0 2806 bool
michael@0 2807 CodeGenerator::visitDefFun(LDefFun *lir)
michael@0 2808 {
michael@0 2809 Register scopeChain = ToRegister(lir->scopeChain());
michael@0 2810
michael@0 2811 pushArg(ImmGCPtr(lir->mir()->fun()));
michael@0 2812 pushArg(scopeChain);
michael@0 2813 pushArg(ImmGCPtr(current->mir()->info().script()));
michael@0 2814
michael@0 2815 return callVM(DefFunOperationInfo, lir);
michael@0 2816 }
michael@0 2817
michael@0 2818 typedef bool (*ReportOverRecursedFn)(JSContext *);
michael@0 2819 static const VMFunction CheckOverRecursedInfo =
michael@0 2820 FunctionInfo<ReportOverRecursedFn>(CheckOverRecursed);
michael@0 2821
michael@0 2822 bool
michael@0 2823 CodeGenerator::visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool)
michael@0 2824 {
michael@0 2825 // The OOL path is hit if the recursion depth has been exceeded.
michael@0 2826 // Throw an InternalError for over-recursion.
michael@0 2827
michael@0 2828 // LFunctionEnvironment can appear before LCheckOverRecursed, so we have
michael@0 2829 // to save all live registers to avoid crashes if CheckOverRecursed triggers
michael@0 2830 // a GC.
michael@0 2831 saveLive(ool->lir());
michael@0 2832
michael@0 2833 if (!callVM(CheckOverRecursedInfo, ool->lir()))
michael@0 2834 return false;
michael@0 2835
michael@0 2836 restoreLive(ool->lir());
michael@0 2837 masm.jump(ool->rejoin());
michael@0 2838 return true;
michael@0 2839 }
michael@0 2840
michael@0 2841 // Out-of-line path to report over-recursed error and fail.
michael@0 2842 class CheckOverRecursedFailurePar : public OutOfLineCodeBase<CodeGenerator>
michael@0 2843 {
michael@0 2844 LCheckOverRecursedPar *lir_;
michael@0 2845
michael@0 2846 public:
michael@0 2847 CheckOverRecursedFailurePar(LCheckOverRecursedPar *lir)
michael@0 2848 : lir_(lir)
michael@0 2849 { }
michael@0 2850
michael@0 2851 bool accept(CodeGenerator *codegen) {
michael@0 2852 return codegen->visitCheckOverRecursedFailurePar(this);
michael@0 2853 }
michael@0 2854
michael@0 2855 LCheckOverRecursedPar *lir() const {
michael@0 2856 return lir_;
michael@0 2857 }
michael@0 2858 };
michael@0 2859
michael@0 2860 bool
michael@0 2861 CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir)
michael@0 2862 {
michael@0 2863 // See above: unlike visitCheckOverRecursed(), this code runs in
michael@0 2864 // parallel mode and hence uses the jitStackLimit from the current
michael@0 2865 // thread state. Also, we must check the interrupt flags because
michael@0 2866 // on interrupt or abort, only the stack limit for the main thread
michael@0 2867 // is reset, not the worker threads. See comment in vm/ForkJoin.h
michael@0 2868 // for more details.
michael@0 2869
michael@0 2870 Register cxReg = ToRegister(lir->forkJoinContext());
michael@0 2871 Register tempReg = ToRegister(lir->getTempReg());
michael@0 2872
michael@0 2873 masm.loadPtr(Address(cxReg, offsetof(ForkJoinContext, perThreadData)), tempReg);
michael@0 2874 masm.loadPtr(Address(tempReg, offsetof(PerThreadData, jitStackLimit)), tempReg);
michael@0 2875
michael@0 2876 // Conditional forward (unlikely) branch to failure.
michael@0 2877 CheckOverRecursedFailurePar *ool = new(alloc()) CheckOverRecursedFailurePar(lir);
michael@0 2878 if (!addOutOfLineCode(ool))
michael@0 2879 return false;
michael@0 2880 masm.branchPtr(Assembler::BelowOrEqual, StackPointer, tempReg, ool->entry());
michael@0 2881 masm.checkInterruptFlagPar(tempReg, ool->entry());
michael@0 2882 masm.bind(ool->rejoin());
michael@0 2883
michael@0 2884 return true;
michael@0 2885 }
michael@0 2886
michael@0 2887 bool
michael@0 2888 CodeGenerator::visitCheckOverRecursedFailurePar(CheckOverRecursedFailurePar *ool)
michael@0 2889 {
michael@0 2890 OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(ool->lir());
michael@0 2891 if (!bail)
michael@0 2892 return false;
michael@0 2893
michael@0 2894 // Avoid saving/restoring the temp register since we will put the
michael@0 2895 // ReturnReg into it below and we don't want to clobber that
michael@0 2896 // during PopRegsInMask():
michael@0 2897 LCheckOverRecursedPar *lir = ool->lir();
michael@0 2898 Register tempReg = ToRegister(lir->getTempReg());
michael@0 2899 RegisterSet saveSet(lir->safepoint()->liveRegs());
michael@0 2900 saveSet.takeUnchecked(tempReg);
michael@0 2901
michael@0 2902 masm.PushRegsInMask(saveSet);
michael@0 2903 masm.movePtr(ToRegister(lir->forkJoinContext()), CallTempReg0);
michael@0 2904 masm.setupUnalignedABICall(1, CallTempReg1);
michael@0 2905 masm.passABIArg(CallTempReg0);
michael@0 2906 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, CheckOverRecursedPar));
michael@0 2907 masm.movePtr(ReturnReg, tempReg);
michael@0 2908 masm.PopRegsInMask(saveSet);
michael@0 2909 masm.branchIfFalseBool(tempReg, bail->entry());
michael@0 2910 masm.jump(ool->rejoin());
michael@0 2911
michael@0 2912 return true;
michael@0 2913 }
michael@0 2914
michael@0 2915 // Out-of-line path to report over-recursed error and fail.
michael@0 2916 class OutOfLineInterruptCheckPar : public OutOfLineCodeBase<CodeGenerator>
michael@0 2917 {
michael@0 2918 public:
michael@0 2919 LInterruptCheckPar *const lir;
michael@0 2920
michael@0 2921 OutOfLineInterruptCheckPar(LInterruptCheckPar *lir)
michael@0 2922 : lir(lir)
michael@0 2923 { }
michael@0 2924
michael@0 2925 bool accept(CodeGenerator *codegen) {
michael@0 2926 return codegen->visitOutOfLineInterruptCheckPar(this);
michael@0 2927 }
michael@0 2928 };
michael@0 2929
michael@0 2930 bool
michael@0 2931 CodeGenerator::visitInterruptCheckPar(LInterruptCheckPar *lir)
michael@0 2932 {
michael@0 2933 // First check for cx->shared->interrupt_.
michael@0 2934 OutOfLineInterruptCheckPar *ool = new(alloc()) OutOfLineInterruptCheckPar(lir);
michael@0 2935 if (!addOutOfLineCode(ool))
michael@0 2936 return false;
michael@0 2937
michael@0 2938 Register tempReg = ToRegister(lir->getTempReg());
michael@0 2939 masm.checkInterruptFlagPar(tempReg, ool->entry());
michael@0 2940 masm.bind(ool->rejoin());
michael@0 2941 return true;
michael@0 2942 }
michael@0 2943
michael@0 2944 bool
michael@0 2945 CodeGenerator::visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool)
michael@0 2946 {
michael@0 2947 OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(ool->lir);
michael@0 2948 if (!bail)
michael@0 2949 return false;
michael@0 2950
michael@0 2951 // Avoid saving/restoring the temp register since we will put the
michael@0 2952 // ReturnReg into it below and we don't want to clobber that
michael@0 2953 // during PopRegsInMask():
michael@0 2954 LInterruptCheckPar *lir = ool->lir;
michael@0 2955 Register tempReg = ToRegister(lir->getTempReg());
michael@0 2956 RegisterSet saveSet(lir->safepoint()->liveRegs());
michael@0 2957 saveSet.takeUnchecked(tempReg);
michael@0 2958
michael@0 2959 masm.PushRegsInMask(saveSet);
michael@0 2960 masm.movePtr(ToRegister(ool->lir->forkJoinContext()), CallTempReg0);
michael@0 2961 masm.setupUnalignedABICall(1, CallTempReg1);
michael@0 2962 masm.passABIArg(CallTempReg0);
michael@0 2963 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InterruptCheckPar));
michael@0 2964 masm.movePtr(ReturnReg, tempReg);
michael@0 2965 masm.PopRegsInMask(saveSet);
michael@0 2966 masm.branchIfFalseBool(tempReg, bail->entry());
michael@0 2967 masm.jump(ool->rejoin());
michael@0 2968
michael@0 2969 return true;
michael@0 2970 }
michael@0 2971
michael@0 2972 IonScriptCounts *
michael@0 2973 CodeGenerator::maybeCreateScriptCounts()
michael@0 2974 {
michael@0 2975 // If scripts are being profiled, create a new IonScriptCounts and attach
michael@0 2976 // it to the script. This must be done on the main thread.
michael@0 2977 JSContext *cx = GetIonContext()->cx;
michael@0 2978 if (!cx || !cx->runtime()->profilingScripts)
michael@0 2979 return nullptr;
michael@0 2980
michael@0 2981 CompileInfo *outerInfo = &gen->info();
michael@0 2982 JSScript *script = outerInfo->script();
michael@0 2983 if (!script)
michael@0 2984 return nullptr;
michael@0 2985
michael@0 2986 if (!script->hasScriptCounts() && !script->initScriptCounts(cx))
michael@0 2987 return nullptr;
michael@0 2988
michael@0 2989 IonScriptCounts *counts = js_new<IonScriptCounts>();
michael@0 2990 if (!counts || !counts->init(graph.numBlocks())) {
michael@0 2991 js_delete(counts);
michael@0 2992 return nullptr;
michael@0 2993 }
michael@0 2994
michael@0 2995 for (size_t i = 0; i < graph.numBlocks(); i++) {
michael@0 2996 MBasicBlock *block = graph.getBlock(i)->mir();
michael@0 2997
michael@0 2998 // Find a PC offset in the outermost script to use. If this block
michael@0 2999 // is from an inlined script, find a location in the outer script
michael@0 3000 // to associate information about the inlining with.
michael@0 3001 MResumePoint *resume = block->entryResumePoint();
michael@0 3002 while (resume->caller())
michael@0 3003 resume = resume->caller();
michael@0 3004
michael@0 3005 uint32_t offset = script->pcToOffset(resume->pc());
michael@0 3006 if (!counts->block(i).init(block->id(), offset, block->numSuccessors())) {
michael@0 3007 js_delete(counts);
michael@0 3008 return nullptr;
michael@0 3009 }
michael@0 3010
michael@0 3011 for (size_t j = 0; j < block->numSuccessors(); j++)
michael@0 3012 counts->block(i).setSuccessor(j, block->getSuccessor(j)->id());
michael@0 3013 }
michael@0 3014
michael@0 3015 script->addIonCounts(counts);
michael@0 3016 return counts;
michael@0 3017 }
michael@0 3018
michael@0 3019 // Structure for managing the state tracked for a block by script counters.
michael@0 3020 struct ScriptCountBlockState
michael@0 3021 {
michael@0 3022 IonBlockCounts &block;
michael@0 3023 MacroAssembler &masm;
michael@0 3024
michael@0 3025 Sprinter printer;
michael@0 3026
michael@0 3027 public:
michael@0 3028 ScriptCountBlockState(IonBlockCounts *block, MacroAssembler *masm)
michael@0 3029 : block(*block), masm(*masm), printer(GetIonContext()->cx)
michael@0 3030 {
michael@0 3031 }
michael@0 3032
michael@0 3033 bool init()
michael@0 3034 {
michael@0 3035 if (!printer.init())
michael@0 3036 return false;
michael@0 3037
michael@0 3038 // Bump the hit count for the block at the start. This code is not
michael@0 3039 // included in either the text for the block or the instruction byte
michael@0 3040 // counts.
michael@0 3041 masm.inc64(AbsoluteAddress(block.addressOfHitCount()));
michael@0 3042
michael@0 3043 // Collect human readable assembly for the code generated in the block.
michael@0 3044 masm.setPrinter(&printer);
michael@0 3045
michael@0 3046 return true;
michael@0 3047 }
michael@0 3048
michael@0 3049 void visitInstruction(LInstruction *ins)
michael@0 3050 {
michael@0 3051 // Prefix stream of assembly instructions with their LIR instruction
michael@0 3052 // name and any associated high level info.
michael@0 3053 if (const char *extra = ins->extraName())
michael@0 3054 printer.printf("[%s:%s]\n", ins->opName(), extra);
michael@0 3055 else
michael@0 3056 printer.printf("[%s]\n", ins->opName());
michael@0 3057 }
michael@0 3058
michael@0 3059 ~ScriptCountBlockState()
michael@0 3060 {
michael@0 3061 masm.setPrinter(nullptr);
michael@0 3062
michael@0 3063 block.setCode(printer.string());
michael@0 3064 }
michael@0 3065 };
michael@0 3066
michael@0 3067 #ifdef DEBUG
michael@0 3068 bool
michael@0 3069 CodeGenerator::branchIfInvalidated(Register temp, Label *invalidated)
michael@0 3070 {
michael@0 3071 CodeOffsetLabel label = masm.movWithPatch(ImmWord(uintptr_t(-1)), temp);
michael@0 3072 if (!ionScriptLabels_.append(label))
michael@0 3073 return false;
michael@0 3074
michael@0 3075 // If IonScript::refcount != 0, the script has been invalidated.
michael@0 3076 masm.branch32(Assembler::NotEqual,
michael@0 3077 Address(temp, IonScript::offsetOfRefcount()),
michael@0 3078 Imm32(0),
michael@0 3079 invalidated);
michael@0 3080 return true;
michael@0 3081 }
michael@0 3082
michael@0 3083 bool
michael@0 3084 CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mir)
michael@0 3085 {
michael@0 3086 if (lir->numDefs() == 0)
michael@0 3087 return true;
michael@0 3088
michael@0 3089 JS_ASSERT(lir->numDefs() == 1);
michael@0 3090 Register output = ToRegister(lir->getDef(0));
michael@0 3091
michael@0 3092 GeneralRegisterSet regs(GeneralRegisterSet::All());
michael@0 3093 regs.take(output);
michael@0 3094
michael@0 3095 Register temp = regs.takeAny();
michael@0 3096 masm.push(temp);
michael@0 3097
michael@0 3098 // Don't check if the script has been invalidated. In that case invalid
michael@0 3099 // types are expected (until we reach the OsiPoint and bailout).
michael@0 3100 Label done;
michael@0 3101 if (!branchIfInvalidated(temp, &done))
michael@0 3102 return false;
michael@0 3103
michael@0 3104 if (mir->type() == MIRType_Object &&
michael@0 3105 mir->resultTypeSet() &&
michael@0 3106 !mir->resultTypeSet()->unknownObject())
michael@0 3107 {
michael@0 3108 // We have a result TypeSet, assert this object is in it.
michael@0 3109 Label miss, ok;
michael@0 3110 if (mir->resultTypeSet()->getObjectCount() > 0)
michael@0 3111 masm.guardObjectType(output, mir->resultTypeSet(), temp, &miss);
michael@0 3112 else
michael@0 3113 masm.jump(&miss);
michael@0 3114 masm.jump(&ok);
michael@0 3115
michael@0 3116 masm.bind(&miss);
michael@0 3117 masm.assumeUnreachable("MIR instruction returned object with unexpected type");
michael@0 3118
michael@0 3119 masm.bind(&ok);
michael@0 3120 }
michael@0 3121
michael@0 3122 // Check that we have a valid GC pointer.
michael@0 3123 if (gen->info().executionMode() != ParallelExecution) {
michael@0 3124 saveVolatile();
michael@0 3125 masm.setupUnalignedABICall(2, temp);
michael@0 3126 masm.loadJSContext(temp);
michael@0 3127 masm.passABIArg(temp);
michael@0 3128 masm.passABIArg(output);
michael@0 3129 masm.callWithABINoProfiling(mir->type() == MIRType_Object
michael@0 3130 ? JS_FUNC_TO_DATA_PTR(void *, AssertValidObjectPtr)
michael@0 3131 : JS_FUNC_TO_DATA_PTR(void *, AssertValidStringPtr));
michael@0 3132 restoreVolatile();
michael@0 3133 }
michael@0 3134
michael@0 3135 masm.bind(&done);
michael@0 3136 masm.pop(temp);
michael@0 3137 return true;
michael@0 3138 }
michael@0 3139
michael@0 3140 bool
michael@0 3141 CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
michael@0 3142 {
michael@0 3143 if (lir->numDefs() == 0)
michael@0 3144 return true;
michael@0 3145
michael@0 3146 JS_ASSERT(lir->numDefs() == BOX_PIECES);
michael@0 3147 if (!lir->getDef(0)->output()->isRegister())
michael@0 3148 return true;
michael@0 3149
michael@0 3150 ValueOperand output = ToOutValue(lir);
michael@0 3151
michael@0 3152 GeneralRegisterSet regs(GeneralRegisterSet::All());
michael@0 3153 regs.take(output);
michael@0 3154
michael@0 3155 Register temp1 = regs.takeAny();
michael@0 3156 Register temp2 = regs.takeAny();
michael@0 3157 masm.push(temp1);
michael@0 3158 masm.push(temp2);
michael@0 3159
michael@0 3160 // Don't check if the script has been invalidated. In that case invalid
michael@0 3161 // types are expected (until we reach the OsiPoint and bailout).
michael@0 3162 Label done;
michael@0 3163 if (!branchIfInvalidated(temp1, &done))
michael@0 3164 return false;
michael@0 3165
michael@0 3166 if (mir->resultTypeSet() && !mir->resultTypeSet()->unknown()) {
michael@0 3167 // We have a result TypeSet, assert this value is in it.
michael@0 3168 Label miss, ok;
michael@0 3169 masm.guardTypeSet(output, mir->resultTypeSet(), temp1, &miss);
michael@0 3170 masm.jump(&ok);
michael@0 3171
michael@0 3172 masm.bind(&miss);
michael@0 3173 masm.assumeUnreachable("MIR instruction returned value with unexpected type");
michael@0 3174
michael@0 3175 masm.bind(&ok);
michael@0 3176 }
michael@0 3177
michael@0 3178 // Check that we have a valid GC pointer.
michael@0 3179 if (gen->info().executionMode() != ParallelExecution) {
michael@0 3180 saveVolatile();
michael@0 3181
michael@0 3182 masm.pushValue(output);
michael@0 3183 masm.movePtr(StackPointer, temp1);
michael@0 3184
michael@0 3185 masm.setupUnalignedABICall(2, temp2);
michael@0 3186 masm.loadJSContext(temp2);
michael@0 3187 masm.passABIArg(temp2);
michael@0 3188 masm.passABIArg(temp1);
michael@0 3189 masm.callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, AssertValidValue));
michael@0 3190 masm.popValue(output);
michael@0 3191 restoreVolatile();
michael@0 3192 }
michael@0 3193
michael@0 3194 masm.bind(&done);
michael@0 3195 masm.pop(temp2);
michael@0 3196 masm.pop(temp1);
michael@0 3197 return true;
michael@0 3198 }
michael@0 3199
michael@0 3200 bool
michael@0 3201 CodeGenerator::emitDebugResultChecks(LInstruction *ins)
michael@0 3202 {
michael@0 3203 // In debug builds, check that LIR instructions return valid values.
michael@0 3204
michael@0 3205 MDefinition *mir = ins->mirRaw();
michael@0 3206 if (!mir)
michael@0 3207 return true;
michael@0 3208
michael@0 3209 switch (mir->type()) {
michael@0 3210 case MIRType_Object:
michael@0 3211 case MIRType_String:
michael@0 3212 return emitObjectOrStringResultChecks(ins, mir);
michael@0 3213 case MIRType_Value:
michael@0 3214 return emitValueResultChecks(ins, mir);
michael@0 3215 default:
michael@0 3216 return true;
michael@0 3217 }
michael@0 3218 }
michael@0 3219 #endif
michael@0 3220
michael@0 3221 bool
michael@0 3222 CodeGenerator::generateBody()
michael@0 3223 {
michael@0 3224 IonScriptCounts *counts = maybeCreateScriptCounts();
michael@0 3225
michael@0 3226 #if defined(JS_ION_PERF)
michael@0 3227 PerfSpewer *perfSpewer = &perfSpewer_;
michael@0 3228 if (gen->compilingAsmJS())
michael@0 3229 perfSpewer = &gen->perfSpewer();
michael@0 3230 #endif
michael@0 3231
michael@0 3232 for (size_t i = 0; i < graph.numBlocks(); i++) {
michael@0 3233 current = graph.getBlock(i);
michael@0 3234 masm.bind(current->label());
michael@0 3235
michael@0 3236 mozilla::Maybe<ScriptCountBlockState> blockCounts;
michael@0 3237 if (counts) {
michael@0 3238 blockCounts.construct(&counts->block(i), &masm);
michael@0 3239 if (!blockCounts.ref().init())
michael@0 3240 return false;
michael@0 3241 }
michael@0 3242
michael@0 3243 #if defined(JS_ION_PERF)
michael@0 3244 perfSpewer->startBasicBlock(current->mir(), masm);
michael@0 3245 #endif
michael@0 3246
michael@0 3247 for (LInstructionIterator iter = current->begin(); iter != current->end(); iter++) {
michael@0 3248 IonSpewStart(IonSpew_Codegen, "instruction %s", iter->opName());
michael@0 3249 #ifdef DEBUG
michael@0 3250 if (const char *extra = iter->extraName())
michael@0 3251 IonSpewCont(IonSpew_Codegen, ":%s", extra);
michael@0 3252 #endif
michael@0 3253 IonSpewFin(IonSpew_Codegen);
michael@0 3254
michael@0 3255 if (counts)
michael@0 3256 blockCounts.ref().visitInstruction(*iter);
michael@0 3257
michael@0 3258 if (iter->safepoint() && pushedArgumentSlots_.length()) {
michael@0 3259 if (!markArgumentSlots(iter->safepoint()))
michael@0 3260 return false;
michael@0 3261 }
michael@0 3262
michael@0 3263 #ifdef CHECK_OSIPOINT_REGISTERS
michael@0 3264 if (iter->safepoint())
michael@0 3265 resetOsiPointRegs(iter->safepoint());
michael@0 3266 #endif
michael@0 3267
michael@0 3268 if (!callTraceLIR(i, *iter))
michael@0 3269 return false;
michael@0 3270
michael@0 3271 if (!iter->accept(this))
michael@0 3272 return false;
michael@0 3273
michael@0 3274 #ifdef DEBUG
michael@0 3275 if (!emitDebugResultChecks(*iter))
michael@0 3276 return false;
michael@0 3277 #endif
michael@0 3278 }
michael@0 3279 if (masm.oom())
michael@0 3280 return false;
michael@0 3281
michael@0 3282 #if defined(JS_ION_PERF)
michael@0 3283 perfSpewer->endBasicBlock(masm);
michael@0 3284 #endif
michael@0 3285 }
michael@0 3286
michael@0 3287 JS_ASSERT(pushedArgumentSlots_.empty());
michael@0 3288 return true;
michael@0 3289 }
michael@0 3290
michael@0 3291 // Out-of-line object allocation for LNewArray.
michael@0 3292 class OutOfLineNewArray : public OutOfLineCodeBase<CodeGenerator>
michael@0 3293 {
michael@0 3294 LNewArray *lir_;
michael@0 3295
michael@0 3296 public:
michael@0 3297 OutOfLineNewArray(LNewArray *lir)
michael@0 3298 : lir_(lir)
michael@0 3299 { }
michael@0 3300
michael@0 3301 bool accept(CodeGenerator *codegen) {
michael@0 3302 return codegen->visitOutOfLineNewArray(this);
michael@0 3303 }
michael@0 3304
michael@0 3305 LNewArray *lir() const {
michael@0 3306 return lir_;
michael@0 3307 }
michael@0 3308 };
michael@0 3309
michael@0 3310 typedef JSObject *(*NewInitArrayFn)(JSContext *, uint32_t, types::TypeObject *);
michael@0 3311 static const VMFunction NewInitArrayInfo =
michael@0 3312 FunctionInfo<NewInitArrayFn>(NewInitArray);
michael@0 3313
michael@0 3314 bool
michael@0 3315 CodeGenerator::visitNewArrayCallVM(LNewArray *lir)
michael@0 3316 {
michael@0 3317 JS_ASSERT(gen->info().executionMode() == SequentialExecution);
michael@0 3318
michael@0 3319 Register objReg = ToRegister(lir->output());
michael@0 3320
michael@0 3321 JS_ASSERT(!lir->isCall());
michael@0 3322 saveLive(lir);
michael@0 3323
michael@0 3324 JSObject *templateObject = lir->mir()->templateObject();
michael@0 3325 types::TypeObject *type =
michael@0 3326 templateObject->hasSingletonType() ? nullptr : templateObject->type();
michael@0 3327
michael@0 3328 pushArg(ImmGCPtr(type));
michael@0 3329 pushArg(Imm32(lir->mir()->count()));
michael@0 3330
michael@0 3331 if (!callVM(NewInitArrayInfo, lir))
michael@0 3332 return false;
michael@0 3333
michael@0 3334 if (ReturnReg != objReg)
michael@0 3335 masm.movePtr(ReturnReg, objReg);
michael@0 3336
michael@0 3337 restoreLive(lir);
michael@0 3338
michael@0 3339 return true;
michael@0 3340 }
michael@0 3341
michael@0 3342 typedef JSObject *(*NewDerivedTypedObjectFn)(JSContext *,
michael@0 3343 HandleObject type,
michael@0 3344 HandleObject owner,
michael@0 3345 int32_t offset);
michael@0 3346 static const VMFunction CreateDerivedTypedObjInfo =
michael@0 3347 FunctionInfo<NewDerivedTypedObjectFn>(CreateDerivedTypedObj);
michael@0 3348
michael@0 3349 bool
michael@0 3350 CodeGenerator::visitNewDerivedTypedObject(LNewDerivedTypedObject *lir)
michael@0 3351 {
michael@0 3352 // Not yet made safe for par exec:
michael@0 3353 JS_ASSERT(gen->info().executionMode() == SequentialExecution);
michael@0 3354
michael@0 3355 pushArg(ToRegister(lir->offset()));
michael@0 3356 pushArg(ToRegister(lir->owner()));
michael@0 3357 pushArg(ToRegister(lir->type()));
michael@0 3358 return callVM(CreateDerivedTypedObjInfo, lir);
michael@0 3359 }
michael@0 3360
michael@0 3361 bool
michael@0 3362 CodeGenerator::visitNewSlots(LNewSlots *lir)
michael@0 3363 {
michael@0 3364 Register temp1 = ToRegister(lir->temp1());
michael@0 3365 Register temp2 = ToRegister(lir->temp2());
michael@0 3366 Register temp3 = ToRegister(lir->temp3());
michael@0 3367 Register output = ToRegister(lir->output());
michael@0 3368
michael@0 3369 masm.mov(ImmPtr(GetIonContext()->runtime), temp1);
michael@0 3370 masm.mov(ImmWord(lir->mir()->nslots()), temp2);
michael@0 3371
michael@0 3372 masm.setupUnalignedABICall(2, temp3);
michael@0 3373 masm.passABIArg(temp1);
michael@0 3374 masm.passABIArg(temp2);
michael@0 3375 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NewSlots));
michael@0 3376
michael@0 3377 if (!bailoutTestPtr(Assembler::Zero, output, output, lir->snapshot()))
michael@0 3378 return false;
michael@0 3379
michael@0 3380 return true;
michael@0 3381 }
michael@0 3382
michael@0 3383 bool CodeGenerator::visitAtan2D(LAtan2D *lir)
michael@0 3384 {
michael@0 3385 Register temp = ToRegister(lir->temp());
michael@0 3386 FloatRegister y = ToFloatRegister(lir->y());
michael@0 3387 FloatRegister x = ToFloatRegister(lir->x());
michael@0 3388
michael@0 3389 masm.setupUnalignedABICall(2, temp);
michael@0 3390 masm.passABIArg(y, MoveOp::DOUBLE);
michael@0 3391 masm.passABIArg(x, MoveOp::DOUBLE);
michael@0 3392 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaAtan2), MoveOp::DOUBLE);
michael@0 3393
michael@0 3394 JS_ASSERT(ToFloatRegister(lir->output()) == ReturnFloatReg);
michael@0 3395 return true;
michael@0 3396 }
michael@0 3397
michael@0 3398 bool CodeGenerator::visitHypot(LHypot *lir)
michael@0 3399 {
michael@0 3400 Register temp = ToRegister(lir->temp());
michael@0 3401 FloatRegister x = ToFloatRegister(lir->x());
michael@0 3402 FloatRegister y = ToFloatRegister(lir->y());
michael@0 3403
michael@0 3404 masm.setupUnalignedABICall(2, temp);
michael@0 3405 masm.passABIArg(x, MoveOp::DOUBLE);
michael@0 3406 masm.passABIArg(y, MoveOp::DOUBLE);
michael@0 3407 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaHypot), MoveOp::DOUBLE);
michael@0 3408
michael@0 3409 JS_ASSERT(ToFloatRegister(lir->output()) == ReturnFloatReg);
michael@0 3410 return true;
michael@0 3411 }
michael@0 3412
michael@0 3413 bool
michael@0 3414 CodeGenerator::visitNewArray(LNewArray *lir)
michael@0 3415 {
michael@0 3416 JS_ASSERT(gen->info().executionMode() == SequentialExecution);
michael@0 3417 Register objReg = ToRegister(lir->output());
michael@0 3418 Register tempReg = ToRegister(lir->temp());
michael@0 3419 JSObject *templateObject = lir->mir()->templateObject();
michael@0 3420 DebugOnly<uint32_t> count = lir->mir()->count();
michael@0 3421
michael@0 3422 JS_ASSERT(count < JSObject::NELEMENTS_LIMIT);
michael@0 3423
michael@0 3424 if (lir->mir()->shouldUseVM())
michael@0 3425 return visitNewArrayCallVM(lir);
michael@0 3426
michael@0 3427 OutOfLineNewArray *ool = new(alloc()) OutOfLineNewArray(lir);
michael@0 3428 if (!addOutOfLineCode(ool))
michael@0 3429 return false;
michael@0 3430
michael@0 3431 masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap());
michael@0 3432 masm.initGCThing(objReg, tempReg, templateObject);
michael@0 3433
michael@0 3434 masm.bind(ool->rejoin());
michael@0 3435 return true;
michael@0 3436 }
michael@0 3437
michael@0 3438 bool
michael@0 3439 CodeGenerator::visitOutOfLineNewArray(OutOfLineNewArray *ool)
michael@0 3440 {
michael@0 3441 if (!visitNewArrayCallVM(ool->lir()))
michael@0 3442 return false;
michael@0 3443 masm.jump(ool->rejoin());
michael@0 3444 return true;
michael@0 3445 }
michael@0 3446
michael@0 3447 // Out-of-line object allocation for JSOP_NEWOBJECT.
michael@0 3448 class OutOfLineNewObject : public OutOfLineCodeBase<CodeGenerator>
michael@0 3449 {
michael@0 3450 LNewObject *lir_;
michael@0 3451
michael@0 3452 public:
michael@0 3453 OutOfLineNewObject(LNewObject *lir)
michael@0 3454 : lir_(lir)
michael@0 3455 { }
michael@0 3456
michael@0 3457 bool accept(CodeGenerator *codegen) {
michael@0 3458 return codegen->visitOutOfLineNewObject(this);
michael@0 3459 }
michael@0 3460
michael@0 3461 LNewObject *lir() const {
michael@0 3462 return lir_;
michael@0 3463 }
michael@0 3464 };
michael@0 3465
michael@0 3466 typedef JSObject *(*NewInitObjectFn)(JSContext *, HandleObject);
michael@0 3467 static const VMFunction NewInitObjectInfo = FunctionInfo<NewInitObjectFn>(NewInitObject);
michael@0 3468
michael@0 3469 typedef JSObject *(*NewInitObjectWithClassPrototypeFn)(JSContext *, HandleObject);
michael@0 3470 static const VMFunction NewInitObjectWithClassPrototypeInfo =
michael@0 3471 FunctionInfo<NewInitObjectWithClassPrototypeFn>(NewInitObjectWithClassPrototype);
michael@0 3472
michael@0 3473 bool
michael@0 3474 CodeGenerator::visitNewObjectVMCall(LNewObject *lir)
michael@0 3475 {
michael@0 3476 JS_ASSERT(gen->info().executionMode() == SequentialExecution);
michael@0 3477
michael@0 3478 Register objReg = ToRegister(lir->output());
michael@0 3479
michael@0 3480 JS_ASSERT(!lir->isCall());
michael@0 3481 saveLive(lir);
michael@0 3482
michael@0 3483 pushArg(ImmGCPtr(lir->mir()->templateObject()));
michael@0 3484
michael@0 3485 // If we're making a new object with a class prototype (that is, an object
michael@0 3486 // that derives its class from its prototype instead of being
michael@0 3487 // JSObject::class_'d) from self-hosted code, we need a different init
michael@0 3488 // function.
michael@0 3489 if (lir->mir()->templateObjectIsClassPrototype()) {
michael@0 3490 if (!callVM(NewInitObjectWithClassPrototypeInfo, lir))
michael@0 3491 return false;
michael@0 3492 } else if (!callVM(NewInitObjectInfo, lir)) {
michael@0 3493 return false;
michael@0 3494 }
michael@0 3495
michael@0 3496 if (ReturnReg != objReg)
michael@0 3497 masm.movePtr(ReturnReg, objReg);
michael@0 3498
michael@0 3499 restoreLive(lir);
michael@0 3500 return true;
michael@0 3501 }
michael@0 3502
michael@0 3503 bool
michael@0 3504 CodeGenerator::visitNewObject(LNewObject *lir)
michael@0 3505 {
michael@0 3506 JS_ASSERT(gen->info().executionMode() == SequentialExecution);
michael@0 3507 Register objReg = ToRegister(lir->output());
michael@0 3508 Register tempReg = ToRegister(lir->temp());
michael@0 3509 JSObject *templateObject = lir->mir()->templateObject();
michael@0 3510
michael@0 3511 if (lir->mir()->shouldUseVM())
michael@0 3512 return visitNewObjectVMCall(lir);
michael@0 3513
michael@0 3514 OutOfLineNewObject *ool = new(alloc()) OutOfLineNewObject(lir);
michael@0 3515 if (!addOutOfLineCode(ool))
michael@0 3516 return false;
michael@0 3517
michael@0 3518 masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap());
michael@0 3519 masm.initGCThing(objReg, tempReg, templateObject);
michael@0 3520
michael@0 3521 masm.bind(ool->rejoin());
michael@0 3522 return true;
michael@0 3523 }
michael@0 3524
michael@0 3525 bool
michael@0 3526 CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject *ool)
michael@0 3527 {
michael@0 3528 if (!visitNewObjectVMCall(ool->lir()))
michael@0 3529 return false;
michael@0 3530 masm.jump(ool->rejoin());
michael@0 3531 return true;
michael@0 3532 }
michael@0 3533
michael@0 3534 typedef js::DeclEnvObject *(*NewDeclEnvObjectFn)(JSContext *, HandleFunction, gc::InitialHeap);
michael@0 3535 static const VMFunction NewDeclEnvObjectInfo =
michael@0 3536 FunctionInfo<NewDeclEnvObjectFn>(DeclEnvObject::createTemplateObject);
michael@0 3537
michael@0 3538 bool
michael@0 3539 CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject *lir)
michael@0 3540 {
michael@0 3541 Register objReg = ToRegister(lir->output());
michael@0 3542 Register tempReg = ToRegister(lir->temp());
michael@0 3543 JSObject *templateObj = lir->mir()->templateObj();
michael@0 3544 CompileInfo &info = lir->mir()->block()->info();
michael@0 3545
michael@0 3546 // If we have a template object, we can inline call object creation.
michael@0 3547 OutOfLineCode *ool = oolCallVM(NewDeclEnvObjectInfo, lir,
michael@0 3548 (ArgList(), ImmGCPtr(info.funMaybeLazy()),
michael@0 3549 Imm32(gc::DefaultHeap)),
michael@0 3550 StoreRegisterTo(objReg));
michael@0 3551 if (!ool)
michael@0 3552 return false;
michael@0 3553
michael@0 3554 masm.newGCThing(objReg, tempReg, templateObj, ool->entry(), gc::DefaultHeap);
michael@0 3555 masm.initGCThing(objReg, tempReg, templateObj);
michael@0 3556 masm.bind(ool->rejoin());
michael@0 3557 return true;
michael@0 3558 }
michael@0 3559
michael@0 3560 typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleShape, HandleTypeObject, HeapSlot *);
michael@0 3561 static const VMFunction NewCallObjectInfo =
michael@0 3562 FunctionInfo<NewCallObjectFn>(NewCallObject);
michael@0 3563
michael@0 3564 bool
michael@0 3565 CodeGenerator::visitNewCallObject(LNewCallObject *lir)
michael@0 3566 {
michael@0 3567 Register objReg = ToRegister(lir->output());
michael@0 3568 Register tempReg = ToRegister(lir->temp());
michael@0 3569
michael@0 3570 JSObject *templateObj = lir->mir()->templateObject();
michael@0 3571
michael@0 3572 OutOfLineCode *ool;
michael@0 3573 if (lir->slots()->isRegister()) {
michael@0 3574 ool = oolCallVM(NewCallObjectInfo, lir,
michael@0 3575 (ArgList(), ImmGCPtr(templateObj->lastProperty()),
michael@0 3576 ImmGCPtr(templateObj->type()),
michael@0 3577 ToRegister(lir->slots())),
michael@0 3578 StoreRegisterTo(objReg));
michael@0 3579 } else {
michael@0 3580 ool = oolCallVM(NewCallObjectInfo, lir,
michael@0 3581 (ArgList(), ImmGCPtr(templateObj->lastProperty()),
michael@0 3582 ImmGCPtr(templateObj->type()),
michael@0 3583 ImmPtr(nullptr)),
michael@0 3584 StoreRegisterTo(objReg));
michael@0 3585 }
michael@0 3586 if (!ool)
michael@0 3587 return false;
michael@0 3588
michael@0 3589 #ifdef JSGC_GENERATIONAL
michael@0 3590 if (templateObj->hasDynamicSlots()) {
michael@0 3591 // Slot initialization is unbarriered in this case, so we must either
michael@0 3592 // allocate in the nursery or bail if that is not possible.
michael@0 3593 masm.jump(ool->entry());
michael@0 3594 } else
michael@0 3595 #endif
michael@0 3596 {
michael@0 3597 // Inline call object creation, using the OOL path only for tricky cases.
michael@0 3598 masm.newGCThing(objReg, tempReg, templateObj, ool->entry(), gc::DefaultHeap);
michael@0 3599 masm.initGCThing(objReg, tempReg, templateObj);
michael@0 3600 }
michael@0 3601
michael@0 3602 if (lir->slots()->isRegister())
michael@0 3603 masm.storePtr(ToRegister(lir->slots()), Address(objReg, JSObject::offsetOfSlots()));
michael@0 3604
michael@0 3605 masm.bind(ool->rejoin());
michael@0 3606 return true;
michael@0 3607 }
michael@0 3608
michael@0 3609 typedef JSObject *(*NewSingletonCallObjectFn)(JSContext *, HandleShape, HeapSlot *);
michael@0 3610 static const VMFunction NewSingletonCallObjectInfo =
michael@0 3611 FunctionInfo<NewSingletonCallObjectFn>(NewSingletonCallObject);
michael@0 3612
michael@0 3613 bool
michael@0 3614 CodeGenerator::visitNewSingletonCallObject(LNewSingletonCallObject *lir)
michael@0 3615 {
michael@0 3616 Register objReg = ToRegister(lir->output());
michael@0 3617
michael@0 3618 JSObject *templateObj = lir->mir()->templateObject();
michael@0 3619
michael@0 3620 OutOfLineCode *ool;
michael@0 3621 if (lir->slots()->isRegister()) {
michael@0 3622 ool = oolCallVM(NewSingletonCallObjectInfo, lir,
michael@0 3623 (ArgList(), ImmGCPtr(templateObj->lastProperty()),
michael@0 3624 ToRegister(lir->slots())),
michael@0 3625 StoreRegisterTo(objReg));
michael@0 3626 } else {
michael@0 3627 ool = oolCallVM(NewSingletonCallObjectInfo, lir,
michael@0 3628 (ArgList(), ImmGCPtr(templateObj->lastProperty()),
michael@0 3629 ImmPtr(nullptr)),
michael@0 3630 StoreRegisterTo(objReg));
michael@0 3631 }
michael@0 3632 if (!ool)
michael@0 3633 return false;
michael@0 3634
michael@0 3635 // Objects can only be given singleton types in VM calls. We make the call
michael@0 3636 // out of line to not bloat inline code, even if (naively) this seems like
michael@0 3637 // extra work.
michael@0 3638 masm.jump(ool->entry());
michael@0 3639 masm.bind(ool->rejoin());
michael@0 3640
michael@0 3641 return true;
michael@0 3642 }
michael@0 3643
michael@0 3644 bool
michael@0 3645 CodeGenerator::visitNewCallObjectPar(LNewCallObjectPar *lir)
michael@0 3646 {
michael@0 3647 Register resultReg = ToRegister(lir->output());
michael@0 3648 Register cxReg = ToRegister(lir->forkJoinContext());
michael@0 3649 Register tempReg1 = ToRegister(lir->getTemp0());
michael@0 3650 Register tempReg2 = ToRegister(lir->getTemp1());
michael@0 3651 JSObject *templateObj = lir->mir()->templateObj();
michael@0 3652
michael@0 3653 emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, templateObj);
michael@0 3654
michael@0 3655 // NB: !lir->slots()->isRegister() implies that there is no slots
michael@0 3656 // array at all, and the memory is already zeroed when copying
michael@0 3657 // from the template object
michael@0 3658
michael@0 3659 if (lir->slots()->isRegister()) {
michael@0 3660 Register slotsReg = ToRegister(lir->slots());
michael@0 3661 JS_ASSERT(slotsReg != resultReg);
michael@0 3662 masm.storePtr(slotsReg, Address(resultReg, JSObject::offsetOfSlots()));
michael@0 3663 }
michael@0 3664
michael@0 3665 return true;
michael@0 3666 }
michael@0 3667
michael@0 3668 bool
michael@0 3669 CodeGenerator::visitNewDenseArrayPar(LNewDenseArrayPar *lir)
michael@0 3670 {
michael@0 3671 Register cxReg = ToRegister(lir->forkJoinContext());
michael@0 3672 Register lengthReg = ToRegister(lir->length());
michael@0 3673 Register tempReg0 = ToRegister(lir->getTemp0());
michael@0 3674 Register tempReg1 = ToRegister(lir->getTemp1());
michael@0 3675 Register tempReg2 = ToRegister(lir->getTemp2());
michael@0 3676 JSObject *templateObj = lir->mir()->templateObject();
michael@0 3677
michael@0 3678 // Allocate the array into tempReg2. Don't use resultReg because it
michael@0 3679 // may alias cxReg etc.
michael@0 3680 emitAllocateGCThingPar(lir, tempReg2, cxReg, tempReg0, tempReg1, templateObj);
michael@0 3681
michael@0 3682 // Invoke a C helper to allocate the elements. For convenience,
michael@0 3683 // this helper also returns the array back to us, or nullptr, which
michael@0 3684 // obviates the need to preserve the register across the call. In
michael@0 3685 // reality, we should probably just have the C helper also
michael@0 3686 // *allocate* the array, but that would require that it initialize
michael@0 3687 // the various fields of the object, and I didn't want to
michael@0 3688 // duplicate the code in initGCThing() that already does such an
michael@0 3689 // admirable job.
michael@0 3690 masm.setupUnalignedABICall(3, tempReg0);
michael@0 3691 masm.passABIArg(cxReg);
michael@0 3692 masm.passABIArg(tempReg2);
michael@0 3693 masm.passABIArg(lengthReg);
michael@0 3694 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ExtendArrayPar));
michael@0 3695
michael@0 3696 Register resultReg = ToRegister(lir->output());
michael@0 3697 JS_ASSERT(resultReg == ReturnReg);
michael@0 3698 OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutOutOfMemory, lir);
michael@0 3699 if (!bail)
michael@0 3700 return false;
michael@0 3701 masm.branchTestPtr(Assembler::Zero, resultReg, resultReg, bail->entry());
michael@0 3702
michael@0 3703 return true;
michael@0 3704 }
michael@0 3705
michael@0 3706 typedef JSObject *(*NewStringObjectFn)(JSContext *, HandleString);
michael@0 3707 static const VMFunction NewStringObjectInfo = FunctionInfo<NewStringObjectFn>(NewStringObject);
michael@0 3708
michael@0 3709 bool
michael@0 3710 CodeGenerator::visitNewStringObject(LNewStringObject *lir)
michael@0 3711 {
michael@0 3712 Register input = ToRegister(lir->input());
michael@0 3713 Register output = ToRegister(lir->output());
michael@0 3714 Register temp = ToRegister(lir->temp());
michael@0 3715
michael@0 3716 StringObject *templateObj = lir->mir()->templateObj();
michael@0 3717
michael@0 3718 OutOfLineCode *ool = oolCallVM(NewStringObjectInfo, lir, (ArgList(), input),
michael@0 3719 StoreRegisterTo(output));
michael@0 3720 if (!ool)
michael@0 3721 return false;
michael@0 3722
michael@0 3723 masm.newGCThing(output, temp, templateObj, ool->entry(), gc::DefaultHeap);
michael@0 3724 masm.initGCThing(output, temp, templateObj);
michael@0 3725
michael@0 3726 masm.loadStringLength(input, temp);
michael@0 3727
michael@0 3728 masm.storeValue(JSVAL_TYPE_STRING, input, Address(output, StringObject::offsetOfPrimitiveValue()));
michael@0 3729 masm.storeValue(JSVAL_TYPE_INT32, temp, Address(output, StringObject::offsetOfLength()));
michael@0 3730
michael@0 3731 masm.bind(ool->rejoin());
michael@0 3732 return true;
michael@0 3733 }
michael@0 3734
michael@0 3735 bool
michael@0 3736 CodeGenerator::visitNewPar(LNewPar *lir)
michael@0 3737 {
michael@0 3738 Register objReg = ToRegister(lir->output());
michael@0 3739 Register cxReg = ToRegister(lir->forkJoinContext());
michael@0 3740 Register tempReg1 = ToRegister(lir->getTemp0());
michael@0 3741 Register tempReg2 = ToRegister(lir->getTemp1());
michael@0 3742 JSObject *templateObject = lir->mir()->templateObject();
michael@0 3743 emitAllocateGCThingPar(lir, objReg, cxReg, tempReg1, tempReg2, templateObject);
michael@0 3744 return true;
michael@0 3745 }
michael@0 3746
michael@0 3747 class OutOfLineNewGCThingPar : public OutOfLineCodeBase<CodeGenerator>
michael@0 3748 {
michael@0 3749 public:
michael@0 3750 LInstruction *lir;
michael@0 3751 gc::AllocKind allocKind;
michael@0 3752 Register objReg;
michael@0 3753 Register cxReg;
michael@0 3754
michael@0 3755 OutOfLineNewGCThingPar(LInstruction *lir, gc::AllocKind allocKind, Register objReg,
michael@0 3756 Register cxReg)
michael@0 3757 : lir(lir), allocKind(allocKind), objReg(objReg), cxReg(cxReg)
michael@0 3758 {}
michael@0 3759
michael@0 3760 bool accept(CodeGenerator *codegen) {
michael@0 3761 return codegen->visitOutOfLineNewGCThingPar(this);
michael@0 3762 }
michael@0 3763 };
michael@0 3764
michael@0 3765 bool
michael@0 3766 CodeGenerator::emitAllocateGCThingPar(LInstruction *lir, Register objReg, Register cxReg,
michael@0 3767 Register tempReg1, Register tempReg2, JSObject *templateObj)
michael@0 3768 {
michael@0 3769 gc::AllocKind allocKind = templateObj->tenuredGetAllocKind();
michael@0 3770 OutOfLineNewGCThingPar *ool = new(alloc()) OutOfLineNewGCThingPar(lir, allocKind, objReg, cxReg);
michael@0 3771 if (!ool || !addOutOfLineCode(ool))
michael@0 3772 return false;
michael@0 3773
michael@0 3774 masm.newGCThingPar(objReg, cxReg, tempReg1, tempReg2, templateObj, ool->entry());
michael@0 3775 masm.bind(ool->rejoin());
michael@0 3776 masm.initGCThing(objReg, tempReg1, templateObj);
michael@0 3777 return true;
michael@0 3778 }
michael@0 3779
michael@0 3780 bool
michael@0 3781 CodeGenerator::visitOutOfLineNewGCThingPar(OutOfLineNewGCThingPar *ool)
michael@0 3782 {
michael@0 3783 // As a fallback for allocation in par. exec. mode, we invoke the
michael@0 3784 // C helper NewGCThingPar(), which calls into the GC code. If it
michael@0 3785 // returns nullptr, we bail. If returns non-nullptr, we rejoin the
michael@0 3786 // original instruction.
michael@0 3787 Register out = ool->objReg;
michael@0 3788
michael@0 3789 saveVolatile(out);
michael@0 3790 masm.setupUnalignedABICall(2, out);
michael@0 3791 masm.passABIArg(ool->cxReg);
michael@0 3792 masm.move32(Imm32(ool->allocKind), out);
michael@0 3793 masm.passABIArg(out);
michael@0 3794 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NewGCThingPar));
michael@0 3795 masm.storeCallResult(out);
michael@0 3796 restoreVolatile(out);
michael@0 3797
michael@0 3798 OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutOutOfMemory, ool->lir);
michael@0 3799 if (!bail)
michael@0 3800 return false;
michael@0 3801 masm.branchTestPtr(Assembler::Zero, out, out, bail->entry());
michael@0 3802 masm.jump(ool->rejoin());
michael@0 3803 return true;
michael@0 3804 }
michael@0 3805
michael@0 3806 bool
michael@0 3807 CodeGenerator::visitAbortPar(LAbortPar *lir)
michael@0 3808 {
michael@0 3809 OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutUnsupported, lir);
michael@0 3810 if (!bail)
michael@0 3811 return false;
michael@0 3812 masm.jump(bail->entry());
michael@0 3813 return true;
michael@0 3814 }
michael@0 3815
michael@0 3816 typedef bool(*InitElemFn)(JSContext *cx, HandleObject obj,
michael@0 3817 HandleValue id, HandleValue value);
michael@0 3818 static const VMFunction InitElemInfo =
michael@0 3819 FunctionInfo<InitElemFn>(InitElemOperation);
michael@0 3820
michael@0 3821 bool
michael@0 3822 CodeGenerator::visitInitElem(LInitElem *lir)
michael@0 3823 {
michael@0 3824 Register objReg = ToRegister(lir->getObject());
michael@0 3825
michael@0 3826 pushArg(ToValue(lir, LInitElem::ValueIndex));
michael@0 3827 pushArg(ToValue(lir, LInitElem::IdIndex));
michael@0 3828 pushArg(objReg);
michael@0 3829
michael@0 3830 return callVM(InitElemInfo, lir);
michael@0 3831 }
michael@0 3832
michael@0 3833 typedef bool (*InitElemGetterSetterFn)(JSContext *, jsbytecode *, HandleObject, HandleValue,
michael@0 3834 HandleObject);
michael@0 3835 static const VMFunction InitElemGetterSetterInfo =
michael@0 3836 FunctionInfo<InitElemGetterSetterFn>(InitGetterSetterOperation);
michael@0 3837
michael@0 3838 bool
michael@0 3839 CodeGenerator::visitInitElemGetterSetter(LInitElemGetterSetter *lir)
michael@0 3840 {
michael@0 3841 Register obj = ToRegister(lir->object());
michael@0 3842 Register value = ToRegister(lir->value());
michael@0 3843
michael@0 3844 pushArg(value);
michael@0 3845 pushArg(ToValue(lir, LInitElemGetterSetter::IdIndex));
michael@0 3846 pushArg(obj);
michael@0 3847 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
michael@0 3848
michael@0 3849 return callVM(InitElemGetterSetterInfo, lir);
michael@0 3850 }
michael@0 3851
michael@0 3852 typedef bool(*MutatePrototypeFn)(JSContext *cx, HandleObject obj, HandleValue value);
michael@0 3853 static const VMFunction MutatePrototypeInfo =
michael@0 3854 FunctionInfo<MutatePrototypeFn>(MutatePrototype);
michael@0 3855
michael@0 3856 bool
michael@0 3857 CodeGenerator::visitMutateProto(LMutateProto *lir)
michael@0 3858 {
michael@0 3859 Register objReg = ToRegister(lir->getObject());
michael@0 3860
michael@0 3861 pushArg(ToValue(lir, LMutateProto::ValueIndex));
michael@0 3862 pushArg(objReg);
michael@0 3863
michael@0 3864 return callVM(MutatePrototypeInfo, lir);
michael@0 3865 }
michael@0 3866
michael@0 3867 typedef bool(*InitPropFn)(JSContext *cx, HandleObject obj,
michael@0 3868 HandlePropertyName name, HandleValue value);
michael@0 3869 static const VMFunction InitPropInfo =
michael@0 3870 FunctionInfo<InitPropFn>(InitProp);
michael@0 3871
michael@0 3872 bool
michael@0 3873 CodeGenerator::visitInitProp(LInitProp *lir)
michael@0 3874 {
michael@0 3875 Register objReg = ToRegister(lir->getObject());
michael@0 3876
michael@0 3877 pushArg(ToValue(lir, LInitProp::ValueIndex));
michael@0 3878 pushArg(ImmGCPtr(lir->mir()->propertyName()));
michael@0 3879 pushArg(objReg);
michael@0 3880
michael@0 3881 return callVM(InitPropInfo, lir);
michael@0 3882 }
michael@0 3883
michael@0 3884 typedef bool(*InitPropGetterSetterFn)(JSContext *, jsbytecode *, HandleObject, HandlePropertyName,
michael@0 3885 HandleObject);
michael@0 3886 static const VMFunction InitPropGetterSetterInfo =
michael@0 3887 FunctionInfo<InitPropGetterSetterFn>(InitGetterSetterOperation);
michael@0 3888
michael@0 3889 bool
michael@0 3890 CodeGenerator::visitInitPropGetterSetter(LInitPropGetterSetter *lir)
michael@0 3891 {
michael@0 3892 Register obj = ToRegister(lir->object());
michael@0 3893 Register value = ToRegister(lir->value());
michael@0 3894
michael@0 3895 pushArg(value);
michael@0 3896 pushArg(ImmGCPtr(lir->mir()->name()));
michael@0 3897 pushArg(obj);
michael@0 3898 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
michael@0 3899
michael@0 3900 return callVM(InitPropGetterSetterInfo, lir);
michael@0 3901 }
michael@0 3902
michael@0 3903 typedef bool (*CreateThisFn)(JSContext *cx, HandleObject callee, MutableHandleValue rval);
michael@0 3904 static const VMFunction CreateThisInfoCodeGen = FunctionInfo<CreateThisFn>(CreateThis);
michael@0 3905
michael@0 3906 bool
michael@0 3907 CodeGenerator::visitCreateThis(LCreateThis *lir)
michael@0 3908 {
michael@0 3909 const LAllocation *callee = lir->getCallee();
michael@0 3910
michael@0 3911 if (callee->isConstant())
michael@0 3912 pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
michael@0 3913 else
michael@0 3914 pushArg(ToRegister(callee));
michael@0 3915
michael@0 3916 return callVM(CreateThisInfoCodeGen, lir);
michael@0 3917 }
michael@0 3918
michael@0 3919 static JSObject *
michael@0 3920 CreateThisForFunctionWithProtoWrapper(JSContext *cx, js::HandleObject callee, JSObject *proto)
michael@0 3921 {
michael@0 3922 return CreateThisForFunctionWithProto(cx, callee, proto);
michael@0 3923 }
michael@0 3924
michael@0 3925 typedef JSObject *(*CreateThisWithProtoFn)(JSContext *cx, HandleObject callee, JSObject *proto);
michael@0 3926 static const VMFunction CreateThisWithProtoInfo =
michael@0 3927 FunctionInfo<CreateThisWithProtoFn>(CreateThisForFunctionWithProtoWrapper);
michael@0 3928
michael@0 3929 bool
michael@0 3930 CodeGenerator::visitCreateThisWithProto(LCreateThisWithProto *lir)
michael@0 3931 {
michael@0 3932 const LAllocation *callee = lir->getCallee();
michael@0 3933 const LAllocation *proto = lir->getPrototype();
michael@0 3934
michael@0 3935 if (proto->isConstant())
michael@0 3936 pushArg(ImmGCPtr(&proto->toConstant()->toObject()));
michael@0 3937 else
michael@0 3938 pushArg(ToRegister(proto));
michael@0 3939
michael@0 3940 if (callee->isConstant())
michael@0 3941 pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
michael@0 3942 else
michael@0 3943 pushArg(ToRegister(callee));
michael@0 3944
michael@0 3945 return callVM(CreateThisWithProtoInfo, lir);
michael@0 3946 }
michael@0 3947
michael@0 3948 typedef JSObject *(*NewGCObjectFn)(JSContext *cx, gc::AllocKind allocKind,
michael@0 3949 gc::InitialHeap initialHeap);
michael@0 3950 static const VMFunction NewGCObjectInfo =
michael@0 3951 FunctionInfo<NewGCObjectFn>(js::jit::NewGCObject);
michael@0 3952
michael@0 3953 bool
michael@0 3954 CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir)
michael@0 3955 {
michael@0 3956 JSObject *templateObject = lir->mir()->templateObject();
michael@0 3957 gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
michael@0 3958 gc::InitialHeap initialHeap = lir->mir()->initialHeap();
michael@0 3959 Register objReg = ToRegister(lir->output());
michael@0 3960 Register tempReg = ToRegister(lir->temp());
michael@0 3961
michael@0 3962 OutOfLineCode *ool = oolCallVM(NewGCObjectInfo, lir,
michael@0 3963 (ArgList(), Imm32(allocKind), Imm32(initialHeap)),
michael@0 3964 StoreRegisterTo(objReg));
michael@0 3965 if (!ool)
michael@0 3966 return false;
michael@0 3967
michael@0 3968 // Allocate. If the FreeList is empty, call to VM, which may GC.
michael@0 3969 masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap());
michael@0 3970
michael@0 3971 // Initialize based on the templateObject.
michael@0 3972 masm.bind(ool->rejoin());
michael@0 3973 masm.initGCThing(objReg, tempReg, templateObject);
michael@0 3974
michael@0 3975 return true;
michael@0 3976 }
michael@0 3977
michael@0 3978 typedef JSObject *(*NewIonArgumentsObjectFn)(JSContext *cx, IonJSFrameLayout *frame, HandleObject);
michael@0 3979 static const VMFunction NewIonArgumentsObjectInfo =
michael@0 3980 FunctionInfo<NewIonArgumentsObjectFn>((NewIonArgumentsObjectFn) ArgumentsObject::createForIon);
michael@0 3981
michael@0 3982 bool
michael@0 3983 CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject *lir)
michael@0 3984 {
michael@0 3985 // This should be getting constructed in the first block only, and not any OSR entry blocks.
michael@0 3986 JS_ASSERT(lir->mir()->block()->id() == 0);
michael@0 3987
michael@0 3988 const LAllocation *callObj = lir->getCallObject();
michael@0 3989 Register temp = ToRegister(lir->getTemp(0));
michael@0 3990
michael@0 3991 masm.movePtr(StackPointer, temp);
michael@0 3992 masm.addPtr(Imm32(frameSize()), temp);
michael@0 3993
michael@0 3994 pushArg(ToRegister(callObj));
michael@0 3995 pushArg(temp);
michael@0 3996 return callVM(NewIonArgumentsObjectInfo, lir);
michael@0 3997 }
michael@0 3998
michael@0 3999 bool
michael@0 4000 CodeGenerator::visitGetArgumentsObjectArg(LGetArgumentsObjectArg *lir)
michael@0 4001 {
michael@0 4002 Register temp = ToRegister(lir->getTemp(0));
michael@0 4003 Register argsObj = ToRegister(lir->getArgsObject());
michael@0 4004 ValueOperand out = ToOutValue(lir);
michael@0 4005
michael@0 4006 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), temp);
michael@0 4007 Address argAddr(temp, ArgumentsData::offsetOfArgs() + lir->mir()->argno() * sizeof(Value));
michael@0 4008 masm.loadValue(argAddr, out);
michael@0 4009 #ifdef DEBUG
michael@0 4010 Label success;
michael@0 4011 masm.branchTestMagic(Assembler::NotEqual, out, &success);
michael@0 4012 masm.assumeUnreachable("Result from ArgumentObject shouldn't be JSVAL_TYPE_MAGIC.");
michael@0 4013 masm.bind(&success);
michael@0 4014 #endif
michael@0 4015 return true;
michael@0 4016 }
michael@0 4017
michael@0 4018 bool
michael@0 4019 CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg *lir)
michael@0 4020 {
michael@0 4021 Register temp = ToRegister(lir->getTemp(0));
michael@0 4022 Register argsObj = ToRegister(lir->getArgsObject());
michael@0 4023 ValueOperand value = ToValue(lir, LSetArgumentsObjectArg::ValueIndex);
michael@0 4024
michael@0 4025 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), temp);
michael@0 4026 Address argAddr(temp, ArgumentsData::offsetOfArgs() + lir->mir()->argno() * sizeof(Value));
michael@0 4027 emitPreBarrier(argAddr, MIRType_Value);
michael@0 4028 #ifdef DEBUG
michael@0 4029 Label success;
michael@0 4030 masm.branchTestMagic(Assembler::NotEqual, argAddr, &success);
michael@0 4031 masm.assumeUnreachable("Result in ArgumentObject shouldn't be JSVAL_TYPE_MAGIC.");
michael@0 4032 masm.bind(&success);
michael@0 4033 #endif
michael@0 4034 masm.storeValue(value, argAddr);
michael@0 4035 return true;
michael@0 4036 }
michael@0 4037
michael@0 4038 bool
michael@0 4039 CodeGenerator::visitReturnFromCtor(LReturnFromCtor *lir)
michael@0 4040 {
michael@0 4041 ValueOperand value = ToValue(lir, LReturnFromCtor::ValueIndex);
michael@0 4042 Register obj = ToRegister(lir->getObject());
michael@0 4043 Register output = ToRegister(lir->output());
michael@0 4044
michael@0 4045 Label valueIsObject, end;
michael@0 4046
michael@0 4047 masm.branchTestObject(Assembler::Equal, value, &valueIsObject);
michael@0 4048
michael@0 4049 // Value is not an object. Return that other object.
michael@0 4050 masm.movePtr(obj, output);
michael@0 4051 masm.jump(&end);
michael@0 4052
michael@0 4053 // Value is an object. Return unbox(Value).
michael@0 4054 masm.bind(&valueIsObject);
michael@0 4055 Register payload = masm.extractObject(value, output);
michael@0 4056 if (payload != output)
michael@0 4057 masm.movePtr(payload, output);
michael@0 4058
michael@0 4059 masm.bind(&end);
michael@0 4060 return true;
michael@0 4061 }
michael@0 4062
michael@0 4063 typedef JSObject *(*BoxNonStrictThisFn)(JSContext *, HandleValue);
michael@0 4064 static const VMFunction BoxNonStrictThisInfo = FunctionInfo<BoxNonStrictThisFn>(BoxNonStrictThis);
michael@0 4065
michael@0 4066 bool
michael@0 4067 CodeGenerator::visitComputeThis(LComputeThis *lir)
michael@0 4068 {
michael@0 4069 ValueOperand value = ToValue(lir, LComputeThis::ValueIndex);
michael@0 4070 Register output = ToRegister(lir->output());
michael@0 4071
michael@0 4072 OutOfLineCode *ool = oolCallVM(BoxNonStrictThisInfo, lir, (ArgList(), value),
michael@0 4073 StoreRegisterTo(output));
michael@0 4074 if (!ool)
michael@0 4075 return false;
michael@0 4076
michael@0 4077 masm.branchTestObject(Assembler::NotEqual, value, ool->entry());
michael@0 4078 masm.unboxObject(value, output);
michael@0 4079
michael@0 4080 masm.bind(ool->rejoin());
michael@0 4081 return true;
michael@0 4082 }
michael@0 4083
michael@0 4084 bool
michael@0 4085 CodeGenerator::visitLoadArrowThis(LLoadArrowThis *lir)
michael@0 4086 {
michael@0 4087 Register callee = ToRegister(lir->callee());
michael@0 4088 ValueOperand output = ToOutValue(lir);
michael@0 4089 masm.loadValue(Address(callee, FunctionExtended::offsetOfArrowThisSlot()), output);
michael@0 4090 return true;
michael@0 4091 }
michael@0 4092
michael@0 4093 bool
michael@0 4094 CodeGenerator::visitArrayLength(LArrayLength *lir)
michael@0 4095 {
michael@0 4096 Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength());
michael@0 4097 masm.load32(length, ToRegister(lir->output()));
michael@0 4098 return true;
michael@0 4099 }
michael@0 4100
michael@0 4101 bool
michael@0 4102 CodeGenerator::visitSetArrayLength(LSetArrayLength *lir)
michael@0 4103 {
michael@0 4104 Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength());
michael@0 4105 Int32Key newLength = ToInt32Key(lir->index());
michael@0 4106
michael@0 4107 masm.bumpKey(&newLength, 1);
michael@0 4108 masm.storeKey(newLength, length);
michael@0 4109 // Restore register value if it is used/captured after.
michael@0 4110 masm.bumpKey(&newLength, -1);
michael@0 4111 return true;
michael@0 4112 }
michael@0 4113
michael@0 4114 bool
michael@0 4115 CodeGenerator::visitTypedArrayLength(LTypedArrayLength *lir)
michael@0 4116 {
michael@0 4117 Register obj = ToRegister(lir->object());
michael@0 4118 Register out = ToRegister(lir->output());
michael@0 4119 masm.unboxInt32(Address(obj, TypedArrayObject::lengthOffset()), out);
michael@0 4120 return true;
michael@0 4121 }
michael@0 4122
michael@0 4123 bool
michael@0 4124 CodeGenerator::visitTypedArrayElements(LTypedArrayElements *lir)
michael@0 4125 {
michael@0 4126 Register obj = ToRegister(lir->object());
michael@0 4127 Register out = ToRegister(lir->output());
michael@0 4128 masm.loadPtr(Address(obj, TypedArrayObject::dataOffset()), out);
michael@0 4129 return true;
michael@0 4130 }
michael@0 4131
michael@0 4132 bool
michael@0 4133 CodeGenerator::visitNeuterCheck(LNeuterCheck *lir)
michael@0 4134 {
michael@0 4135 Register obj = ToRegister(lir->object());
michael@0 4136 Register temp = ToRegister(lir->temp());
michael@0 4137
michael@0 4138 masm.extractObject(Address(obj, TypedObject::offsetOfOwnerSlot()), temp);
michael@0 4139 masm.unboxInt32(Address(temp, ArrayBufferObject::flagsOffset()), temp);
michael@0 4140
michael@0 4141 Imm32 flag(ArrayBufferObject::neuteredFlag());
michael@0 4142 if (!bailoutTest32(Assembler::NonZero, temp, flag, lir->snapshot()))
michael@0 4143 return false;
michael@0 4144
michael@0 4145 return true;
michael@0 4146 }
michael@0 4147
michael@0 4148 bool
michael@0 4149 CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir)
michael@0 4150 {
michael@0 4151 Register obj = ToRegister(lir->object());
michael@0 4152 Register out = ToRegister(lir->output());
michael@0 4153 masm.loadPtr(Address(obj, TypedObject::offsetOfDataSlot()), out);
michael@0 4154 return true;
michael@0 4155 }
michael@0 4156
michael@0 4157 bool
michael@0 4158 CodeGenerator::visitSetTypedObjectOffset(LSetTypedObjectOffset *lir)
michael@0 4159 {
michael@0 4160 Register object = ToRegister(lir->object());
michael@0 4161 Register offset = ToRegister(lir->offset());
michael@0 4162 Register temp0 = ToRegister(lir->temp0());
michael@0 4163
michael@0 4164 // `offset` is an absolute offset into the base buffer. One way
michael@0 4165 // to implement this instruction would be load the base address
michael@0 4166 // from the buffer and add `offset`. But that'd be an extra load.
michael@0 4167 // We can instead load the current base pointer and current
michael@0 4168 // offset, compute the difference with `offset`, and then adjust
michael@0 4169 // the current base pointer. This is two loads but to adjacent
michael@0 4170 // fields in the same object, which should come in the same cache
michael@0 4171 // line.
michael@0 4172 //
michael@0 4173 // The C code I would probably write is the following:
michael@0 4174 //
michael@0 4175 // void SetTypedObjectOffset(TypedObject *obj, int32_t offset) {
michael@0 4176 // int32_t temp0 = obj->byteOffset;
michael@0 4177 // obj->pointer = obj->pointer - temp0 + offset;
michael@0 4178 // obj->byteOffset = offset;
michael@0 4179 // }
michael@0 4180 //
michael@0 4181 // But what we actually compute is more like this, because it
michael@0 4182 // saves us a temporary to do it this way:
michael@0 4183 //
michael@0 4184 // void SetTypedObjectOffset(TypedObject *obj, int32_t offset) {
michael@0 4185 // int32_t temp0 = obj->byteOffset;
michael@0 4186 // obj->pointer = obj->pointer - (temp0 - offset);
michael@0 4187 // obj->byteOffset = offset;
michael@0 4188 // }
michael@0 4189
michael@0 4190 // temp0 = typedObj->byteOffset;
michael@0 4191 masm.unboxInt32(Address(object, TypedObject::offsetOfByteOffsetSlot()), temp0);
michael@0 4192
michael@0 4193 // temp0 -= offset;
michael@0 4194 masm.subPtr(offset, temp0);
michael@0 4195
michael@0 4196 // obj->pointer -= temp0;
michael@0 4197 masm.subPtr(temp0, Address(object, TypedObject::offsetOfDataSlot()));
michael@0 4198
michael@0 4199 // obj->byteOffset = offset;
michael@0 4200 masm.storeValue(JSVAL_TYPE_INT32, offset,
michael@0 4201 Address(object, TypedObject::offsetOfByteOffsetSlot()));
michael@0 4202
michael@0 4203 return true;
michael@0 4204 }
michael@0 4205
michael@0 4206 bool
michael@0 4207 CodeGenerator::visitStringLength(LStringLength *lir)
michael@0 4208 {
michael@0 4209 Register input = ToRegister(lir->string());
michael@0 4210 Register output = ToRegister(lir->output());
michael@0 4211
michael@0 4212 masm.loadStringLength(input, output);
michael@0 4213 return true;
michael@0 4214 }
michael@0 4215
michael@0 4216 bool
michael@0 4217 CodeGenerator::visitMinMaxI(LMinMaxI *ins)
michael@0 4218 {
michael@0 4219 Register first = ToRegister(ins->first());
michael@0 4220 Register output = ToRegister(ins->output());
michael@0 4221
michael@0 4222 JS_ASSERT(first == output);
michael@0 4223
michael@0 4224 Label done;
michael@0 4225 Assembler::Condition cond = ins->mir()->isMax()
michael@0 4226 ? Assembler::GreaterThan
michael@0 4227 : Assembler::LessThan;
michael@0 4228
michael@0 4229 if (ins->second()->isConstant()) {
michael@0 4230 masm.branch32(cond, first, Imm32(ToInt32(ins->second())), &done);
michael@0 4231 masm.move32(Imm32(ToInt32(ins->second())), output);
michael@0 4232 } else {
michael@0 4233 masm.branch32(cond, first, ToRegister(ins->second()), &done);
michael@0 4234 masm.move32(ToRegister(ins->second()), output);
michael@0 4235 }
michael@0 4236
michael@0 4237 masm.bind(&done);
michael@0 4238 return true;
michael@0 4239 }
michael@0 4240
michael@0 4241 bool
michael@0 4242 CodeGenerator::visitAbsI(LAbsI *ins)
michael@0 4243 {
michael@0 4244 Register input = ToRegister(ins->input());
michael@0 4245 Label positive;
michael@0 4246
michael@0 4247 JS_ASSERT(input == ToRegister(ins->output()));
michael@0 4248 masm.branchTest32(Assembler::NotSigned, input, input, &positive);
michael@0 4249 masm.neg32(input);
michael@0 4250 #ifdef JS_CODEGEN_MIPS
michael@0 4251 LSnapshot *snapshot = ins->snapshot();
michael@0 4252 if (snapshot && !bailoutCmp32(Assembler::Equal, input, Imm32(INT32_MIN), snapshot))
michael@0 4253 return false;
michael@0 4254 #else
michael@0 4255 if (ins->snapshot() && !bailoutIf(Assembler::Overflow, ins->snapshot()))
michael@0 4256 return false;
michael@0 4257 #endif
michael@0 4258 masm.bind(&positive);
michael@0 4259
michael@0 4260 return true;
michael@0 4261 }
michael@0 4262
michael@0 4263 bool
michael@0 4264 CodeGenerator::visitPowI(LPowI *ins)
michael@0 4265 {
michael@0 4266 FloatRegister value = ToFloatRegister(ins->value());
michael@0 4267 Register power = ToRegister(ins->power());
michael@0 4268 Register temp = ToRegister(ins->temp());
michael@0 4269
michael@0 4270 JS_ASSERT(power != temp);
michael@0 4271
michael@0 4272 // In all implementations, setupUnalignedABICall() relinquishes use of
michael@0 4273 // its scratch register. We can therefore save an input register by
michael@0 4274 // reusing the scratch register to pass constants to callWithABI.
michael@0 4275 masm.setupUnalignedABICall(2, temp);
michael@0 4276 masm.passABIArg(value, MoveOp::DOUBLE);
michael@0 4277 masm.passABIArg(power);
michael@0 4278
michael@0 4279 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::powi), MoveOp::DOUBLE);
michael@0 4280 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
michael@0 4281
michael@0 4282 return true;
michael@0 4283 }
michael@0 4284
michael@0 4285 bool
michael@0 4286 CodeGenerator::visitPowD(LPowD *ins)
michael@0 4287 {
michael@0 4288 FloatRegister value = ToFloatRegister(ins->value());
michael@0 4289 FloatRegister power = ToFloatRegister(ins->power());
michael@0 4290 Register temp = ToRegister(ins->temp());
michael@0 4291
michael@0 4292 masm.setupUnalignedABICall(2, temp);
michael@0 4293 masm.passABIArg(value, MoveOp::DOUBLE);
michael@0 4294 masm.passABIArg(power, MoveOp::DOUBLE);
michael@0 4295 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaPow), MoveOp::DOUBLE);
michael@0 4296
michael@0 4297 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
michael@0 4298 return true;
michael@0 4299 }
michael@0 4300
michael@0 4301 bool
michael@0 4302 CodeGenerator::visitRandom(LRandom *ins)
michael@0 4303 {
michael@0 4304 Register temp = ToRegister(ins->temp());
michael@0 4305 Register temp2 = ToRegister(ins->temp2());
michael@0 4306
michael@0 4307 masm.loadJSContext(temp);
michael@0 4308
michael@0 4309 masm.setupUnalignedABICall(1, temp2);
michael@0 4310 masm.passABIArg(temp);
michael@0 4311 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, math_random_no_outparam), MoveOp::DOUBLE);
michael@0 4312
michael@0 4313 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
michael@0 4314 return true;
michael@0 4315 }
michael@0 4316
michael@0 4317 bool
michael@0 4318 CodeGenerator::visitMathFunctionD(LMathFunctionD *ins)
michael@0 4319 {
michael@0 4320 Register temp = ToRegister(ins->temp());
michael@0 4321 FloatRegister input = ToFloatRegister(ins->input());
michael@0 4322 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
michael@0 4323
michael@0 4324 const MathCache *mathCache = ins->mir()->cache();
michael@0 4325
michael@0 4326 masm.setupUnalignedABICall(mathCache ? 2 : 1, temp);
michael@0 4327 if (mathCache) {
michael@0 4328 masm.movePtr(ImmPtr(mathCache), temp);
michael@0 4329 masm.passABIArg(temp);
michael@0 4330 }
michael@0 4331 masm.passABIArg(input, MoveOp::DOUBLE);
michael@0 4332
michael@0 4333 # define MAYBE_CACHED(fcn) (mathCache ? (void*)fcn ## _impl : (void*)fcn ## _uncached)
michael@0 4334
michael@0 4335 void *funptr = nullptr;
michael@0 4336 switch (ins->mir()->function()) {
michael@0 4337 case MMathFunction::Log:
michael@0 4338 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_log));
michael@0 4339 break;
michael@0 4340 case MMathFunction::Sin:
michael@0 4341 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_sin));
michael@0 4342 break;
michael@0 4343 case MMathFunction::Cos:
michael@0 4344 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_cos));
michael@0 4345 break;
michael@0 4346 case MMathFunction::Exp:
michael@0 4347 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_exp));
michael@0 4348 break;
michael@0 4349 case MMathFunction::Tan:
michael@0 4350 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_tan));
michael@0 4351 break;
michael@0 4352 case MMathFunction::ATan:
michael@0 4353 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_atan));
michael@0 4354 break;
michael@0 4355 case MMathFunction::ASin:
michael@0 4356 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_asin));
michael@0 4357 break;
michael@0 4358 case MMathFunction::ACos:
michael@0 4359 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_acos));
michael@0 4360 break;
michael@0 4361 case MMathFunction::Log10:
michael@0 4362 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_log10));
michael@0 4363 break;
michael@0 4364 case MMathFunction::Log2:
michael@0 4365 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_log2));
michael@0 4366 break;
michael@0 4367 case MMathFunction::Log1P:
michael@0 4368 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_log1p));
michael@0 4369 break;
michael@0 4370 case MMathFunction::ExpM1:
michael@0 4371 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_expm1));
michael@0 4372 break;
michael@0 4373 case MMathFunction::CosH:
michael@0 4374 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_cosh));
michael@0 4375 break;
michael@0 4376 case MMathFunction::SinH:
michael@0 4377 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_sinh));
michael@0 4378 break;
michael@0 4379 case MMathFunction::TanH:
michael@0 4380 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_tanh));
michael@0 4381 break;
michael@0 4382 case MMathFunction::ACosH:
michael@0 4383 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_acosh));
michael@0 4384 break;
michael@0 4385 case MMathFunction::ASinH:
michael@0 4386 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_asinh));
michael@0 4387 break;
michael@0 4388 case MMathFunction::ATanH:
michael@0 4389 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_atanh));
michael@0 4390 break;
michael@0 4391 case MMathFunction::Sign:
michael@0 4392 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_sign));
michael@0 4393 break;
michael@0 4394 case MMathFunction::Trunc:
michael@0 4395 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_trunc));
michael@0 4396 break;
michael@0 4397 case MMathFunction::Cbrt:
michael@0 4398 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_cbrt));
michael@0 4399 break;
michael@0 4400 case MMathFunction::Floor:
michael@0 4401 funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_floor_impl);
michael@0 4402 break;
michael@0 4403 case MMathFunction::Ceil:
michael@0 4404 funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_ceil_impl);
michael@0 4405 break;
michael@0 4406 case MMathFunction::Round:
michael@0 4407 funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_round_impl);
michael@0 4408 break;
michael@0 4409 default:
michael@0 4410 MOZ_ASSUME_UNREACHABLE("Unknown math function");
michael@0 4411 }
michael@0 4412
michael@0 4413 # undef MAYBE_CACHED
michael@0 4414
michael@0 4415 masm.callWithABI(funptr, MoveOp::DOUBLE);
michael@0 4416 return true;
michael@0 4417 }
michael@0 4418
michael@0 4419 bool
michael@0 4420 CodeGenerator::visitMathFunctionF(LMathFunctionF *ins)
michael@0 4421 {
michael@0 4422 Register temp = ToRegister(ins->temp());
michael@0 4423 FloatRegister input = ToFloatRegister(ins->input());
michael@0 4424 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
michael@0 4425
michael@0 4426 masm.setupUnalignedABICall(1, temp);
michael@0 4427 masm.passABIArg(input, MoveOp::FLOAT32);
michael@0 4428
michael@0 4429 void *funptr = nullptr;
michael@0 4430 switch (ins->mir()->function()) {
michael@0 4431 case MMathFunction::Floor: funptr = JS_FUNC_TO_DATA_PTR(void *, floorf); break;
michael@0 4432 case MMathFunction::Round: funptr = JS_FUNC_TO_DATA_PTR(void *, math_roundf_impl); break;
michael@0 4433 case MMathFunction::Ceil: funptr = JS_FUNC_TO_DATA_PTR(void *, ceilf); break;
michael@0 4434 default:
michael@0 4435 MOZ_ASSUME_UNREACHABLE("Unknown or unsupported float32 math function");
michael@0 4436 }
michael@0 4437
michael@0 4438 masm.callWithABI(funptr, MoveOp::FLOAT32);
michael@0 4439 return true;
michael@0 4440 }
michael@0 4441
michael@0 4442 bool
michael@0 4443 CodeGenerator::visitModD(LModD *ins)
michael@0 4444 {
michael@0 4445 FloatRegister lhs = ToFloatRegister(ins->lhs());
michael@0 4446 FloatRegister rhs = ToFloatRegister(ins->rhs());
michael@0 4447 Register temp = ToRegister(ins->temp());
michael@0 4448
michael@0 4449 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
michael@0 4450
michael@0 4451 masm.setupUnalignedABICall(2, temp);
michael@0 4452 masm.passABIArg(lhs, MoveOp::DOUBLE);
michael@0 4453 masm.passABIArg(rhs, MoveOp::DOUBLE);
michael@0 4454
michael@0 4455 if (gen->compilingAsmJS())
michael@0 4456 masm.callWithABI(AsmJSImm_ModD, MoveOp::DOUBLE);
michael@0 4457 else
michael@0 4458 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NumberMod), MoveOp::DOUBLE);
michael@0 4459 return true;
michael@0 4460 }
michael@0 4461
michael@0 4462 typedef bool (*BinaryFn)(JSContext *, MutableHandleValue, MutableHandleValue, MutableHandleValue);
michael@0 4463 typedef bool (*BinaryParFn)(ForkJoinContext *, HandleValue, HandleValue, MutableHandleValue);
michael@0 4464
michael@0 4465 static const VMFunction AddInfo = FunctionInfo<BinaryFn>(js::AddValues);
michael@0 4466 static const VMFunction SubInfo = FunctionInfo<BinaryFn>(js::SubValues);
michael@0 4467 static const VMFunction MulInfo = FunctionInfo<BinaryFn>(js::MulValues);
michael@0 4468 static const VMFunction DivInfo = FunctionInfo<BinaryFn>(js::DivValues);
michael@0 4469 static const VMFunction ModInfo = FunctionInfo<BinaryFn>(js::ModValues);
michael@0 4470 static const VMFunctionsModal UrshInfo = VMFunctionsModal(
michael@0 4471 FunctionInfo<BinaryFn>(js::UrshValues),
michael@0 4472 FunctionInfo<BinaryParFn>(UrshValuesPar));
michael@0 4473
michael@0 4474 bool
michael@0 4475 CodeGenerator::visitBinaryV(LBinaryV *lir)
michael@0 4476 {
michael@0 4477 pushArg(ToValue(lir, LBinaryV::RhsInput));
michael@0 4478 pushArg(ToValue(lir, LBinaryV::LhsInput));
michael@0 4479
michael@0 4480 switch (lir->jsop()) {
michael@0 4481 case JSOP_ADD:
michael@0 4482 return callVM(AddInfo, lir);
michael@0 4483
michael@0 4484 case JSOP_SUB:
michael@0 4485 return callVM(SubInfo, lir);
michael@0 4486
michael@0 4487 case JSOP_MUL:
michael@0 4488 return callVM(MulInfo, lir);
michael@0 4489
michael@0 4490 case JSOP_DIV:
michael@0 4491 return callVM(DivInfo, lir);
michael@0 4492
michael@0 4493 case JSOP_MOD:
michael@0 4494 return callVM(ModInfo, lir);
michael@0 4495
michael@0 4496 case JSOP_URSH:
michael@0 4497 return callVM(UrshInfo, lir);
michael@0 4498
michael@0 4499 default:
michael@0 4500 MOZ_ASSUME_UNREACHABLE("Unexpected binary op");
michael@0 4501 }
michael@0 4502 }
michael@0 4503
michael@0 4504 typedef bool (*StringCompareFn)(JSContext *, HandleString, HandleString, bool *);
michael@0 4505 typedef bool (*StringCompareParFn)(ForkJoinContext *, HandleString, HandleString, bool *);
michael@0 4506 static const VMFunctionsModal StringsEqualInfo = VMFunctionsModal(
michael@0 4507 FunctionInfo<StringCompareFn>(jit::StringsEqual<true>),
michael@0 4508 FunctionInfo<StringCompareParFn>(jit::StringsEqualPar));
michael@0 4509 static const VMFunctionsModal StringsNotEqualInfo = VMFunctionsModal(
michael@0 4510 FunctionInfo<StringCompareFn>(jit::StringsEqual<false>),
michael@0 4511 FunctionInfo<StringCompareParFn>(jit::StringsUnequalPar));
michael@0 4512
michael@0 4513 bool
michael@0 4514 CodeGenerator::emitCompareS(LInstruction *lir, JSOp op, Register left, Register right,
michael@0 4515 Register output, Register temp)
michael@0 4516 {
michael@0 4517 JS_ASSERT(lir->isCompareS() || lir->isCompareStrictS());
michael@0 4518
michael@0 4519 OutOfLineCode *ool = nullptr;
michael@0 4520
michael@0 4521 if (op == JSOP_EQ || op == JSOP_STRICTEQ) {
michael@0 4522 ool = oolCallVM(StringsEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
michael@0 4523 } else {
michael@0 4524 JS_ASSERT(op == JSOP_NE || op == JSOP_STRICTNE);
michael@0 4525 ool = oolCallVM(StringsNotEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
michael@0 4526 }
michael@0 4527 if (!ool)
michael@0 4528 return false;
michael@0 4529
michael@0 4530 masm.compareStrings(op, left, right, output, temp, ool->entry());
michael@0 4531
michael@0 4532 masm.bind(ool->rejoin());
michael@0 4533 return true;
michael@0 4534 }
michael@0 4535
michael@0 4536 bool
michael@0 4537 CodeGenerator::visitCompareStrictS(LCompareStrictS *lir)
michael@0 4538 {
michael@0 4539 JSOp op = lir->mir()->jsop();
michael@0 4540 JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
michael@0 4541
michael@0 4542 const ValueOperand leftV = ToValue(lir, LCompareStrictS::Lhs);
michael@0 4543 Register right = ToRegister(lir->right());
michael@0 4544 Register output = ToRegister(lir->output());
michael@0 4545 Register temp = ToRegister(lir->temp());
michael@0 4546 Register tempToUnbox = ToTempUnboxRegister(lir->tempToUnbox());
michael@0 4547
michael@0 4548 Label string, done;
michael@0 4549
michael@0 4550 masm.branchTestString(Assembler::Equal, leftV, &string);
michael@0 4551 masm.move32(Imm32(op == JSOP_STRICTNE), output);
michael@0 4552 masm.jump(&done);
michael@0 4553
michael@0 4554 masm.bind(&string);
michael@0 4555 Register left = masm.extractString(leftV, tempToUnbox);
michael@0 4556 if (!emitCompareS(lir, op, left, right, output, temp))
michael@0 4557 return false;
michael@0 4558
michael@0 4559 masm.bind(&done);
michael@0 4560
michael@0 4561 return true;
michael@0 4562 }
michael@0 4563
michael@0 4564 bool
michael@0 4565 CodeGenerator::visitCompareS(LCompareS *lir)
michael@0 4566 {
michael@0 4567 JSOp op = lir->mir()->jsop();
michael@0 4568 Register left = ToRegister(lir->left());
michael@0 4569 Register right = ToRegister(lir->right());
michael@0 4570 Register output = ToRegister(lir->output());
michael@0 4571 Register temp = ToRegister(lir->temp());
michael@0 4572
michael@0 4573 return emitCompareS(lir, op, left, right, output, temp);
michael@0 4574 }
michael@0 4575
michael@0 4576 typedef bool (*CompareFn)(JSContext *, MutableHandleValue, MutableHandleValue, bool *);
michael@0 4577 typedef bool (*CompareParFn)(ForkJoinContext *, MutableHandleValue, MutableHandleValue, bool *);
michael@0 4578 static const VMFunctionsModal EqInfo = VMFunctionsModal(
michael@0 4579 FunctionInfo<CompareFn>(jit::LooselyEqual<true>),
michael@0 4580 FunctionInfo<CompareParFn>(jit::LooselyEqualPar));
michael@0 4581 static const VMFunctionsModal NeInfo = VMFunctionsModal(
michael@0 4582 FunctionInfo<CompareFn>(jit::LooselyEqual<false>),
michael@0 4583 FunctionInfo<CompareParFn>(jit::LooselyUnequalPar));
michael@0 4584 static const VMFunctionsModal StrictEqInfo = VMFunctionsModal(
michael@0 4585 FunctionInfo<CompareFn>(jit::StrictlyEqual<true>),
michael@0 4586 FunctionInfo<CompareParFn>(jit::StrictlyEqualPar));
michael@0 4587 static const VMFunctionsModal StrictNeInfo = VMFunctionsModal(
michael@0 4588 FunctionInfo<CompareFn>(jit::StrictlyEqual<false>),
michael@0 4589 FunctionInfo<CompareParFn>(jit::StrictlyUnequalPar));
michael@0 4590 static const VMFunctionsModal LtInfo = VMFunctionsModal(
michael@0 4591 FunctionInfo<CompareFn>(jit::LessThan),
michael@0 4592 FunctionInfo<CompareParFn>(jit::LessThanPar));
michael@0 4593 static const VMFunctionsModal LeInfo = VMFunctionsModal(
michael@0 4594 FunctionInfo<CompareFn>(jit::LessThanOrEqual),
michael@0 4595 FunctionInfo<CompareParFn>(jit::LessThanOrEqualPar));
michael@0 4596 static const VMFunctionsModal GtInfo = VMFunctionsModal(
michael@0 4597 FunctionInfo<CompareFn>(jit::GreaterThan),
michael@0 4598 FunctionInfo<CompareParFn>(jit::GreaterThanPar));
michael@0 4599 static const VMFunctionsModal GeInfo = VMFunctionsModal(
michael@0 4600 FunctionInfo<CompareFn>(jit::GreaterThanOrEqual),
michael@0 4601 FunctionInfo<CompareParFn>(jit::GreaterThanOrEqualPar));
michael@0 4602
michael@0 4603 bool
michael@0 4604 CodeGenerator::visitCompareVM(LCompareVM *lir)
michael@0 4605 {
michael@0 4606 pushArg(ToValue(lir, LBinaryV::RhsInput));
michael@0 4607 pushArg(ToValue(lir, LBinaryV::LhsInput));
michael@0 4608
michael@0 4609 switch (lir->mir()->jsop()) {
michael@0 4610 case JSOP_EQ:
michael@0 4611 return callVM(EqInfo, lir);
michael@0 4612
michael@0 4613 case JSOP_NE:
michael@0 4614 return callVM(NeInfo, lir);
michael@0 4615
michael@0 4616 case JSOP_STRICTEQ:
michael@0 4617 return callVM(StrictEqInfo, lir);
michael@0 4618
michael@0 4619 case JSOP_STRICTNE:
michael@0 4620 return callVM(StrictNeInfo, lir);
michael@0 4621
michael@0 4622 case JSOP_LT:
michael@0 4623 return callVM(LtInfo, lir);
michael@0 4624
michael@0 4625 case JSOP_LE:
michael@0 4626 return callVM(LeInfo, lir);
michael@0 4627
michael@0 4628 case JSOP_GT:
michael@0 4629 return callVM(GtInfo, lir);
michael@0 4630
michael@0 4631 case JSOP_GE:
michael@0 4632 return callVM(GeInfo, lir);
michael@0 4633
michael@0 4634 default:
michael@0 4635 MOZ_ASSUME_UNREACHABLE("Unexpected compare op");
michael@0 4636 }
michael@0 4637 }
michael@0 4638
michael@0 4639 bool
michael@0 4640 CodeGenerator::visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir)
michael@0 4641 {
michael@0 4642 JSOp op = lir->mir()->jsop();
michael@0 4643 MCompare::CompareType compareType = lir->mir()->compareType();
michael@0 4644 JS_ASSERT(compareType == MCompare::Compare_Undefined ||
michael@0 4645 compareType == MCompare::Compare_Null);
michael@0 4646
michael@0 4647 const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefined::Value);
michael@0 4648 Register output = ToRegister(lir->output());
michael@0 4649
michael@0 4650 if (op == JSOP_EQ || op == JSOP_NE) {
michael@0 4651 MOZ_ASSERT(lir->mir()->lhs()->type() != MIRType_Object ||
michael@0 4652 lir->mir()->operandMightEmulateUndefined(),
michael@0 4653 "Operands which can't emulate undefined should have been folded");
michael@0 4654
michael@0 4655 OutOfLineTestObjectWithLabels *ool = nullptr;
michael@0 4656 Maybe<Label> label1, label2;
michael@0 4657 Label *nullOrLikeUndefined;
michael@0 4658 Label *notNullOrLikeUndefined;
michael@0 4659 if (lir->mir()->operandMightEmulateUndefined()) {
michael@0 4660 ool = new(alloc()) OutOfLineTestObjectWithLabels();
michael@0 4661 if (!addOutOfLineCode(ool))
michael@0 4662 return false;
michael@0 4663 nullOrLikeUndefined = ool->label1();
michael@0 4664 notNullOrLikeUndefined = ool->label2();
michael@0 4665 } else {
michael@0 4666 label1.construct();
michael@0 4667 label2.construct();
michael@0 4668 nullOrLikeUndefined = label1.addr();
michael@0 4669 notNullOrLikeUndefined = label2.addr();
michael@0 4670 }
michael@0 4671
michael@0 4672 Register tag = masm.splitTagForTest(value);
michael@0 4673
michael@0 4674 masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined);
michael@0 4675 masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined);
michael@0 4676
michael@0 4677 if (ool) {
michael@0 4678 // Check whether it's a truthy object or a falsy object that emulates
michael@0 4679 // undefined.
michael@0 4680 masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined);
michael@0 4681
michael@0 4682 Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
michael@0 4683 branchTestObjectEmulatesUndefined(objreg, nullOrLikeUndefined, notNullOrLikeUndefined,
michael@0 4684 ToRegister(lir->temp()), ool);
michael@0 4685 // fall through
michael@0 4686 }
michael@0 4687
michael@0 4688 Label done;
michael@0 4689
michael@0 4690 // It's not null or undefined, and if it's an object it doesn't
michael@0 4691 // emulate undefined, so it's not like undefined.
michael@0 4692 masm.move32(Imm32(op == JSOP_NE), output);
michael@0 4693 masm.jump(&done);
michael@0 4694
michael@0 4695 masm.bind(nullOrLikeUndefined);
michael@0 4696 masm.move32(Imm32(op == JSOP_EQ), output);
michael@0 4697
michael@0 4698 // Both branches meet here.
michael@0 4699 masm.bind(&done);
michael@0 4700 return true;
michael@0 4701 }
michael@0 4702
michael@0 4703 JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
michael@0 4704
michael@0 4705 Assembler::Condition cond = JSOpToCondition(compareType, op);
michael@0 4706 if (compareType == MCompare::Compare_Null)
michael@0 4707 masm.testNullSet(cond, value, output);
michael@0 4708 else
michael@0 4709 masm.testUndefinedSet(cond, value, output);
michael@0 4710
michael@0 4711 return true;
michael@0 4712 }
michael@0 4713
michael@0 4714 bool
michael@0 4715 CodeGenerator::visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir)
michael@0 4716 {
michael@0 4717 JSOp op = lir->cmpMir()->jsop();
michael@0 4718 MCompare::CompareType compareType = lir->cmpMir()->compareType();
michael@0 4719 JS_ASSERT(compareType == MCompare::Compare_Undefined ||
michael@0 4720 compareType == MCompare::Compare_Null);
michael@0 4721
michael@0 4722 const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedAndBranch::Value);
michael@0 4723
michael@0 4724 if (op == JSOP_EQ || op == JSOP_NE) {
michael@0 4725 MBasicBlock *ifTrue;
michael@0 4726 MBasicBlock *ifFalse;
michael@0 4727
michael@0 4728 if (op == JSOP_EQ) {
michael@0 4729 ifTrue = lir->ifTrue();
michael@0 4730 ifFalse = lir->ifFalse();
michael@0 4731 } else {
michael@0 4732 // Swap branches.
michael@0 4733 ifTrue = lir->ifFalse();
michael@0 4734 ifFalse = lir->ifTrue();
michael@0 4735 op = JSOP_EQ;
michael@0 4736 }
michael@0 4737
michael@0 4738 MOZ_ASSERT(lir->cmpMir()->lhs()->type() != MIRType_Object ||
michael@0 4739 lir->cmpMir()->operandMightEmulateUndefined(),
michael@0 4740 "Operands which can't emulate undefined should have been folded");
michael@0 4741
michael@0 4742 OutOfLineTestObject *ool = nullptr;
michael@0 4743 if (lir->cmpMir()->operandMightEmulateUndefined()) {
michael@0 4744 ool = new(alloc()) OutOfLineTestObject();
michael@0 4745 if (!addOutOfLineCode(ool))
michael@0 4746 return false;
michael@0 4747 }
michael@0 4748
michael@0 4749 Register tag = masm.splitTagForTest(value);
michael@0 4750
michael@0 4751 Label *ifTrueLabel = getJumpLabelForBranch(ifTrue);
michael@0 4752 Label *ifFalseLabel = getJumpLabelForBranch(ifFalse);
michael@0 4753
michael@0 4754 masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel);
michael@0 4755 masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel);
michael@0 4756
michael@0 4757 if (ool) {
michael@0 4758 masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel);
michael@0 4759
michael@0 4760 // Objects that emulate undefined are loosely equal to null/undefined.
michael@0 4761 Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
michael@0 4762 Register scratch = ToRegister(lir->temp());
michael@0 4763 testObjectEmulatesUndefined(objreg, ifTrueLabel, ifFalseLabel, scratch, ool);
michael@0 4764 } else {
michael@0 4765 masm.jump(ifFalseLabel);
michael@0 4766 }
michael@0 4767 return true;
michael@0 4768 }
michael@0 4769
michael@0 4770 JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
michael@0 4771
michael@0 4772 Assembler::Condition cond = JSOpToCondition(compareType, op);
michael@0 4773 if (compareType == MCompare::Compare_Null)
michael@0 4774 testNullEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse());
michael@0 4775 else
michael@0 4776 testUndefinedEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse());
michael@0 4777
michael@0 4778 return true;
michael@0 4779 }
michael@0 4780
michael@0 4781 bool
michael@0 4782 CodeGenerator::visitEmulatesUndefined(LEmulatesUndefined *lir)
michael@0 4783 {
michael@0 4784 MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined ||
michael@0 4785 lir->mir()->compareType() == MCompare::Compare_Null);
michael@0 4786 MOZ_ASSERT(lir->mir()->lhs()->type() == MIRType_Object);
michael@0 4787 MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(),
michael@0 4788 "If the object couldn't emulate undefined, this should have been folded.");
michael@0 4789
michael@0 4790 JSOp op = lir->mir()->jsop();
michael@0 4791 MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded");
michael@0 4792
michael@0 4793 OutOfLineTestObjectWithLabels *ool = new(alloc()) OutOfLineTestObjectWithLabels();
michael@0 4794 if (!addOutOfLineCode(ool))
michael@0 4795 return false;
michael@0 4796
michael@0 4797 Label *emulatesUndefined = ool->label1();
michael@0 4798 Label *doesntEmulateUndefined = ool->label2();
michael@0 4799
michael@0 4800 Register objreg = ToRegister(lir->input());
michael@0 4801 Register output = ToRegister(lir->output());
michael@0 4802 branchTestObjectEmulatesUndefined(objreg, emulatesUndefined, doesntEmulateUndefined,
michael@0 4803 output, ool);
michael@0 4804
michael@0 4805 Label done;
michael@0 4806
michael@0 4807 masm.move32(Imm32(op == JSOP_NE), output);
michael@0 4808 masm.jump(&done);
michael@0 4809
michael@0 4810 masm.bind(emulatesUndefined);
michael@0 4811 masm.move32(Imm32(op == JSOP_EQ), output);
michael@0 4812 masm.bind(&done);
michael@0 4813 return true;
michael@0 4814 }
michael@0 4815
michael@0 4816 bool
michael@0 4817 CodeGenerator::visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir)
michael@0 4818 {
michael@0 4819 MOZ_ASSERT(lir->cmpMir()->compareType() == MCompare::Compare_Undefined ||
michael@0 4820 lir->cmpMir()->compareType() == MCompare::Compare_Null);
michael@0 4821 MOZ_ASSERT(lir->cmpMir()->operandMightEmulateUndefined(),
michael@0 4822 "Operands which can't emulate undefined should have been folded");
michael@0 4823
michael@0 4824 JSOp op = lir->cmpMir()->jsop();
michael@0 4825 MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded");
michael@0 4826
michael@0 4827 OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject();
michael@0 4828 if (!addOutOfLineCode(ool))
michael@0 4829 return false;
michael@0 4830
michael@0 4831 Label *equal;
michael@0 4832 Label *unequal;
michael@0 4833
michael@0 4834 {
michael@0 4835 MBasicBlock *ifTrue;
michael@0 4836 MBasicBlock *ifFalse;
michael@0 4837
michael@0 4838 if (op == JSOP_EQ) {
michael@0 4839 ifTrue = lir->ifTrue();
michael@0 4840 ifFalse = lir->ifFalse();
michael@0 4841 } else {
michael@0 4842 // Swap branches.
michael@0 4843 ifTrue = lir->ifFalse();
michael@0 4844 ifFalse = lir->ifTrue();
michael@0 4845 op = JSOP_EQ;
michael@0 4846 }
michael@0 4847
michael@0 4848 equal = getJumpLabelForBranch(ifTrue);
michael@0 4849 unequal = getJumpLabelForBranch(ifFalse);
michael@0 4850 }
michael@0 4851
michael@0 4852 Register objreg = ToRegister(lir->input());
michael@0 4853
michael@0 4854 testObjectEmulatesUndefined(objreg, equal, unequal, ToRegister(lir->temp()), ool);
michael@0 4855 return true;
michael@0 4856 }
michael@0 4857
michael@0 4858 typedef JSString *(*ConcatStringsFn)(ThreadSafeContext *, HandleString, HandleString);
michael@0 4859 typedef JSString *(*ConcatStringsParFn)(ForkJoinContext *, HandleString, HandleString);
michael@0 4860 static const VMFunctionsModal ConcatStringsInfo = VMFunctionsModal(
michael@0 4861 FunctionInfo<ConcatStringsFn>(ConcatStrings<CanGC>),
michael@0 4862 FunctionInfo<ConcatStringsParFn>(ConcatStringsPar));
michael@0 4863
michael@0 4864 bool
michael@0 4865 CodeGenerator::emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output)
michael@0 4866 {
michael@0 4867 OutOfLineCode *ool = oolCallVM(ConcatStringsInfo, lir, (ArgList(), lhs, rhs),
michael@0 4868 StoreRegisterTo(output));
michael@0 4869 if (!ool)
michael@0 4870 return false;
michael@0 4871
michael@0 4872 ExecutionMode mode = gen->info().executionMode();
michael@0 4873 JitCode *stringConcatStub = gen->compartment->jitCompartment()->stringConcatStub(mode);
michael@0 4874 masm.call(stringConcatStub);
michael@0 4875 masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
michael@0 4876
michael@0 4877 masm.bind(ool->rejoin());
michael@0 4878 return true;
michael@0 4879 }
michael@0 4880
michael@0 4881 bool
michael@0 4882 CodeGenerator::visitConcat(LConcat *lir)
michael@0 4883 {
michael@0 4884 Register lhs = ToRegister(lir->lhs());
michael@0 4885 Register rhs = ToRegister(lir->rhs());
michael@0 4886
michael@0 4887 Register output = ToRegister(lir->output());
michael@0 4888
michael@0 4889 JS_ASSERT(lhs == CallTempReg0);
michael@0 4890 JS_ASSERT(rhs == CallTempReg1);
michael@0 4891 JS_ASSERT(ToRegister(lir->temp1()) == CallTempReg0);
michael@0 4892 JS_ASSERT(ToRegister(lir->temp2()) == CallTempReg1);
michael@0 4893 JS_ASSERT(ToRegister(lir->temp3()) == CallTempReg2);
michael@0 4894 JS_ASSERT(ToRegister(lir->temp4()) == CallTempReg3);
michael@0 4895 JS_ASSERT(ToRegister(lir->temp5()) == CallTempReg4);
michael@0 4896 JS_ASSERT(output == CallTempReg5);
michael@0 4897
michael@0 4898 return emitConcat(lir, lhs, rhs, output);
michael@0 4899 }
michael@0 4900
michael@0 4901 bool
michael@0 4902 CodeGenerator::visitConcatPar(LConcatPar *lir)
michael@0 4903 {
michael@0 4904 DebugOnly<Register> cx = ToRegister(lir->forkJoinContext());
michael@0 4905 Register lhs = ToRegister(lir->lhs());
michael@0 4906 Register rhs = ToRegister(lir->rhs());
michael@0 4907 Register output = ToRegister(lir->output());
michael@0 4908
michael@0 4909 JS_ASSERT(lhs == CallTempReg0);
michael@0 4910 JS_ASSERT(rhs == CallTempReg1);
michael@0 4911 JS_ASSERT((Register)cx == CallTempReg4);
michael@0 4912 JS_ASSERT(ToRegister(lir->temp1()) == CallTempReg0);
michael@0 4913 JS_ASSERT(ToRegister(lir->temp2()) == CallTempReg1);
michael@0 4914 JS_ASSERT(ToRegister(lir->temp3()) == CallTempReg2);
michael@0 4915 JS_ASSERT(ToRegister(lir->temp4()) == CallTempReg3);
michael@0 4916 JS_ASSERT(output == CallTempReg5);
michael@0 4917
michael@0 4918 return emitConcat(lir, lhs, rhs, output);
michael@0 4919 }
michael@0 4920
michael@0 4921 static void
michael@0 4922 CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch)
michael@0 4923 {
michael@0 4924 // Copy |len| jschars from |from| to |to|. Assumes len > 0 (checked below in
michael@0 4925 // debug builds), and when done |to| must point to the next available char.
michael@0 4926
michael@0 4927 #ifdef DEBUG
michael@0 4928 Label ok;
michael@0 4929 masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
michael@0 4930 masm.assumeUnreachable("Length should be greater than 0.");
michael@0 4931 masm.bind(&ok);
michael@0 4932 #endif
michael@0 4933
michael@0 4934 JS_STATIC_ASSERT(sizeof(jschar) == 2);
michael@0 4935
michael@0 4936 Label start;
michael@0 4937 masm.bind(&start);
michael@0 4938 masm.load16ZeroExtend(Address(from, 0), scratch);
michael@0 4939 masm.store16(scratch, Address(to, 0));
michael@0 4940 masm.addPtr(Imm32(2), from);
michael@0 4941 masm.addPtr(Imm32(2), to);
michael@0 4942 masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start);
michael@0 4943 }
michael@0 4944
michael@0 4945 JitCode *
michael@0 4946 JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
michael@0 4947 {
michael@0 4948 MacroAssembler masm(cx);
michael@0 4949
michael@0 4950 Register lhs = CallTempReg0;
michael@0 4951 Register rhs = CallTempReg1;
michael@0 4952 Register temp1 = CallTempReg2;
michael@0 4953 Register temp2 = CallTempReg3;
michael@0 4954 Register temp3 = CallTempReg4;
michael@0 4955 Register output = CallTempReg5;
michael@0 4956
michael@0 4957 // In parallel execution, we pass in the ForkJoinContext in CallTempReg4, as
michael@0 4958 // by the time we need to use the temp3 we no longer have need of the
michael@0 4959 // cx.
michael@0 4960 Register forkJoinContext = CallTempReg4;
michael@0 4961
michael@0 4962 Label failure, failurePopTemps;
michael@0 4963
michael@0 4964 // If lhs is empty, return rhs.
michael@0 4965 Label leftEmpty;
michael@0 4966 masm.loadStringLength(lhs, temp1);
michael@0 4967 masm.branchTest32(Assembler::Zero, temp1, temp1, &leftEmpty);
michael@0 4968
michael@0 4969 // If rhs is empty, return lhs.
michael@0 4970 Label rightEmpty;
michael@0 4971 masm.loadStringLength(rhs, temp2);
michael@0 4972 masm.branchTest32(Assembler::Zero, temp2, temp2, &rightEmpty);
michael@0 4973
michael@0 4974 masm.add32(temp1, temp2);
michael@0 4975
michael@0 4976 // Check if we can use a JSFatInlineString.
michael@0 4977 Label isFatInline;
michael@0 4978 masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(JSFatInlineString::MAX_FAT_INLINE_LENGTH),
michael@0 4979 &isFatInline);
michael@0 4980
michael@0 4981 // Ensure result length <= JSString::MAX_LENGTH.
michael@0 4982 masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure);
michael@0 4983
michael@0 4984 // Allocate a new rope.
michael@0 4985 switch (mode) {
michael@0 4986 case SequentialExecution:
michael@0 4987 masm.newGCString(output, temp3, &failure);
michael@0 4988 break;
michael@0 4989 case ParallelExecution:
michael@0 4990 masm.push(temp1);
michael@0 4991 masm.push(temp2);
michael@0 4992 masm.newGCStringPar(output, forkJoinContext, temp1, temp2, &failurePopTemps);
michael@0 4993 masm.pop(temp2);
michael@0 4994 masm.pop(temp1);
michael@0 4995 break;
michael@0 4996 default:
michael@0 4997 MOZ_ASSUME_UNREACHABLE("No such execution mode");
michael@0 4998 }
michael@0 4999
michael@0 5000 // Store lengthAndFlags.
michael@0 5001 JS_STATIC_ASSERT(JSString::ROPE_FLAGS == 0);
michael@0 5002 masm.lshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp2);
michael@0 5003 masm.storePtr(temp2, Address(output, JSString::offsetOfLengthAndFlags()));
michael@0 5004
michael@0 5005 // Store left and right nodes.
michael@0 5006 masm.storePtr(lhs, Address(output, JSRope::offsetOfLeft()));
michael@0 5007 masm.storePtr(rhs, Address(output, JSRope::offsetOfRight()));
michael@0 5008 masm.ret();
michael@0 5009
michael@0 5010 masm.bind(&leftEmpty);
michael@0 5011 masm.mov(rhs, output);
michael@0 5012 masm.ret();
michael@0 5013
michael@0 5014 masm.bind(&rightEmpty);
michael@0 5015 masm.mov(lhs, output);
michael@0 5016 masm.ret();
michael@0 5017
michael@0 5018 masm.bind(&isFatInline);
michael@0 5019
michael@0 5020 // State: lhs length in temp1, result length in temp2.
michael@0 5021
michael@0 5022 // Ensure both strings are linear (flags != 0).
michael@0 5023 JS_STATIC_ASSERT(JSString::ROPE_FLAGS == 0);
michael@0 5024 masm.branchTestPtr(Assembler::Zero, Address(lhs, JSString::offsetOfLengthAndFlags()),
michael@0 5025 Imm32(JSString::FLAGS_MASK), &failure);
michael@0 5026 masm.branchTestPtr(Assembler::Zero, Address(rhs, JSString::offsetOfLengthAndFlags()),
michael@0 5027 Imm32(JSString::FLAGS_MASK), &failure);
michael@0 5028
michael@0 5029 // Allocate a JSFatInlineString.
michael@0 5030 switch (mode) {
michael@0 5031 case SequentialExecution:
michael@0 5032 masm.newGCFatInlineString(output, temp3, &failure);
michael@0 5033 break;
michael@0 5034 case ParallelExecution:
michael@0 5035 masm.push(temp1);
michael@0 5036 masm.push(temp2);
michael@0 5037 masm.newGCFatInlineStringPar(output, forkJoinContext, temp1, temp2, &failurePopTemps);
michael@0 5038 masm.pop(temp2);
michael@0 5039 masm.pop(temp1);
michael@0 5040 break;
michael@0 5041 default:
michael@0 5042 MOZ_ASSUME_UNREACHABLE("No such execution mode");
michael@0 5043 }
michael@0 5044
michael@0 5045 // Set lengthAndFlags.
michael@0 5046 masm.lshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp2);
michael@0 5047 masm.orPtr(Imm32(JSString::FIXED_FLAGS), temp2);
michael@0 5048 masm.storePtr(temp2, Address(output, JSString::offsetOfLengthAndFlags()));
michael@0 5049
michael@0 5050 // Set chars pointer, keep in temp2 for copy loop below.
michael@0 5051 masm.computeEffectiveAddress(Address(output, JSFatInlineString::offsetOfInlineStorage()), temp2);
michael@0 5052 masm.storePtr(temp2, Address(output, JSFatInlineString::offsetOfChars()));
michael@0 5053
michael@0 5054 {
michael@0 5055 // We use temp3 in this block, which in parallel execution also holds
michael@0 5056 // a live ForkJoinContext pointer. If we are compiling for parallel
michael@0 5057 // execution, be sure to save and restore the ForkJoinContext.
michael@0 5058 if (mode == ParallelExecution)
michael@0 5059 masm.push(temp3);
michael@0 5060
michael@0 5061 // Copy lhs chars. Temp1 still holds the lhs length. Note that this
michael@0 5062 // advances temp2 to point to the next char. Note that this also
michael@0 5063 // repurposes the lhs register.
michael@0 5064 masm.loadPtr(Address(lhs, JSString::offsetOfChars()), lhs);
michael@0 5065 CopyStringChars(masm, temp2, lhs, temp1, temp3);
michael@0 5066
michael@0 5067 // Copy rhs chars.
michael@0 5068 masm.loadStringLength(rhs, temp1);
michael@0 5069 masm.loadPtr(Address(rhs, JSString::offsetOfChars()), rhs);
michael@0 5070 CopyStringChars(masm, temp2, rhs, temp1, temp3);
michael@0 5071
michael@0 5072 if (mode == ParallelExecution)
michael@0 5073 masm.pop(temp3);
michael@0 5074 }
michael@0 5075
michael@0 5076 // Null-terminate.
michael@0 5077 masm.store16(Imm32(0), Address(temp2, 0));
michael@0 5078 masm.ret();
michael@0 5079
michael@0 5080 masm.bind(&failurePopTemps);
michael@0 5081 masm.pop(temp2);
michael@0 5082 masm.pop(temp1);
michael@0 5083
michael@0 5084 masm.bind(&failure);
michael@0 5085 masm.movePtr(ImmPtr(nullptr), output);
michael@0 5086 masm.ret();
michael@0 5087
michael@0 5088 Linker linker(masm);
michael@0 5089 AutoFlushICache afc("StringConcatStub");
michael@0 5090 JitCode *code = linker.newCode<CanGC>(cx, JSC::OTHER_CODE);
michael@0 5091
michael@0 5092 #ifdef JS_ION_PERF
michael@0 5093 writePerfSpewerJitCodeProfile(code, "StringConcatStub");
michael@0 5094 #endif
michael@0 5095
michael@0 5096 return code;
michael@0 5097 }
michael@0 5098
michael@0 5099 typedef bool (*CharCodeAtFn)(JSContext *, HandleString, int32_t, uint32_t *);
michael@0 5100 static const VMFunction CharCodeAtInfo = FunctionInfo<CharCodeAtFn>(jit::CharCodeAt);
michael@0 5101
michael@0 5102 bool
michael@0 5103 CodeGenerator::visitCharCodeAt(LCharCodeAt *lir)
michael@0 5104 {
michael@0 5105 Register str = ToRegister(lir->str());
michael@0 5106 Register index = ToRegister(lir->index());
michael@0 5107 Register output = ToRegister(lir->output());
michael@0 5108
michael@0 5109 OutOfLineCode *ool = oolCallVM(CharCodeAtInfo, lir, (ArgList(), str, index), StoreRegisterTo(output));
michael@0 5110 if (!ool)
michael@0 5111 return false;
michael@0 5112
michael@0 5113 Address lengthAndFlagsAddr(str, JSString::offsetOfLengthAndFlags());
michael@0 5114 masm.branchTest32(Assembler::Zero, lengthAndFlagsAddr, Imm32(JSString::FLAGS_MASK), ool->entry());
michael@0 5115
michael@0 5116 // getChars
michael@0 5117 Address charsAddr(str, JSString::offsetOfChars());
michael@0 5118 masm.loadPtr(charsAddr, output);
michael@0 5119 masm.load16ZeroExtend(BaseIndex(output, index, TimesTwo, 0), output);
michael@0 5120
michael@0 5121 masm.bind(ool->rejoin());
michael@0 5122 return true;
michael@0 5123 }
michael@0 5124
michael@0 5125 typedef JSFlatString *(*StringFromCharCodeFn)(JSContext *, int32_t);
michael@0 5126 static const VMFunction StringFromCharCodeInfo = FunctionInfo<StringFromCharCodeFn>(jit::StringFromCharCode);
michael@0 5127
michael@0 5128 bool
michael@0 5129 CodeGenerator::visitFromCharCode(LFromCharCode *lir)
michael@0 5130 {
michael@0 5131 Register code = ToRegister(lir->code());
michael@0 5132 Register output = ToRegister(lir->output());
michael@0 5133
michael@0 5134 OutOfLineCode *ool = oolCallVM(StringFromCharCodeInfo, lir, (ArgList(), code), StoreRegisterTo(output));
michael@0 5135 if (!ool)
michael@0 5136 return false;
michael@0 5137
michael@0 5138 // OOL path if code >= UNIT_STATIC_LIMIT.
michael@0 5139 masm.branch32(Assembler::AboveOrEqual, code, Imm32(StaticStrings::UNIT_STATIC_LIMIT),
michael@0 5140 ool->entry());
michael@0 5141
michael@0 5142 masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings().unitStaticTable), output);
michael@0 5143 masm.loadPtr(BaseIndex(output, code, ScalePointer), output);
michael@0 5144
michael@0 5145 masm.bind(ool->rejoin());
michael@0 5146 return true;
michael@0 5147 }
michael@0 5148
michael@0 5149 typedef JSObject *(*StringSplitFn)(JSContext *, HandleTypeObject, HandleString, HandleString);
michael@0 5150 static const VMFunction StringSplitInfo = FunctionInfo<StringSplitFn>(js::str_split_string);
michael@0 5151
michael@0 5152 bool
michael@0 5153 CodeGenerator::visitStringSplit(LStringSplit *lir)
michael@0 5154 {
michael@0 5155 pushArg(ToRegister(lir->separator()));
michael@0 5156 pushArg(ToRegister(lir->string()));
michael@0 5157 pushArg(ImmGCPtr(lir->mir()->typeObject()));
michael@0 5158
michael@0 5159 return callVM(StringSplitInfo, lir);
michael@0 5160 }
michael@0 5161
michael@0 5162 bool
michael@0 5163 CodeGenerator::visitInitializedLength(LInitializedLength *lir)
michael@0 5164 {
michael@0 5165 Address initLength(ToRegister(lir->elements()), ObjectElements::offsetOfInitializedLength());
michael@0 5166 masm.load32(initLength, ToRegister(lir->output()));
michael@0 5167 return true;
michael@0 5168 }
michael@0 5169
michael@0 5170 bool
michael@0 5171 CodeGenerator::visitSetInitializedLength(LSetInitializedLength *lir)
michael@0 5172 {
michael@0 5173 Address initLength(ToRegister(lir->elements()), ObjectElements::offsetOfInitializedLength());
michael@0 5174 Int32Key index = ToInt32Key(lir->index());
michael@0 5175
michael@0 5176 masm.bumpKey(&index, 1);
michael@0 5177 masm.storeKey(index, initLength);
michael@0 5178 // Restore register value if it is used/captured after.
michael@0 5179 masm.bumpKey(&index, -1);
michael@0 5180 return true;
michael@0 5181 }
michael@0 5182
michael@0 5183 bool
michael@0 5184 CodeGenerator::visitNotO(LNotO *lir)
michael@0 5185 {
michael@0 5186 MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(),
michael@0 5187 "This should be constant-folded if the object can't emulate undefined.");
michael@0 5188
michael@0 5189 OutOfLineTestObjectWithLabels *ool = new(alloc()) OutOfLineTestObjectWithLabels();
michael@0 5190 if (!addOutOfLineCode(ool))
michael@0 5191 return false;
michael@0 5192
michael@0 5193 Label *ifEmulatesUndefined = ool->label1();
michael@0 5194 Label *ifDoesntEmulateUndefined = ool->label2();
michael@0 5195
michael@0 5196 Register objreg = ToRegister(lir->input());
michael@0 5197 Register output = ToRegister(lir->output());
michael@0 5198 branchTestObjectEmulatesUndefined(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined,
michael@0 5199 output, ool);
michael@0 5200 // fall through
michael@0 5201
michael@0 5202 Label join;
michael@0 5203
michael@0 5204 masm.move32(Imm32(0), output);
michael@0 5205 masm.jump(&join);
michael@0 5206
michael@0 5207 masm.bind(ifEmulatesUndefined);
michael@0 5208 masm.move32(Imm32(1), output);
michael@0 5209
michael@0 5210 masm.bind(&join);
michael@0 5211 return true;
michael@0 5212 }
michael@0 5213
michael@0 5214 bool
michael@0 5215 CodeGenerator::visitNotV(LNotV *lir)
michael@0 5216 {
michael@0 5217 Maybe<Label> ifTruthyLabel, ifFalsyLabel;
michael@0 5218 Label *ifTruthy;
michael@0 5219 Label *ifFalsy;
michael@0 5220
michael@0 5221 OutOfLineTestObjectWithLabels *ool = nullptr;
michael@0 5222 if (lir->mir()->operandMightEmulateUndefined()) {
michael@0 5223 ool = new(alloc()) OutOfLineTestObjectWithLabels();
michael@0 5224 if (!addOutOfLineCode(ool))
michael@0 5225 return false;
michael@0 5226 ifTruthy = ool->label1();
michael@0 5227 ifFalsy = ool->label2();
michael@0 5228 } else {
michael@0 5229 ifTruthyLabel.construct();
michael@0 5230 ifFalsyLabel.construct();
michael@0 5231 ifTruthy = ifTruthyLabel.addr();
michael@0 5232 ifFalsy = ifFalsyLabel.addr();
michael@0 5233 }
michael@0 5234
michael@0 5235 testValueTruthyKernel(ToValue(lir, LNotV::Input), lir->temp1(), lir->temp2(),
michael@0 5236 ToFloatRegister(lir->tempFloat()),
michael@0 5237 ifTruthy, ifFalsy, ool);
michael@0 5238
michael@0 5239 Label join;
michael@0 5240 Register output = ToRegister(lir->output());
michael@0 5241
michael@0 5242 // Note that the testValueTruthyKernel call above may choose to fall through
michael@0 5243 // to ifTruthy instead of branching there.
michael@0 5244 masm.bind(ifTruthy);
michael@0 5245 masm.move32(Imm32(0), output);
michael@0 5246 masm.jump(&join);
michael@0 5247
michael@0 5248 masm.bind(ifFalsy);
michael@0 5249 masm.move32(Imm32(1), output);
michael@0 5250
michael@0 5251 // both branches meet here.
michael@0 5252 masm.bind(&join);
michael@0 5253 return true;
michael@0 5254 }
michael@0 5255
michael@0 5256 bool
michael@0 5257 CodeGenerator::visitBoundsCheck(LBoundsCheck *lir)
michael@0 5258 {
michael@0 5259 if (lir->index()->isConstant()) {
michael@0 5260 // Use uint32 so that the comparison is unsigned.
michael@0 5261 uint32_t index = ToInt32(lir->index());
michael@0 5262 if (lir->length()->isConstant()) {
michael@0 5263 uint32_t length = ToInt32(lir->length());
michael@0 5264 if (index < length)
michael@0 5265 return true;
michael@0 5266 return bailout(lir->snapshot());
michael@0 5267 }
michael@0 5268 return bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), Imm32(index),
michael@0 5269 lir->snapshot());
michael@0 5270 }
michael@0 5271 if (lir->length()->isConstant()) {
michael@0 5272 return bailoutCmp32(Assembler::AboveOrEqual, ToRegister(lir->index()),
michael@0 5273 Imm32(ToInt32(lir->length())), lir->snapshot());
michael@0 5274 }
michael@0 5275 return bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()),
michael@0 5276 ToRegister(lir->index()), lir->snapshot());
michael@0 5277 }
michael@0 5278
michael@0 5279 bool
michael@0 5280 CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange *lir)
michael@0 5281 {
michael@0 5282 int32_t min = lir->mir()->minimum();
michael@0 5283 int32_t max = lir->mir()->maximum();
michael@0 5284 JS_ASSERT(max >= min);
michael@0 5285
michael@0 5286 Register temp = ToRegister(lir->getTemp(0));
michael@0 5287 if (lir->index()->isConstant()) {
michael@0 5288 int32_t nmin, nmax;
michael@0 5289 int32_t index = ToInt32(lir->index());
michael@0 5290 if (SafeAdd(index, min, &nmin) && SafeAdd(index, max, &nmax) && nmin >= 0) {
michael@0 5291 return bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), Imm32(nmax),
michael@0 5292 lir->snapshot());
michael@0 5293 }
michael@0 5294 masm.mov(ImmWord(index), temp);
michael@0 5295 } else {
michael@0 5296 masm.mov(ToRegister(lir->index()), temp);
michael@0 5297 }
michael@0 5298
michael@0 5299 // If the minimum and maximum differ then do an underflow check first.
michael@0 5300 // If the two are the same then doing an unsigned comparison on the
michael@0 5301 // length will also catch a negative index.
michael@0 5302 if (min != max) {
michael@0 5303 if (min != 0) {
michael@0 5304 Label bail;
michael@0 5305 masm.branchAdd32(Assembler::Overflow, Imm32(min), temp, &bail);
michael@0 5306 if (!bailoutFrom(&bail, lir->snapshot()))
michael@0 5307 return false;
michael@0 5308 }
michael@0 5309
michael@0 5310 if (!bailoutCmp32(Assembler::LessThan, temp, Imm32(0), lir->snapshot()))
michael@0 5311 return false;
michael@0 5312
michael@0 5313 if (min != 0) {
michael@0 5314 int32_t diff;
michael@0 5315 if (SafeSub(max, min, &diff))
michael@0 5316 max = diff;
michael@0 5317 else
michael@0 5318 masm.sub32(Imm32(min), temp);
michael@0 5319 }
michael@0 5320 }
michael@0 5321
michael@0 5322 // Compute the maximum possible index. No overflow check is needed when
michael@0 5323 // max > 0. We can only wraparound to a negative number, which will test as
michael@0 5324 // larger than all nonnegative numbers in the unsigned comparison, and the
michael@0 5325 // length is required to be nonnegative (else testing a negative length
michael@0 5326 // would succeed on any nonnegative index).
michael@0 5327 if (max != 0) {
michael@0 5328 if (max < 0) {
michael@0 5329 Label bail;
michael@0 5330 masm.branchAdd32(Assembler::Overflow, Imm32(max), temp, &bail);
michael@0 5331 if (!bailoutFrom(&bail, lir->snapshot()))
michael@0 5332 return false;
michael@0 5333 } else {
michael@0 5334 masm.add32(Imm32(max), temp);
michael@0 5335 }
michael@0 5336 }
michael@0 5337
michael@0 5338 return bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), temp, lir->snapshot());
michael@0 5339 }
michael@0 5340
michael@0 5341 bool
michael@0 5342 CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower *lir)
michael@0 5343 {
michael@0 5344 int32_t min = lir->mir()->minimum();
michael@0 5345 return bailoutCmp32(Assembler::LessThan, ToRegister(lir->index()), Imm32(min),
michael@0 5346 lir->snapshot());
michael@0 5347 }
michael@0 5348
michael@0 5349 class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator>
michael@0 5350 {
michael@0 5351 LInstruction *ins_;
michael@0 5352 Label rejoinStore_;
michael@0 5353
michael@0 5354 public:
michael@0 5355 OutOfLineStoreElementHole(LInstruction *ins)
michael@0 5356 : ins_(ins)
michael@0 5357 {
michael@0 5358 JS_ASSERT(ins->isStoreElementHoleV() || ins->isStoreElementHoleT());
michael@0 5359 }
michael@0 5360
michael@0 5361 bool accept(CodeGenerator *codegen) {
michael@0 5362 return codegen->visitOutOfLineStoreElementHole(this);
michael@0 5363 }
michael@0 5364 LInstruction *ins() const {
michael@0 5365 return ins_;
michael@0 5366 }
michael@0 5367 Label *rejoinStore() {
michael@0 5368 return &rejoinStore_;
michael@0 5369 }
michael@0 5370 };
michael@0 5371
michael@0 5372 bool
michael@0 5373 CodeGenerator::emitStoreHoleCheck(Register elements, const LAllocation *index, LSnapshot *snapshot)
michael@0 5374 {
michael@0 5375 Label bail;
michael@0 5376 if (index->isConstant()) {
michael@0 5377 masm.branchTestMagic(Assembler::Equal,
michael@0 5378 Address(elements, ToInt32(index) * sizeof(js::Value)), &bail);
michael@0 5379 } else {
michael@0 5380 masm.branchTestMagic(Assembler::Equal,
michael@0 5381 BaseIndex(elements, ToRegister(index), TimesEight), &bail);
michael@0 5382 }
michael@0 5383 return bailoutFrom(&bail, snapshot);
michael@0 5384 }
michael@0 5385
michael@0 5386 bool
michael@0 5387 CodeGenerator::visitStoreElementT(LStoreElementT *store)
michael@0 5388 {
michael@0 5389 Register elements = ToRegister(store->elements());
michael@0 5390 const LAllocation *index = store->index();
michael@0 5391
michael@0 5392 if (store->mir()->needsBarrier())
michael@0 5393 emitPreBarrier(elements, index, store->mir()->elementType());
michael@0 5394
michael@0 5395 if (store->mir()->needsHoleCheck() && !emitStoreHoleCheck(elements, index, store->snapshot()))
michael@0 5396 return false;
michael@0 5397
michael@0 5398 storeElementTyped(store->value(), store->mir()->value()->type(), store->mir()->elementType(),
michael@0 5399 elements, index);
michael@0 5400 return true;
michael@0 5401 }
michael@0 5402
michael@0 5403 bool
michael@0 5404 CodeGenerator::visitStoreElementV(LStoreElementV *lir)
michael@0 5405 {
michael@0 5406 const ValueOperand value = ToValue(lir, LStoreElementV::Value);
michael@0 5407 Register elements = ToRegister(lir->elements());
michael@0 5408 const LAllocation *index = lir->index();
michael@0 5409
michael@0 5410 if (lir->mir()->needsBarrier())
michael@0 5411 emitPreBarrier(elements, index, MIRType_Value);
michael@0 5412
michael@0 5413 if (lir->mir()->needsHoleCheck() && !emitStoreHoleCheck(elements, index, lir->snapshot()))
michael@0 5414 return false;
michael@0 5415
michael@0 5416 if (lir->index()->isConstant())
michael@0 5417 masm.storeValue(value, Address(elements, ToInt32(lir->index()) * sizeof(js::Value)));
michael@0 5418 else
michael@0 5419 masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight));
michael@0 5420 return true;
michael@0 5421 }
michael@0 5422
michael@0 5423 bool
michael@0 5424 CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT *lir)
michael@0 5425 {
michael@0 5426 OutOfLineStoreElementHole *ool = new(alloc()) OutOfLineStoreElementHole(lir);
michael@0 5427 if (!addOutOfLineCode(ool))
michael@0 5428 return false;
michael@0 5429
michael@0 5430 Register elements = ToRegister(lir->elements());
michael@0 5431 const LAllocation *index = lir->index();
michael@0 5432
michael@0 5433 // OOL path if index >= initializedLength.
michael@0 5434 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
michael@0 5435 masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry());
michael@0 5436
michael@0 5437 if (lir->mir()->needsBarrier())
michael@0 5438 emitPreBarrier(elements, index, lir->mir()->elementType());
michael@0 5439
michael@0 5440 masm.bind(ool->rejoinStore());
michael@0 5441 storeElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(),
michael@0 5442 elements, index);
michael@0 5443
michael@0 5444 masm.bind(ool->rejoin());
michael@0 5445 return true;
michael@0 5446 }
michael@0 5447
michael@0 5448 bool
michael@0 5449 CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV *lir)
michael@0 5450 {
michael@0 5451 OutOfLineStoreElementHole *ool = new(alloc()) OutOfLineStoreElementHole(lir);
michael@0 5452 if (!addOutOfLineCode(ool))
michael@0 5453 return false;
michael@0 5454
michael@0 5455 Register elements = ToRegister(lir->elements());
michael@0 5456 const LAllocation *index = lir->index();
michael@0 5457 const ValueOperand value = ToValue(lir, LStoreElementHoleV::Value);
michael@0 5458
michael@0 5459 // OOL path if index >= initializedLength.
michael@0 5460 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
michael@0 5461 masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry());
michael@0 5462
michael@0 5463 if (lir->mir()->needsBarrier())
michael@0 5464 emitPreBarrier(elements, index, lir->mir()->elementType());
michael@0 5465
michael@0 5466 masm.bind(ool->rejoinStore());
michael@0 5467 if (lir->index()->isConstant())
michael@0 5468 masm.storeValue(value, Address(elements, ToInt32(lir->index()) * sizeof(js::Value)));
michael@0 5469 else
michael@0 5470 masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight));
michael@0 5471
michael@0 5472 masm.bind(ool->rejoin());
michael@0 5473 return true;
michael@0 5474 }
michael@0 5475
michael@0 5476 typedef bool (*SetDenseElementFn)(JSContext *, HandleObject, int32_t, HandleValue,
michael@0 5477 bool strict);
michael@0 5478 typedef bool (*SetDenseElementParFn)(ForkJoinContext *, HandleObject, int32_t, HandleValue, bool);
michael@0 5479 static const VMFunctionsModal SetDenseElementInfo = VMFunctionsModal(
michael@0 5480 FunctionInfo<SetDenseElementFn>(SetDenseElement),
michael@0 5481 FunctionInfo<SetDenseElementParFn>(SetDenseElementPar));
michael@0 5482
michael@0 5483 bool
michael@0 5484 CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
michael@0 5485 {
michael@0 5486 Register object, elements;
michael@0 5487 LInstruction *ins = ool->ins();
michael@0 5488 const LAllocation *index;
michael@0 5489 MIRType valueType;
michael@0 5490 ConstantOrRegister value;
michael@0 5491
michael@0 5492 if (ins->isStoreElementHoleV()) {
michael@0 5493 LStoreElementHoleV *store = ins->toStoreElementHoleV();
michael@0 5494 object = ToRegister(store->object());
michael@0 5495 elements = ToRegister(store->elements());
michael@0 5496 index = store->index();
michael@0 5497 valueType = store->mir()->value()->type();
michael@0 5498 value = TypedOrValueRegister(ToValue(store, LStoreElementHoleV::Value));
michael@0 5499 } else {
michael@0 5500 LStoreElementHoleT *store = ins->toStoreElementHoleT();
michael@0 5501 object = ToRegister(store->object());
michael@0 5502 elements = ToRegister(store->elements());
michael@0 5503 index = store->index();
michael@0 5504 valueType = store->mir()->value()->type();
michael@0 5505 if (store->value()->isConstant())
michael@0 5506 value = ConstantOrRegister(*store->value()->toConstant());
michael@0 5507 else
michael@0 5508 value = TypedOrValueRegister(valueType, ToAnyRegister(store->value()));
michael@0 5509 }
michael@0 5510
michael@0 5511 // If index == initializedLength, try to bump the initialized length inline.
michael@0 5512 // If index > initializedLength, call a stub. Note that this relies on the
michael@0 5513 // condition flags sticking from the incoming branch.
michael@0 5514 Label callStub;
michael@0 5515 #ifdef JS_CODEGEN_MIPS
michael@0 5516 // Had to reimplement for MIPS because there are no flags.
michael@0 5517 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
michael@0 5518 masm.branchKey(Assembler::NotEqual, initLength, ToInt32Key(index), &callStub);
michael@0 5519 #else
michael@0 5520 masm.j(Assembler::NotEqual, &callStub);
michael@0 5521 #endif
michael@0 5522
michael@0 5523 Int32Key key = ToInt32Key(index);
michael@0 5524
michael@0 5525 // Check array capacity.
michael@0 5526 masm.branchKey(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()),
michael@0 5527 key, &callStub);
michael@0 5528
michael@0 5529 // Update initialized length. The capacity guard above ensures this won't overflow,
michael@0 5530 // due to NELEMENTS_LIMIT.
michael@0 5531 masm.bumpKey(&key, 1);
michael@0 5532 masm.storeKey(key, Address(elements, ObjectElements::offsetOfInitializedLength()));
michael@0 5533
michael@0 5534 // Update length if length < initializedLength.
michael@0 5535 Label dontUpdate;
michael@0 5536 masm.branchKey(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()),
michael@0 5537 key, &dontUpdate);
michael@0 5538 masm.storeKey(key, Address(elements, ObjectElements::offsetOfLength()));
michael@0 5539 masm.bind(&dontUpdate);
michael@0 5540
michael@0 5541 masm.bumpKey(&key, -1);
michael@0 5542
michael@0 5543 if (ins->isStoreElementHoleT() && valueType != MIRType_Double) {
michael@0 5544 // The inline path for StoreElementHoleT does not always store the type tag,
michael@0 5545 // so we do the store on the OOL path. We use MIRType_None for the element type
michael@0 5546 // so that storeElementTyped will always store the type tag.
michael@0 5547 storeElementTyped(ins->toStoreElementHoleT()->value(), valueType, MIRType_None, elements,
michael@0 5548 index);
michael@0 5549 masm.jump(ool->rejoin());
michael@0 5550 } else {
michael@0 5551 // Jump to the inline path where we will store the value.
michael@0 5552 masm.jump(ool->rejoinStore());
michael@0 5553 }
michael@0 5554
michael@0 5555 masm.bind(&callStub);
michael@0 5556 saveLive(ins);
michael@0 5557
michael@0 5558 pushArg(Imm32(current->mir()->strict()));
michael@0 5559 pushArg(value);
michael@0 5560 if (index->isConstant())
michael@0 5561 pushArg(Imm32(ToInt32(index)));
michael@0 5562 else
michael@0 5563 pushArg(ToRegister(index));
michael@0 5564 pushArg(object);
michael@0 5565 if (!callVM(SetDenseElementInfo, ins))
michael@0 5566 return false;
michael@0 5567
michael@0 5568 restoreLive(ins);
michael@0 5569 masm.jump(ool->rejoin());
michael@0 5570 return true;
michael@0 5571 }
michael@0 5572
michael@0 5573 typedef bool (*ArrayPopShiftFn)(JSContext *, HandleObject, MutableHandleValue);
michael@0 5574 static const VMFunction ArrayPopDenseInfo = FunctionInfo<ArrayPopShiftFn>(jit::ArrayPopDense);
michael@0 5575 static const VMFunction ArrayShiftDenseInfo = FunctionInfo<ArrayPopShiftFn>(jit::ArrayShiftDense);
michael@0 5576
michael@0 5577 bool
michael@0 5578 CodeGenerator::emitArrayPopShift(LInstruction *lir, const MArrayPopShift *mir, Register obj,
michael@0 5579 Register elementsTemp, Register lengthTemp, TypedOrValueRegister out)
michael@0 5580 {
michael@0 5581 OutOfLineCode *ool;
michael@0 5582
michael@0 5583 if (mir->mode() == MArrayPopShift::Pop) {
michael@0 5584 ool = oolCallVM(ArrayPopDenseInfo, lir, (ArgList(), obj), StoreValueTo(out));
michael@0 5585 if (!ool)
michael@0 5586 return false;
michael@0 5587 } else {
michael@0 5588 JS_ASSERT(mir->mode() == MArrayPopShift::Shift);
michael@0 5589 ool = oolCallVM(ArrayShiftDenseInfo, lir, (ArgList(), obj), StoreValueTo(out));
michael@0 5590 if (!ool)
michael@0 5591 return false;
michael@0 5592 }
michael@0 5593
michael@0 5594 // VM call if a write barrier is necessary.
michael@0 5595 masm.branchTestNeedsBarrier(Assembler::NonZero, lengthTemp, ool->entry());
michael@0 5596
michael@0 5597 // Load elements and length.
michael@0 5598 masm.loadPtr(Address(obj, JSObject::offsetOfElements()), elementsTemp);
michael@0 5599 masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp);
michael@0 5600
michael@0 5601 // VM call if length != initializedLength.
michael@0 5602 Int32Key key = Int32Key(lengthTemp);
michael@0 5603 Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
michael@0 5604 masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
michael@0 5605
michael@0 5606 // Test for length != 0. On zero length either take a VM call or generate
michael@0 5607 // an undefined value, depending on whether the call is known to produce
michael@0 5608 // undefined.
michael@0 5609 Label done;
michael@0 5610 if (mir->maybeUndefined()) {
michael@0 5611 Label notEmpty;
michael@0 5612 masm.branchTest32(Assembler::NonZero, lengthTemp, lengthTemp, &notEmpty);
michael@0 5613 masm.moveValue(UndefinedValue(), out.valueReg());
michael@0 5614 masm.jump(&done);
michael@0 5615 masm.bind(&notEmpty);
michael@0 5616 } else {
michael@0 5617 masm.branchTest32(Assembler::Zero, lengthTemp, lengthTemp, ool->entry());
michael@0 5618 }
michael@0 5619
michael@0 5620 masm.bumpKey(&key, -1);
michael@0 5621
michael@0 5622 if (mir->mode() == MArrayPopShift::Pop) {
michael@0 5623 masm.loadElementTypedOrValue(BaseIndex(elementsTemp, lengthTemp, TimesEight), out,
michael@0 5624 mir->needsHoleCheck(), ool->entry());
michael@0 5625 } else {
michael@0 5626 JS_ASSERT(mir->mode() == MArrayPopShift::Shift);
michael@0 5627 masm.loadElementTypedOrValue(Address(elementsTemp, 0), out, mir->needsHoleCheck(),
michael@0 5628 ool->entry());
michael@0 5629 }
michael@0 5630
michael@0 5631 // Handle the failure case when the array length is non-writable in the
michael@0 5632 // OOL path. (Unlike in the adding-an-element cases, we can't rely on the
michael@0 5633 // capacity <= length invariant for such arrays to avoid an explicit
michael@0 5634 // check.)
michael@0 5635 Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
michael@0 5636 Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
michael@0 5637 masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
michael@0 5638
michael@0 5639 // Now adjust length and initializedLength.
michael@0 5640 masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
michael@0 5641 masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
michael@0 5642
michael@0 5643 if (mir->mode() == MArrayPopShift::Shift) {
michael@0 5644 // Don't save the temp registers.
michael@0 5645 RegisterSet temps;
michael@0 5646 temps.add(elementsTemp);
michael@0 5647 temps.add(lengthTemp);
michael@0 5648
michael@0 5649 saveVolatile(temps);
michael@0 5650 masm.setupUnalignedABICall(1, lengthTemp);
michael@0 5651 masm.passABIArg(obj);
michael@0 5652 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ArrayShiftMoveElements));
michael@0 5653 restoreVolatile(temps);
michael@0 5654 }
michael@0 5655
michael@0 5656 masm.bind(&done);
michael@0 5657 masm.bind(ool->rejoin());
michael@0 5658 return true;
michael@0 5659 }
michael@0 5660
michael@0 5661 bool
michael@0 5662 CodeGenerator::visitArrayPopShiftV(LArrayPopShiftV *lir)
michael@0 5663 {
michael@0 5664 Register obj = ToRegister(lir->object());
michael@0 5665 Register elements = ToRegister(lir->temp0());
michael@0 5666 Register length = ToRegister(lir->temp1());
michael@0 5667 TypedOrValueRegister out(ToOutValue(lir));
michael@0 5668 return emitArrayPopShift(lir, lir->mir(), obj, elements, length, out);
michael@0 5669 }
michael@0 5670
michael@0 5671 bool
michael@0 5672 CodeGenerator::visitArrayPopShiftT(LArrayPopShiftT *lir)
michael@0 5673 {
michael@0 5674 Register obj = ToRegister(lir->object());
michael@0 5675 Register elements = ToRegister(lir->temp0());
michael@0 5676 Register length = ToRegister(lir->temp1());
michael@0 5677 TypedOrValueRegister out(lir->mir()->type(), ToAnyRegister(lir->output()));
michael@0 5678 return emitArrayPopShift(lir, lir->mir(), obj, elements, length, out);
michael@0 5679 }
michael@0 5680
michael@0 5681 typedef bool (*ArrayPushDenseFn)(JSContext *, HandleObject, HandleValue, uint32_t *);
michael@0 5682 static const VMFunction ArrayPushDenseInfo =
michael@0 5683 FunctionInfo<ArrayPushDenseFn>(jit::ArrayPushDense);
michael@0 5684
michael@0 5685 bool
michael@0 5686 CodeGenerator::emitArrayPush(LInstruction *lir, const MArrayPush *mir, Register obj,
michael@0 5687 ConstantOrRegister value, Register elementsTemp, Register length)
michael@0 5688 {
michael@0 5689 OutOfLineCode *ool = oolCallVM(ArrayPushDenseInfo, lir, (ArgList(), obj, value), StoreRegisterTo(length));
michael@0 5690 if (!ool)
michael@0 5691 return false;
michael@0 5692
michael@0 5693 // Load elements and length.
michael@0 5694 masm.loadPtr(Address(obj, JSObject::offsetOfElements()), elementsTemp);
michael@0 5695 masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
michael@0 5696
michael@0 5697 Int32Key key = Int32Key(length);
michael@0 5698 Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
michael@0 5699 Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
michael@0 5700
michael@0 5701 // Guard length == initializedLength.
michael@0 5702 masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
michael@0 5703
michael@0 5704 // Guard length < capacity.
michael@0 5705 masm.branchKey(Assembler::BelowOrEqual, capacity, key, ool->entry());
michael@0 5706
michael@0 5707 masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight));
michael@0 5708
michael@0 5709 masm.bumpKey(&key, 1);
michael@0 5710 masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
michael@0 5711 masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
michael@0 5712
michael@0 5713 masm.bind(ool->rejoin());
michael@0 5714 return true;
michael@0 5715 }
michael@0 5716
michael@0 5717 bool
michael@0 5718 CodeGenerator::visitArrayPushV(LArrayPushV *lir)
michael@0 5719 {
michael@0 5720 Register obj = ToRegister(lir->object());
michael@0 5721 Register elementsTemp = ToRegister(lir->temp());
michael@0 5722 Register length = ToRegister(lir->output());
michael@0 5723 ConstantOrRegister value = TypedOrValueRegister(ToValue(lir, LArrayPushV::Value));
michael@0 5724 return emitArrayPush(lir, lir->mir(), obj, value, elementsTemp, length);
michael@0 5725 }
michael@0 5726
michael@0 5727 bool
michael@0 5728 CodeGenerator::visitArrayPushT(LArrayPushT *lir)
michael@0 5729 {
michael@0 5730 Register obj = ToRegister(lir->object());
michael@0 5731 Register elementsTemp = ToRegister(lir->temp());
michael@0 5732 Register length = ToRegister(lir->output());
michael@0 5733 ConstantOrRegister value;
michael@0 5734 if (lir->value()->isConstant())
michael@0 5735 value = ConstantOrRegister(*lir->value()->toConstant());
michael@0 5736 else
michael@0 5737 value = TypedOrValueRegister(lir->mir()->value()->type(), ToAnyRegister(lir->value()));
michael@0 5738 return emitArrayPush(lir, lir->mir(), obj, value, elementsTemp, length);
michael@0 5739 }
michael@0 5740
michael@0 5741 typedef JSObject *(*ArrayConcatDenseFn)(JSContext *, HandleObject, HandleObject, HandleObject);
michael@0 5742 static const VMFunction ArrayConcatDenseInfo = FunctionInfo<ArrayConcatDenseFn>(ArrayConcatDense);
michael@0 5743
michael@0 5744 bool
michael@0 5745 CodeGenerator::visitArrayConcat(LArrayConcat *lir)
michael@0 5746 {
michael@0 5747 Register lhs = ToRegister(lir->lhs());
michael@0 5748 Register rhs = ToRegister(lir->rhs());
michael@0 5749 Register temp1 = ToRegister(lir->temp1());
michael@0 5750 Register temp2 = ToRegister(lir->temp2());
michael@0 5751
michael@0 5752 // If 'length == initializedLength' for both arrays we try to allocate an object
michael@0 5753 // inline and pass it to the stub. Else, we just pass nullptr and the stub falls
michael@0 5754 // back to a slow path.
michael@0 5755 Label fail, call;
michael@0 5756 masm.loadPtr(Address(lhs, JSObject::offsetOfElements()), temp1);
michael@0 5757 masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
michael@0 5758 masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
michael@0 5759
michael@0 5760 masm.loadPtr(Address(rhs, JSObject::offsetOfElements()), temp1);
michael@0 5761 masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
michael@0 5762 masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
michael@0 5763
michael@0 5764 // Try to allocate an object.
michael@0 5765 JSObject *templateObj = lir->mir()->templateObj();
michael@0 5766 masm.newGCThing(temp1, temp2, templateObj, &fail, lir->mir()->initialHeap());
michael@0 5767 masm.initGCThing(temp1, temp2, templateObj);
michael@0 5768 masm.jump(&call);
michael@0 5769 {
michael@0 5770 masm.bind(&fail);
michael@0 5771 masm.movePtr(ImmPtr(nullptr), temp1);
michael@0 5772 }
michael@0 5773 masm.bind(&call);
michael@0 5774
michael@0 5775 pushArg(temp1);
michael@0 5776 pushArg(ToRegister(lir->rhs()));
michael@0 5777 pushArg(ToRegister(lir->lhs()));
michael@0 5778 return callVM(ArrayConcatDenseInfo, lir);
michael@0 5779 }
michael@0 5780
michael@0 5781 typedef JSObject *(*GetIteratorObjectFn)(JSContext *, HandleObject, uint32_t);
michael@0 5782 static const VMFunction GetIteratorObjectInfo = FunctionInfo<GetIteratorObjectFn>(GetIteratorObject);
michael@0 5783
michael@0 5784 bool
michael@0 5785 CodeGenerator::visitCallIteratorStart(LCallIteratorStart *lir)
michael@0 5786 {
michael@0 5787 pushArg(Imm32(lir->mir()->flags()));
michael@0 5788 pushArg(ToRegister(lir->object()));
michael@0 5789 return callVM(GetIteratorObjectInfo, lir);
michael@0 5790 }
michael@0 5791
michael@0 5792 bool
michael@0 5793 CodeGenerator::visitIteratorStart(LIteratorStart *lir)
michael@0 5794 {
michael@0 5795 const Register obj = ToRegister(lir->object());
michael@0 5796 const Register output = ToRegister(lir->output());
michael@0 5797
michael@0 5798 uint32_t flags = lir->mir()->flags();
michael@0 5799
michael@0 5800 OutOfLineCode *ool = oolCallVM(GetIteratorObjectInfo, lir,
michael@0 5801 (ArgList(), obj, Imm32(flags)), StoreRegisterTo(output));
michael@0 5802 if (!ool)
michael@0 5803 return false;
michael@0 5804
michael@0 5805 const Register temp1 = ToRegister(lir->temp1());
michael@0 5806 const Register temp2 = ToRegister(lir->temp2());
michael@0 5807 const Register niTemp = ToRegister(lir->temp3()); // Holds the NativeIterator object.
michael@0 5808
michael@0 5809 // Iterators other than for-in should use LCallIteratorStart.
michael@0 5810 JS_ASSERT(flags == JSITER_ENUMERATE);
michael@0 5811
michael@0 5812 // Fetch the most recent iterator and ensure it's not nullptr.
michael@0 5813 masm.loadPtr(AbsoluteAddress(GetIonContext()->runtime->addressOfLastCachedNativeIterator()), output);
michael@0 5814 masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
michael@0 5815
michael@0 5816 // Load NativeIterator.
michael@0 5817 masm.loadObjPrivate(output, JSObject::ITER_CLASS_NFIXED_SLOTS, niTemp);
michael@0 5818
michael@0 5819 // Ensure the |active| and |unreusable| bits are not set.
michael@0 5820 masm.branchTest32(Assembler::NonZero, Address(niTemp, offsetof(NativeIterator, flags)),
michael@0 5821 Imm32(JSITER_ACTIVE|JSITER_UNREUSABLE), ool->entry());
michael@0 5822
michael@0 5823 // Load the iterator's shape array.
michael@0 5824 masm.loadPtr(Address(niTemp, offsetof(NativeIterator, shapes_array)), temp2);
michael@0 5825
michael@0 5826 // Compare shape of object with the first shape.
michael@0 5827 masm.loadObjShape(obj, temp1);
michael@0 5828 masm.branchPtr(Assembler::NotEqual, Address(temp2, 0), temp1, ool->entry());
michael@0 5829
michael@0 5830 // Compare shape of object's prototype with the second shape.
michael@0 5831 masm.loadObjProto(obj, temp1);
michael@0 5832 masm.loadObjShape(temp1, temp1);
michael@0 5833 masm.branchPtr(Assembler::NotEqual, Address(temp2, sizeof(Shape *)), temp1, ool->entry());
michael@0 5834
michael@0 5835 // Ensure the object's prototype's prototype is nullptr. The last native
michael@0 5836 // iterator will always have a prototype chain length of one (i.e. it must
michael@0 5837 // be a plain ), so we do not need to generate a loop here.
michael@0 5838 masm.loadObjProto(obj, temp1);
michael@0 5839 masm.loadObjProto(temp1, temp1);
michael@0 5840 masm.branchTestPtr(Assembler::NonZero, temp1, temp1, ool->entry());
michael@0 5841
michael@0 5842 // Ensure the object does not have any elements. The presence of dense
michael@0 5843 // elements is not captured by the shape tests above.
michael@0 5844 masm.branchPtr(Assembler::NotEqual,
michael@0 5845 Address(obj, JSObject::offsetOfElements()),
michael@0 5846 ImmPtr(js::emptyObjectElements),
michael@0 5847 ool->entry());
michael@0 5848
michael@0 5849 // Write barrier for stores to the iterator. We only need to take a write
michael@0 5850 // barrier if NativeIterator::obj is actually going to change.
michael@0 5851 {
michael@0 5852 #ifdef JSGC_GENERATIONAL
michael@0 5853 // Bug 867815: When using a nursery, we unconditionally take this out-
michael@0 5854 // of-line so that we do not have to post-barrier the store to
michael@0 5855 // NativeIter::obj. This just needs JIT support for the Cell* buffer.
michael@0 5856 Address objAddr(niTemp, offsetof(NativeIterator, obj));
michael@0 5857 masm.branchPtr(Assembler::NotEqual, objAddr, obj, ool->entry());
michael@0 5858 #else
michael@0 5859 Label noBarrier;
michael@0 5860 masm.branchTestNeedsBarrier(Assembler::Zero, temp1, &noBarrier);
michael@0 5861
michael@0 5862 Address objAddr(niTemp, offsetof(NativeIterator, obj));
michael@0 5863 masm.branchPtr(Assembler::NotEqual, objAddr, obj, ool->entry());
michael@0 5864
michael@0 5865 masm.bind(&noBarrier);
michael@0 5866 #endif // !JSGC_GENERATIONAL
michael@0 5867 }
michael@0 5868
michael@0 5869 // Mark iterator as active.
michael@0 5870 masm.storePtr(obj, Address(niTemp, offsetof(NativeIterator, obj)));
michael@0 5871 masm.or32(Imm32(JSITER_ACTIVE), Address(niTemp, offsetof(NativeIterator, flags)));
michael@0 5872
michael@0 5873 // Chain onto the active iterator stack.
michael@0 5874 masm.loadPtr(AbsoluteAddress(gen->compartment->addressOfEnumerators()), temp1);
michael@0 5875
michael@0 5876 // ni->next = list
michael@0 5877 masm.storePtr(temp1, Address(niTemp, NativeIterator::offsetOfNext()));
michael@0 5878
michael@0 5879 // ni->prev = list->prev
michael@0 5880 masm.loadPtr(Address(temp1, NativeIterator::offsetOfPrev()), temp2);
michael@0 5881 masm.storePtr(temp2, Address(niTemp, NativeIterator::offsetOfPrev()));
michael@0 5882
michael@0 5883 // list->prev->next = ni
michael@0 5884 masm.storePtr(niTemp, Address(temp2, NativeIterator::offsetOfNext()));
michael@0 5885
michael@0 5886 // list->prev = ni
michael@0 5887 masm.storePtr(niTemp, Address(temp1, NativeIterator::offsetOfPrev()));
michael@0 5888
michael@0 5889 masm.bind(ool->rejoin());
michael@0 5890 return true;
michael@0 5891 }
michael@0 5892
michael@0 5893 static void
michael@0 5894 LoadNativeIterator(MacroAssembler &masm, Register obj, Register dest, Label *failures)
michael@0 5895 {
michael@0 5896 JS_ASSERT(obj != dest);
michael@0 5897
michael@0 5898 // Test class.
michael@0 5899 masm.branchTestObjClass(Assembler::NotEqual, obj, dest, &PropertyIteratorObject::class_, failures);
michael@0 5900
michael@0 5901 // Load NativeIterator object.
michael@0 5902 masm.loadObjPrivate(obj, JSObject::ITER_CLASS_NFIXED_SLOTS, dest);
michael@0 5903 }
michael@0 5904
michael@0 5905 typedef bool (*IteratorNextFn)(JSContext *, HandleObject, MutableHandleValue);
michael@0 5906 static const VMFunction IteratorNextInfo = FunctionInfo<IteratorNextFn>(js_IteratorNext);
michael@0 5907
michael@0 5908 bool
michael@0 5909 CodeGenerator::visitIteratorNext(LIteratorNext *lir)
michael@0 5910 {
michael@0 5911 const Register obj = ToRegister(lir->object());
michael@0 5912 const Register temp = ToRegister(lir->temp());
michael@0 5913 const ValueOperand output = ToOutValue(lir);
michael@0 5914
michael@0 5915 OutOfLineCode *ool = oolCallVM(IteratorNextInfo, lir, (ArgList(), obj), StoreValueTo(output));
michael@0 5916 if (!ool)
michael@0 5917 return false;
michael@0 5918
michael@0 5919 LoadNativeIterator(masm, obj, temp, ool->entry());
michael@0 5920
michael@0 5921 masm.branchTest32(Assembler::NonZero, Address(temp, offsetof(NativeIterator, flags)),
michael@0 5922 Imm32(JSITER_FOREACH), ool->entry());
michael@0 5923
michael@0 5924 // Get cursor, next string.
michael@0 5925 masm.loadPtr(Address(temp, offsetof(NativeIterator, props_cursor)), output.scratchReg());
michael@0 5926 masm.loadPtr(Address(output.scratchReg(), 0), output.scratchReg());
michael@0 5927 masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output);
michael@0 5928
michael@0 5929 // Increase the cursor.
michael@0 5930 masm.addPtr(Imm32(sizeof(JSString *)), Address(temp, offsetof(NativeIterator, props_cursor)));
michael@0 5931
michael@0 5932 masm.bind(ool->rejoin());
michael@0 5933 return true;
michael@0 5934 }
michael@0 5935
michael@0 5936 typedef bool (*IteratorMoreFn)(JSContext *, HandleObject, bool *);
michael@0 5937 static const VMFunction IteratorMoreInfo = FunctionInfo<IteratorMoreFn>(jit::IteratorMore);
michael@0 5938
michael@0 5939 bool
michael@0 5940 CodeGenerator::visitIteratorMore(LIteratorMore *lir)
michael@0 5941 {
michael@0 5942 const Register obj = ToRegister(lir->object());
michael@0 5943 const Register output = ToRegister(lir->output());
michael@0 5944 const Register temp = ToRegister(lir->temp());
michael@0 5945
michael@0 5946 OutOfLineCode *ool = oolCallVM(IteratorMoreInfo, lir,
michael@0 5947 (ArgList(), obj), StoreRegisterTo(output));
michael@0 5948 if (!ool)
michael@0 5949 return false;
michael@0 5950
michael@0 5951 LoadNativeIterator(masm, obj, output, ool->entry());
michael@0 5952
michael@0 5953 masm.branchTest32(Assembler::NonZero, Address(output, offsetof(NativeIterator, flags)),
michael@0 5954 Imm32(JSITER_FOREACH), ool->entry());
michael@0 5955
michael@0 5956 // Set output to true if props_cursor < props_end.
michael@0 5957 masm.loadPtr(Address(output, offsetof(NativeIterator, props_end)), temp);
michael@0 5958 masm.cmpPtrSet(Assembler::LessThan, Address(output, offsetof(NativeIterator, props_cursor)),
michael@0 5959 temp, output);
michael@0 5960
michael@0 5961 masm.bind(ool->rejoin());
michael@0 5962 return true;
michael@0 5963 }
michael@0 5964
michael@0 5965 typedef bool (*CloseIteratorFn)(JSContext *, HandleObject);
michael@0 5966 static const VMFunction CloseIteratorInfo = FunctionInfo<CloseIteratorFn>(CloseIterator);
michael@0 5967
michael@0 5968 bool
michael@0 5969 CodeGenerator::visitIteratorEnd(LIteratorEnd *lir)
michael@0 5970 {
michael@0 5971 const Register obj = ToRegister(lir->object());
michael@0 5972 const Register temp1 = ToRegister(lir->temp1());
michael@0 5973 const Register temp2 = ToRegister(lir->temp2());
michael@0 5974 const Register temp3 = ToRegister(lir->temp3());
michael@0 5975
michael@0 5976 OutOfLineCode *ool = oolCallVM(CloseIteratorInfo, lir, (ArgList(), obj), StoreNothing());
michael@0 5977 if (!ool)
michael@0 5978 return false;
michael@0 5979
michael@0 5980 LoadNativeIterator(masm, obj, temp1, ool->entry());
michael@0 5981
michael@0 5982 masm.branchTest32(Assembler::Zero, Address(temp1, offsetof(NativeIterator, flags)),
michael@0 5983 Imm32(JSITER_ENUMERATE), ool->entry());
michael@0 5984
michael@0 5985 // Clear active bit.
michael@0 5986 masm.and32(Imm32(~JSITER_ACTIVE), Address(temp1, offsetof(NativeIterator, flags)));
michael@0 5987
michael@0 5988 // Reset property cursor.
michael@0 5989 masm.loadPtr(Address(temp1, offsetof(NativeIterator, props_array)), temp2);
michael@0 5990 masm.storePtr(temp2, Address(temp1, offsetof(NativeIterator, props_cursor)));
michael@0 5991
michael@0 5992 // Unlink from the iterator list.
michael@0 5993 const Register next = temp2;
michael@0 5994 const Register prev = temp3;
michael@0 5995 masm.loadPtr(Address(temp1, NativeIterator::offsetOfNext()), next);
michael@0 5996 masm.loadPtr(Address(temp1, NativeIterator::offsetOfPrev()), prev);
michael@0 5997 masm.storePtr(prev, Address(next, NativeIterator::offsetOfPrev()));
michael@0 5998 masm.storePtr(next, Address(prev, NativeIterator::offsetOfNext()));
michael@0 5999 #ifdef DEBUG
michael@0 6000 masm.storePtr(ImmPtr(nullptr), Address(temp1, NativeIterator::offsetOfNext()));
michael@0 6001 masm.storePtr(ImmPtr(nullptr), Address(temp1, NativeIterator::offsetOfPrev()));
michael@0 6002 #endif
michael@0 6003
michael@0 6004 masm.bind(ool->rejoin());
michael@0 6005 return true;
michael@0 6006 }
michael@0 6007
michael@0 6008 bool
michael@0 6009 CodeGenerator::visitArgumentsLength(LArgumentsLength *lir)
michael@0 6010 {
michael@0 6011 // read number of actual arguments from the JS frame.
michael@0 6012 Register argc = ToRegister(lir->output());
michael@0 6013 Address ptr(StackPointer, frameSize() + IonJSFrameLayout::offsetOfNumActualArgs());
michael@0 6014
michael@0 6015 masm.loadPtr(ptr, argc);
michael@0 6016 return true;
michael@0 6017 }
michael@0 6018
michael@0 6019 bool
michael@0 6020 CodeGenerator::visitGetFrameArgument(LGetFrameArgument *lir)
michael@0 6021 {
michael@0 6022 ValueOperand result = GetValueOutput(lir);
michael@0 6023 const LAllocation *index = lir->index();
michael@0 6024 size_t argvOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs();
michael@0 6025
michael@0 6026 if (index->isConstant()) {
michael@0 6027 int32_t i = index->toConstant()->toInt32();
michael@0 6028 Address argPtr(StackPointer, sizeof(Value) * i + argvOffset);
michael@0 6029 masm.loadValue(argPtr, result);
michael@0 6030 } else {
michael@0 6031 Register i = ToRegister(index);
michael@0 6032 BaseIndex argPtr(StackPointer, i, ScaleFromElemWidth(sizeof(Value)), argvOffset);
michael@0 6033 masm.loadValue(argPtr, result);
michael@0 6034 }
michael@0 6035 return true;
michael@0 6036 }
michael@0 6037
michael@0 6038 bool
michael@0 6039 CodeGenerator::visitSetFrameArgumentT(LSetFrameArgumentT *lir)
michael@0 6040 {
michael@0 6041 size_t argOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs() +
michael@0 6042 (sizeof(Value) * lir->mir()->argno());
michael@0 6043
michael@0 6044 MIRType type = lir->mir()->value()->type();
michael@0 6045
michael@0 6046 if (type == MIRType_Double) {
michael@0 6047 // Store doubles directly.
michael@0 6048 FloatRegister input = ToFloatRegister(lir->input());
michael@0 6049 masm.storeDouble(input, Address(StackPointer, argOffset));
michael@0 6050
michael@0 6051 } else {
michael@0 6052 Register input = ToRegister(lir->input());
michael@0 6053 masm.storeValue(ValueTypeFromMIRType(type), input, Address(StackPointer, argOffset));
michael@0 6054 }
michael@0 6055 return true;
michael@0 6056 }
michael@0 6057
michael@0 6058 bool
michael@0 6059 CodeGenerator:: visitSetFrameArgumentC(LSetFrameArgumentC *lir)
michael@0 6060 {
michael@0 6061 size_t argOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs() +
michael@0 6062 (sizeof(Value) * lir->mir()->argno());
michael@0 6063 masm.storeValue(lir->val(), Address(StackPointer, argOffset));
michael@0 6064 return true;
michael@0 6065 }
michael@0 6066
michael@0 6067 bool
michael@0 6068 CodeGenerator:: visitSetFrameArgumentV(LSetFrameArgumentV *lir)
michael@0 6069 {
michael@0 6070 const ValueOperand val = ToValue(lir, LSetFrameArgumentV::Input);
michael@0 6071 size_t argOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs() +
michael@0 6072 (sizeof(Value) * lir->mir()->argno());
michael@0 6073 masm.storeValue(val, Address(StackPointer, argOffset));
michael@0 6074 return true;
michael@0 6075 }
michael@0 6076
michael@0 6077 typedef bool (*RunOnceScriptPrologueFn)(JSContext *, HandleScript);
michael@0 6078 static const VMFunction RunOnceScriptPrologueInfo =
michael@0 6079 FunctionInfo<RunOnceScriptPrologueFn>(js::RunOnceScriptPrologue);
michael@0 6080
michael@0 6081 bool
michael@0 6082 CodeGenerator::visitRunOncePrologue(LRunOncePrologue *lir)
michael@0 6083 {
michael@0 6084 pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
michael@0 6085 return callVM(RunOnceScriptPrologueInfo, lir);
michael@0 6086 }
michael@0 6087
michael@0 6088
michael@0 6089 typedef JSObject *(*InitRestParameterFn)(JSContext *, uint32_t, Value *, HandleObject,
michael@0 6090 HandleObject);
michael@0 6091 typedef JSObject *(*InitRestParameterParFn)(ForkJoinContext *, uint32_t, Value *,
michael@0 6092 HandleObject, HandleObject);
michael@0 6093 static const VMFunctionsModal InitRestParameterInfo = VMFunctionsModal(
michael@0 6094 FunctionInfo<InitRestParameterFn>(InitRestParameter),
michael@0 6095 FunctionInfo<InitRestParameterParFn>(InitRestParameterPar));
michael@0 6096
michael@0 6097 bool
michael@0 6098 CodeGenerator::emitRest(LInstruction *lir, Register array, Register numActuals,
michael@0 6099 Register temp0, Register temp1, unsigned numFormals,
michael@0 6100 JSObject *templateObject)
michael@0 6101 {
michael@0 6102 // Compute actuals() + numFormals.
michael@0 6103 size_t actualsOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs();
michael@0 6104 masm.movePtr(StackPointer, temp1);
michael@0 6105 masm.addPtr(Imm32(sizeof(Value) * numFormals + actualsOffset), temp1);
michael@0 6106
michael@0 6107 // Compute numActuals - numFormals.
michael@0 6108 Label emptyLength, joinLength;
michael@0 6109 masm.movePtr(numActuals, temp0);
michael@0 6110 masm.branch32(Assembler::LessThanOrEqual, temp0, Imm32(numFormals), &emptyLength);
michael@0 6111 masm.sub32(Imm32(numFormals), temp0);
michael@0 6112 masm.jump(&joinLength);
michael@0 6113 {
michael@0 6114 masm.bind(&emptyLength);
michael@0 6115 masm.move32(Imm32(0), temp0);
michael@0 6116 }
michael@0 6117 masm.bind(&joinLength);
michael@0 6118
michael@0 6119 pushArg(array);
michael@0 6120 pushArg(ImmGCPtr(templateObject));
michael@0 6121 pushArg(temp1);
michael@0 6122 pushArg(temp0);
michael@0 6123
michael@0 6124 return callVM(InitRestParameterInfo, lir);
michael@0 6125 }
michael@0 6126
michael@0 6127 bool
michael@0 6128 CodeGenerator::visitRest(LRest *lir)
michael@0 6129 {
michael@0 6130 Register numActuals = ToRegister(lir->numActuals());
michael@0 6131 Register temp0 = ToRegister(lir->getTemp(0));
michael@0 6132 Register temp1 = ToRegister(lir->getTemp(1));
michael@0 6133 Register temp2 = ToRegister(lir->getTemp(2));
michael@0 6134 unsigned numFormals = lir->mir()->numFormals();
michael@0 6135 JSObject *templateObject = lir->mir()->templateObject();
michael@0 6136
michael@0 6137 Label joinAlloc, failAlloc;
michael@0 6138 masm.newGCThing(temp2, temp0, templateObject, &failAlloc, gc::DefaultHeap);
michael@0 6139 masm.initGCThing(temp2, temp0, templateObject);
michael@0 6140 masm.jump(&joinAlloc);
michael@0 6141 {
michael@0 6142 masm.bind(&failAlloc);
michael@0 6143 masm.movePtr(ImmPtr(nullptr), temp2);
michael@0 6144 }
michael@0 6145 masm.bind(&joinAlloc);
michael@0 6146
michael@0 6147 return emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject);
michael@0 6148 }
michael@0 6149
michael@0 6150 bool
michael@0 6151 CodeGenerator::visitRestPar(LRestPar *lir)
michael@0 6152 {
michael@0 6153 Register numActuals = ToRegister(lir->numActuals());
michael@0 6154 Register cx = ToRegister(lir->forkJoinContext());
michael@0 6155 Register temp0 = ToRegister(lir->getTemp(0));
michael@0 6156 Register temp1 = ToRegister(lir->getTemp(1));
michael@0 6157 Register temp2 = ToRegister(lir->getTemp(2));
michael@0 6158 unsigned numFormals = lir->mir()->numFormals();
michael@0 6159 JSObject *templateObject = lir->mir()->templateObject();
michael@0 6160
michael@0 6161 if (!emitAllocateGCThingPar(lir, temp2, cx, temp0, temp1, templateObject))
michael@0 6162 return false;
michael@0 6163
michael@0 6164 return emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject);
michael@0 6165 }
michael@0 6166
michael@0 6167 bool
michael@0 6168 CodeGenerator::generateAsmJS(Label *stackOverflowLabel)
michael@0 6169 {
michael@0 6170 IonSpew(IonSpew_Codegen, "# Emitting asm.js code");
michael@0 6171
michael@0 6172 // AsmJS doesn't do profiler instrumentation.
michael@0 6173 sps_.disable();
michael@0 6174
michael@0 6175 // The caller (either another asm.js function or the external-entry
michael@0 6176 // trampoline) has placed all arguments in registers and on the stack
michael@0 6177 // according to the system ABI. The MAsmJSParameters which represent these
michael@0 6178 // parameters have been useFixed()ed to these ABI-specified positions.
michael@0 6179 // Thus, there is nothing special to do in the prologue except (possibly)
michael@0 6180 // bump the stack.
michael@0 6181 if (!generateAsmJSPrologue(stackOverflowLabel))
michael@0 6182 return false;
michael@0 6183 if (!generateBody())
michael@0 6184 return false;
michael@0 6185 if (!generateEpilogue())
michael@0 6186 return false;
michael@0 6187 #if defined(JS_ION_PERF)
michael@0 6188 // Note the end of the inline code and start of the OOL code.
michael@0 6189 gen->perfSpewer().noteEndInlineCode(masm);
michael@0 6190 #endif
michael@0 6191 if (!generateOutOfLineCode())
michael@0 6192 return false;
michael@0 6193
michael@0 6194 // The only remaining work needed to compile this function is to patch the
michael@0 6195 // switch-statement jump tables (the entries of the table need the absolute
michael@0 6196 // address of the cases). These table entries are accmulated as CodeLabels
michael@0 6197 // in the MacroAssembler's codeLabels_ list and processed all at once at in
michael@0 6198 // the "static-link" phase of module compilation. It is critical that there
michael@0 6199 // is nothing else to do after this point since the LifoAlloc memory
michael@0 6200 // holding the MIR graph is about to be popped and reused. In particular,
michael@0 6201 // every step in CodeGenerator::link must be a nop, as asserted here:
michael@0 6202 JS_ASSERT(snapshots_.listSize() == 0);
michael@0 6203 JS_ASSERT(snapshots_.RVATableSize() == 0);
michael@0 6204 JS_ASSERT(recovers_.size() == 0);
michael@0 6205 JS_ASSERT(bailouts_.empty());
michael@0 6206 JS_ASSERT(graph.numConstants() == 0);
michael@0 6207 JS_ASSERT(safepointIndices_.empty());
michael@0 6208 JS_ASSERT(osiIndices_.empty());
michael@0 6209 JS_ASSERT(cacheList_.empty());
michael@0 6210 JS_ASSERT(safepoints_.size() == 0);
michael@0 6211 return true;
michael@0 6212 }
michael@0 6213
michael@0 6214 bool
michael@0 6215 CodeGenerator::generate()
michael@0 6216 {
michael@0 6217 IonSpew(IonSpew_Codegen, "# Emitting code for script %s:%d",
michael@0 6218 gen->info().script()->filename(),
michael@0 6219 gen->info().script()->lineno());
michael@0 6220
michael@0 6221 if (!snapshots_.init())
michael@0 6222 return false;
michael@0 6223
michael@0 6224 if (!safepoints_.init(gen->alloc(), graph.totalSlotCount()))
michael@0 6225 return false;
michael@0 6226
michael@0 6227 #ifdef JS_TRACE_LOGGING
michael@0 6228 if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
michael@0 6229 if (!emitTracelogScriptStart())
michael@0 6230 return false;
michael@0 6231 if (!emitTracelogStartEvent(TraceLogger::IonMonkey))
michael@0 6232 return false;
michael@0 6233 }
michael@0 6234 #endif
michael@0 6235
michael@0 6236 // Before generating any code, we generate type checks for all parameters.
michael@0 6237 // This comes before deoptTable_, because we can't use deopt tables without
michael@0 6238 // creating the actual frame.
michael@0 6239 if (!generateArgumentsChecks())
michael@0 6240 return false;
michael@0 6241
michael@0 6242 if (frameClass_ != FrameSizeClass::None()) {
michael@0 6243 deoptTable_ = gen->jitRuntime()->getBailoutTable(frameClass_);
michael@0 6244 if (!deoptTable_)
michael@0 6245 return false;
michael@0 6246 }
michael@0 6247
michael@0 6248 #ifdef JS_TRACE_LOGGING
michael@0 6249 Label skip;
michael@0 6250 masm.jump(&skip);
michael@0 6251 #endif
michael@0 6252
michael@0 6253 // Remember the entry offset to skip the argument check.
michael@0 6254 masm.flushBuffer();
michael@0 6255 setSkipArgCheckEntryOffset(masm.size());
michael@0 6256
michael@0 6257 #ifdef JS_TRACE_LOGGING
michael@0 6258 if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
michael@0 6259 if (!emitTracelogScriptStart())
michael@0 6260 return false;
michael@0 6261 if (!emitTracelogStartEvent(TraceLogger::IonMonkey))
michael@0 6262 return false;
michael@0 6263 }
michael@0 6264 masm.bind(&skip);
michael@0 6265 #endif
michael@0 6266
michael@0 6267 #ifdef DEBUG
michael@0 6268 // Assert that the argument types are correct.
michael@0 6269 if (!generateArgumentsChecks(/* bailout = */ false))
michael@0 6270 return false;
michael@0 6271 #endif
michael@0 6272
michael@0 6273 if (!generatePrologue())
michael@0 6274 return false;
michael@0 6275 if (!generateBody())
michael@0 6276 return false;
michael@0 6277 if (!generateEpilogue())
michael@0 6278 return false;
michael@0 6279 if (!generateInvalidateEpilogue())
michael@0 6280 return false;
michael@0 6281 #if defined(JS_ION_PERF)
michael@0 6282 // Note the end of the inline code and start of the OOL code.
michael@0 6283 perfSpewer_.noteEndInlineCode(masm);
michael@0 6284 #endif
michael@0 6285 if (!generateOutOfLineCode())
michael@0 6286 return false;
michael@0 6287
michael@0 6288 return !masm.oom();
michael@0 6289 }
michael@0 6290
michael@0 6291 bool
michael@0 6292 CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
michael@0 6293 {
michael@0 6294 RootedScript script(cx, gen->info().script());
michael@0 6295 ExecutionMode executionMode = gen->info().executionMode();
michael@0 6296 OptimizationLevel optimizationLevel = gen->optimizationInfo().level();
michael@0 6297
michael@0 6298 JS_ASSERT_IF(HasIonScript(script, executionMode), executionMode == SequentialExecution);
michael@0 6299
michael@0 6300 // We finished the new IonScript. Invalidate the current active IonScript,
michael@0 6301 // so we can replace it with this new (probably higher optimized) version.
michael@0 6302 if (HasIonScript(script, executionMode)) {
michael@0 6303 JS_ASSERT(GetIonScript(script, executionMode)->isRecompiling());
michael@0 6304 // Do a normal invalidate, except don't cancel offThread compilations,
michael@0 6305 // since that will cancel this compilation too.
michael@0 6306 if (!Invalidate(cx, script, SequentialExecution,
michael@0 6307 /* resetUses */ false, /* cancelOffThread*/ false))
michael@0 6308 {
michael@0 6309 return false;
michael@0 6310 }
michael@0 6311 }
michael@0 6312
michael@0 6313 // Check to make sure we didn't have a mid-build invalidation. If so, we
michael@0 6314 // will trickle to jit::Compile() and return Method_Skipped.
michael@0 6315 types::RecompileInfo recompileInfo;
michael@0 6316 if (!types::FinishCompilation(cx, script, executionMode, constraints, &recompileInfo))
michael@0 6317 return true;
michael@0 6318
michael@0 6319 uint32_t scriptFrameSize = frameClass_ == FrameSizeClass::None()
michael@0 6320 ? frameDepth_
michael@0 6321 : FrameSizeClass::FromDepth(frameDepth_).frameSize();
michael@0 6322
michael@0 6323 // We encode safepoints after the OSI-point offsets have been determined.
michael@0 6324 encodeSafepoints();
michael@0 6325
michael@0 6326 // List of possible scripts that this graph may call. Currently this is
michael@0 6327 // only tracked when compiling for parallel execution.
michael@0 6328 CallTargetVector callTargets(alloc());
michael@0 6329 if (executionMode == ParallelExecution)
michael@0 6330 AddPossibleCallees(cx, graph.mir(), callTargets);
michael@0 6331
michael@0 6332 IonScript *ionScript =
michael@0 6333 IonScript::New(cx, recompileInfo,
michael@0 6334 graph.totalSlotCount(), scriptFrameSize,
michael@0 6335 snapshots_.listSize(), snapshots_.RVATableSize(),
michael@0 6336 recovers_.size(), bailouts_.length(), graph.numConstants(),
michael@0 6337 safepointIndices_.length(), osiIndices_.length(),
michael@0 6338 cacheList_.length(), runtimeData_.length(),
michael@0 6339 safepoints_.size(), callTargets.length(),
michael@0 6340 patchableBackedges_.length(), optimizationLevel);
michael@0 6341 if (!ionScript) {
michael@0 6342 recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
michael@0 6343 return false;
michael@0 6344 }
michael@0 6345
michael@0 6346 // Lock the runtime against interrupt callbacks during the link.
michael@0 6347 // We don't want an interrupt request to protect the code for the script
michael@0 6348 // before it has been filled in, as we could segv before the runtime's
michael@0 6349 // patchable backedges have been fully updated.
michael@0 6350 JSRuntime::AutoLockForInterrupt lock(cx->runtime());
michael@0 6351
michael@0 6352 // Make sure we don't segv while filling in the code, to avoid deadlocking
michael@0 6353 // inside the signal handler.
michael@0 6354 cx->runtime()->jitRuntime()->ensureIonCodeAccessible(cx->runtime());
michael@0 6355
michael@0 6356 // Implicit interrupts are used only for sequential code. In parallel mode
michael@0 6357 // use the normal executable allocator so that we cannot segv during
michael@0 6358 // execution off the main thread.
michael@0 6359 Linker linker(masm);
michael@0 6360 AutoFlushICache afc("IonLink");
michael@0 6361 JitCode *code = (executionMode == SequentialExecution)
michael@0 6362 ? linker.newCodeForIonScript(cx)
michael@0 6363 : linker.newCode<CanGC>(cx, JSC::ION_CODE);
michael@0 6364 if (!code) {
michael@0 6365 // Use js_free instead of IonScript::Destroy: the cache list and
michael@0 6366 // backedge list are still uninitialized.
michael@0 6367 js_free(ionScript);
michael@0 6368 recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
michael@0 6369 return false;
michael@0 6370 }
michael@0 6371
michael@0 6372 ionScript->setMethod(code);
michael@0 6373 ionScript->setSkipArgCheckEntryOffset(getSkipArgCheckEntryOffset());
michael@0 6374
michael@0 6375 // If SPS is enabled, mark IonScript as having been instrumented with SPS
michael@0 6376 if (sps_.enabled())
michael@0 6377 ionScript->setHasSPSInstrumentation();
michael@0 6378
michael@0 6379 SetIonScript(script, executionMode, ionScript);
michael@0 6380
michael@0 6381 if (cx->runtime()->spsProfiler.enabled()) {
michael@0 6382 const char *filename = script->filename();
michael@0 6383 if (filename == nullptr)
michael@0 6384 filename = "<unknown>";
michael@0 6385 unsigned len = strlen(filename) + 50;
michael@0 6386 char *buf = js_pod_malloc<char>(len);
michael@0 6387 if (!buf)
michael@0 6388 return false;
michael@0 6389 JS_snprintf(buf, len, "Ion compiled %s:%d", filename, (int) script->lineno());
michael@0 6390 cx->runtime()->spsProfiler.markEvent(buf);
michael@0 6391 js_free(buf);
michael@0 6392 }
michael@0 6393
michael@0 6394 // In parallel execution mode, when we first compile a script, we
michael@0 6395 // don't know that its potential callees are compiled, so set a
michael@0 6396 // flag warning that the callees may not be fully compiled.
michael@0 6397 if (!callTargets.empty())
michael@0 6398 ionScript->setHasUncompiledCallTarget();
michael@0 6399
michael@0 6400 invalidateEpilogueData_.fixup(&masm);
michael@0 6401 Assembler::patchDataWithValueCheck(CodeLocationLabel(code, invalidateEpilogueData_),
michael@0 6402 ImmPtr(ionScript),
michael@0 6403 ImmPtr((void*)-1));
michael@0 6404
michael@0 6405 IonSpew(IonSpew_Codegen, "Created IonScript %p (raw %p)",
michael@0 6406 (void *) ionScript, (void *) code->raw());
michael@0 6407
michael@0 6408 ionScript->setInvalidationEpilogueDataOffset(invalidateEpilogueData_.offset());
michael@0 6409 ionScript->setOsrPc(gen->info().osrPc());
michael@0 6410 ionScript->setOsrEntryOffset(getOsrEntryOffset());
michael@0 6411 ptrdiff_t real_invalidate = masm.actualOffset(invalidate_.offset());
michael@0 6412 ionScript->setInvalidationEpilogueOffset(real_invalidate);
michael@0 6413
michael@0 6414 ionScript->setDeoptTable(deoptTable_);
michael@0 6415
michael@0 6416 #if defined(JS_ION_PERF)
michael@0 6417 if (PerfEnabled())
michael@0 6418 perfSpewer_.writeProfile(script, code, masm);
michael@0 6419 #endif
michael@0 6420
michael@0 6421 for (size_t i = 0; i < ionScriptLabels_.length(); i++) {
michael@0 6422 ionScriptLabels_[i].fixup(&masm);
michael@0 6423 Assembler::patchDataWithValueCheck(CodeLocationLabel(code, ionScriptLabels_[i]),
michael@0 6424 ImmPtr(ionScript),
michael@0 6425 ImmPtr((void*)-1));
michael@0 6426 }
michael@0 6427
michael@0 6428 // for generating inline caches during the execution.
michael@0 6429 if (runtimeData_.length())
michael@0 6430 ionScript->copyRuntimeData(&runtimeData_[0]);
michael@0 6431 if (cacheList_.length())
michael@0 6432 ionScript->copyCacheEntries(&cacheList_[0], masm);
michael@0 6433
michael@0 6434 // for marking during GC.
michael@0 6435 if (safepointIndices_.length())
michael@0 6436 ionScript->copySafepointIndices(&safepointIndices_[0], masm);
michael@0 6437 if (safepoints_.size())
michael@0 6438 ionScript->copySafepoints(&safepoints_);
michael@0 6439
michael@0 6440 // for reconvering from an Ion Frame.
michael@0 6441 if (bailouts_.length())
michael@0 6442 ionScript->copyBailoutTable(&bailouts_[0]);
michael@0 6443 if (osiIndices_.length())
michael@0 6444 ionScript->copyOsiIndices(&osiIndices_[0], masm);
michael@0 6445 if (snapshots_.listSize())
michael@0 6446 ionScript->copySnapshots(&snapshots_);
michael@0 6447 MOZ_ASSERT_IF(snapshots_.listSize(), recovers_.size());
michael@0 6448 if (recovers_.size())
michael@0 6449 ionScript->copyRecovers(&recovers_);
michael@0 6450 if (graph.numConstants())
michael@0 6451 ionScript->copyConstants(graph.constantPool());
michael@0 6452 if (callTargets.length() > 0)
michael@0 6453 ionScript->copyCallTargetEntries(callTargets.begin());
michael@0 6454 if (patchableBackedges_.length() > 0)
michael@0 6455 ionScript->copyPatchableBackedges(cx, code, patchableBackedges_.begin());
michael@0 6456
michael@0 6457 #ifdef JS_TRACE_LOGGING
michael@0 6458 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
michael@0 6459 for (uint32_t i = 0; i < patchableTraceLoggers_.length(); i++) {
michael@0 6460 patchableTraceLoggers_[i].fixup(&masm);
michael@0 6461 Assembler::patchDataWithValueCheck(CodeLocationLabel(code, patchableTraceLoggers_[i]),
michael@0 6462 ImmPtr(logger),
michael@0 6463 ImmPtr(nullptr));
michael@0 6464 }
michael@0 6465 uint32_t scriptId = TraceLogCreateTextId(logger, script);
michael@0 6466 for (uint32_t i = 0; i < patchableTLScripts_.length(); i++) {
michael@0 6467 patchableTLScripts_[i].fixup(&masm);
michael@0 6468 Assembler::patchDataWithValueCheck(CodeLocationLabel(code, patchableTLScripts_[i]),
michael@0 6469 ImmPtr((void *) uintptr_t(scriptId)),
michael@0 6470 ImmPtr((void *)0));
michael@0 6471 }
michael@0 6472 #endif
michael@0 6473
michael@0 6474 switch (executionMode) {
michael@0 6475 case SequentialExecution:
michael@0 6476 // The correct state for prebarriers is unknown until the end of compilation,
michael@0 6477 // since a GC can occur during code generation. All barriers are emitted
michael@0 6478 // off-by-default, and are toggled on here if necessary.
michael@0 6479 if (cx->zone()->needsBarrier())
michael@0 6480 ionScript->toggleBarriers(true);
michael@0 6481 break;
michael@0 6482 case ParallelExecution:
michael@0 6483 // We don't run incremental GC during parallel execution; no need to
michael@0 6484 // turn on barriers.
michael@0 6485 break;
michael@0 6486 default:
michael@0 6487 MOZ_ASSUME_UNREACHABLE("No such execution mode");
michael@0 6488 }
michael@0 6489
michael@0 6490 return true;
michael@0 6491 }
michael@0 6492
michael@0 6493 // An out-of-line path to convert a boxed int32 to either a float or double.
michael@0 6494 class OutOfLineUnboxFloatingPoint : public OutOfLineCodeBase<CodeGenerator>
michael@0 6495 {
michael@0 6496 LUnboxFloatingPoint *unboxFloatingPoint_;
michael@0 6497
michael@0 6498 public:
michael@0 6499 OutOfLineUnboxFloatingPoint(LUnboxFloatingPoint *unboxFloatingPoint)
michael@0 6500 : unboxFloatingPoint_(unboxFloatingPoint)
michael@0 6501 { }
michael@0 6502
michael@0 6503 bool accept(CodeGenerator *codegen) {
michael@0 6504 return codegen->visitOutOfLineUnboxFloatingPoint(this);
michael@0 6505 }
michael@0 6506
michael@0 6507 LUnboxFloatingPoint *unboxFloatingPoint() const {
michael@0 6508 return unboxFloatingPoint_;
michael@0 6509 }
michael@0 6510 };
michael@0 6511
michael@0 6512 bool
michael@0 6513 CodeGenerator::visitUnboxFloatingPoint(LUnboxFloatingPoint *lir)
michael@0 6514 {
michael@0 6515 const ValueOperand box = ToValue(lir, LUnboxFloatingPoint::Input);
michael@0 6516 const LDefinition *result = lir->output();
michael@0 6517
michael@0 6518 // Out-of-line path to convert int32 to double or bailout
michael@0 6519 // if this instruction is fallible.
michael@0 6520 OutOfLineUnboxFloatingPoint *ool = new(alloc()) OutOfLineUnboxFloatingPoint(lir);
michael@0 6521 if (!addOutOfLineCode(ool))
michael@0 6522 return false;
michael@0 6523
michael@0 6524 FloatRegister resultReg = ToFloatRegister(result);
michael@0 6525 masm.branchTestDouble(Assembler::NotEqual, box, ool->entry());
michael@0 6526 masm.unboxDouble(box, resultReg);
michael@0 6527 if (lir->type() == MIRType_Float32)
michael@0 6528 masm.convertDoubleToFloat32(resultReg, resultReg);
michael@0 6529 masm.bind(ool->rejoin());
michael@0 6530 return true;
michael@0 6531 }
michael@0 6532
michael@0 6533 bool
michael@0 6534 CodeGenerator::visitOutOfLineUnboxFloatingPoint(OutOfLineUnboxFloatingPoint *ool)
michael@0 6535 {
michael@0 6536 LUnboxFloatingPoint *ins = ool->unboxFloatingPoint();
michael@0 6537 const ValueOperand value = ToValue(ins, LUnboxFloatingPoint::Input);
michael@0 6538
michael@0 6539 if (ins->mir()->fallible()) {
michael@0 6540 Label bail;
michael@0 6541 masm.branchTestInt32(Assembler::NotEqual, value, &bail);
michael@0 6542 if (!bailoutFrom(&bail, ins->snapshot()))
michael@0 6543 return false;
michael@0 6544 }
michael@0 6545 masm.int32ValueToFloatingPoint(value, ToFloatRegister(ins->output()), ins->type());
michael@0 6546 masm.jump(ool->rejoin());
michael@0 6547 return true;
michael@0 6548 }
michael@0 6549
michael@0 6550 typedef bool (*GetPropertyFn)(JSContext *, HandleValue, HandlePropertyName, MutableHandleValue);
michael@0 6551 static const VMFunction GetPropertyInfo = FunctionInfo<GetPropertyFn>(GetProperty);
michael@0 6552 static const VMFunction CallPropertyInfo = FunctionInfo<GetPropertyFn>(CallProperty);
michael@0 6553
michael@0 6554 bool
michael@0 6555 CodeGenerator::visitCallGetProperty(LCallGetProperty *lir)
michael@0 6556 {
michael@0 6557 pushArg(ImmGCPtr(lir->mir()->name()));
michael@0 6558 pushArg(ToValue(lir, LCallGetProperty::Value));
michael@0 6559
michael@0 6560 if (lir->mir()->callprop())
michael@0 6561 return callVM(CallPropertyInfo, lir);
michael@0 6562 return callVM(GetPropertyInfo, lir);
michael@0 6563 }
michael@0 6564
michael@0 6565 typedef bool (*GetOrCallElementFn)(JSContext *, MutableHandleValue, HandleValue, MutableHandleValue);
michael@0 6566 static const VMFunction GetElementInfo = FunctionInfo<GetOrCallElementFn>(js::GetElement);
michael@0 6567 static const VMFunction CallElementInfo = FunctionInfo<GetOrCallElementFn>(js::CallElement);
michael@0 6568
michael@0 6569 bool
michael@0 6570 CodeGenerator::visitCallGetElement(LCallGetElement *lir)
michael@0 6571 {
michael@0 6572 pushArg(ToValue(lir, LCallGetElement::RhsInput));
michael@0 6573 pushArg(ToValue(lir, LCallGetElement::LhsInput));
michael@0 6574
michael@0 6575 JSOp op = JSOp(*lir->mir()->resumePoint()->pc());
michael@0 6576
michael@0 6577 if (op == JSOP_GETELEM) {
michael@0 6578 return callVM(GetElementInfo, lir);
michael@0 6579 } else {
michael@0 6580 JS_ASSERT(op == JSOP_CALLELEM);
michael@0 6581 return callVM(CallElementInfo, lir);
michael@0 6582 }
michael@0 6583 }
michael@0 6584
michael@0 6585 typedef bool (*SetObjectElementFn)(JSContext *, HandleObject, HandleValue, HandleValue,
michael@0 6586 bool strict);
michael@0 6587 typedef bool (*SetElementParFn)(ForkJoinContext *, HandleObject, HandleValue, HandleValue, bool);
michael@0 6588 static const VMFunctionsModal SetObjectElementInfo = VMFunctionsModal(
michael@0 6589 FunctionInfo<SetObjectElementFn>(SetObjectElement),
michael@0 6590 FunctionInfo<SetElementParFn>(SetElementPar));
michael@0 6591
michael@0 6592 bool
michael@0 6593 CodeGenerator::visitCallSetElement(LCallSetElement *lir)
michael@0 6594 {
michael@0 6595 pushArg(Imm32(current->mir()->strict()));
michael@0 6596 pushArg(ToValue(lir, LCallSetElement::Value));
michael@0 6597 pushArg(ToValue(lir, LCallSetElement::Index));
michael@0 6598 pushArg(ToRegister(lir->getOperand(0)));
michael@0 6599 return callVM(SetObjectElementInfo, lir);
michael@0 6600 }
michael@0 6601
michael@0 6602 typedef bool (*InitElementArrayFn)(JSContext *, jsbytecode *, HandleObject, uint32_t, HandleValue);
michael@0 6603 static const VMFunction InitElementArrayInfo = FunctionInfo<InitElementArrayFn>(js::InitElementArray);
michael@0 6604
michael@0 6605 bool
michael@0 6606 CodeGenerator::visitCallInitElementArray(LCallInitElementArray *lir)
michael@0 6607 {
michael@0 6608 pushArg(ToValue(lir, LCallInitElementArray::Value));
michael@0 6609 pushArg(Imm32(lir->mir()->index()));
michael@0 6610 pushArg(ToRegister(lir->getOperand(0)));
michael@0 6611 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
michael@0 6612 return callVM(InitElementArrayInfo, lir);
michael@0 6613 }
michael@0 6614
michael@0 6615 bool
michael@0 6616 CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV *ins)
michael@0 6617 {
michael@0 6618 const Register obj = ToRegister(ins->getOperand(0));
michael@0 6619 size_t slot = ins->mir()->slot();
michael@0 6620 ValueOperand result = GetValueOutput(ins);
michael@0 6621
michael@0 6622 masm.loadValue(Address(obj, JSObject::getFixedSlotOffset(slot)), result);
michael@0 6623 return true;
michael@0 6624 }
michael@0 6625
michael@0 6626 bool
michael@0 6627 CodeGenerator::visitLoadFixedSlotT(LLoadFixedSlotT *ins)
michael@0 6628 {
michael@0 6629 const Register obj = ToRegister(ins->getOperand(0));
michael@0 6630 size_t slot = ins->mir()->slot();
michael@0 6631 AnyRegister result = ToAnyRegister(ins->getDef(0));
michael@0 6632 MIRType type = ins->mir()->type();
michael@0 6633
michael@0 6634 masm.loadUnboxedValue(Address(obj, JSObject::getFixedSlotOffset(slot)), type, result);
michael@0 6635
michael@0 6636 return true;
michael@0 6637 }
michael@0 6638
michael@0 6639 bool
michael@0 6640 CodeGenerator::visitStoreFixedSlotV(LStoreFixedSlotV *ins)
michael@0 6641 {
michael@0 6642 const Register obj = ToRegister(ins->getOperand(0));
michael@0 6643 size_t slot = ins->mir()->slot();
michael@0 6644
michael@0 6645 const ValueOperand value = ToValue(ins, LStoreFixedSlotV::Value);
michael@0 6646
michael@0 6647 Address address(obj, JSObject::getFixedSlotOffset(slot));
michael@0 6648 if (ins->mir()->needsBarrier())
michael@0 6649 emitPreBarrier(address, MIRType_Value);
michael@0 6650
michael@0 6651 masm.storeValue(value, address);
michael@0 6652
michael@0 6653 return true;
michael@0 6654 }
michael@0 6655
michael@0 6656 bool
michael@0 6657 CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT *ins)
michael@0 6658 {
michael@0 6659 const Register obj = ToRegister(ins->getOperand(0));
michael@0 6660 size_t slot = ins->mir()->slot();
michael@0 6661
michael@0 6662 const LAllocation *value = ins->value();
michael@0 6663 MIRType valueType = ins->mir()->value()->type();
michael@0 6664
michael@0 6665 ConstantOrRegister nvalue = value->isConstant()
michael@0 6666 ? ConstantOrRegister(*value->toConstant())
michael@0 6667 : TypedOrValueRegister(valueType, ToAnyRegister(value));
michael@0 6668
michael@0 6669 Address address(obj, JSObject::getFixedSlotOffset(slot));
michael@0 6670 if (ins->mir()->needsBarrier())
michael@0 6671 emitPreBarrier(address, MIRType_Value);
michael@0 6672
michael@0 6673 masm.storeConstantOrRegister(nvalue, address);
michael@0 6674
michael@0 6675 return true;
michael@0 6676 }
michael@0 6677
michael@0 6678 bool
michael@0 6679 CodeGenerator::visitCallsiteCloneCache(LCallsiteCloneCache *ins)
michael@0 6680 {
michael@0 6681 const MCallsiteCloneCache *mir = ins->mir();
michael@0 6682 Register callee = ToRegister(ins->callee());
michael@0 6683 Register output = ToRegister(ins->output());
michael@0 6684
michael@0 6685 CallsiteCloneIC cache(callee, mir->block()->info().script(), mir->callPc(), output);
michael@0 6686 return addCache(ins, allocateCache(cache));
michael@0 6687 }
michael@0 6688
michael@0 6689 typedef JSObject *(*CallsiteCloneICFn)(JSContext *, size_t, HandleObject);
michael@0 6690 const VMFunction CallsiteCloneIC::UpdateInfo =
michael@0 6691 FunctionInfo<CallsiteCloneICFn>(CallsiteCloneIC::update);
michael@0 6692
michael@0 6693 bool
michael@0 6694 CodeGenerator::visitCallsiteCloneIC(OutOfLineUpdateCache *ool, DataPtr<CallsiteCloneIC> &ic)
michael@0 6695 {
michael@0 6696 LInstruction *lir = ool->lir();
michael@0 6697 saveLive(lir);
michael@0 6698
michael@0 6699 pushArg(ic->calleeReg());
michael@0 6700 pushArg(Imm32(ool->getCacheIndex()));
michael@0 6701 if (!callVM(CallsiteCloneIC::UpdateInfo, lir))
michael@0 6702 return false;
michael@0 6703 StoreRegisterTo(ic->outputReg()).generate(this);
michael@0 6704 restoreLiveIgnore(lir, StoreRegisterTo(ic->outputReg()).clobbered());
michael@0 6705
michael@0 6706 masm.jump(ool->rejoin());
michael@0 6707 return true;
michael@0 6708 }
michael@0 6709
michael@0 6710 bool
michael@0 6711 CodeGenerator::visitGetNameCache(LGetNameCache *ins)
michael@0 6712 {
michael@0 6713 RegisterSet liveRegs = ins->safepoint()->liveRegs();
michael@0 6714 Register scopeChain = ToRegister(ins->scopeObj());
michael@0 6715 TypedOrValueRegister output(GetValueOutput(ins));
michael@0 6716 bool isTypeOf = ins->mir()->accessKind() != MGetNameCache::NAME;
michael@0 6717
michael@0 6718 NameIC cache(liveRegs, isTypeOf, scopeChain, ins->mir()->name(), output);
michael@0 6719 return addCache(ins, allocateCache(cache));
michael@0 6720 }
michael@0 6721
michael@0 6722 typedef bool (*NameICFn)(JSContext *, size_t, HandleObject, MutableHandleValue);
michael@0 6723 const VMFunction NameIC::UpdateInfo = FunctionInfo<NameICFn>(NameIC::update);
michael@0 6724
michael@0 6725 bool
michael@0 6726 CodeGenerator::visitNameIC(OutOfLineUpdateCache *ool, DataPtr<NameIC> &ic)
michael@0 6727 {
michael@0 6728 LInstruction *lir = ool->lir();
michael@0 6729 saveLive(lir);
michael@0 6730
michael@0 6731 pushArg(ic->scopeChainReg());
michael@0 6732 pushArg(Imm32(ool->getCacheIndex()));
michael@0 6733 if (!callVM(NameIC::UpdateInfo, lir))
michael@0 6734 return false;
michael@0 6735 StoreValueTo(ic->outputReg()).generate(this);
michael@0 6736 restoreLiveIgnore(lir, StoreValueTo(ic->outputReg()).clobbered());
michael@0 6737
michael@0 6738 masm.jump(ool->rejoin());
michael@0 6739 return true;
michael@0 6740 }
michael@0 6741
michael@0 6742 bool
michael@0 6743 CodeGenerator::addGetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
michael@0 6744 PropertyName *name, TypedOrValueRegister output,
michael@0 6745 bool monitoredResult)
michael@0 6746 {
michael@0 6747 switch (gen->info().executionMode()) {
michael@0 6748 case SequentialExecution: {
michael@0 6749 GetPropertyIC cache(liveRegs, objReg, name, output, monitoredResult);
michael@0 6750 return addCache(ins, allocateCache(cache));
michael@0 6751 }
michael@0 6752 case ParallelExecution: {
michael@0 6753 GetPropertyParIC cache(objReg, name, output);
michael@0 6754 return addCache(ins, allocateCache(cache));
michael@0 6755 }
michael@0 6756 default:
michael@0 6757 MOZ_ASSUME_UNREACHABLE("Bad execution mode");
michael@0 6758 }
michael@0 6759 }
michael@0 6760
michael@0 6761 bool
michael@0 6762 CodeGenerator::addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
michael@0 6763 PropertyName *name, ConstantOrRegister value, bool strict,
michael@0 6764 bool needsTypeBarrier)
michael@0 6765 {
michael@0 6766 switch (gen->info().executionMode()) {
michael@0 6767 case SequentialExecution: {
michael@0 6768 SetPropertyIC cache(liveRegs, objReg, name, value, strict, needsTypeBarrier);
michael@0 6769 return addCache(ins, allocateCache(cache));
michael@0 6770 }
michael@0 6771 case ParallelExecution: {
michael@0 6772 SetPropertyParIC cache(objReg, name, value, strict, needsTypeBarrier);
michael@0 6773 return addCache(ins, allocateCache(cache));
michael@0 6774 }
michael@0 6775 default:
michael@0 6776 MOZ_ASSUME_UNREACHABLE("Bad execution mode");
michael@0 6777 }
michael@0 6778 }
michael@0 6779
michael@0 6780 bool
michael@0 6781 CodeGenerator::addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex,
michael@0 6782 Register temp, FloatRegister tempFloat, ValueOperand index,
michael@0 6783 ConstantOrRegister value, bool strict, bool guardHoles)
michael@0 6784 {
michael@0 6785 switch (gen->info().executionMode()) {
michael@0 6786 case SequentialExecution: {
michael@0 6787 SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict,
michael@0 6788 guardHoles);
michael@0 6789 return addCache(ins, allocateCache(cache));
michael@0 6790 }
michael@0 6791 case ParallelExecution: {
michael@0 6792 SetElementParIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict,
michael@0 6793 guardHoles);
michael@0 6794 return addCache(ins, allocateCache(cache));
michael@0 6795 }
michael@0 6796 default:
michael@0 6797 MOZ_ASSUME_UNREACHABLE("Bad execution mode");
michael@0 6798 }
michael@0 6799 }
michael@0 6800
michael@0 6801 bool
michael@0 6802 CodeGenerator::visitGetPropertyCacheV(LGetPropertyCacheV *ins)
michael@0 6803 {
michael@0 6804 RegisterSet liveRegs = ins->safepoint()->liveRegs();
michael@0 6805 Register objReg = ToRegister(ins->getOperand(0));
michael@0 6806 PropertyName *name = ins->mir()->name();
michael@0 6807 bool monitoredResult = ins->mir()->monitoredResult();
michael@0 6808 TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
michael@0 6809
michael@0 6810 return addGetPropertyCache(ins, liveRegs, objReg, name, output, monitoredResult);
michael@0 6811 }
michael@0 6812
michael@0 6813 bool
michael@0 6814 CodeGenerator::visitGetPropertyCacheT(LGetPropertyCacheT *ins)
michael@0 6815 {
michael@0 6816 RegisterSet liveRegs = ins->safepoint()->liveRegs();
michael@0 6817 Register objReg = ToRegister(ins->getOperand(0));
michael@0 6818 PropertyName *name = ins->mir()->name();
michael@0 6819 bool monitoredResult = ins->mir()->monitoredResult();
michael@0 6820 TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->getDef(0)));
michael@0 6821
michael@0 6822 return addGetPropertyCache(ins, liveRegs, objReg, name, output, monitoredResult);
michael@0 6823 }
michael@0 6824
michael@0 6825 typedef bool (*GetPropertyICFn)(JSContext *, size_t, HandleObject, MutableHandleValue);
michael@0 6826 const VMFunction GetPropertyIC::UpdateInfo =
michael@0 6827 FunctionInfo<GetPropertyICFn>(GetPropertyIC::update);
michael@0 6828
michael@0 6829 bool
michael@0 6830 CodeGenerator::visitGetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyIC> &ic)
michael@0 6831 {
michael@0 6832 LInstruction *lir = ool->lir();
michael@0 6833
michael@0 6834 if (ic->idempotent()) {
michael@0 6835 size_t numLocs;
michael@0 6836 CacheLocationList &cacheLocs = lir->mirRaw()->toGetPropertyCache()->location();
michael@0 6837 size_t locationBase = addCacheLocations(cacheLocs, &numLocs);
michael@0 6838 ic->setLocationInfo(locationBase, numLocs);
michael@0 6839 }
michael@0 6840
michael@0 6841 saveLive(lir);
michael@0 6842
michael@0 6843 pushArg(ic->object());
michael@0 6844 pushArg(Imm32(ool->getCacheIndex()));
michael@0 6845 if (!callVM(GetPropertyIC::UpdateInfo, lir))
michael@0 6846 return false;
michael@0 6847 StoreValueTo(ic->output()).generate(this);
michael@0 6848 restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
michael@0 6849
michael@0 6850 masm.jump(ool->rejoin());
michael@0 6851 return true;
michael@0 6852 }
michael@0 6853
michael@0 6854 typedef bool (*GetPropertyParICFn)(ForkJoinContext *, size_t, HandleObject, MutableHandleValue);
michael@0 6855 const VMFunction GetPropertyParIC::UpdateInfo =
michael@0 6856 FunctionInfo<GetPropertyParICFn>(GetPropertyParIC::update);
michael@0 6857
michael@0 6858 bool
michael@0 6859 CodeGenerator::visitGetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyParIC> &ic)
michael@0 6860 {
michael@0 6861 LInstruction *lir = ool->lir();
michael@0 6862 saveLive(lir);
michael@0 6863
michael@0 6864 pushArg(ic->object());
michael@0 6865 pushArg(Imm32(ool->getCacheIndex()));
michael@0 6866 if (!callVM(GetPropertyParIC::UpdateInfo, lir))
michael@0 6867 return false;
michael@0 6868 StoreValueTo(ic->output()).generate(this);
michael@0 6869 restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
michael@0 6870
michael@0 6871 masm.jump(ool->rejoin());
michael@0 6872 return true;
michael@0 6873 }
michael@0 6874
michael@0 6875 bool
michael@0 6876 CodeGenerator::addGetElementCache(LInstruction *ins, Register obj, ConstantOrRegister index,
michael@0 6877 TypedOrValueRegister output, bool monitoredResult,
michael@0 6878 bool allowDoubleResult)
michael@0 6879 {
michael@0 6880 switch (gen->info().executionMode()) {
michael@0 6881 case SequentialExecution: {
michael@0 6882 RegisterSet liveRegs = ins->safepoint()->liveRegs();
michael@0 6883 GetElementIC cache(liveRegs, obj, index, output, monitoredResult, allowDoubleResult);
michael@0 6884 return addCache(ins, allocateCache(cache));
michael@0 6885 }
michael@0 6886 case ParallelExecution: {
michael@0 6887 GetElementParIC cache(obj, index, output, monitoredResult, allowDoubleResult);
michael@0 6888 return addCache(ins, allocateCache(cache));
michael@0 6889 }
michael@0 6890 default:
michael@0 6891 MOZ_ASSUME_UNREACHABLE("No such execution mode");
michael@0 6892 }
michael@0 6893 }
michael@0 6894
michael@0 6895 bool
michael@0 6896 CodeGenerator::visitGetElementCacheV(LGetElementCacheV *ins)
michael@0 6897 {
michael@0 6898 Register obj = ToRegister(ins->object());
michael@0 6899 ConstantOrRegister index = TypedOrValueRegister(ToValue(ins, LGetElementCacheV::Index));
michael@0 6900 TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
michael@0 6901 const MGetElementCache *mir = ins->mir();
michael@0 6902
michael@0 6903 return addGetElementCache(ins, obj, index, output, mir->monitoredResult(), mir->allowDoubleResult());
michael@0 6904 }
michael@0 6905
michael@0 6906 bool
michael@0 6907 CodeGenerator::visitGetElementCacheT(LGetElementCacheT *ins)
michael@0 6908 {
michael@0 6909 Register obj = ToRegister(ins->object());
michael@0 6910 ConstantOrRegister index = TypedOrValueRegister(MIRType_Int32, ToAnyRegister(ins->index()));
michael@0 6911 TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->output()));
michael@0 6912 const MGetElementCache *mir = ins->mir();
michael@0 6913
michael@0 6914 return addGetElementCache(ins, obj, index, output, mir->monitoredResult(), mir->allowDoubleResult());
michael@0 6915 }
michael@0 6916
michael@0 6917 typedef bool (*GetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, MutableHandleValue);
michael@0 6918 const VMFunction GetElementIC::UpdateInfo =
michael@0 6919 FunctionInfo<GetElementICFn>(GetElementIC::update);
michael@0 6920
michael@0 6921 bool
michael@0 6922 CodeGenerator::visitGetElementIC(OutOfLineUpdateCache *ool, DataPtr<GetElementIC> &ic)
michael@0 6923 {
michael@0 6924 LInstruction *lir = ool->lir();
michael@0 6925 saveLive(lir);
michael@0 6926
michael@0 6927 pushArg(ic->index());
michael@0 6928 pushArg(ic->object());
michael@0 6929 pushArg(Imm32(ool->getCacheIndex()));
michael@0 6930 if (!callVM(GetElementIC::UpdateInfo, lir))
michael@0 6931 return false;
michael@0 6932 StoreValueTo(ic->output()).generate(this);
michael@0 6933 restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
michael@0 6934
michael@0 6935 masm.jump(ool->rejoin());
michael@0 6936 return true;
michael@0 6937 }
michael@0 6938
michael@0 6939 bool
michael@0 6940 CodeGenerator::visitSetElementCacheV(LSetElementCacheV *ins)
michael@0 6941 {
michael@0 6942 Register obj = ToRegister(ins->object());
michael@0 6943 Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
michael@0 6944 Register temp = ToRegister(ins->temp());
michael@0 6945 FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
michael@0 6946 ValueOperand index = ToValue(ins, LSetElementCacheV::Index);
michael@0 6947 ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetElementCacheV::Value));
michael@0 6948
michael@0 6949 return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value,
michael@0 6950 ins->mir()->strict(), ins->mir()->guardHoles());
michael@0 6951 }
michael@0 6952
michael@0 6953 bool
michael@0 6954 CodeGenerator::visitSetElementCacheT(LSetElementCacheT *ins)
michael@0 6955 {
michael@0 6956 Register obj = ToRegister(ins->object());
michael@0 6957 Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
michael@0 6958 Register temp = ToRegister(ins->temp());
michael@0 6959 FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
michael@0 6960 ValueOperand index = ToValue(ins, LSetElementCacheT::Index);
michael@0 6961 ConstantOrRegister value;
michael@0 6962 const LAllocation *tmp = ins->value();
michael@0 6963 if (tmp->isConstant())
michael@0 6964 value = *tmp->toConstant();
michael@0 6965 else
michael@0 6966 value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(tmp));
michael@0 6967
michael@0 6968 return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value,
michael@0 6969 ins->mir()->strict(), ins->mir()->guardHoles());
michael@0 6970 }
michael@0 6971
michael@0 6972 typedef bool (*SetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, HandleValue);
michael@0 6973 const VMFunction SetElementIC::UpdateInfo =
michael@0 6974 FunctionInfo<SetElementICFn>(SetElementIC::update);
michael@0 6975
michael@0 6976 bool
michael@0 6977 CodeGenerator::visitSetElementIC(OutOfLineUpdateCache *ool, DataPtr<SetElementIC> &ic)
michael@0 6978 {
michael@0 6979 LInstruction *lir = ool->lir();
michael@0 6980 saveLive(lir);
michael@0 6981
michael@0 6982 pushArg(ic->value());
michael@0 6983 pushArg(ic->index());
michael@0 6984 pushArg(ic->object());
michael@0 6985 pushArg(Imm32(ool->getCacheIndex()));
michael@0 6986 if (!callVM(SetElementIC::UpdateInfo, lir))
michael@0 6987 return false;
michael@0 6988 restoreLive(lir);
michael@0 6989
michael@0 6990 masm.jump(ool->rejoin());
michael@0 6991 return true;
michael@0 6992 }
michael@0 6993
michael@0 6994 typedef bool (*SetElementParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue, HandleValue);
michael@0 6995 const VMFunction SetElementParIC::UpdateInfo =
michael@0 6996 FunctionInfo<SetElementParICFn>(SetElementParIC::update);
michael@0 6997
michael@0 6998 bool
michael@0 6999 CodeGenerator::visitSetElementParIC(OutOfLineUpdateCache *ool, DataPtr<SetElementParIC> &ic)
michael@0 7000 {
michael@0 7001 LInstruction *lir = ool->lir();
michael@0 7002 saveLive(lir);
michael@0 7003
michael@0 7004 pushArg(ic->value());
michael@0 7005 pushArg(ic->index());
michael@0 7006 pushArg(ic->object());
michael@0 7007 pushArg(Imm32(ool->getCacheIndex()));
michael@0 7008 if (!callVM(SetElementParIC::UpdateInfo, lir))
michael@0 7009 return false;
michael@0 7010 restoreLive(lir);
michael@0 7011
michael@0 7012 masm.jump(ool->rejoin());
michael@0 7013 return true;
michael@0 7014 }
michael@0 7015
michael@0 7016 typedef bool (*GetElementParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue,
michael@0 7017 MutableHandleValue);
michael@0 7018 const VMFunction GetElementParIC::UpdateInfo =
michael@0 7019 FunctionInfo<GetElementParICFn>(GetElementParIC::update);
michael@0 7020
michael@0 7021 bool
michael@0 7022 CodeGenerator::visitGetElementParIC(OutOfLineUpdateCache *ool, DataPtr<GetElementParIC> &ic)
michael@0 7023 {
michael@0 7024 LInstruction *lir = ool->lir();
michael@0 7025 saveLive(lir);
michael@0 7026
michael@0 7027 pushArg(ic->index());
michael@0 7028 pushArg(ic->object());
michael@0 7029 pushArg(Imm32(ool->getCacheIndex()));
michael@0 7030 if (!callVM(GetElementParIC::UpdateInfo, lir))
michael@0 7031 return false;
michael@0 7032 StoreValueTo(ic->output()).generate(this);
michael@0 7033 restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
michael@0 7034
michael@0 7035 masm.jump(ool->rejoin());
michael@0 7036 return true;
michael@0 7037 }
michael@0 7038
michael@0 7039 bool
michael@0 7040 CodeGenerator::visitBindNameCache(LBindNameCache *ins)
michael@0 7041 {
michael@0 7042 Register scopeChain = ToRegister(ins->scopeChain());
michael@0 7043 Register output = ToRegister(ins->output());
michael@0 7044 BindNameIC cache(scopeChain, ins->mir()->name(), output);
michael@0 7045
michael@0 7046 return addCache(ins, allocateCache(cache));
michael@0 7047 }
michael@0 7048
michael@0 7049 typedef JSObject *(*BindNameICFn)(JSContext *, size_t, HandleObject);
michael@0 7050 const VMFunction BindNameIC::UpdateInfo =
michael@0 7051 FunctionInfo<BindNameICFn>(BindNameIC::update);
michael@0 7052
michael@0 7053 bool
michael@0 7054 CodeGenerator::visitBindNameIC(OutOfLineUpdateCache *ool, DataPtr<BindNameIC> &ic)
michael@0 7055 {
michael@0 7056 LInstruction *lir = ool->lir();
michael@0 7057 saveLive(lir);
michael@0 7058
michael@0 7059 pushArg(ic->scopeChainReg());
michael@0 7060 pushArg(Imm32(ool->getCacheIndex()));
michael@0 7061 if (!callVM(BindNameIC::UpdateInfo, lir))
michael@0 7062 return false;
michael@0 7063 StoreRegisterTo(ic->outputReg()).generate(this);
michael@0 7064 restoreLiveIgnore(lir, StoreRegisterTo(ic->outputReg()).clobbered());
michael@0 7065
michael@0 7066 masm.jump(ool->rejoin());
michael@0 7067 return true;
michael@0 7068 }
michael@0 7069
michael@0 7070 typedef bool (*SetPropertyFn)(JSContext *, HandleObject,
michael@0 7071 HandlePropertyName, const HandleValue, bool, jsbytecode *);
michael@0 7072 typedef bool (*SetPropertyParFn)(ForkJoinContext *, HandleObject,
michael@0 7073 HandlePropertyName, const HandleValue, bool, jsbytecode *);
michael@0 7074 static const VMFunctionsModal SetPropertyInfo = VMFunctionsModal(
michael@0 7075 FunctionInfo<SetPropertyFn>(SetProperty),
michael@0 7076 FunctionInfo<SetPropertyParFn>(SetPropertyPar));
michael@0 7077
michael@0 7078 bool
michael@0 7079 CodeGenerator::visitCallSetProperty(LCallSetProperty *ins)
michael@0 7080 {
michael@0 7081 ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LCallSetProperty::Value));
michael@0 7082
michael@0 7083 const Register objReg = ToRegister(ins->getOperand(0));
michael@0 7084
michael@0 7085 pushArg(ImmPtr(ins->mir()->resumePoint()->pc()));
michael@0 7086 pushArg(Imm32(ins->mir()->strict()));
michael@0 7087
michael@0 7088 pushArg(value);
michael@0 7089 pushArg(ImmGCPtr(ins->mir()->name()));
michael@0 7090 pushArg(objReg);
michael@0 7091
michael@0 7092 return callVM(SetPropertyInfo, ins);
michael@0 7093 }
michael@0 7094
michael@0 7095 typedef bool (*DeletePropertyFn)(JSContext *, HandleValue, HandlePropertyName, bool *);
michael@0 7096 static const VMFunction DeletePropertyStrictInfo =
michael@0 7097 FunctionInfo<DeletePropertyFn>(DeleteProperty<true>);
michael@0 7098 static const VMFunction DeletePropertyNonStrictInfo =
michael@0 7099 FunctionInfo<DeletePropertyFn>(DeleteProperty<false>);
michael@0 7100
michael@0 7101 bool
michael@0 7102 CodeGenerator::visitCallDeleteProperty(LCallDeleteProperty *lir)
michael@0 7103 {
michael@0 7104 pushArg(ImmGCPtr(lir->mir()->name()));
michael@0 7105 pushArg(ToValue(lir, LCallDeleteProperty::Value));
michael@0 7106
michael@0 7107 if (lir->mir()->block()->info().script()->strict())
michael@0 7108 return callVM(DeletePropertyStrictInfo, lir);
michael@0 7109
michael@0 7110 return callVM(DeletePropertyNonStrictInfo, lir);
michael@0 7111 }
michael@0 7112
michael@0 7113 typedef bool (*DeleteElementFn)(JSContext *, HandleValue, HandleValue, bool *);
michael@0 7114 static const VMFunction DeleteElementStrictInfo =
michael@0 7115 FunctionInfo<DeleteElementFn>(DeleteElement<true>);
michael@0 7116 static const VMFunction DeleteElementNonStrictInfo =
michael@0 7117 FunctionInfo<DeleteElementFn>(DeleteElement<false>);
michael@0 7118
michael@0 7119 bool
michael@0 7120 CodeGenerator::visitCallDeleteElement(LCallDeleteElement *lir)
michael@0 7121 {
michael@0 7122 pushArg(ToValue(lir, LCallDeleteElement::Index));
michael@0 7123 pushArg(ToValue(lir, LCallDeleteElement::Value));
michael@0 7124
michael@0 7125 if (lir->mir()->block()->info().script()->strict())
michael@0 7126 return callVM(DeleteElementStrictInfo, lir);
michael@0 7127
michael@0 7128 return callVM(DeleteElementNonStrictInfo, lir);
michael@0 7129 }
michael@0 7130
michael@0 7131 bool
michael@0 7132 CodeGenerator::visitSetPropertyCacheV(LSetPropertyCacheV *ins)
michael@0 7133 {
michael@0 7134 RegisterSet liveRegs = ins->safepoint()->liveRegs();
michael@0 7135 Register objReg = ToRegister(ins->getOperand(0));
michael@0 7136 ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetPropertyCacheV::Value));
michael@0 7137
michael@0 7138 return addSetPropertyCache(ins, liveRegs, objReg, ins->mir()->name(), value,
michael@0 7139 ins->mir()->strict(), ins->mir()->needsTypeBarrier());
michael@0 7140 }
michael@0 7141
michael@0 7142 bool
michael@0 7143 CodeGenerator::visitSetPropertyCacheT(LSetPropertyCacheT *ins)
michael@0 7144 {
michael@0 7145 RegisterSet liveRegs = ins->safepoint()->liveRegs();
michael@0 7146 Register objReg = ToRegister(ins->getOperand(0));
michael@0 7147 ConstantOrRegister value;
michael@0 7148
michael@0 7149 if (ins->getOperand(1)->isConstant())
michael@0 7150 value = ConstantOrRegister(*ins->getOperand(1)->toConstant());
michael@0 7151 else
michael@0 7152 value = TypedOrValueRegister(ins->valueType(), ToAnyRegister(ins->getOperand(1)));
michael@0 7153
michael@0 7154 return addSetPropertyCache(ins, liveRegs, objReg, ins->mir()->name(), value,
michael@0 7155 ins->mir()->strict(), ins->mir()->needsTypeBarrier());
michael@0 7156 }
michael@0 7157
michael@0 7158 typedef bool (*SetPropertyICFn)(JSContext *, size_t, HandleObject, HandleValue);
michael@0 7159 const VMFunction SetPropertyIC::UpdateInfo =
michael@0 7160 FunctionInfo<SetPropertyICFn>(SetPropertyIC::update);
michael@0 7161
michael@0 7162 bool
michael@0 7163 CodeGenerator::visitSetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyIC> &ic)
michael@0 7164 {
michael@0 7165 LInstruction *lir = ool->lir();
michael@0 7166 saveLive(lir);
michael@0 7167
michael@0 7168 pushArg(ic->value());
michael@0 7169 pushArg(ic->object());
michael@0 7170 pushArg(Imm32(ool->getCacheIndex()));
michael@0 7171 if (!callVM(SetPropertyIC::UpdateInfo, lir))
michael@0 7172 return false;
michael@0 7173 restoreLive(lir);
michael@0 7174
michael@0 7175 masm.jump(ool->rejoin());
michael@0 7176 return true;
michael@0 7177 }
michael@0 7178
michael@0 7179 typedef bool (*SetPropertyParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue);
michael@0 7180 const VMFunction SetPropertyParIC::UpdateInfo =
michael@0 7181 FunctionInfo<SetPropertyParICFn>(SetPropertyParIC::update);
michael@0 7182
michael@0 7183 bool
michael@0 7184 CodeGenerator::visitSetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyParIC> &ic)
michael@0 7185 {
michael@0 7186 LInstruction *lir = ool->lir();
michael@0 7187 saveLive(lir);
michael@0 7188
michael@0 7189 pushArg(ic->value());
michael@0 7190 pushArg(ic->object());
michael@0 7191 pushArg(Imm32(ool->getCacheIndex()));
michael@0 7192 if (!callVM(SetPropertyParIC::UpdateInfo, lir))
michael@0 7193 return false;
michael@0 7194 restoreLive(lir);
michael@0 7195
michael@0 7196 masm.jump(ool->rejoin());
michael@0 7197 return true;
michael@0 7198 }
michael@0 7199
michael@0 7200 typedef bool (*ThrowFn)(JSContext *, HandleValue);
michael@0 7201 static const VMFunction ThrowInfoCodeGen = FunctionInfo<ThrowFn>(js::Throw);
michael@0 7202
michael@0 7203 bool
michael@0 7204 CodeGenerator::visitThrow(LThrow *lir)
michael@0 7205 {
michael@0 7206 pushArg(ToValue(lir, LThrow::Value));
michael@0 7207 return callVM(ThrowInfoCodeGen, lir);
michael@0 7208 }
michael@0 7209
michael@0 7210 typedef bool (*BitNotFn)(JSContext *, HandleValue, int *p);
michael@0 7211 typedef bool (*BitNotParFn)(ForkJoinContext *, HandleValue, int32_t *);
michael@0 7212 static const VMFunctionsModal BitNotInfo = VMFunctionsModal(
michael@0 7213 FunctionInfo<BitNotFn>(BitNot),
michael@0 7214 FunctionInfo<BitNotParFn>(BitNotPar));
michael@0 7215
michael@0 7216 bool
michael@0 7217 CodeGenerator::visitBitNotV(LBitNotV *lir)
michael@0 7218 {
michael@0 7219 pushArg(ToValue(lir, LBitNotV::Input));
michael@0 7220 return callVM(BitNotInfo, lir);
michael@0 7221 }
michael@0 7222
michael@0 7223 typedef bool (*BitopFn)(JSContext *, HandleValue, HandleValue, int *p);
michael@0 7224 typedef bool (*BitopParFn)(ForkJoinContext *, HandleValue, HandleValue, int32_t *);
michael@0 7225 static const VMFunctionsModal BitAndInfo = VMFunctionsModal(
michael@0 7226 FunctionInfo<BitopFn>(BitAnd),
michael@0 7227 FunctionInfo<BitopParFn>(BitAndPar));
michael@0 7228 static const VMFunctionsModal BitOrInfo = VMFunctionsModal(
michael@0 7229 FunctionInfo<BitopFn>(BitOr),
michael@0 7230 FunctionInfo<BitopParFn>(BitOrPar));
michael@0 7231 static const VMFunctionsModal BitXorInfo = VMFunctionsModal(
michael@0 7232 FunctionInfo<BitopFn>(BitXor),
michael@0 7233 FunctionInfo<BitopParFn>(BitXorPar));
michael@0 7234 static const VMFunctionsModal BitLhsInfo = VMFunctionsModal(
michael@0 7235 FunctionInfo<BitopFn>(BitLsh),
michael@0 7236 FunctionInfo<BitopParFn>(BitLshPar));
michael@0 7237 static const VMFunctionsModal BitRhsInfo = VMFunctionsModal(
michael@0 7238 FunctionInfo<BitopFn>(BitRsh),
michael@0 7239 FunctionInfo<BitopParFn>(BitRshPar));
michael@0 7240
michael@0 7241 bool
michael@0 7242 CodeGenerator::visitBitOpV(LBitOpV *lir)
michael@0 7243 {
michael@0 7244 pushArg(ToValue(lir, LBitOpV::RhsInput));
michael@0 7245 pushArg(ToValue(lir, LBitOpV::LhsInput));
michael@0 7246
michael@0 7247 switch (lir->jsop()) {
michael@0 7248 case JSOP_BITAND:
michael@0 7249 return callVM(BitAndInfo, lir);
michael@0 7250 case JSOP_BITOR:
michael@0 7251 return callVM(BitOrInfo, lir);
michael@0 7252 case JSOP_BITXOR:
michael@0 7253 return callVM(BitXorInfo, lir);
michael@0 7254 case JSOP_LSH:
michael@0 7255 return callVM(BitLhsInfo, lir);
michael@0 7256 case JSOP_RSH:
michael@0 7257 return callVM(BitRhsInfo, lir);
michael@0 7258 default:
michael@0 7259 break;
michael@0 7260 }
michael@0 7261 MOZ_ASSUME_UNREACHABLE("unexpected bitop");
michael@0 7262 }
michael@0 7263
michael@0 7264 class OutOfLineTypeOfV : public OutOfLineCodeBase<CodeGenerator>
michael@0 7265 {
michael@0 7266 LTypeOfV *ins_;
michael@0 7267
michael@0 7268 public:
michael@0 7269 OutOfLineTypeOfV(LTypeOfV *ins)
michael@0 7270 : ins_(ins)
michael@0 7271 { }
michael@0 7272
michael@0 7273 bool accept(CodeGenerator *codegen) {
michael@0 7274 return codegen->visitOutOfLineTypeOfV(this);
michael@0 7275 }
michael@0 7276 LTypeOfV *ins() const {
michael@0 7277 return ins_;
michael@0 7278 }
michael@0 7279 };
michael@0 7280
michael@0 7281 bool
michael@0 7282 CodeGenerator::visitTypeOfV(LTypeOfV *lir)
michael@0 7283 {
michael@0 7284 const ValueOperand value = ToValue(lir, LTypeOfV::Input);
michael@0 7285 Register output = ToRegister(lir->output());
michael@0 7286 Register tag = masm.splitTagForTest(value);
michael@0 7287
michael@0 7288 const JSAtomState &names = GetIonContext()->runtime->names();
michael@0 7289 Label done;
michael@0 7290
michael@0 7291 OutOfLineTypeOfV *ool = nullptr;
michael@0 7292 if (lir->mir()->inputMaybeCallableOrEmulatesUndefined()) {
michael@0 7293 // The input may be a callable object (result is "function") or may
michael@0 7294 // emulate undefined (result is "undefined"). Use an OOL path.
michael@0 7295 ool = new(alloc()) OutOfLineTypeOfV(lir);
michael@0 7296 if (!addOutOfLineCode(ool))
michael@0 7297 return false;
michael@0 7298
michael@0 7299 masm.branchTestObject(Assembler::Equal, tag, ool->entry());
michael@0 7300 } else {
michael@0 7301 // Input is not callable and does not emulate undefined, so if
michael@0 7302 // it's an object the result is always "object".
michael@0 7303 Label notObject;
michael@0 7304 masm.branchTestObject(Assembler::NotEqual, tag, &notObject);
michael@0 7305 masm.movePtr(ImmGCPtr(names.object), output);
michael@0 7306 masm.jump(&done);
michael@0 7307 masm.bind(&notObject);
michael@0 7308 }
michael@0 7309
michael@0 7310 Label notNumber;
michael@0 7311 masm.branchTestNumber(Assembler::NotEqual, tag, &notNumber);
michael@0 7312 masm.movePtr(ImmGCPtr(names.number), output);
michael@0 7313 masm.jump(&done);
michael@0 7314 masm.bind(&notNumber);
michael@0 7315
michael@0 7316 Label notUndefined;
michael@0 7317 masm.branchTestUndefined(Assembler::NotEqual, tag, &notUndefined);
michael@0 7318 masm.movePtr(ImmGCPtr(names.undefined), output);
michael@0 7319 masm.jump(&done);
michael@0 7320 masm.bind(&notUndefined);
michael@0 7321
michael@0 7322 Label notNull;
michael@0 7323 masm.branchTestNull(Assembler::NotEqual, tag, &notNull);
michael@0 7324 masm.movePtr(ImmGCPtr(names.object), output);
michael@0 7325 masm.jump(&done);
michael@0 7326 masm.bind(&notNull);
michael@0 7327
michael@0 7328 Label notBoolean;
michael@0 7329 masm.branchTestBoolean(Assembler::NotEqual, tag, &notBoolean);
michael@0 7330 masm.movePtr(ImmGCPtr(names.boolean), output);
michael@0 7331 masm.jump(&done);
michael@0 7332 masm.bind(&notBoolean);
michael@0 7333
michael@0 7334 masm.movePtr(ImmGCPtr(names.string), output);
michael@0 7335
michael@0 7336 masm.bind(&done);
michael@0 7337 if (ool)
michael@0 7338 masm.bind(ool->rejoin());
michael@0 7339 return true;
michael@0 7340 }
michael@0 7341
michael@0 7342 bool
michael@0 7343 CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV *ool)
michael@0 7344 {
michael@0 7345 LTypeOfV *ins = ool->ins();
michael@0 7346
michael@0 7347 ValueOperand input = ToValue(ins, LTypeOfV::Input);
michael@0 7348 Register temp = ToTempUnboxRegister(ins->tempToUnbox());
michael@0 7349 Register output = ToRegister(ins->output());
michael@0 7350
michael@0 7351 Register obj = masm.extractObject(input, temp);
michael@0 7352
michael@0 7353 saveVolatile(output);
michael@0 7354 masm.setupUnalignedABICall(2, output);
michael@0 7355 masm.passABIArg(obj);
michael@0 7356 masm.movePtr(ImmPtr(GetIonContext()->runtime), output);
michael@0 7357 masm.passABIArg(output);
michael@0 7358 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::TypeOfObjectOperation));
michael@0 7359 masm.storeCallResult(output);
michael@0 7360 restoreVolatile(output);
michael@0 7361
michael@0 7362 masm.jump(ool->rejoin());
michael@0 7363 return true;
michael@0 7364 }
michael@0 7365
michael@0 7366 typedef bool (*ToIdFn)(JSContext *, HandleScript, jsbytecode *, HandleValue, HandleValue,
michael@0 7367 MutableHandleValue);
michael@0 7368 static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(ToIdOperation);
michael@0 7369
michael@0 7370 bool
michael@0 7371 CodeGenerator::visitToIdV(LToIdV *lir)
michael@0 7372 {
michael@0 7373 Label notInt32;
michael@0 7374 FloatRegister temp = ToFloatRegister(lir->tempFloat());
michael@0 7375 const ValueOperand out = ToOutValue(lir);
michael@0 7376 ValueOperand index = ToValue(lir, LToIdV::Index);
michael@0 7377
michael@0 7378 OutOfLineCode *ool = oolCallVM(ToIdInfo, lir,
michael@0 7379 (ArgList(),
michael@0 7380 ImmGCPtr(current->mir()->info().script()),
michael@0 7381 ImmPtr(lir->mir()->resumePoint()->pc()),
michael@0 7382 ToValue(lir, LToIdV::Object),
michael@0 7383 ToValue(lir, LToIdV::Index)),
michael@0 7384 StoreValueTo(out));
michael@0 7385
michael@0 7386 Register tag = masm.splitTagForTest(index);
michael@0 7387
michael@0 7388 masm.branchTestInt32(Assembler::NotEqual, tag, &notInt32);
michael@0 7389 masm.moveValue(index, out);
michael@0 7390 masm.jump(ool->rejoin());
michael@0 7391
michael@0 7392 masm.bind(&notInt32);
michael@0 7393 masm.branchTestDouble(Assembler::NotEqual, tag, ool->entry());
michael@0 7394 masm.unboxDouble(index, temp);
michael@0 7395 masm.convertDoubleToInt32(temp, out.scratchReg(), ool->entry(), true);
michael@0 7396 masm.tagValue(JSVAL_TYPE_INT32, out.scratchReg(), out);
michael@0 7397
michael@0 7398 masm.bind(ool->rejoin());
michael@0 7399 return true;
michael@0 7400 }
michael@0 7401
michael@0 7402 bool
michael@0 7403 CodeGenerator::visitLoadElementV(LLoadElementV *load)
michael@0 7404 {
michael@0 7405 Register elements = ToRegister(load->elements());
michael@0 7406 const ValueOperand out = ToOutValue(load);
michael@0 7407
michael@0 7408 if (load->index()->isConstant())
michael@0 7409 masm.loadValue(Address(elements, ToInt32(load->index()) * sizeof(Value)), out);
michael@0 7410 else
michael@0 7411 masm.loadValue(BaseIndex(elements, ToRegister(load->index()), TimesEight), out);
michael@0 7412
michael@0 7413 if (load->mir()->needsHoleCheck()) {
michael@0 7414 Label testMagic;
michael@0 7415 masm.branchTestMagic(Assembler::Equal, out, &testMagic);
michael@0 7416 if (!bailoutFrom(&testMagic, load->snapshot()))
michael@0 7417 return false;
michael@0 7418 }
michael@0 7419
michael@0 7420 return true;
michael@0 7421 }
michael@0 7422
michael@0 7423 bool
michael@0 7424 CodeGenerator::visitLoadElementHole(LLoadElementHole *lir)
michael@0 7425 {
michael@0 7426 Register elements = ToRegister(lir->elements());
michael@0 7427 Register initLength = ToRegister(lir->initLength());
michael@0 7428 const ValueOperand out = ToOutValue(lir);
michael@0 7429
michael@0 7430 const MLoadElementHole *mir = lir->mir();
michael@0 7431
michael@0 7432 // If the index is out of bounds, load |undefined|. Otherwise, load the
michael@0 7433 // value.
michael@0 7434 Label undefined, done;
michael@0 7435 if (lir->index()->isConstant()) {
michael@0 7436 masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(ToInt32(lir->index())), &undefined);
michael@0 7437 masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out);
michael@0 7438 } else {
michael@0 7439 masm.branch32(Assembler::BelowOrEqual, initLength, ToRegister(lir->index()), &undefined);
michael@0 7440 masm.loadValue(BaseIndex(elements, ToRegister(lir->index()), TimesEight), out);
michael@0 7441 }
michael@0 7442
michael@0 7443 // If a hole check is needed, and the value wasn't a hole, we're done.
michael@0 7444 // Otherwise, we'll load undefined.
michael@0 7445 if (lir->mir()->needsHoleCheck())
michael@0 7446 masm.branchTestMagic(Assembler::NotEqual, out, &done);
michael@0 7447 else
michael@0 7448 masm.jump(&done);
michael@0 7449
michael@0 7450 masm.bind(&undefined);
michael@0 7451
michael@0 7452 if (mir->needsNegativeIntCheck()) {
michael@0 7453 if (lir->index()->isConstant()) {
michael@0 7454 if (ToInt32(lir->index()) < 0 && !bailout(lir->snapshot()))
michael@0 7455 return false;
michael@0 7456 } else {
michael@0 7457 Label negative;
michael@0 7458 masm.branch32(Assembler::LessThan, ToRegister(lir->index()), Imm32(0), &negative);
michael@0 7459 if (!bailoutFrom(&negative, lir->snapshot()))
michael@0 7460 return false;
michael@0 7461 }
michael@0 7462 }
michael@0 7463
michael@0 7464 masm.moveValue(UndefinedValue(), out);
michael@0 7465 masm.bind(&done);
michael@0 7466 return true;
michael@0 7467 }
michael@0 7468
michael@0 7469 bool
michael@0 7470 CodeGenerator::visitLoadTypedArrayElement(LLoadTypedArrayElement *lir)
michael@0 7471 {
michael@0 7472 Register elements = ToRegister(lir->elements());
michael@0 7473 Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
michael@0 7474 AnyRegister out = ToAnyRegister(lir->output());
michael@0 7475
michael@0 7476 int arrayType = lir->mir()->arrayType();
michael@0 7477 int width = TypedArrayObject::slotWidth(arrayType);
michael@0 7478
michael@0 7479 Label fail;
michael@0 7480 if (lir->index()->isConstant()) {
michael@0 7481 Address source(elements, ToInt32(lir->index()) * width);
michael@0 7482 masm.loadFromTypedArray(arrayType, source, out, temp, &fail);
michael@0 7483 } else {
michael@0 7484 BaseIndex source(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
michael@0 7485 masm.loadFromTypedArray(arrayType, source, out, temp, &fail);
michael@0 7486 }
michael@0 7487
michael@0 7488 if (fail.used() && !bailoutFrom(&fail, lir->snapshot()))
michael@0 7489 return false;
michael@0 7490
michael@0 7491 return true;
michael@0 7492 }
michael@0 7493
michael@0 7494 bool
michael@0 7495 CodeGenerator::visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole *lir)
michael@0 7496 {
michael@0 7497 Register object = ToRegister(lir->object());
michael@0 7498 const ValueOperand out = ToOutValue(lir);
michael@0 7499
michael@0 7500 // Load the length.
michael@0 7501 Register scratch = out.scratchReg();
michael@0 7502 Int32Key key = ToInt32Key(lir->index());
michael@0 7503 masm.unboxInt32(Address(object, TypedArrayObject::lengthOffset()), scratch);
michael@0 7504
michael@0 7505 // Load undefined unless length > key.
michael@0 7506 Label inbounds, done;
michael@0 7507 masm.branchKey(Assembler::Above, scratch, key, &inbounds);
michael@0 7508 masm.moveValue(UndefinedValue(), out);
michael@0 7509 masm.jump(&done);
michael@0 7510
michael@0 7511 // Load the elements vector.
michael@0 7512 masm.bind(&inbounds);
michael@0 7513 masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), scratch);
michael@0 7514
michael@0 7515 int arrayType = lir->mir()->arrayType();
michael@0 7516 int width = TypedArrayObject::slotWidth(arrayType);
michael@0 7517
michael@0 7518 Label fail;
michael@0 7519 if (key.isConstant()) {
michael@0 7520 Address source(scratch, key.constant() * width);
michael@0 7521 masm.loadFromTypedArray(arrayType, source, out, lir->mir()->allowDouble(),
michael@0 7522 out.scratchReg(), &fail);
michael@0 7523 } else {
michael@0 7524 BaseIndex source(scratch, key.reg(), ScaleFromElemWidth(width));
michael@0 7525 masm.loadFromTypedArray(arrayType, source, out, lir->mir()->allowDouble(),
michael@0 7526 out.scratchReg(), &fail);
michael@0 7527 }
michael@0 7528
michael@0 7529 if (fail.used() && !bailoutFrom(&fail, lir->snapshot()))
michael@0 7530 return false;
michael@0 7531
michael@0 7532 masm.bind(&done);
michael@0 7533 return true;
michael@0 7534 }
michael@0 7535
michael@0 7536 template <typename T>
michael@0 7537 static inline void
michael@0 7538 StoreToTypedArray(MacroAssembler &masm, int arrayType, const LAllocation *value, const T &dest)
michael@0 7539 {
michael@0 7540 if (arrayType == ScalarTypeDescr::TYPE_FLOAT32 ||
michael@0 7541 arrayType == ScalarTypeDescr::TYPE_FLOAT64)
michael@0 7542 {
michael@0 7543 masm.storeToTypedFloatArray(arrayType, ToFloatRegister(value), dest);
michael@0 7544 } else {
michael@0 7545 if (value->isConstant())
michael@0 7546 masm.storeToTypedIntArray(arrayType, Imm32(ToInt32(value)), dest);
michael@0 7547 else
michael@0 7548 masm.storeToTypedIntArray(arrayType, ToRegister(value), dest);
michael@0 7549 }
michael@0 7550 }
michael@0 7551
michael@0 7552 bool
michael@0 7553 CodeGenerator::visitStoreTypedArrayElement(LStoreTypedArrayElement *lir)
michael@0 7554 {
michael@0 7555 Register elements = ToRegister(lir->elements());
michael@0 7556 const LAllocation *value = lir->value();
michael@0 7557
michael@0 7558 int arrayType = lir->mir()->arrayType();
michael@0 7559 int width = TypedArrayObject::slotWidth(arrayType);
michael@0 7560
michael@0 7561 if (lir->index()->isConstant()) {
michael@0 7562 Address dest(elements, ToInt32(lir->index()) * width);
michael@0 7563 StoreToTypedArray(masm, arrayType, value, dest);
michael@0 7564 } else {
michael@0 7565 BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
michael@0 7566 StoreToTypedArray(masm, arrayType, value, dest);
michael@0 7567 }
michael@0 7568
michael@0 7569 return true;
michael@0 7570 }
michael@0 7571
michael@0 7572 bool
michael@0 7573 CodeGenerator::visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole *lir)
michael@0 7574 {
michael@0 7575 Register elements = ToRegister(lir->elements());
michael@0 7576 const LAllocation *value = lir->value();
michael@0 7577
michael@0 7578 int arrayType = lir->mir()->arrayType();
michael@0 7579 int width = TypedArrayObject::slotWidth(arrayType);
michael@0 7580
michael@0 7581 bool guardLength = true;
michael@0 7582 if (lir->index()->isConstant() && lir->length()->isConstant()) {
michael@0 7583 uint32_t idx = ToInt32(lir->index());
michael@0 7584 uint32_t len = ToInt32(lir->length());
michael@0 7585 if (idx >= len)
michael@0 7586 return true;
michael@0 7587 guardLength = false;
michael@0 7588 }
michael@0 7589 Label skip;
michael@0 7590 if (lir->index()->isConstant()) {
michael@0 7591 uint32_t idx = ToInt32(lir->index());
michael@0 7592 if (guardLength)
michael@0 7593 masm.branch32(Assembler::BelowOrEqual, ToOperand(lir->length()), Imm32(idx), &skip);
michael@0 7594 Address dest(elements, idx * width);
michael@0 7595 StoreToTypedArray(masm, arrayType, value, dest);
michael@0 7596 } else {
michael@0 7597 Register idxReg = ToRegister(lir->index());
michael@0 7598 JS_ASSERT(guardLength);
michael@0 7599 if (lir->length()->isConstant())
michael@0 7600 masm.branch32(Assembler::AboveOrEqual, idxReg, Imm32(ToInt32(lir->length())), &skip);
michael@0 7601 else
michael@0 7602 masm.branch32(Assembler::BelowOrEqual, ToOperand(lir->length()), idxReg, &skip);
michael@0 7603 BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
michael@0 7604 StoreToTypedArray(masm, arrayType, value, dest);
michael@0 7605 }
michael@0 7606 if (guardLength)
michael@0 7607 masm.bind(&skip);
michael@0 7608
michael@0 7609 return true;
michael@0 7610 }
michael@0 7611
michael@0 7612 bool
michael@0 7613 CodeGenerator::visitClampIToUint8(LClampIToUint8 *lir)
michael@0 7614 {
michael@0 7615 Register output = ToRegister(lir->output());
michael@0 7616 JS_ASSERT(output == ToRegister(lir->input()));
michael@0 7617 masm.clampIntToUint8(output);
michael@0 7618 return true;
michael@0 7619 }
michael@0 7620
michael@0 7621 bool
michael@0 7622 CodeGenerator::visitClampDToUint8(LClampDToUint8 *lir)
michael@0 7623 {
michael@0 7624 FloatRegister input = ToFloatRegister(lir->input());
michael@0 7625 Register output = ToRegister(lir->output());
michael@0 7626 masm.clampDoubleToUint8(input, output);
michael@0 7627 return true;
michael@0 7628 }
michael@0 7629
michael@0 7630 bool
michael@0 7631 CodeGenerator::visitClampVToUint8(LClampVToUint8 *lir)
michael@0 7632 {
michael@0 7633 ValueOperand operand = ToValue(lir, LClampVToUint8::Input);
michael@0 7634 FloatRegister tempFloat = ToFloatRegister(lir->tempFloat());
michael@0 7635 Register output = ToRegister(lir->output());
michael@0 7636 MDefinition *input = lir->mir()->input();
michael@0 7637
michael@0 7638 Label *stringEntry, *stringRejoin;
michael@0 7639 if (input->mightBeType(MIRType_String)) {
michael@0 7640 OutOfLineCode *oolString = oolCallVM(StringToNumberInfo, lir, (ArgList(), output),
michael@0 7641 StoreFloatRegisterTo(tempFloat));
michael@0 7642 if (!oolString)
michael@0 7643 return false;
michael@0 7644 stringEntry = oolString->entry();
michael@0 7645 stringRejoin = oolString->rejoin();
michael@0 7646 } else {
michael@0 7647 stringEntry = nullptr;
michael@0 7648 stringRejoin = nullptr;
michael@0 7649 }
michael@0 7650
michael@0 7651 Label fails;
michael@0 7652 masm.clampValueToUint8(operand, input,
michael@0 7653 stringEntry, stringRejoin,
michael@0 7654 output, tempFloat, output, &fails);
michael@0 7655
michael@0 7656 if (!bailoutFrom(&fails, lir->snapshot()))
michael@0 7657 return false;
michael@0 7658
michael@0 7659 return true;
michael@0 7660 }
michael@0 7661
michael@0 7662 typedef bool (*OperatorInFn)(JSContext *, HandleValue, HandleObject, bool *);
michael@0 7663 static const VMFunction OperatorInInfo = FunctionInfo<OperatorInFn>(OperatorIn);
michael@0 7664
michael@0 7665 bool
michael@0 7666 CodeGenerator::visitIn(LIn *ins)
michael@0 7667 {
michael@0 7668 pushArg(ToRegister(ins->rhs()));
michael@0 7669 pushArg(ToValue(ins, LIn::LHS));
michael@0 7670
michael@0 7671 return callVM(OperatorInInfo, ins);
michael@0 7672 }
michael@0 7673
michael@0 7674 typedef bool (*OperatorInIFn)(JSContext *, uint32_t, HandleObject, bool *);
michael@0 7675 static const VMFunction OperatorInIInfo = FunctionInfo<OperatorInIFn>(OperatorInI);
michael@0 7676
michael@0 7677 bool
michael@0 7678 CodeGenerator::visitInArray(LInArray *lir)
michael@0 7679 {
michael@0 7680 const MInArray *mir = lir->mir();
michael@0 7681 Register elements = ToRegister(lir->elements());
michael@0 7682 Register initLength = ToRegister(lir->initLength());
michael@0 7683 Register output = ToRegister(lir->output());
michael@0 7684
michael@0 7685 // When the array is not packed we need to do a hole check in addition to the bounds check.
michael@0 7686 Label falseBranch, done, trueBranch;
michael@0 7687
michael@0 7688 OutOfLineCode *ool = nullptr;
michael@0 7689 Label* failedInitLength = &falseBranch;
michael@0 7690
michael@0 7691 if (lir->index()->isConstant()) {
michael@0 7692 int32_t index = ToInt32(lir->index());
michael@0 7693
michael@0 7694 JS_ASSERT_IF(index < 0, mir->needsNegativeIntCheck());
michael@0 7695 if (mir->needsNegativeIntCheck()) {
michael@0 7696 ool = oolCallVM(OperatorInIInfo, lir,
michael@0 7697 (ArgList(), Imm32(index), ToRegister(lir->object())),
michael@0 7698 StoreRegisterTo(output));
michael@0 7699 failedInitLength = ool->entry();
michael@0 7700 }
michael@0 7701
michael@0 7702 masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), failedInitLength);
michael@0 7703 if (mir->needsHoleCheck()) {
michael@0 7704 Address address = Address(elements, index * sizeof(Value));
michael@0 7705 masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
michael@0 7706 }
michael@0 7707 } else {
michael@0 7708 Label negativeIntCheck;
michael@0 7709 Register index = ToRegister(lir->index());
michael@0 7710
michael@0 7711 if (mir->needsNegativeIntCheck())
michael@0 7712 failedInitLength = &negativeIntCheck;
michael@0 7713
michael@0 7714 masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength);
michael@0 7715 if (mir->needsHoleCheck()) {
michael@0 7716 BaseIndex address = BaseIndex(elements, ToRegister(lir->index()), TimesEight);
michael@0 7717 masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
michael@0 7718 }
michael@0 7719 masm.jump(&trueBranch);
michael@0 7720
michael@0 7721 if (mir->needsNegativeIntCheck()) {
michael@0 7722 masm.bind(&negativeIntCheck);
michael@0 7723 ool = oolCallVM(OperatorInIInfo, lir,
michael@0 7724 (ArgList(), index, ToRegister(lir->object())),
michael@0 7725 StoreRegisterTo(output));
michael@0 7726
michael@0 7727 masm.branch32(Assembler::LessThan, index, Imm32(0), ool->entry());
michael@0 7728 masm.jump(&falseBranch);
michael@0 7729 }
michael@0 7730 }
michael@0 7731
michael@0 7732 masm.bind(&trueBranch);
michael@0 7733 masm.move32(Imm32(1), output);
michael@0 7734 masm.jump(&done);
michael@0 7735
michael@0 7736 masm.bind(&falseBranch);
michael@0 7737 masm.move32(Imm32(0), output);
michael@0 7738 masm.bind(&done);
michael@0 7739
michael@0 7740 if (ool)
michael@0 7741 masm.bind(ool->rejoin());
michael@0 7742
michael@0 7743 return true;
michael@0 7744 }
michael@0 7745
michael@0 7746 bool
michael@0 7747 CodeGenerator::visitInstanceOfO(LInstanceOfO *ins)
michael@0 7748 {
michael@0 7749 return emitInstanceOf(ins, ins->mir()->prototypeObject());
michael@0 7750 }
michael@0 7751
michael@0 7752 bool
michael@0 7753 CodeGenerator::visitInstanceOfV(LInstanceOfV *ins)
michael@0 7754 {
michael@0 7755 return emitInstanceOf(ins, ins->mir()->prototypeObject());
michael@0 7756 }
michael@0 7757
michael@0 7758 // Wrap IsDelegateOfObject, which takes a JSObject*, not a HandleObject
michael@0 7759 static bool
michael@0 7760 IsDelegateObject(JSContext *cx, HandleObject protoObj, HandleObject obj, bool *res)
michael@0 7761 {
michael@0 7762 return IsDelegateOfObject(cx, protoObj, obj, res);
michael@0 7763 }
michael@0 7764
michael@0 7765 typedef bool (*IsDelegateObjectFn)(JSContext *, HandleObject, HandleObject, bool *);
michael@0 7766 static const VMFunction IsDelegateObjectInfo = FunctionInfo<IsDelegateObjectFn>(IsDelegateObject);
michael@0 7767
michael@0 7768 bool
michael@0 7769 CodeGenerator::emitInstanceOf(LInstruction *ins, JSObject *prototypeObject)
michael@0 7770 {
michael@0 7771 // This path implements fun_hasInstance when the function's prototype is
michael@0 7772 // known to be prototypeObject.
michael@0 7773
michael@0 7774 Label done;
michael@0 7775 Register output = ToRegister(ins->getDef(0));
michael@0 7776
michael@0 7777 // If the lhs is a primitive, the result is false.
michael@0 7778 Register objReg;
michael@0 7779 if (ins->isInstanceOfV()) {
michael@0 7780 Label isObject;
michael@0 7781 ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LHS);
michael@0 7782 masm.branchTestObject(Assembler::Equal, lhsValue, &isObject);
michael@0 7783 masm.mov(ImmWord(0), output);
michael@0 7784 masm.jump(&done);
michael@0 7785 masm.bind(&isObject);
michael@0 7786 objReg = masm.extractObject(lhsValue, output);
michael@0 7787 } else {
michael@0 7788 objReg = ToRegister(ins->toInstanceOfO()->lhs());
michael@0 7789 }
michael@0 7790
michael@0 7791 // Crawl the lhs's prototype chain in a loop to search for prototypeObject.
michael@0 7792 // This follows the main loop of js::IsDelegate, though additionally breaks
michael@0 7793 // out of the loop on Proxy::LazyProto.
michael@0 7794
michael@0 7795 // Load the lhs's prototype.
michael@0 7796 masm.loadObjProto(objReg, output);
michael@0 7797
michael@0 7798 Label testLazy;
michael@0 7799 {
michael@0 7800 Label loopPrototypeChain;
michael@0 7801 masm.bind(&loopPrototypeChain);
michael@0 7802
michael@0 7803 // Test for the target prototype object.
michael@0 7804 Label notPrototypeObject;
michael@0 7805 masm.branchPtr(Assembler::NotEqual, output, ImmGCPtr(prototypeObject), &notPrototypeObject);
michael@0 7806 masm.mov(ImmWord(1), output);
michael@0 7807 masm.jump(&done);
michael@0 7808 masm.bind(&notPrototypeObject);
michael@0 7809
michael@0 7810 JS_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1);
michael@0 7811
michael@0 7812 // Test for nullptr or Proxy::LazyProto
michael@0 7813 masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy);
michael@0 7814
michael@0 7815 // Load the current object's prototype.
michael@0 7816 masm.loadObjProto(output, output);
michael@0 7817
michael@0 7818 masm.jump(&loopPrototypeChain);
michael@0 7819 }
michael@0 7820
michael@0 7821 // Make a VM call if an object with a lazy proto was found on the prototype
michael@0 7822 // chain. This currently occurs only for cross compartment wrappers, which
michael@0 7823 // we do not expect to be compared with non-wrapper functions from this
michael@0 7824 // compartment. Otherwise, we stopped on a nullptr prototype and the output
michael@0 7825 // register is already correct.
michael@0 7826
michael@0 7827 OutOfLineCode *ool = oolCallVM(IsDelegateObjectInfo, ins,
michael@0 7828 (ArgList(), ImmGCPtr(prototypeObject), objReg),
michael@0 7829 StoreRegisterTo(output));
michael@0 7830
michael@0 7831 // Regenerate the original lhs object for the VM call.
michael@0 7832 Label regenerate, *lazyEntry;
michael@0 7833 if (objReg != output) {
michael@0 7834 lazyEntry = ool->entry();
michael@0 7835 } else {
michael@0 7836 masm.bind(&regenerate);
michael@0 7837 lazyEntry = &regenerate;
michael@0 7838 if (ins->isInstanceOfV()) {
michael@0 7839 ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LHS);
michael@0 7840 objReg = masm.extractObject(lhsValue, output);
michael@0 7841 } else {
michael@0 7842 objReg = ToRegister(ins->toInstanceOfO()->lhs());
michael@0 7843 }
michael@0 7844 JS_ASSERT(objReg == output);
michael@0 7845 masm.jump(ool->entry());
michael@0 7846 }
michael@0 7847
michael@0 7848 masm.bind(&testLazy);
michael@0 7849 masm.branchPtr(Assembler::Equal, output, ImmWord(1), lazyEntry);
michael@0 7850
michael@0 7851 masm.bind(&done);
michael@0 7852 masm.bind(ool->rejoin());
michael@0 7853 return true;
michael@0 7854 }
michael@0 7855
michael@0 7856 typedef bool (*HasInstanceFn)(JSContext *, HandleObject, HandleValue, bool *);
michael@0 7857 static const VMFunction HasInstanceInfo = FunctionInfo<HasInstanceFn>(js::HasInstance);
michael@0 7858
michael@0 7859 bool
michael@0 7860 CodeGenerator::visitCallInstanceOf(LCallInstanceOf *ins)
michael@0 7861 {
michael@0 7862 ValueOperand lhs = ToValue(ins, LCallInstanceOf::LHS);
michael@0 7863 Register rhs = ToRegister(ins->rhs());
michael@0 7864 JS_ASSERT(ToRegister(ins->output()) == ReturnReg);
michael@0 7865
michael@0 7866 pushArg(lhs);
michael@0 7867 pushArg(rhs);
michael@0 7868 return callVM(HasInstanceInfo, ins);
michael@0 7869 }
michael@0 7870
michael@0 7871 bool
michael@0 7872 CodeGenerator::visitGetDOMProperty(LGetDOMProperty *ins)
michael@0 7873 {
michael@0 7874 const Register JSContextReg = ToRegister(ins->getJSContextReg());
michael@0 7875 const Register ObjectReg = ToRegister(ins->getObjectReg());
michael@0 7876 const Register PrivateReg = ToRegister(ins->getPrivReg());
michael@0 7877 const Register ValueReg = ToRegister(ins->getValueReg());
michael@0 7878
michael@0 7879 DebugOnly<uint32_t> initialStack = masm.framePushed();
michael@0 7880
michael@0 7881 masm.checkStackAlignment();
michael@0 7882
michael@0 7883 // Make space for the outparam. Pre-initialize it to UndefinedValue so we
michael@0 7884 // can trace it at GC time.
michael@0 7885 masm.Push(UndefinedValue());
michael@0 7886 // We pass the pointer to our out param as an instance of
michael@0 7887 // JSJitGetterCallArgs, since on the binary level it's the same thing.
michael@0 7888 JS_STATIC_ASSERT(sizeof(JSJitGetterCallArgs) == sizeof(Value*));
michael@0 7889 masm.movePtr(StackPointer, ValueReg);
michael@0 7890
michael@0 7891 masm.Push(ObjectReg);
michael@0 7892
michael@0 7893 // GetReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate()
michael@0 7894 masm.loadPrivate(Address(ObjectReg, JSObject::getFixedSlotOffset(0)), PrivateReg);
michael@0 7895
michael@0 7896 // Rooting will happen at GC time.
michael@0 7897 masm.movePtr(StackPointer, ObjectReg);
michael@0 7898
michael@0 7899 uint32_t safepointOffset;
michael@0 7900 if (!masm.buildFakeExitFrame(JSContextReg, &safepointOffset))
michael@0 7901 return false;
michael@0 7902 masm.enterFakeExitFrame(ION_FRAME_DOMGETTER);
michael@0 7903
michael@0 7904 if (!markSafepointAt(safepointOffset, ins))
michael@0 7905 return false;
michael@0 7906
michael@0 7907 masm.setupUnalignedABICall(4, JSContextReg);
michael@0 7908
michael@0 7909 masm.loadJSContext(JSContextReg);
michael@0 7910
michael@0 7911 masm.passABIArg(JSContextReg);
michael@0 7912 masm.passABIArg(ObjectReg);
michael@0 7913 masm.passABIArg(PrivateReg);
michael@0 7914 masm.passABIArg(ValueReg);
michael@0 7915 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ins->mir()->fun()));
michael@0 7916
michael@0 7917 if (ins->mir()->isInfallible()) {
michael@0 7918 masm.loadValue(Address(StackPointer, IonDOMExitFrameLayout::offsetOfResult()),
michael@0 7919 JSReturnOperand);
michael@0 7920 } else {
michael@0 7921 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
michael@0 7922
michael@0 7923 masm.loadValue(Address(StackPointer, IonDOMExitFrameLayout::offsetOfResult()),
michael@0 7924 JSReturnOperand);
michael@0 7925 }
michael@0 7926 masm.adjustStack(IonDOMExitFrameLayout::Size());
michael@0 7927
michael@0 7928 JS_ASSERT(masm.framePushed() == initialStack);
michael@0 7929 return true;
michael@0 7930 }
michael@0 7931
michael@0 7932 bool
michael@0 7933 CodeGenerator::visitGetDOMMember(LGetDOMMember *ins)
michael@0 7934 {
michael@0 7935 // It's simple to duplicate visitLoadFixedSlotV here than it is to try to
michael@0 7936 // use an LLoadFixedSlotV or some subclass of it for this case: that would
michael@0 7937 // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then
michael@0 7938 // we'd have to duplicate a bunch of stuff we now get for free from
michael@0 7939 // MGetDOMProperty.
michael@0 7940 Register object = ToRegister(ins->object());
michael@0 7941 size_t slot = ins->mir()->domMemberSlotIndex();
michael@0 7942 ValueOperand result = GetValueOutput(ins);
michael@0 7943
michael@0 7944 masm.loadValue(Address(object, JSObject::getFixedSlotOffset(slot)), result);
michael@0 7945 return true;
michael@0 7946 }
michael@0 7947
michael@0 7948 bool
michael@0 7949 CodeGenerator::visitSetDOMProperty(LSetDOMProperty *ins)
michael@0 7950 {
michael@0 7951 const Register JSContextReg = ToRegister(ins->getJSContextReg());
michael@0 7952 const Register ObjectReg = ToRegister(ins->getObjectReg());
michael@0 7953 const Register PrivateReg = ToRegister(ins->getPrivReg());
michael@0 7954 const Register ValueReg = ToRegister(ins->getValueReg());
michael@0 7955
michael@0 7956 DebugOnly<uint32_t> initialStack = masm.framePushed();
michael@0 7957
michael@0 7958 masm.checkStackAlignment();
michael@0 7959
michael@0 7960 // Push the argument. Rooting will happen at GC time.
michael@0 7961 ValueOperand argVal = ToValue(ins, LSetDOMProperty::Value);
michael@0 7962 masm.Push(argVal);
michael@0 7963 // We pass the pointer to our out param as an instance of
michael@0 7964 // JSJitGetterCallArgs, since on the binary level it's the same thing.
michael@0 7965 JS_STATIC_ASSERT(sizeof(JSJitSetterCallArgs) == sizeof(Value*));
michael@0 7966 masm.movePtr(StackPointer, ValueReg);
michael@0 7967
michael@0 7968 masm.Push(ObjectReg);
michael@0 7969
michael@0 7970 // GetReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate()
michael@0 7971 masm.loadPrivate(Address(ObjectReg, JSObject::getFixedSlotOffset(0)), PrivateReg);
michael@0 7972
michael@0 7973 // Rooting will happen at GC time.
michael@0 7974 masm.movePtr(StackPointer, ObjectReg);
michael@0 7975
michael@0 7976 uint32_t safepointOffset;
michael@0 7977 if (!masm.buildFakeExitFrame(JSContextReg, &safepointOffset))
michael@0 7978 return false;
michael@0 7979 masm.enterFakeExitFrame(ION_FRAME_DOMSETTER);
michael@0 7980
michael@0 7981 if (!markSafepointAt(safepointOffset, ins))
michael@0 7982 return false;
michael@0 7983
michael@0 7984 masm.setupUnalignedABICall(4, JSContextReg);
michael@0 7985
michael@0 7986 masm.loadJSContext(JSContextReg);
michael@0 7987
michael@0 7988 masm.passABIArg(JSContextReg);
michael@0 7989 masm.passABIArg(ObjectReg);
michael@0 7990 masm.passABIArg(PrivateReg);
michael@0 7991 masm.passABIArg(ValueReg);
michael@0 7992 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ins->mir()->fun()));
michael@0 7993
michael@0 7994 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
michael@0 7995
michael@0 7996 masm.adjustStack(IonDOMExitFrameLayout::Size());
michael@0 7997
michael@0 7998 JS_ASSERT(masm.framePushed() == initialStack);
michael@0 7999 return true;
michael@0 8000 }
michael@0 8001
michael@0 8002 typedef bool(*SPSFn)(JSContext *, HandleScript);
michael@0 8003 static const VMFunction SPSEnterInfo = FunctionInfo<SPSFn>(SPSEnter);
michael@0 8004 static const VMFunction SPSExitInfo = FunctionInfo<SPSFn>(SPSExit);
michael@0 8005
michael@0 8006 bool
michael@0 8007 CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir)
michael@0 8008 {
michael@0 8009 Register temp = ToRegister(lir->temp()->output());
michael@0 8010 bool inlinedFunction = lir->inlineLevel() > 0;
michael@0 8011
michael@0 8012 switch (lir->type()) {
michael@0 8013 case MProfilerStackOp::InlineEnter:
michael@0 8014 // Multiple scripts can be inlined at one depth, but there is only
michael@0 8015 // one InlineExit node to signify this. To deal with this, if we
michael@0 8016 // reach the entry of another inline script on the same level, then
michael@0 8017 // just reset the sps metadata about the frame. We must balance
michael@0 8018 // calls to leave()/reenter(), so perform the balance without
michael@0 8019 // emitting any instrumentation. Technically the previous inline
michael@0 8020 // call at this same depth has reentered, but the instrumentation
michael@0 8021 // will be emitted at the common join point for all inlines at the
michael@0 8022 // same depth.
michael@0 8023 if (sps_.inliningDepth() == lir->inlineLevel()) {
michael@0 8024 sps_.leaveInlineFrame();
michael@0 8025 sps_.skipNextReenter();
michael@0 8026 sps_.reenter(masm, temp);
michael@0 8027 }
michael@0 8028
michael@0 8029 sps_.leave(masm, temp, /* inlinedFunction = */ true);
michael@0 8030 if (!sps_.enterInlineFrame())
michael@0 8031 return false;
michael@0 8032 // fallthrough
michael@0 8033
michael@0 8034 case MProfilerStackOp::Enter:
michael@0 8035 if (gen->options.spsSlowAssertionsEnabled()) {
michael@0 8036 if (!inlinedFunction || js_JitOptions.profileInlineFrames) {
michael@0 8037 saveLive(lir);
michael@0 8038 pushArg(ImmGCPtr(lir->script()));
michael@0 8039 if (!callVM(SPSEnterInfo, lir))
michael@0 8040 return false;
michael@0 8041 restoreLive(lir);
michael@0 8042 }
michael@0 8043 sps_.pushManual(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction);
michael@0 8044 return true;
michael@0 8045 }
michael@0 8046
michael@0 8047 return sps_.push(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction);
michael@0 8048
michael@0 8049 case MProfilerStackOp::InlineExit:
michael@0 8050 // all inline returns were covered with ::Exit, so we just need to
michael@0 8051 // maintain the state of inline frames currently active and then
michael@0 8052 // reenter the caller
michael@0 8053 sps_.leaveInlineFrame();
michael@0 8054 sps_.reenter(masm, temp, /* inlinedFunction = */ true);
michael@0 8055 return true;
michael@0 8056
michael@0 8057 case MProfilerStackOp::Exit:
michael@0 8058 if (gen->options.spsSlowAssertionsEnabled()) {
michael@0 8059 if (!inlinedFunction || js_JitOptions.profileInlineFrames) {
michael@0 8060 saveLive(lir);
michael@0 8061 pushArg(ImmGCPtr(lir->script()));
michael@0 8062 // Once we've exited, then we shouldn't emit instrumentation for
michael@0 8063 // the corresponding reenter() because we no longer have a
michael@0 8064 // frame.
michael@0 8065 sps_.skipNextReenter();
michael@0 8066 if (!callVM(SPSExitInfo, lir))
michael@0 8067 return false;
michael@0 8068 restoreLive(lir);
michael@0 8069 }
michael@0 8070 return true;
michael@0 8071 }
michael@0 8072
michael@0 8073 sps_.pop(masm, temp, /* inlinedFunction = */ inlinedFunction);
michael@0 8074 return true;
michael@0 8075
michael@0 8076 default:
michael@0 8077 MOZ_ASSUME_UNREACHABLE("invalid LProfilerStackOp type");
michael@0 8078 }
michael@0 8079 }
michael@0 8080
michael@0 8081 bool
michael@0 8082 CodeGenerator::visitOutOfLineAbortPar(OutOfLineAbortPar *ool)
michael@0 8083 {
michael@0 8084 ParallelBailoutCause cause = ool->cause();
michael@0 8085 jsbytecode *bytecode = ool->bytecode();
michael@0 8086
michael@0 8087 masm.move32(Imm32(cause), CallTempReg0);
michael@0 8088 loadOutermostJSScript(CallTempReg1);
michael@0 8089 loadJSScriptForBlock(ool->basicBlock(), CallTempReg2);
michael@0 8090 masm.movePtr(ImmPtr(bytecode), CallTempReg3);
michael@0 8091
michael@0 8092 masm.setupUnalignedABICall(4, CallTempReg4);
michael@0 8093 masm.passABIArg(CallTempReg0);
michael@0 8094 masm.passABIArg(CallTempReg1);
michael@0 8095 masm.passABIArg(CallTempReg2);
michael@0 8096 masm.passABIArg(CallTempReg3);
michael@0 8097 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, AbortPar));
michael@0 8098
michael@0 8099 masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
michael@0 8100 masm.jump(&returnLabel_);
michael@0 8101 return true;
michael@0 8102 }
michael@0 8103
michael@0 8104 bool
michael@0 8105 CodeGenerator::visitIsCallable(LIsCallable *ins)
michael@0 8106 {
michael@0 8107 Register object = ToRegister(ins->object());
michael@0 8108 Register output = ToRegister(ins->output());
michael@0 8109
michael@0 8110 masm.loadObjClass(object, output);
michael@0 8111
michael@0 8112 // An object is callable iff (is<JSFunction>() || getClass()->call).
michael@0 8113 Label notFunction, done, notCall;
michael@0 8114 masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), &notFunction);
michael@0 8115 masm.move32(Imm32(1), output);
michael@0 8116 masm.jump(&done);
michael@0 8117
michael@0 8118 masm.bind(&notFunction);
michael@0 8119 masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::Class, call)), ImmPtr(nullptr), output);
michael@0 8120 masm.bind(&done);
michael@0 8121
michael@0 8122 return true;
michael@0 8123 }
michael@0 8124
michael@0 8125 void
michael@0 8126 CodeGenerator::loadOutermostJSScript(Register reg)
michael@0 8127 {
michael@0 8128 // The "outermost" JSScript means the script that we are compiling
michael@0 8129 // basically; this is not always the script associated with the
michael@0 8130 // current basic block, which might be an inlined script.
michael@0 8131
michael@0 8132 MIRGraph &graph = current->mir()->graph();
michael@0 8133 MBasicBlock *entryBlock = graph.entryBlock();
michael@0 8134 masm.movePtr(ImmGCPtr(entryBlock->info().script()), reg);
michael@0 8135 }
michael@0 8136
michael@0 8137 void
michael@0 8138 CodeGenerator::loadJSScriptForBlock(MBasicBlock *block, Register reg)
michael@0 8139 {
michael@0 8140 // The current JSScript means the script for the current
michael@0 8141 // basic block. This may be an inlined script.
michael@0 8142
michael@0 8143 JSScript *script = block->info().script();
michael@0 8144 masm.movePtr(ImmGCPtr(script), reg);
michael@0 8145 }
michael@0 8146
michael@0 8147 bool
michael@0 8148 CodeGenerator::visitOutOfLinePropagateAbortPar(OutOfLinePropagateAbortPar *ool)
michael@0 8149 {
michael@0 8150 loadOutermostJSScript(CallTempReg0);
michael@0 8151 loadJSScriptForBlock(ool->lir()->mirRaw()->block(), CallTempReg1);
michael@0 8152
michael@0 8153 masm.setupUnalignedABICall(2, CallTempReg2);
michael@0 8154 masm.passABIArg(CallTempReg0);
michael@0 8155 masm.passABIArg(CallTempReg1);
michael@0 8156 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, PropagateAbortPar));
michael@0 8157
michael@0 8158 masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
michael@0 8159 masm.jump(&returnLabel_);
michael@0 8160 return true;
michael@0 8161 }
michael@0 8162
michael@0 8163 bool
michael@0 8164 CodeGenerator::visitHaveSameClass(LHaveSameClass *ins)
michael@0 8165 {
michael@0 8166 Register lhs = ToRegister(ins->lhs());
michael@0 8167 Register rhs = ToRegister(ins->rhs());
michael@0 8168 Register temp = ToRegister(ins->getTemp(0));
michael@0 8169 Register output = ToRegister(ins->output());
michael@0 8170
michael@0 8171 masm.loadObjClass(lhs, temp);
michael@0 8172 masm.loadObjClass(rhs, output);
michael@0 8173 masm.cmpPtrSet(Assembler::Equal, temp, output, output);
michael@0 8174
michael@0 8175 return true;
michael@0 8176 }
michael@0 8177
michael@0 8178 bool
michael@0 8179 CodeGenerator::visitHasClass(LHasClass *ins)
michael@0 8180 {
michael@0 8181 Register lhs = ToRegister(ins->lhs());
michael@0 8182 Register output = ToRegister(ins->output());
michael@0 8183
michael@0 8184 masm.loadObjClass(lhs, output);
michael@0 8185 masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()), output);
michael@0 8186
michael@0 8187 return true;
michael@0 8188 }
michael@0 8189
michael@0 8190 bool
michael@0 8191 CodeGenerator::visitAsmJSCall(LAsmJSCall *ins)
michael@0 8192 {
michael@0 8193 MAsmJSCall *mir = ins->mir();
michael@0 8194
michael@0 8195 #if defined(JS_CODEGEN_ARM)
michael@0 8196 if (!useHardFpABI() && mir->callee().which() == MAsmJSCall::Callee::Builtin) {
michael@0 8197 for (unsigned i = 0, e = ins->numOperands(); i < e; i++) {
michael@0 8198 LAllocation *a = ins->getOperand(i);
michael@0 8199 if (a->isFloatReg()) {
michael@0 8200 FloatRegister fr = ToFloatRegister(a);
michael@0 8201 int srcId = fr.code() * 2;
michael@0 8202 masm.ma_vxfer(fr, Register::FromCode(srcId), Register::FromCode(srcId+1));
michael@0 8203 }
michael@0 8204 }
michael@0 8205 }
michael@0 8206 #endif
michael@0 8207
michael@0 8208 if (mir->spIncrement())
michael@0 8209 masm.freeStack(mir->spIncrement());
michael@0 8210
michael@0 8211 JS_ASSERT((AlignmentAtPrologue + masm.framePushed()) % StackAlignment == 0);
michael@0 8212
michael@0 8213 #ifdef DEBUG
michael@0 8214 Label ok;
michael@0 8215 JS_ASSERT(IsPowerOfTwo(StackAlignment));
michael@0 8216 masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok);
michael@0 8217 masm.assumeUnreachable("Stack should be aligned.");
michael@0 8218 masm.bind(&ok);
michael@0 8219 #endif
michael@0 8220
michael@0 8221 MAsmJSCall::Callee callee = mir->callee();
michael@0 8222 switch (callee.which()) {
michael@0 8223 case MAsmJSCall::Callee::Internal:
michael@0 8224 masm.call(mir->desc(), callee.internal());
michael@0 8225 break;
michael@0 8226 case MAsmJSCall::Callee::Dynamic:
michael@0 8227 masm.call(mir->desc(), ToRegister(ins->getOperand(mir->dynamicCalleeOperandIndex())));
michael@0 8228 break;
michael@0 8229 case MAsmJSCall::Callee::Builtin:
michael@0 8230 masm.call(mir->desc(), callee.builtin());
michael@0 8231 break;
michael@0 8232 }
michael@0 8233
michael@0 8234 if (mir->spIncrement())
michael@0 8235 masm.reserveStack(mir->spIncrement());
michael@0 8236
michael@0 8237 postAsmJSCall(ins);
michael@0 8238 return true;
michael@0 8239 }
michael@0 8240
michael@0 8241 bool
michael@0 8242 CodeGenerator::visitAsmJSParameter(LAsmJSParameter *lir)
michael@0 8243 {
michael@0 8244 return true;
michael@0 8245 }
michael@0 8246
michael@0 8247 bool
michael@0 8248 CodeGenerator::visitAsmJSReturn(LAsmJSReturn *lir)
michael@0 8249 {
michael@0 8250 // Don't emit a jump to the return label if this is the last block.
michael@0 8251 if (current->mir() != *gen->graph().poBegin())
michael@0 8252 masm.jump(&returnLabel_);
michael@0 8253 return true;
michael@0 8254 }
michael@0 8255
michael@0 8256 bool
michael@0 8257 CodeGenerator::visitAsmJSVoidReturn(LAsmJSVoidReturn *lir)
michael@0 8258 {
michael@0 8259 // Don't emit a jump to the return label if this is the last block.
michael@0 8260 if (current->mir() != *gen->graph().poBegin())
michael@0 8261 masm.jump(&returnLabel_);
michael@0 8262 return true;
michael@0 8263 }
michael@0 8264
michael@0 8265 bool
michael@0 8266 CodeGenerator::emitAssertRangeI(const Range *r, Register input)
michael@0 8267 {
michael@0 8268 // Check the lower bound.
michael@0 8269 if (r->hasInt32LowerBound() && r->lower() > INT32_MIN) {
michael@0 8270 Label success;
michael@0 8271 masm.branch32(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()), &success);
michael@0 8272 masm.assumeUnreachable("Integer input should be equal or higher than Lowerbound.");
michael@0 8273 masm.bind(&success);
michael@0 8274 }
michael@0 8275
michael@0 8276 // Check the upper bound.
michael@0 8277 if (r->hasInt32UpperBound() && r->upper() < INT32_MAX) {
michael@0 8278 Label success;
michael@0 8279 masm.branch32(Assembler::LessThanOrEqual, input, Imm32(r->upper()), &success);
michael@0 8280 masm.assumeUnreachable("Integer input should be lower or equal than Upperbound.");
michael@0 8281 masm.bind(&success);
michael@0 8282 }
michael@0 8283
michael@0 8284 // For r->canHaveFractionalPart() and r->exponent(), there's nothing to check, because
michael@0 8285 // if we ended up in the integer range checking code, the value is already
michael@0 8286 // in an integer register in the integer range.
michael@0 8287
michael@0 8288 return true;
michael@0 8289 }
michael@0 8290
michael@0 8291 bool
michael@0 8292 CodeGenerator::emitAssertRangeD(const Range *r, FloatRegister input, FloatRegister temp)
michael@0 8293 {
michael@0 8294 // Check the lower bound.
michael@0 8295 if (r->hasInt32LowerBound()) {
michael@0 8296 Label success;
michael@0 8297 masm.loadConstantDouble(r->lower(), temp);
michael@0 8298 if (r->canBeNaN())
michael@0 8299 masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
michael@0 8300 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, &success);
michael@0 8301 masm.assumeUnreachable("Double input should be equal or higher than Lowerbound.");
michael@0 8302 masm.bind(&success);
michael@0 8303 }
michael@0 8304 // Check the upper bound.
michael@0 8305 if (r->hasInt32UpperBound()) {
michael@0 8306 Label success;
michael@0 8307 masm.loadConstantDouble(r->upper(), temp);
michael@0 8308 if (r->canBeNaN())
michael@0 8309 masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
michael@0 8310 masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &success);
michael@0 8311 masm.assumeUnreachable("Double input should be lower or equal than Upperbound.");
michael@0 8312 masm.bind(&success);
michael@0 8313 }
michael@0 8314
michael@0 8315 // This code does not yet check r->canHaveFractionalPart(). This would require new
michael@0 8316 // assembler interfaces to make rounding instructions available.
michael@0 8317
michael@0 8318 if (!r->hasInt32Bounds() && !r->canBeInfiniteOrNaN() &&
michael@0 8319 r->exponent() < FloatingPoint<double>::ExponentBias)
michael@0 8320 {
michael@0 8321 // Check the bounds implied by the maximum exponent.
michael@0 8322 Label exponentLoOk;
michael@0 8323 masm.loadConstantDouble(pow(2.0, r->exponent() + 1), temp);
michael@0 8324 masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentLoOk);
michael@0 8325 masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &exponentLoOk);
michael@0 8326 masm.assumeUnreachable("Check for exponent failed.");
michael@0 8327 masm.bind(&exponentLoOk);
michael@0 8328
michael@0 8329 Label exponentHiOk;
michael@0 8330 masm.loadConstantDouble(-pow(2.0, r->exponent() + 1), temp);
michael@0 8331 masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentHiOk);
michael@0 8332 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, &exponentHiOk);
michael@0 8333 masm.assumeUnreachable("Check for exponent failed.");
michael@0 8334 masm.bind(&exponentHiOk);
michael@0 8335 } else if (!r->hasInt32Bounds() && !r->canBeNaN()) {
michael@0 8336 // If we think the value can't be NaN, check that it isn't.
michael@0 8337 Label notnan;
michael@0 8338 masm.branchDouble(Assembler::DoubleOrdered, input, input, &notnan);
michael@0 8339 masm.assumeUnreachable("Input shouldn't be NaN.");
michael@0 8340 masm.bind(&notnan);
michael@0 8341
michael@0 8342 // If we think the value also can't be an infinity, check that it isn't.
michael@0 8343 if (!r->canBeInfiniteOrNaN()) {
michael@0 8344 Label notposinf;
michael@0 8345 masm.loadConstantDouble(PositiveInfinity<double>(), temp);
michael@0 8346 masm.branchDouble(Assembler::DoubleLessThan, input, temp, &notposinf);
michael@0 8347 masm.assumeUnreachable("Input shouldn't be +Inf.");
michael@0 8348 masm.bind(&notposinf);
michael@0 8349
michael@0 8350 Label notneginf;
michael@0 8351 masm.loadConstantDouble(NegativeInfinity<double>(), temp);
michael@0 8352 masm.branchDouble(Assembler::DoubleGreaterThan, input, temp, &notneginf);
michael@0 8353 masm.assumeUnreachable("Input shouldn't be -Inf.");
michael@0 8354 masm.bind(&notneginf);
michael@0 8355 }
michael@0 8356 }
michael@0 8357
michael@0 8358 return true;
michael@0 8359 }
michael@0 8360
michael@0 8361 bool
michael@0 8362 CodeGenerator::visitAssertRangeI(LAssertRangeI *ins)
michael@0 8363 {
michael@0 8364 Register input = ToRegister(ins->input());
michael@0 8365 const Range *r = ins->range();
michael@0 8366
michael@0 8367 return emitAssertRangeI(r, input);
michael@0 8368 }
michael@0 8369
michael@0 8370 bool
michael@0 8371 CodeGenerator::visitAssertRangeD(LAssertRangeD *ins)
michael@0 8372 {
michael@0 8373 FloatRegister input = ToFloatRegister(ins->input());
michael@0 8374 FloatRegister temp = ToFloatRegister(ins->temp());
michael@0 8375 const Range *r = ins->range();
michael@0 8376
michael@0 8377 return emitAssertRangeD(r, input, temp);
michael@0 8378 }
michael@0 8379
michael@0 8380 bool
michael@0 8381 CodeGenerator::visitAssertRangeF(LAssertRangeF *ins)
michael@0 8382 {
michael@0 8383 FloatRegister input = ToFloatRegister(ins->input());
michael@0 8384 FloatRegister temp = ToFloatRegister(ins->temp());
michael@0 8385 const Range *r = ins->range();
michael@0 8386
michael@0 8387 masm.convertFloat32ToDouble(input, input);
michael@0 8388 bool success = emitAssertRangeD(r, input, temp);
michael@0 8389 masm.convertDoubleToFloat32(input, input);
michael@0 8390 return success;
michael@0 8391 }
michael@0 8392
michael@0 8393 bool
michael@0 8394 CodeGenerator::visitAssertRangeV(LAssertRangeV *ins)
michael@0 8395 {
michael@0 8396 const Range *r = ins->range();
michael@0 8397 const ValueOperand value = ToValue(ins, LAssertRangeV::Input);
michael@0 8398 Register tag = masm.splitTagForTest(value);
michael@0 8399 Label done;
michael@0 8400
michael@0 8401 {
michael@0 8402 Label isNotInt32;
michael@0 8403 masm.branchTestInt32(Assembler::NotEqual, tag, &isNotInt32);
michael@0 8404 Register unboxInt32 = ToTempUnboxRegister(ins->temp());
michael@0 8405 Register input = masm.extractInt32(value, unboxInt32);
michael@0 8406 emitAssertRangeI(r, input);
michael@0 8407 masm.jump(&done);
michael@0 8408 masm.bind(&isNotInt32);
michael@0 8409 }
michael@0 8410
michael@0 8411 {
michael@0 8412 Label isNotDouble;
michael@0 8413 masm.branchTestDouble(Assembler::NotEqual, tag, &isNotDouble);
michael@0 8414 FloatRegister input = ToFloatRegister(ins->floatTemp1());
michael@0 8415 FloatRegister temp = ToFloatRegister(ins->floatTemp2());
michael@0 8416 masm.unboxDouble(value, input);
michael@0 8417 emitAssertRangeD(r, input, temp);
michael@0 8418 masm.jump(&done);
michael@0 8419 masm.bind(&isNotDouble);
michael@0 8420 }
michael@0 8421
michael@0 8422 masm.assumeUnreachable("Incorrect range for Value.");
michael@0 8423 masm.bind(&done);
michael@0 8424 return true;
michael@0 8425 }
michael@0 8426
michael@0 8427 typedef bool (*RecompileFn)(JSContext *);
michael@0 8428 static const VMFunction RecompileFnInfo = FunctionInfo<RecompileFn>(Recompile);
michael@0 8429
michael@0 8430 bool
michael@0 8431 CodeGenerator::visitRecompileCheck(LRecompileCheck *ins)
michael@0 8432 {
michael@0 8433 Label done;
michael@0 8434 Register tmp = ToRegister(ins->scratch());
michael@0 8435 OutOfLineCode *ool = oolCallVM(RecompileFnInfo, ins, (ArgList()), StoreRegisterTo(tmp));
michael@0 8436
michael@0 8437 // Check if usecount is high enough.
michael@0 8438 masm.movePtr(ImmPtr(ins->mir()->script()->addressOfUseCount()), tmp);
michael@0 8439 Address ptr(tmp, 0);
michael@0 8440 masm.add32(Imm32(1), tmp);
michael@0 8441 masm.branch32(Assembler::BelowOrEqual, ptr, Imm32(ins->mir()->useCount()), &done);
michael@0 8442
michael@0 8443 // Check if not yet recompiling.
michael@0 8444 CodeOffsetLabel label = masm.movWithPatch(ImmWord(uintptr_t(-1)), tmp);
michael@0 8445 if (!ionScriptLabels_.append(label))
michael@0 8446 return false;
michael@0 8447 masm.branch32(Assembler::Equal,
michael@0 8448 Address(tmp, IonScript::offsetOfRecompiling()),
michael@0 8449 Imm32(0),
michael@0 8450 ool->entry());
michael@0 8451 masm.bind(ool->rejoin());
michael@0 8452 masm.bind(&done);
michael@0 8453
michael@0 8454 return true;
michael@0 8455 }
michael@0 8456
michael@0 8457 } // namespace jit
michael@0 8458 } // namespace js

mercurial