diff -r 000000000000 -r 6474c204b198 js/src/jit/CodeGenerator.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/js/src/jit/CodeGenerator.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,8458 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/CodeGenerator.h" + +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/DebugOnly.h" + +#include "jslibmath.h" +#include "jsmath.h" +#include "jsnum.h" +#include "jsprf.h" + +#include "builtin/Eval.h" +#include "builtin/TypedObject.h" +#ifdef JSGC_GENERATIONAL +# include "gc/Nursery.h" +#endif +#include "jit/IonCaches.h" +#include "jit/IonLinker.h" +#include "jit/IonOptimizationLevels.h" +#include "jit/IonSpewer.h" +#include "jit/MIRGenerator.h" +#include "jit/MoveEmitter.h" +#include "jit/ParallelFunctions.h" +#include "jit/ParallelSafetyAnalysis.h" +#include "jit/RangeAnalysis.h" +#include "vm/ForkJoin.h" +#include "vm/TraceLogging.h" + +#include "jsboolinlines.h" + +#include "jit/ExecutionMode-inl.h" +#include "jit/shared/CodeGenerator-shared-inl.h" +#include "vm/Interpreter-inl.h" + +using namespace js; +using namespace js::jit; + +using mozilla::DebugOnly; +using mozilla::FloatingPoint; +using mozilla::Maybe; +using mozilla::NegativeInfinity; +using mozilla::PositiveInfinity; +using JS::GenericNaN; + +namespace js { +namespace jit { + +// This out-of-line cache is used to do a double dispatch including it-self and +// the wrapped IonCache. +class OutOfLineUpdateCache : + public OutOfLineCodeBase, + public IonCacheVisitor +{ + private: + LInstruction *lir_; + size_t cacheIndex_; + AddCacheState state_; + + public: + OutOfLineUpdateCache(LInstruction *lir, size_t cacheIndex) + : lir_(lir), + cacheIndex_(cacheIndex) + { } + + void bind(MacroAssembler *masm) { + // The binding of the initial jump is done in + // CodeGenerator::visitOutOfLineCache. + } + + size_t getCacheIndex() const { + return cacheIndex_; + } + LInstruction *lir() const { + return lir_; + } + AddCacheState &state() { + return state_; + } + + bool accept(CodeGenerator *codegen) { + return codegen->visitOutOfLineCache(this); + } + + // ICs' visit functions delegating the work to the CodeGen visit funtions. +#define VISIT_CACHE_FUNCTION(op) \ + bool visit##op##IC(CodeGenerator *codegen) { \ + CodeGenerator::DataPtr ic(codegen, getCacheIndex()); \ + return codegen->visit##op##IC(this, ic); \ + } + + IONCACHE_KIND_LIST(VISIT_CACHE_FUNCTION) +#undef VISIT_CACHE_FUNCTION +}; + +// This function is declared here because it needs to instantiate an +// OutOfLineUpdateCache, but we want to keep it visible inside the +// CodeGeneratorShared such as we can specialize inline caches in function of +// the architecture. +bool +CodeGeneratorShared::addCache(LInstruction *lir, size_t cacheIndex) +{ + if (cacheIndex == SIZE_MAX) + return false; + + DataPtr cache(this, cacheIndex); + MInstruction *mir = lir->mirRaw()->toInstruction(); + if (mir->resumePoint()) + cache->setScriptedLocation(mir->block()->info().script(), + mir->resumePoint()->pc()); + else + cache->setIdempotent(); + + OutOfLineUpdateCache *ool = new(alloc()) OutOfLineUpdateCache(lir, cacheIndex); + if (!addOutOfLineCode(ool)) + return false; + + // OOL-specific state depends on the type of cache. + cache->initializeAddCacheState(lir, &ool->state()); + + cache->emitInitialJump(masm, ool->state()); + masm.bind(ool->rejoin()); + + return true; +} + +bool +CodeGenerator::visitOutOfLineCache(OutOfLineUpdateCache *ool) +{ + DataPtr cache(this, ool->getCacheIndex()); + + // Register the location of the OOL path in the IC. + cache->setFallbackLabel(masm.labelForPatch()); + cache->bindInitialJump(masm, ool->state()); + + // Dispatch to ICs' accept functions. + return cache->accept(this, ool); +} + +StringObject * +MNewStringObject::templateObj() const { + return &templateObj_->as(); +} + +CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm) + : CodeGeneratorSpecific(gen, graph, masm) + , ionScriptLabels_(gen->alloc()) +{ +} + +CodeGenerator::~CodeGenerator() +{ + JS_ASSERT_IF(!gen->compilingAsmJS(), masm.numAsmJSAbsoluteLinks() == 0); +} + +typedef bool (*StringToNumberFn)(ThreadSafeContext *, JSString *, double *); +typedef bool (*StringToNumberParFn)(ForkJoinContext *, JSString *, double *); +static const VMFunctionsModal StringToNumberInfo = VMFunctionsModal( + FunctionInfo(StringToNumber), + FunctionInfo(StringToNumberPar)); + +bool +CodeGenerator::visitValueToInt32(LValueToInt32 *lir) +{ + ValueOperand operand = ToValue(lir, LValueToInt32::Input); + Register output = ToRegister(lir->output()); + FloatRegister temp = ToFloatRegister(lir->tempFloat()); + + MDefinition *input; + if (lir->mode() == LValueToInt32::NORMAL) + input = lir->mirNormal()->input(); + else + input = lir->mirTruncate()->input(); + + Label fails; + if (lir->mode() == LValueToInt32::TRUNCATE) { + OutOfLineCode *oolDouble = oolTruncateDouble(temp, output); + if (!oolDouble) + return false; + + // We can only handle strings in truncation contexts, like bitwise + // operations. + Label *stringEntry, *stringRejoin; + Register stringReg; + if (input->mightBeType(MIRType_String)) { + stringReg = ToRegister(lir->temp()); + OutOfLineCode *oolString = oolCallVM(StringToNumberInfo, lir, (ArgList(), stringReg), + StoreFloatRegisterTo(temp)); + if (!oolString) + return false; + stringEntry = oolString->entry(); + stringRejoin = oolString->rejoin(); + } else { + stringReg = InvalidReg; + stringEntry = nullptr; + stringRejoin = nullptr; + } + + masm.truncateValueToInt32(operand, input, stringEntry, stringRejoin, oolDouble->entry(), + stringReg, temp, output, &fails); + masm.bind(oolDouble->rejoin()); + } else { + masm.convertValueToInt32(operand, input, temp, output, &fails, + lir->mirNormal()->canBeNegativeZero(), + lir->mirNormal()->conversion()); + } + + return bailoutFrom(&fails, lir->snapshot()); +} + +bool +CodeGenerator::visitValueToDouble(LValueToDouble *lir) +{ + MToDouble *mir = lir->mir(); + ValueOperand operand = ToValue(lir, LValueToDouble::Input); + FloatRegister output = ToFloatRegister(lir->output()); + + Register tag = masm.splitTagForTest(operand); + + Label isDouble, isInt32, isBool, isNull, isUndefined, done; + bool hasBoolean = false, hasNull = false, hasUndefined = false; + + masm.branchTestDouble(Assembler::Equal, tag, &isDouble); + masm.branchTestInt32(Assembler::Equal, tag, &isInt32); + + if (mir->conversion() != MToDouble::NumbersOnly) { + masm.branchTestBoolean(Assembler::Equal, tag, &isBool); + masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined); + hasBoolean = true; + hasUndefined = true; + if (mir->conversion() != MToDouble::NonNullNonStringPrimitives) { + masm.branchTestNull(Assembler::Equal, tag, &isNull); + hasNull = true; + } + } + + if (!bailout(lir->snapshot())) + return false; + + if (hasNull) { + masm.bind(&isNull); + masm.loadConstantDouble(0.0, output); + masm.jump(&done); + } + + if (hasUndefined) { + masm.bind(&isUndefined); + masm.loadConstantDouble(GenericNaN(), output); + masm.jump(&done); + } + + if (hasBoolean) { + masm.bind(&isBool); + masm.boolValueToDouble(operand, output); + masm.jump(&done); + } + + masm.bind(&isInt32); + masm.int32ValueToDouble(operand, output); + masm.jump(&done); + + masm.bind(&isDouble); + masm.unboxDouble(operand, output); + masm.bind(&done); + + return true; +} + +bool +CodeGenerator::visitValueToFloat32(LValueToFloat32 *lir) +{ + MToFloat32 *mir = lir->mir(); + ValueOperand operand = ToValue(lir, LValueToFloat32::Input); + FloatRegister output = ToFloatRegister(lir->output()); + + Register tag = masm.splitTagForTest(operand); + + Label isDouble, isInt32, isBool, isNull, isUndefined, done; + bool hasBoolean = false, hasNull = false, hasUndefined = false; + + masm.branchTestDouble(Assembler::Equal, tag, &isDouble); + masm.branchTestInt32(Assembler::Equal, tag, &isInt32); + + if (mir->conversion() != MToFloat32::NumbersOnly) { + masm.branchTestBoolean(Assembler::Equal, tag, &isBool); + masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined); + hasBoolean = true; + hasUndefined = true; + if (mir->conversion() != MToFloat32::NonNullNonStringPrimitives) { + masm.branchTestNull(Assembler::Equal, tag, &isNull); + hasNull = true; + } + } + + if (!bailout(lir->snapshot())) + return false; + + if (hasNull) { + masm.bind(&isNull); + masm.loadConstantFloat32(0.0f, output); + masm.jump(&done); + } + + if (hasUndefined) { + masm.bind(&isUndefined); + masm.loadConstantFloat32(float(GenericNaN()), output); + masm.jump(&done); + } + + if (hasBoolean) { + masm.bind(&isBool); + masm.boolValueToFloat32(operand, output); + masm.jump(&done); + } + + masm.bind(&isInt32); + masm.int32ValueToFloat32(operand, output); + masm.jump(&done); + + masm.bind(&isDouble); + masm.unboxDouble(operand, output); + masm.convertDoubleToFloat32(output, output); + masm.bind(&done); + + return true; +} + +bool +CodeGenerator::visitInt32ToDouble(LInt32ToDouble *lir) +{ + masm.convertInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output())); + return true; +} + +bool +CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble *lir) +{ + masm.convertFloat32ToDouble(ToFloatRegister(lir->input()), ToFloatRegister(lir->output())); + return true; +} + +bool +CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32 *lir) +{ + masm.convertDoubleToFloat32(ToFloatRegister(lir->input()), ToFloatRegister(lir->output())); + return true; +} + +bool +CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32 *lir) +{ + masm.convertInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output())); + return true; +} + +bool +CodeGenerator::visitDoubleToInt32(LDoubleToInt32 *lir) +{ + Label fail; + FloatRegister input = ToFloatRegister(lir->input()); + Register output = ToRegister(lir->output()); + masm.convertDoubleToInt32(input, output, &fail, lir->mir()->canBeNegativeZero()); + if (!bailoutFrom(&fail, lir->snapshot())) + return false; + return true; +} + +bool +CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32 *lir) +{ + Label fail; + FloatRegister input = ToFloatRegister(lir->input()); + Register output = ToRegister(lir->output()); + masm.convertFloat32ToInt32(input, output, &fail, lir->mir()->canBeNegativeZero()); + if (!bailoutFrom(&fail, lir->snapshot())) + return false; + return true; +} + +void +CodeGenerator::emitOOLTestObject(Register objreg, + Label *ifEmulatesUndefined, + Label *ifDoesntEmulateUndefined, + Register scratch) +{ + saveVolatile(scratch); + masm.setupUnalignedABICall(1, scratch); + masm.passABIArg(objreg); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::EmulatesUndefined)); + masm.storeCallResult(scratch); + restoreVolatile(scratch); + + masm.branchIfTrueBool(scratch, ifEmulatesUndefined); + masm.jump(ifDoesntEmulateUndefined); +} + +// Base out-of-line code generator for all tests of the truthiness of an +// object, where the object might not be truthy. (Recall that per spec all +// objects are truthy, but we implement the JSCLASS_EMULATES_UNDEFINED class +// flag to permit objects to look like |undefined| in certain contexts, +// including in object truthiness testing.) We check truthiness inline except +// when we're testing it on a proxy (or if TI guarantees us that the specified +// object will never emulate |undefined|), in which case out-of-line code will +// call EmulatesUndefined for a conclusive answer. +class OutOfLineTestObject : public OutOfLineCodeBase +{ + Register objreg_; + Register scratch_; + + Label *ifEmulatesUndefined_; + Label *ifDoesntEmulateUndefined_; + +#ifdef DEBUG + bool initialized() { return ifEmulatesUndefined_ != nullptr; } +#endif + + public: + OutOfLineTestObject() +#ifdef DEBUG + : ifEmulatesUndefined_(nullptr), ifDoesntEmulateUndefined_(nullptr) +#endif + { } + + bool accept(CodeGenerator *codegen) MOZ_FINAL MOZ_OVERRIDE { + MOZ_ASSERT(initialized()); + codegen->emitOOLTestObject(objreg_, ifEmulatesUndefined_, ifDoesntEmulateUndefined_, + scratch_); + return true; + } + + // Specify the register where the object to be tested is found, labels to + // jump to if the object is truthy or falsy, and a scratch register for + // use in the out-of-line path. + void setInputAndTargets(Register objreg, Label *ifEmulatesUndefined, Label *ifDoesntEmulateUndefined, + Register scratch) + { + MOZ_ASSERT(!initialized()); + MOZ_ASSERT(ifEmulatesUndefined); + objreg_ = objreg; + scratch_ = scratch; + ifEmulatesUndefined_ = ifEmulatesUndefined; + ifDoesntEmulateUndefined_ = ifDoesntEmulateUndefined; + } +}; + +// A subclass of OutOfLineTestObject containing two extra labels, for use when +// the ifTruthy/ifFalsy labels are needed in inline code as well as out-of-line +// code. The user should bind these labels in inline code, and specify them as +// targets via setInputAndTargets, as appropriate. +class OutOfLineTestObjectWithLabels : public OutOfLineTestObject +{ + Label label1_; + Label label2_; + + public: + OutOfLineTestObjectWithLabels() { } + + Label *label1() { return &label1_; } + Label *label2() { return &label2_; } +}; + +void +CodeGenerator::testObjectEmulatesUndefinedKernel(Register objreg, + Label *ifEmulatesUndefined, + Label *ifDoesntEmulateUndefined, + Register scratch, OutOfLineTestObject *ool) +{ + ool->setInputAndTargets(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined, scratch); + + // Perform a fast-path check of the object's class flags if the object's + // not a proxy. Let out-of-line code handle the slow cases that require + // saving registers, making a function call, and restoring registers. + masm.branchTestObjectTruthy(false, objreg, scratch, ool->entry(), ifEmulatesUndefined); +} + +void +CodeGenerator::branchTestObjectEmulatesUndefined(Register objreg, + Label *ifEmulatesUndefined, + Label *ifDoesntEmulateUndefined, + Register scratch, OutOfLineTestObject *ool) +{ + MOZ_ASSERT(!ifDoesntEmulateUndefined->bound(), + "ifDoesntEmulateUndefined will be bound to the fallthrough path"); + + testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined, + scratch, ool); + masm.bind(ifDoesntEmulateUndefined); +} + +void +CodeGenerator::testObjectEmulatesUndefined(Register objreg, + Label *ifEmulatesUndefined, + Label *ifDoesntEmulateUndefined, + Register scratch, OutOfLineTestObject *ool) +{ + testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined, + scratch, ool); + masm.jump(ifDoesntEmulateUndefined); +} + +void +CodeGenerator::testValueTruthyKernel(const ValueOperand &value, + const LDefinition *scratch1, const LDefinition *scratch2, + FloatRegister fr, + Label *ifTruthy, Label *ifFalsy, + OutOfLineTestObject *ool) +{ + Register tag = masm.splitTagForTest(value); + + // Eventually we will want some sort of type filter here. For now, just + // emit all easy cases. For speed we use the cached tag for all comparison, + // except for doubles, which we test last (as the operation can clobber the + // tag, which may be in ScratchReg). + masm.branchTestUndefined(Assembler::Equal, tag, ifFalsy); + masm.branchTestNull(Assembler::Equal, tag, ifFalsy); + + Label notBoolean; + masm.branchTestBoolean(Assembler::NotEqual, tag, ¬Boolean); + masm.branchTestBooleanTruthy(false, value, ifFalsy); + masm.jump(ifTruthy); + masm.bind(¬Boolean); + + Label notInt32; + masm.branchTestInt32(Assembler::NotEqual, tag, ¬Int32); + masm.branchTestInt32Truthy(false, value, ifFalsy); + masm.jump(ifTruthy); + masm.bind(¬Int32); + + if (ool) { + Label notObject; + + masm.branchTestObject(Assembler::NotEqual, tag, ¬Object); + + Register objreg = masm.extractObject(value, ToRegister(scratch1)); + testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, ToRegister(scratch2), ool); + + masm.bind(¬Object); + } else { + masm.branchTestObject(Assembler::Equal, tag, ifTruthy); + } + + // Test if a string is non-empty. + Label notString; + masm.branchTestString(Assembler::NotEqual, tag, ¬String); + masm.branchTestStringTruthy(false, value, ifFalsy); + masm.jump(ifTruthy); + masm.bind(¬String); + + // If we reach here the value is a double. + masm.unboxDouble(value, fr); + masm.branchTestDoubleTruthy(false, fr, ifFalsy); + + // Fall through for truthy. +} + +void +CodeGenerator::testValueTruthy(const ValueOperand &value, + const LDefinition *scratch1, const LDefinition *scratch2, + FloatRegister fr, + Label *ifTruthy, Label *ifFalsy, + OutOfLineTestObject *ool) +{ + testValueTruthyKernel(value, scratch1, scratch2, fr, ifTruthy, ifFalsy, ool); + masm.jump(ifTruthy); +} + +Label * +CodeGenerator::getJumpLabelForBranch(MBasicBlock *block) +{ + if (!labelForBackedgeWithImplicitCheck(block)) + return block->lir()->label(); + + // We need to use a patchable jump for this backedge, but want to treat + // this as a normal label target to simplify codegen. Efficiency isn't so + // important here as these tests are extremely unlikely to be used in loop + // backedges, so emit inline code for the patchable jump. Heap allocating + // the label allows it to be used by out of line blocks. + Label *res = GetIonContext()->temp->lifoAlloc()->new_