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/BaselineFrameInfo.h" michael@0: michael@0: #ifdef DEBUG michael@0: # include "jit/BytecodeAnalysis.h" michael@0: #endif michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: bool michael@0: FrameInfo::init(TempAllocator &alloc) michael@0: { michael@0: // One slot is always needed for this/arguments type checks. michael@0: size_t nstack = Max(script->nslots() - script->nfixed(), size_t(1)); michael@0: if (!stack.init(alloc, nstack)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: FrameInfo::sync(StackValue *val) michael@0: { michael@0: switch (val->kind()) { michael@0: case StackValue::Stack: michael@0: break; michael@0: case StackValue::LocalSlot: michael@0: masm.pushValue(addressOfLocal(val->localSlot())); michael@0: break; michael@0: case StackValue::ArgSlot: michael@0: masm.pushValue(addressOfArg(val->argSlot())); michael@0: break; michael@0: case StackValue::ThisSlot: michael@0: masm.pushValue(addressOfThis()); michael@0: break; michael@0: case StackValue::Register: michael@0: masm.pushValue(val->reg()); michael@0: break; michael@0: case StackValue::Constant: michael@0: masm.pushValue(val->constant()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid kind"); michael@0: } michael@0: michael@0: val->setStack(); michael@0: } michael@0: michael@0: void michael@0: FrameInfo::syncStack(uint32_t uses) michael@0: { michael@0: JS_ASSERT(uses <= stackDepth()); michael@0: michael@0: uint32_t depth = stackDepth() - uses; michael@0: michael@0: for (uint32_t i = 0; i < depth; i++) { michael@0: StackValue *current = &stack[i]; michael@0: sync(current); michael@0: } michael@0: } michael@0: michael@0: uint32_t michael@0: FrameInfo::numUnsyncedSlots() michael@0: { michael@0: // Start at the bottom, find the first value that's not synced. michael@0: uint32_t i = 0; michael@0: for (; i < stackDepth(); i++) { michael@0: if (peek(-int32_t(i + 1))->kind() == StackValue::Stack) michael@0: break; michael@0: } michael@0: return i; michael@0: } michael@0: michael@0: void michael@0: FrameInfo::popValue(ValueOperand dest) michael@0: { michael@0: StackValue *val = peek(-1); michael@0: michael@0: switch (val->kind()) { michael@0: case StackValue::Constant: michael@0: masm.moveValue(val->constant(), dest); michael@0: break; michael@0: case StackValue::LocalSlot: michael@0: masm.loadValue(addressOfLocal(val->localSlot()), dest); michael@0: break; michael@0: case StackValue::ArgSlot: michael@0: masm.loadValue(addressOfArg(val->argSlot()), dest); michael@0: break; michael@0: case StackValue::ThisSlot: michael@0: masm.loadValue(addressOfThis(), dest); michael@0: break; michael@0: case StackValue::Stack: michael@0: masm.popValue(dest); michael@0: break; michael@0: case StackValue::Register: michael@0: masm.moveValue(val->reg(), dest); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid kind"); michael@0: } michael@0: michael@0: // masm.popValue already adjusted the stack pointer, don't do it twice. michael@0: pop(DontAdjustStack); michael@0: } michael@0: michael@0: void michael@0: FrameInfo::popRegsAndSync(uint32_t uses) michael@0: { michael@0: // x86 has only 3 Value registers. Only support 2 regs here for now, michael@0: // so that there's always a scratch Value register for reg -> reg michael@0: // moves. michael@0: JS_ASSERT(uses > 0); michael@0: JS_ASSERT(uses <= 2); michael@0: JS_ASSERT(uses <= stackDepth()); michael@0: michael@0: syncStack(uses); michael@0: michael@0: switch (uses) { michael@0: case 1: michael@0: popValue(R0); michael@0: break; michael@0: case 2: { michael@0: // If the second value is in R1, move it to R2 so that it's not michael@0: // clobbered by the first popValue. michael@0: StackValue *val = peek(-2); michael@0: if (val->kind() == StackValue::Register && val->reg() == R1) { michael@0: masm.moveValue(R1, R2); michael@0: val->setRegister(R2); michael@0: } michael@0: popValue(R1); michael@0: popValue(R0); michael@0: break; michael@0: } michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid uses"); michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: FrameInfo::assertValidState(const BytecodeInfo &info) michael@0: { michael@0: // Check stack depth. michael@0: JS_ASSERT(stackDepth() == info.stackDepth); michael@0: michael@0: // Start at the bottom, find the first value that's not synced. michael@0: uint32_t i = 0; michael@0: for (; i < stackDepth(); i++) { michael@0: if (stack[i].kind() != StackValue::Stack) michael@0: break; michael@0: } michael@0: michael@0: // Assert all values on top of it are also not synced. michael@0: for (; i < stackDepth(); i++) michael@0: JS_ASSERT(stack[i].kind() != StackValue::Stack); michael@0: michael@0: // Assert every Value register is used by at most one StackValue. michael@0: // R2 is used as scratch register by the compiler and FrameInfo, michael@0: // so it shouldn't be used for StackValues. michael@0: bool usedR0 = false, usedR1 = false; michael@0: michael@0: for (i = 0; i < stackDepth(); i++) { michael@0: if (stack[i].kind() == StackValue::Register) { michael@0: ValueOperand reg = stack[i].reg(); michael@0: if (reg == R0) { michael@0: JS_ASSERT(!usedR0); michael@0: usedR0 = true; michael@0: } else if (reg == R1) { michael@0: JS_ASSERT(!usedR1); michael@0: usedR1 = true; michael@0: } else { michael@0: MOZ_ASSUME_UNREACHABLE("Invalid register"); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: #endif