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