From 40736c5763bf61337c8c14e16d8587db021a87d4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 6 Jan 2012 14:44:00 +0100 Subject: Imported WebKit commit 2ea9d364d0f6efa8fa64acf19f451504c59be0e4 (http://svn.webkit.org/repository/webkit/trunk@104285) --- .../bytecompiler/BytecodeGenerator.cpp | 2371 ++++++++++++++++++++ .../bytecompiler/BytecodeGenerator.h | 588 +++++ Source/JavaScriptCore/bytecompiler/Label.h | 90 + Source/JavaScriptCore/bytecompiler/LabelScope.h | 79 + .../JavaScriptCore/bytecompiler/NodesCodegen.cpp | 2076 +++++++++++++++++ Source/JavaScriptCore/bytecompiler/RegisterID.h | 121 + 6 files changed, 5325 insertions(+) create mode 100644 Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp create mode 100644 Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h create mode 100644 Source/JavaScriptCore/bytecompiler/Label.h create mode 100644 Source/JavaScriptCore/bytecompiler/LabelScope.h create mode 100644 Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp create mode 100644 Source/JavaScriptCore/bytecompiler/RegisterID.h (limited to 'Source/JavaScriptCore/bytecompiler') diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp new file mode 100644 index 000000000..dec5c826e --- /dev/null +++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp @@ -0,0 +1,2371 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008 Cameron Zwarich + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "BytecodeGenerator.h" + +#include "BatchedTransitionOptimizer.h" +#include "JSFunction.h" +#include "Interpreter.h" +#include "ScopeChain.h" +#include "StrongInlines.h" +#include "UString.h" + +using namespace std; + +namespace JSC { + +/* + The layout of a register frame looks like this: + + For + + function f(x, y) { + var v1; + function g() { } + var v2; + return (x) * (y); + } + + assuming (x) and (y) generated temporaries t1 and t2, you would have + + ------------------------------------ + | x | y | g | v2 | v1 | t1 | t2 | <-- value held + ------------------------------------ + | -5 | -4 | -3 | -2 | -1 | +0 | +1 | <-- register index + ------------------------------------ + | params->|<-locals | temps-> + + Because temporary registers are allocated in a stack-like fashion, we + can reclaim them with a simple popping algorithm. The same goes for labels. + (We never reclaim parameter or local registers, because parameters and + locals are DontDelete.) + + The register layout before a function call looks like this: + + For + + function f(x, y) + { + } + + f(1); + + > <------------------------------ + < > reserved: call frame | 1 | <-- value held + > >snip< <------------------------------ + < > +0 | +1 | +2 | +3 | +4 | +5 | <-- register index + > <------------------------------ + | params->|<-locals | temps-> + + The call instruction fills in the "call frame" registers. It also pads + missing arguments at the end of the call: + + > <----------------------------------- + < > reserved: call frame | 1 | ? | <-- value held ("?" stands for "undefined") + > >snip< <----------------------------------- + < > +0 | +1 | +2 | +3 | +4 | +5 | +6 | <-- register index + > <----------------------------------- + | params->|<-locals | temps-> + + After filling in missing arguments, the call instruction sets up the new + stack frame to overlap the end of the old stack frame: + + |----------------------------------> < + | reserved: call frame | 1 | ? < > <-- value held ("?" stands for "undefined") + |----------------------------------> >snip< < + | -7 | -6 | -5 | -4 | -3 | -2 | -1 < > <-- register index + |----------------------------------> < + | | params->|<-locals | temps-> + + That way, arguments are "copied" into the callee's stack frame for free. + + If the caller supplies too many arguments, this trick doesn't work. The + extra arguments protrude into space reserved for locals and temporaries. + In that case, the call instruction makes a real copy of the call frame header, + along with just the arguments expected by the callee, leaving the original + call frame header and arguments behind. (The call instruction can't just discard + extra arguments, because the "arguments" object may access them later.) + This copying strategy ensures that all named values will be at the indices + expected by the callee. +*/ + +#ifndef NDEBUG +static bool s_dumpsGeneratedCode = false; +#endif + +void BytecodeGenerator::setDumpsGeneratedCode(bool dumpsGeneratedCode) +{ +#ifndef NDEBUG + s_dumpsGeneratedCode = dumpsGeneratedCode; +#else + UNUSED_PARAM(dumpsGeneratedCode); +#endif +} + +bool BytecodeGenerator::dumpsGeneratedCode() +{ +#ifndef NDEBUG + return s_dumpsGeneratedCode; +#else + return false; +#endif +} + +JSObject* BytecodeGenerator::generate() +{ + SamplingRegion samplingRegion("Bytecode Generation"); + + m_codeBlock->setThisRegister(m_thisRegister.index()); + + m_scopeNode->emitBytecode(*this); + + m_codeBlock->setInstructionCount(m_codeBlock->instructions().size()); + +#ifndef NDEBUG + if (s_dumpsGeneratedCode) + m_codeBlock->dump(m_scopeChain->globalObject->globalExec()); +#endif + + if ((m_codeType == FunctionCode && !m_codeBlock->needsFullScopeChain() && !m_codeBlock->usesArguments()) || m_codeType == EvalCode) + symbolTable().clear(); + + m_codeBlock->shrinkToFit(); + + if (m_expressionTooDeep) + return createOutOfMemoryError(m_scopeChain->globalObject.get()); + return 0; +} + +bool BytecodeGenerator::addVar(const Identifier& ident, bool isConstant, RegisterID*& r0) +{ + int index = m_calleeRegisters.size(); + SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0); + pair result = symbolTable().add(ident.impl(), newEntry); + + if (!result.second) { + r0 = ®isterFor(result.first->second.getIndex()); + return false; + } + + r0 = addVar(); + return true; +} + +int BytecodeGenerator::addGlobalVar(const Identifier& ident, bool isConstant) +{ + int index = symbolTable().size(); + SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0); + pair result = symbolTable().add(ident.impl(), newEntry); + if (!result.second) + index = result.first->second.getIndex(); + return index; +} + +void BytecodeGenerator::preserveLastVar() +{ + if ((m_firstConstantIndex = m_calleeRegisters.size()) != 0) + m_lastVar = &m_calleeRegisters.last(); +} + +BytecodeGenerator::BytecodeGenerator(ProgramNode* programNode, ScopeChainNode* scopeChain, SymbolTable* symbolTable, ProgramCodeBlock* codeBlock, CompilationKind compilationKind) + : m_shouldEmitDebugHooks(scopeChain->globalObject->debugger()) + , m_shouldEmitProfileHooks(scopeChain->globalObject->globalObjectMethodTable()->supportsProfiling(scopeChain->globalObject.get())) + , m_shouldEmitRichSourceInfo(scopeChain->globalObject->globalObjectMethodTable()->supportsRichSourceInfo(scopeChain->globalObject.get())) + , m_scopeChain(*scopeChain->globalData, scopeChain) + , m_symbolTable(symbolTable) + , m_scopeNode(programNode) + , m_codeBlock(codeBlock) + , m_thisRegister(CallFrame::thisArgumentOffset()) + , m_finallyDepth(0) + , m_dynamicScopeDepth(0) + , m_baseScopeDepth(0) + , m_codeType(GlobalCode) + , m_nextConstantOffset(0) + , m_globalConstantIndex(0) + , m_hasCreatedActivation(true) + , m_firstLazyFunction(0) + , m_lastLazyFunction(0) + , m_globalData(scopeChain->globalData) + , m_lastOpcodeID(op_end) +#ifndef NDEBUG + , m_lastOpcodePosition(0) +#endif + , m_stack(m_globalData->stack()) + , m_usesExceptions(false) + , m_expressionTooDeep(false) +{ + m_globalData->startedCompiling(m_codeBlock); + if (m_shouldEmitDebugHooks) + m_codeBlock->setNeedsFullScopeChain(true); + + emitOpcode(op_enter); + codeBlock->setGlobalData(m_globalData); + + // FIXME: Move code that modifies the global object to Interpreter::execute. + + m_codeBlock->m_numParameters = 1; // Allocate space for "this" + codeBlock->m_numCapturedVars = codeBlock->m_numVars; + + if (compilationKind == OptimizingCompilation) + return; + + JSGlobalObject* globalObject = scopeChain->globalObject.get(); + ExecState* exec = globalObject->globalExec(); + + BatchedTransitionOptimizer optimizer(*m_globalData, globalObject); + + const VarStack& varStack = programNode->varStack(); + const FunctionStack& functionStack = programNode->functionStack(); + + size_t newGlobals = varStack.size() + functionStack.size(); + if (!newGlobals) + return; + globalObject->resizeRegisters(symbolTable->size() + newGlobals); + + for (size_t i = 0; i < functionStack.size(); ++i) { + FunctionBodyNode* function = functionStack[i]; + globalObject->removeDirect(*m_globalData, function->ident()); // Newly declared functions overwrite existing properties. + + JSValue value = JSFunction::create(exec, makeFunction(exec, function), scopeChain); + int index = addGlobalVar(function->ident(), false); + globalObject->registerAt(index).set(*m_globalData, globalObject, value); + } + + for (size_t i = 0; i < varStack.size(); ++i) { + if (globalObject->hasProperty(exec, *varStack[i].first)) + continue; + addGlobalVar(*varStack[i].first, varStack[i].second & DeclarationStacks::IsConstant); + } +} + +BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, ScopeChainNode* scopeChain, SymbolTable* symbolTable, CodeBlock* codeBlock, CompilationKind) + : m_shouldEmitDebugHooks(scopeChain->globalObject->debugger()) + , m_shouldEmitProfileHooks(scopeChain->globalObject->globalObjectMethodTable()->supportsProfiling(scopeChain->globalObject.get())) + , m_shouldEmitRichSourceInfo(scopeChain->globalObject->globalObjectMethodTable()->supportsRichSourceInfo(scopeChain->globalObject.get())) + , m_scopeChain(*scopeChain->globalData, scopeChain) + , m_symbolTable(symbolTable) + , m_scopeNode(functionBody) + , m_codeBlock(codeBlock) + , m_activationRegister(0) + , m_finallyDepth(0) + , m_dynamicScopeDepth(0) + , m_baseScopeDepth(0) + , m_codeType(FunctionCode) + , m_nextConstantOffset(0) + , m_globalConstantIndex(0) + , m_hasCreatedActivation(false) + , m_firstLazyFunction(0) + , m_lastLazyFunction(0) + , m_globalData(scopeChain->globalData) + , m_lastOpcodeID(op_end) +#ifndef NDEBUG + , m_lastOpcodePosition(0) +#endif + , m_stack(m_globalData->stack()) + , m_usesExceptions(false) + , m_expressionTooDeep(false) +{ + m_globalData->startedCompiling(m_codeBlock); + if (m_shouldEmitDebugHooks) + m_codeBlock->setNeedsFullScopeChain(true); + + codeBlock->setGlobalData(m_globalData); + + emitOpcode(op_enter); + if (m_codeBlock->needsFullScopeChain()) { + m_activationRegister = addVar(); + emitInitLazyRegister(m_activationRegister); + m_codeBlock->setActivationRegister(m_activationRegister->index()); + } + + // Both op_tear_off_activation and op_tear_off_arguments tear off the 'arguments' + // object, if created. + if (m_codeBlock->needsFullScopeChain() || functionBody->usesArguments()) { + RegisterID* unmodifiedArgumentsRegister = addVar(); // Anonymous, so it can't be modified by user code. + RegisterID* argumentsRegister = addVar(propertyNames().arguments, false); // Can be changed by assigning to 'arguments'. + + // We can save a little space by hard-coding the knowledge that the two + // 'arguments' values are stored in consecutive registers, and storing + // only the index of the assignable one. + codeBlock->setArgumentsRegister(argumentsRegister->index()); + ASSERT_UNUSED(unmodifiedArgumentsRegister, unmodifiedArgumentsRegister->index() == JSC::unmodifiedArgumentsRegister(codeBlock->argumentsRegister())); + + emitInitLazyRegister(argumentsRegister); + emitInitLazyRegister(unmodifiedArgumentsRegister); + + if (m_codeBlock->isStrictMode()) { + emitOpcode(op_create_arguments); + instructions().append(argumentsRegister->index()); + } + + // The debugger currently retrieves the arguments object from an activation rather than pulling + // it from a call frame. In the long-term it should stop doing that (), + // but for now we force eager creation of the arguments object when debugging. + if (m_shouldEmitDebugHooks) { + emitOpcode(op_create_arguments); + instructions().append(argumentsRegister->index()); + } + } + + const DeclarationStacks::FunctionStack& functionStack = functionBody->functionStack(); + const DeclarationStacks::VarStack& varStack = functionBody->varStack(); + + // Captured variables and functions go first so that activations don't have + // to step over the non-captured locals to mark them. + m_hasCreatedActivation = false; + if (functionBody->hasCapturedVariables()) { + for (size_t i = 0; i < functionStack.size(); ++i) { + FunctionBodyNode* function = functionStack[i]; + const Identifier& ident = function->ident(); + if (functionBody->captures(ident)) { + if (!m_hasCreatedActivation) { + m_hasCreatedActivation = true; + emitOpcode(op_create_activation); + instructions().append(m_activationRegister->index()); + } + m_functions.add(ident.impl()); + emitNewFunction(addVar(ident, false), function); + } + } + for (size_t i = 0; i < varStack.size(); ++i) { + const Identifier& ident = *varStack[i].first; + if (functionBody->captures(ident)) + addVar(ident, varStack[i].second & DeclarationStacks::IsConstant); + } + } + bool canLazilyCreateFunctions = !functionBody->needsActivationForMoreThanVariables() && !m_shouldEmitDebugHooks; + if (!canLazilyCreateFunctions && !m_hasCreatedActivation) { + m_hasCreatedActivation = true; + emitOpcode(op_create_activation); + instructions().append(m_activationRegister->index()); + } + + codeBlock->m_numCapturedVars = codeBlock->m_numVars; + m_firstLazyFunction = codeBlock->m_numVars; + for (size_t i = 0; i < functionStack.size(); ++i) { + FunctionBodyNode* function = functionStack[i]; + const Identifier& ident = function->ident(); + if (!functionBody->captures(ident)) { + m_functions.add(ident.impl()); + RefPtr reg = addVar(ident, false); + // Don't lazily create functions that override the name 'arguments' + // as this would complicate lazy instantiation of actual arguments. + if (!canLazilyCreateFunctions || ident == propertyNames().arguments) + emitNewFunction(reg.get(), function); + else { + emitInitLazyRegister(reg.get()); + m_lazyFunctions.set(reg->index(), function); + } + } + } + m_lastLazyFunction = canLazilyCreateFunctions ? codeBlock->m_numVars : m_firstLazyFunction; + for (size_t i = 0; i < varStack.size(); ++i) { + const Identifier& ident = *varStack[i].first; + if (!functionBody->captures(ident)) + addVar(ident, varStack[i].second & DeclarationStacks::IsConstant); + } + + if (m_shouldEmitDebugHooks) + codeBlock->m_numCapturedVars = codeBlock->m_numVars; + + FunctionParameters& parameters = *functionBody->parameters(); + m_parameters.grow(parameters.size() + 1); // reserve space for "this" + + // Add "this" as a parameter + int nextParameterIndex = CallFrame::thisArgumentOffset(); + m_thisRegister.setIndex(nextParameterIndex--); + ++m_codeBlock->m_numParameters; + + for (size_t i = 0; i < parameters.size(); ++i) + addParameter(parameters[i], nextParameterIndex--); + + preserveLastVar(); + + if (isConstructor()) { + RefPtr func = newTemporary(); + RefPtr funcProto = newTemporary(); + + emitOpcode(op_get_callee); + instructions().append(func->index()); + // Load prototype. + emitGetById(funcProto.get(), func.get(), globalData()->propertyNames->prototype); + + emitOpcode(op_create_this); + instructions().append(m_thisRegister.index()); + instructions().append(funcProto->index()); + } else if (!codeBlock->isStrictMode() && (functionBody->usesThis() || codeBlock->usesEval() || m_shouldEmitDebugHooks)) { + emitOpcode(op_convert_this); + instructions().append(m_thisRegister.index()); + } +} + +BytecodeGenerator::BytecodeGenerator(EvalNode* evalNode, ScopeChainNode* scopeChain, SymbolTable* symbolTable, EvalCodeBlock* codeBlock, CompilationKind) + : m_shouldEmitDebugHooks(scopeChain->globalObject->debugger()) + , m_shouldEmitProfileHooks(scopeChain->globalObject->globalObjectMethodTable()->supportsProfiling(scopeChain->globalObject.get())) + , m_shouldEmitRichSourceInfo(scopeChain->globalObject->globalObjectMethodTable()->supportsRichSourceInfo(scopeChain->globalObject.get())) + , m_scopeChain(*scopeChain->globalData, scopeChain) + , m_symbolTable(symbolTable) + , m_scopeNode(evalNode) + , m_codeBlock(codeBlock) + , m_thisRegister(CallFrame::thisArgumentOffset()) + , m_finallyDepth(0) + , m_dynamicScopeDepth(0) + , m_baseScopeDepth(codeBlock->baseScopeDepth()) + , m_codeType(EvalCode) + , m_nextConstantOffset(0) + , m_globalConstantIndex(0) + , m_hasCreatedActivation(true) + , m_firstLazyFunction(0) + , m_lastLazyFunction(0) + , m_globalData(scopeChain->globalData) + , m_lastOpcodeID(op_end) +#ifndef NDEBUG + , m_lastOpcodePosition(0) +#endif + , m_stack(m_globalData->stack()) + , m_usesExceptions(false) + , m_expressionTooDeep(false) +{ + m_globalData->startedCompiling(m_codeBlock); + if (m_shouldEmitDebugHooks || m_baseScopeDepth) + m_codeBlock->setNeedsFullScopeChain(true); + + emitOpcode(op_enter); + codeBlock->setGlobalData(m_globalData); + m_codeBlock->m_numParameters = 1; // Allocate space for "this" + + const DeclarationStacks::FunctionStack& functionStack = evalNode->functionStack(); + for (size_t i = 0; i < functionStack.size(); ++i) + m_codeBlock->addFunctionDecl(makeFunction(m_globalData, functionStack[i])); + + const DeclarationStacks::VarStack& varStack = evalNode->varStack(); + unsigned numVariables = varStack.size(); + Vector variables; + variables.reserveCapacity(numVariables); + for (size_t i = 0; i < numVariables; ++i) + variables.append(*varStack[i].first); + codeBlock->adoptVariables(variables); + codeBlock->m_numCapturedVars = codeBlock->m_numVars; + preserveLastVar(); +} + +BytecodeGenerator::~BytecodeGenerator() +{ + m_globalData->finishedCompiling(m_codeBlock); +} + +RegisterID* BytecodeGenerator::emitInitLazyRegister(RegisterID* reg) +{ + emitOpcode(op_init_lazy_reg); + instructions().append(reg->index()); + return reg; +} + +void BytecodeGenerator::addParameter(const Identifier& ident, int parameterIndex) +{ + // Parameters overwrite var declarations, but not function declarations. + StringImpl* rep = ident.impl(); + if (!m_functions.contains(rep)) { + symbolTable().set(rep, parameterIndex); + RegisterID& parameter = registerFor(parameterIndex); + parameter.setIndex(parameterIndex); + } + + // To maintain the calling convention, we have to allocate unique space for + // each parameter, even if the parameter doesn't make it into the symbol table. + ++m_codeBlock->m_numParameters; +} + +RegisterID* BytecodeGenerator::registerFor(const Identifier& ident) +{ + if (ident == propertyNames().thisIdentifier) + return &m_thisRegister; + + if (m_codeType == GlobalCode) + return 0; + + if (!shouldOptimizeLocals()) + return 0; + + SymbolTableEntry entry = symbolTable().get(ident.impl()); + if (entry.isNull()) + return 0; + + if (ident == propertyNames().arguments) + createArgumentsIfNecessary(); + + return createLazyRegisterIfNecessary(®isterFor(entry.getIndex())); +} + +RegisterID* BytecodeGenerator::constRegisterFor(const Identifier& ident) +{ + if (m_codeType == EvalCode) + return 0; + + if (m_codeType == GlobalCode) + return 0; + + SymbolTableEntry entry = symbolTable().get(ident.impl()); + if (entry.isNull()) + return 0; + + return createLazyRegisterIfNecessary(®isterFor(entry.getIndex())); +} + +bool BytecodeGenerator::willResolveToArguments(const Identifier& ident) +{ + if (ident != propertyNames().arguments) + return false; + + if (!shouldOptimizeLocals()) + return false; + + SymbolTableEntry entry = symbolTable().get(ident.impl()); + if (entry.isNull()) + return false; + + if (m_codeBlock->usesArguments() && m_codeType == FunctionCode) + return true; + + return false; +} + +RegisterID* BytecodeGenerator::uncheckedRegisterForArguments() +{ + ASSERT(willResolveToArguments(propertyNames().arguments)); + + SymbolTableEntry entry = symbolTable().get(propertyNames().arguments.impl()); + ASSERT(!entry.isNull()); + return ®isterFor(entry.getIndex()); +} + +RegisterID* BytecodeGenerator::createLazyRegisterIfNecessary(RegisterID* reg) +{ + if (m_lastLazyFunction <= reg->index() || reg->index() < m_firstLazyFunction) + return reg; + emitLazyNewFunction(reg, m_lazyFunctions.get(reg->index())); + return reg; +} + +bool BytecodeGenerator::isLocal(const Identifier& ident) +{ + if (ident == propertyNames().thisIdentifier) + return true; + + return shouldOptimizeLocals() && symbolTable().contains(ident.impl()); +} + +bool BytecodeGenerator::isLocalConstant(const Identifier& ident) +{ + return symbolTable().get(ident.impl()).isReadOnly(); +} + +RegisterID* BytecodeGenerator::newRegister() +{ + m_calleeRegisters.append(m_calleeRegisters.size()); + m_codeBlock->m_numCalleeRegisters = max(m_codeBlock->m_numCalleeRegisters, m_calleeRegisters.size()); + return &m_calleeRegisters.last(); +} + +RegisterID* BytecodeGenerator::newTemporary() +{ + // Reclaim free register IDs. + while (m_calleeRegisters.size() && !m_calleeRegisters.last().refCount()) + m_calleeRegisters.removeLast(); + + RegisterID* result = newRegister(); + result->setTemporary(); + return result; +} + +RegisterID* BytecodeGenerator::highestUsedRegister() +{ + size_t count = m_codeBlock->m_numCalleeRegisters; + while (m_calleeRegisters.size() < count) + newRegister(); + return &m_calleeRegisters.last(); +} + +PassRefPtr BytecodeGenerator::newLabelScope(LabelScope::Type type, const Identifier* name) +{ + // Reclaim free label scopes. + while (m_labelScopes.size() && !m_labelScopes.last().refCount()) + m_labelScopes.removeLast(); + + // Allocate new label scope. + LabelScope scope(type, name, scopeDepth(), newLabel(), type == LabelScope::Loop ? newLabel() : PassRefPtr