blob: 388e7cb7982aed6a20554048a3c41e3be0b05603 [file] [log] [blame]
/*
* Copyright (C) 2011 Apple Inc. All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
#ifndef DFGJITCompiler_h
#define DFGJITCompiler_h
#if ENABLE(DFG_JIT)
#include <assembler/LinkBuffer.h>
#include <assembler/MacroAssembler.h>
#include <bytecode/CodeBlock.h>
#include <dfg/DFGAssemblyHelpers.h>
#include <dfg/DFGFPRInfo.h>
#include <dfg/DFGGPRInfo.h>
#include <dfg/DFGGraph.h>
#include <dfg/DFGRegisterBank.h>
#include <jit/JITCode.h>
namespace JSC {
class AbstractSamplingCounter;
class CodeBlock;
class JSGlobalData;
namespace DFG {
class JITCodeGenerator;
class NodeToRegisterMap;
class SpeculativeJIT;
class SpeculationRecovery;
struct EntryLocation;
struct OSRExit;
// === CallLinkRecord ===
//
// A record of a call out from JIT code that needs linking to a helper function.
// Every CallLinkRecord contains a reference to the call instruction & the function
// that it needs to be linked to.
struct CallLinkRecord {
CallLinkRecord(MacroAssembler::Call call, FunctionPtr function)
: m_call(call)
, m_function(function)
{
}
MacroAssembler::Call m_call;
FunctionPtr m_function;
};
// === CallExceptionRecord ===
//
// A record of a call out from JIT code that might throw an exception.
// Calls that might throw an exception also record the Jump taken on exception
// (unset if not present) and code origin used to recover handler/source info.
struct CallExceptionRecord {
CallExceptionRecord(MacroAssembler::Call call, CodeOrigin codeOrigin)
: m_call(call)
, m_codeOrigin(codeOrigin)
{
}
CallExceptionRecord(MacroAssembler::Call call, MacroAssembler::Jump exceptionCheck, CodeOrigin codeOrigin)
: m_call(call)
, m_exceptionCheck(exceptionCheck)
, m_codeOrigin(codeOrigin)
{
}
MacroAssembler::Call m_call;
MacroAssembler::Jump m_exceptionCheck;
CodeOrigin m_codeOrigin;
};
struct PropertyAccessRecord {
#if USE(JSVALUE64)
PropertyAccessRecord(MacroAssembler::DataLabelPtr deltaCheckImmToCall, MacroAssembler::Call functionCall, MacroAssembler::Jump deltaCallToStructCheck, MacroAssembler::DataLabelCompact deltaCallToLoadOrStore, MacroAssembler::Label deltaCallToSlowCase, MacroAssembler::Label deltaCallToDone, int8_t baseGPR, int8_t valueGPR, int8_t scratchGPR)
#elif USE(JSVALUE32_64)
PropertyAccessRecord(MacroAssembler::DataLabelPtr deltaCheckImmToCall, MacroAssembler::Call functionCall, MacroAssembler::Jump deltaCallToStructCheck, MacroAssembler::DataLabelCompact deltaCallToTagLoadOrStore, MacroAssembler::DataLabelCompact deltaCallToPayloadLoadOrStore, MacroAssembler::Label deltaCallToSlowCase, MacroAssembler::Label deltaCallToDone, int8_t baseGPR, int8_t valueTagGPR, int8_t valueGPR, int8_t scratchGPR)
#endif
: m_deltaCheckImmToCall(deltaCheckImmToCall)
, m_functionCall(functionCall)
, m_deltaCallToStructCheck(deltaCallToStructCheck)
#if USE(JSVALUE64)
, m_deltaCallToLoadOrStore(deltaCallToLoadOrStore)
#elif USE(JSVALUE32_64)
, m_deltaCallToTagLoadOrStore(deltaCallToTagLoadOrStore)
, m_deltaCallToPayloadLoadOrStore(deltaCallToPayloadLoadOrStore)
#endif
, m_deltaCallToSlowCase(deltaCallToSlowCase)
, m_deltaCallToDone(deltaCallToDone)
, m_baseGPR(baseGPR)
#if USE(JSVALUE32_64)
, m_valueTagGPR(valueTagGPR)
#endif
, m_valueGPR(valueGPR)
, m_scratchGPR(scratchGPR)
{
}
MacroAssembler::DataLabelPtr m_deltaCheckImmToCall;
MacroAssembler::Call m_functionCall;
MacroAssembler::Jump m_deltaCallToStructCheck;
#if USE(JSVALUE64)
MacroAssembler::DataLabelCompact m_deltaCallToLoadOrStore;
#elif USE(JSVALUE32_64)
MacroAssembler::DataLabelCompact m_deltaCallToTagLoadOrStore;
MacroAssembler::DataLabelCompact m_deltaCallToPayloadLoadOrStore;
#endif
MacroAssembler::Label m_deltaCallToSlowCase;
MacroAssembler::Label m_deltaCallToDone;
int8_t m_baseGPR;
#if USE(JSVALUE32_64)
int8_t m_valueTagGPR;
#endif
int8_t m_valueGPR;
int8_t m_scratchGPR;
};
// === JITCompiler ===
//
// DFG::JITCompiler is responsible for generating JIT code from the dataflow graph.
// It does so by delegating to the speculative & non-speculative JITs, which
// generate to a MacroAssembler (which the JITCompiler owns through an inheritance
// relationship). The JITCompiler holds references to information required during
// compilation, and also records information used in linking (e.g. a list of all
// call to be linked).
class JITCompiler : public AssemblyHelpers {
public:
JITCompiler(JSGlobalData* globalData, Graph& dfg, CodeBlock* codeBlock)
: AssemblyHelpers(globalData, codeBlock)
, m_graph(dfg)
{
}
void compile(JITCode& entry);
void compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWithArityCheck);
// Accessors for properties.
Graph& graph() { return m_graph; }
// Notify the JIT of a call that does not require linking.
void notifyCall(Call functionCall, CodeOrigin codeOrigin)
{
m_exceptionChecks.append(CallExceptionRecord(functionCall, codeOrigin));
}
// Add a call out from JIT code, without an exception check.
Call appendCall(const FunctionPtr& function)
{
Call functionCall = call();
m_calls.append(CallLinkRecord(functionCall, function));
return functionCall;
}
// Add a call out from JIT code, with an exception check.
Call addExceptionCheck(Call functionCall, CodeOrigin codeOrigin)
{
#if USE(JSVALUE64)
Jump exceptionCheck = branchTestPtr(NonZero, AbsoluteAddress(&globalData()->exception));
#elif USE(JSVALUE32_64)
Jump exceptionCheck = branch32(NotEqual, AbsoluteAddress(reinterpret_cast<char*>(&globalData()->exception) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), TrustedImm32(JSValue::EmptyValueTag));
#endif
m_exceptionChecks.append(CallExceptionRecord(functionCall, exceptionCheck, codeOrigin));
return functionCall;
}
// Add a call out from JIT code, with a fast exception check that tests if the return value is zero.
Call addFastExceptionCheck(Call functionCall, CodeOrigin codeOrigin)
{
Jump exceptionCheck = branchTestPtr(Zero, GPRInfo::returnValueGPR);
m_exceptionChecks.append(CallExceptionRecord(functionCall, exceptionCheck, codeOrigin));
return functionCall;
}
// Helper methods to check nodes for constants.
bool isConstant(NodeIndex nodeIndex) { return graph().isConstant(nodeIndex); }
bool isJSConstant(NodeIndex nodeIndex) { return graph().isJSConstant(nodeIndex); }
bool isInt32Constant(NodeIndex nodeIndex) { return graph().isInt32Constant(codeBlock(), nodeIndex); }
bool isDoubleConstant(NodeIndex nodeIndex) { return graph().isDoubleConstant(codeBlock(), nodeIndex); }
bool isNumberConstant(NodeIndex nodeIndex) { return graph().isNumberConstant(codeBlock(), nodeIndex); }
bool isBooleanConstant(NodeIndex nodeIndex) { return graph().isBooleanConstant(codeBlock(), nodeIndex); }
bool isFunctionConstant(NodeIndex nodeIndex) { return graph().isFunctionConstant(codeBlock(), nodeIndex); }
// Helper methods get constant values from nodes.
JSValue valueOfJSConstant(NodeIndex nodeIndex) { return graph().valueOfJSConstant(codeBlock(), nodeIndex); }
int32_t valueOfInt32Constant(NodeIndex nodeIndex) { return graph().valueOfInt32Constant(codeBlock(), nodeIndex); }
double valueOfNumberConstant(NodeIndex nodeIndex) { return graph().valueOfNumberConstant(codeBlock(), nodeIndex); }
bool valueOfBooleanConstant(NodeIndex nodeIndex) { return graph().valueOfBooleanConstant(codeBlock(), nodeIndex); }
JSFunction* valueOfFunctionConstant(NodeIndex nodeIndex) { return graph().valueOfFunctionConstant(codeBlock(), nodeIndex); }
// Helper methods to get predictions
PredictedType getPrediction(Node& node) { return node.prediction(); }
PredictedType getPrediction(NodeIndex nodeIndex) { return getPrediction(graph()[nodeIndex]); }
#if USE(JSVALUE32_64)
void* addressOfDoubleConstant(NodeIndex nodeIndex)
{
ASSERT(isNumberConstant(nodeIndex));
unsigned constantIndex = graph()[nodeIndex].constantNumber();
return &(codeBlock()->constantRegister(FirstConstantRegisterIndex + constantIndex));
}
void emitLoadTag(NodeIndex, GPRReg tag);
void emitLoadPayload(NodeIndex, GPRReg payload);
void emitLoad(const JSValue&, GPRReg tag, GPRReg payload);
void emitLoad(NodeIndex, GPRReg tag, GPRReg payload);
void emitLoad2(NodeIndex index1, GPRReg tag1, GPRReg payload1, NodeIndex index2, GPRReg tag2, GPRReg payload2);
void emitLoadDouble(NodeIndex, FPRReg value);
void emitLoadInt32ToDouble(NodeIndex, FPRReg value);
void emitStore(NodeIndex, GPRReg tag, GPRReg payload);
void emitStore(NodeIndex, const JSValue constant);
void emitStoreInt32(NodeIndex, GPRReg payload, bool indexIsInt32 = false);
void emitStoreInt32(NodeIndex, TrustedImm32 payload, bool indexIsInt32 = false);
void emitStoreCell(NodeIndex, GPRReg payload, bool indexIsCell = false);
void emitStoreBool(NodeIndex, GPRReg payload, bool indexIsBool = false);
void emitStoreDouble(NodeIndex, FPRReg value);
#endif
void addPropertyAccess(const PropertyAccessRecord& record)
{
m_propertyAccesses.append(record);
}
void addMethodGet(Call slowCall, DataLabelPtr structToCompare, DataLabelPtr protoObj, DataLabelPtr protoStructToCompare, DataLabelPtr putFunction)
{
m_methodGets.append(MethodGetRecord(slowCall, structToCompare, protoObj, protoStructToCompare, putFunction));
}
void addJSCall(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, CodeOrigin codeOrigin)
{
m_jsCalls.append(JSCallRecord(fastCall, slowCall, targetToCheck, callType, codeOrigin));
}
void noticeOSREntry(BasicBlock& basicBlock, JITCompiler::Label blockHead, LinkBuffer& linkBuffer)
{
#if DFG_ENABLE(OSR_ENTRY)
OSREntryData* entry = codeBlock()->appendDFGOSREntryData(basicBlock.bytecodeBegin, linkBuffer.offsetOf(blockHead));
entry->m_expectedValues = basicBlock.valuesAtHead;
// Fix the expected values: in our protocol, a dead variable will have an expected
// value of (None, []). But the old JIT may stash some values there. So we really
// need (Top, TOP).
for (size_t argument = 0; argument < basicBlock.variablesAtHead.numberOfArguments(); ++argument) {
if (basicBlock.variablesAtHead.argument(argument) == NoNode)
entry->m_expectedValues.argument(argument).makeTop();
}
for (size_t local = 0; local < basicBlock.variablesAtHead.numberOfLocals(); ++local) {
if (basicBlock.variablesAtHead.local(local) == NoNode)
entry->m_expectedValues.local(local).makeTop();
}
#else
UNUSED_PARAM(basicBlock);
UNUSED_PARAM(blockHead);
UNUSED_PARAM(linkBuffer);
#endif
}
ValueProfile* valueProfileFor(NodeIndex nodeIndex)
{
if (nodeIndex == NoNode)
return 0;
return m_graph.valueProfileFor(nodeIndex, baselineCodeBlockFor(m_graph[nodeIndex].codeOrigin));
}
private:
// Internal implementation to compile.
void compileEntry();
void compileBody(SpeculativeJIT&);
void link(LinkBuffer&);
void exitSpeculativeWithOSR(const OSRExit&, SpeculationRecovery*);
void linkOSRExits();
// The dataflow graph currently being generated.
Graph& m_graph;
// Vector of calls out from JIT code, including exception handler information.
// Count of the number of CallRecords with exception handlers.
Vector<CallLinkRecord> m_calls;
Vector<CallExceptionRecord> m_exceptionChecks;
struct MethodGetRecord {
MethodGetRecord(Call slowCall, DataLabelPtr structToCompare, DataLabelPtr protoObj, DataLabelPtr protoStructToCompare, DataLabelPtr putFunction)
: m_slowCall(slowCall)
, m_structToCompare(structToCompare)
, m_protoObj(protoObj)
, m_protoStructToCompare(protoStructToCompare)
, m_putFunction(putFunction)
{
}
Call m_slowCall;
DataLabelPtr m_structToCompare;
DataLabelPtr m_protoObj;
DataLabelPtr m_protoStructToCompare;
DataLabelPtr m_putFunction;
};
struct JSCallRecord {
JSCallRecord(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, CodeOrigin codeOrigin)
: m_fastCall(fastCall)
, m_slowCall(slowCall)
, m_targetToCheck(targetToCheck)
, m_callType(callType)
, m_codeOrigin(codeOrigin)
{
}
Call m_fastCall;
Call m_slowCall;
DataLabelPtr m_targetToCheck;
CallLinkInfo::CallType m_callType;
CodeOrigin m_codeOrigin;
};
Vector<PropertyAccessRecord, 4> m_propertyAccesses;
Vector<MethodGetRecord, 4> m_methodGets;
Vector<JSCallRecord, 4> m_jsCalls;
};
} } // namespace JSC::DFG
#endif
#endif