blob: 5dfe96e81ec9f6fa9f77a0d651567253b16e7609 [file] [log] [blame]
/*
* Copyright (C) 2015 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.
*/
#include "config.h"
#include "CallFrameShuffler.h"
#if ENABLE(JIT) && USE(JSVALUE32_64)
#include "CCallHelpers.h"
#include "DataFormat.h"
#include "JSCInlines.h"
namespace JSC {
DataFormat CallFrameShuffler::emitStore(CachedRecovery& location, MacroAssembler::Address address)
{
ASSERT(!location.recovery().isInJSStack());
switch (location.recovery().technique()) {
case UnboxedInt32InGPR:
m_jit.store32(MacroAssembler::TrustedImm32(JSValue::Int32Tag),
address.withOffset(TagOffset));
m_jit.store32(location.recovery().gpr(), address.withOffset(PayloadOffset));
return DataFormatInt32;
case UnboxedCellInGPR:
m_jit.store32(MacroAssembler::TrustedImm32(JSValue::CellTag),
address.withOffset(TagOffset));
m_jit.store32(location.recovery().gpr(), address.withOffset(PayloadOffset));
return DataFormatCell;
case Constant:
m_jit.storeTrustedValue(location.recovery().constant(), address);
return DataFormatJS;
case InPair:
m_jit.storeValue(location.recovery().jsValueRegs(), address);
return DataFormatJS;
case UnboxedBooleanInGPR:
m_jit.store32(MacroAssembler::TrustedImm32(JSValue::BooleanTag),
address.withOffset(TagOffset));
m_jit.store32(location.recovery().gpr(), address.withOffset(PayloadOffset));
return DataFormatBoolean;
case InFPR:
case UnboxedDoubleInFPR:
m_jit.storeDouble(location.recovery().fpr(), address);
return DataFormatJS;
default:
RELEASE_ASSERT_NOT_REACHED();
}
}
void CallFrameShuffler::emitBox(CachedRecovery& location)
{
// Nothing to do, we're good! JSValues and doubles can be stored
// immediately, and other formats don't need any transformation -
// just storing a constant tag separately.
ASSERT_UNUSED(location, canBox(location));
}
void CallFrameShuffler::emitLoad(CachedRecovery& location)
{
if (!location.recovery().isInJSStack())
return;
if (verbose)
dataLog(" * Loading ", location.recovery(), " into ");
VirtualRegister reg { location.recovery().virtualRegister() };
MacroAssembler::Address address { addressForOld(reg) };
bool tryFPR { true };
JSValueRegs wantedJSValueRegs { location.wantedJSValueRegs() };
if (wantedJSValueRegs) {
if (wantedJSValueRegs.payloadGPR() != InvalidGPRReg
&& !m_registers[wantedJSValueRegs.payloadGPR()]
&& !m_lockedRegisters.get(wantedJSValueRegs.payloadGPR()))
tryFPR = false;
if (wantedJSValueRegs.tagGPR() != InvalidGPRReg
&& !m_registers[wantedJSValueRegs.tagGPR()]
&& !m_lockedRegisters.get(wantedJSValueRegs.tagGPR()))
tryFPR = false;
}
if (tryFPR && location.loadsIntoFPR()) {
FPRReg resultFPR = location.wantedFPR();
if (resultFPR == InvalidFPRReg || m_registers[resultFPR] || m_lockedRegisters.get(resultFPR))
resultFPR = getFreeFPR();
if (resultFPR != InvalidFPRReg) {
m_jit.loadDouble(address, resultFPR);
DataFormat dataFormat = DataFormatJS;
if (location.recovery().dataFormat() == DataFormatDouble)
dataFormat = DataFormatDouble;
updateRecovery(location,
ValueRecovery::inFPR(resultFPR, dataFormat));
if (verbose)
dataLog(location.recovery(), "\n");
if (reg == newAsOld(dangerFrontier()))
updateDangerFrontier();
return;
}
}
if (location.loadsIntoGPR()) {
GPRReg resultGPR { wantedJSValueRegs.payloadGPR() };
if (resultGPR == InvalidGPRReg || m_registers[resultGPR] || m_lockedRegisters.get(resultGPR))
resultGPR = getFreeGPR();
ASSERT(resultGPR != InvalidGPRReg);
m_jit.loadPtr(address.withOffset(PayloadOffset), resultGPR);
updateRecovery(location,
ValueRecovery::inGPR(resultGPR, location.recovery().dataFormat()));
if (verbose)
dataLog(location.recovery(), "\n");
if (reg == newAsOld(dangerFrontier()))
updateDangerFrontier();
return;
}
ASSERT(location.recovery().technique() == DisplacedInJSStack);
GPRReg payloadGPR { wantedJSValueRegs.payloadGPR() };
GPRReg tagGPR { wantedJSValueRegs.tagGPR() };
if (payloadGPR == InvalidGPRReg || m_registers[payloadGPR] || m_lockedRegisters.get(payloadGPR))
payloadGPR = getFreeGPR();
m_lockedRegisters.set(payloadGPR);
if (tagGPR == InvalidGPRReg || m_registers[tagGPR] || m_lockedRegisters.get(tagGPR))
tagGPR = getFreeGPR();
m_lockedRegisters.clear(payloadGPR);
ASSERT(payloadGPR != InvalidGPRReg && tagGPR != InvalidGPRReg && tagGPR != payloadGPR);
m_jit.loadPtr(address.withOffset(PayloadOffset), payloadGPR);
m_jit.loadPtr(address.withOffset(TagOffset), tagGPR);
updateRecovery(location,
ValueRecovery::inPair(tagGPR, payloadGPR));
if (verbose)
dataLog(location.recovery(), "\n");
if (reg == newAsOld(dangerFrontier()))
updateDangerFrontier();
}
bool CallFrameShuffler::canLoad(CachedRecovery& location)
{
if (!location.recovery().isInJSStack())
return true;
if (location.loadsIntoFPR() && getFreeFPR() != InvalidFPRReg)
return true;
if (location.loadsIntoGPR() && getFreeGPR() != InvalidGPRReg)
return true;
if (location.recovery().technique() == DisplacedInJSStack) {
GPRReg payloadGPR { getFreeGPR() };
if (payloadGPR == InvalidGPRReg)
return false;
m_lockedRegisters.set(payloadGPR);
GPRReg tagGPR { getFreeGPR() };
m_lockedRegisters.clear(payloadGPR);
return tagGPR != InvalidGPRReg;
}
return false;
}
void CallFrameShuffler::emitDisplace(CachedRecovery& location)
{
ASSERT(location.recovery().isInRegisters());
JSValueRegs wantedJSValueRegs { location.wantedJSValueRegs() };
ASSERT(wantedJSValueRegs); // We don't support wanted FPRs on 32bit platforms
GPRReg wantedTagGPR { wantedJSValueRegs.tagGPR() };
GPRReg wantedPayloadGPR { wantedJSValueRegs.payloadGPR() };
if (wantedTagGPR != InvalidGPRReg) {
ASSERT(!m_lockedRegisters.get(wantedTagGPR));
if (CachedRecovery* currentTag { m_registers[wantedTagGPR] }) {
if (currentTag == &location) {
if (verbose)
dataLog(" + ", wantedTagGPR, " is OK\n");
} else {
// This can never happen on 32bit platforms since we
// have at most one wanted JSValueRegs, for the
// callee, and no callee-save registers.
RELEASE_ASSERT_NOT_REACHED();
}
}
}
if (wantedPayloadGPR != InvalidGPRReg) {
ASSERT(!m_lockedRegisters.get(wantedPayloadGPR));
if (CachedRecovery* currentPayload { m_registers[wantedPayloadGPR] }) {
if (currentPayload == &location) {
if (verbose)
dataLog(" + ", wantedPayloadGPR, " is OK\n");
} else {
// See above
RELEASE_ASSERT_NOT_REACHED();
}
}
}
if (location.recovery().technique() == InPair
|| location.recovery().isInGPR()) {
GPRReg payloadGPR;
if (location.recovery().technique() == InPair)
payloadGPR = location.recovery().payloadGPR();
else
payloadGPR = location.recovery().gpr();
if (wantedPayloadGPR == InvalidGPRReg)
wantedPayloadGPR = payloadGPR;
if (payloadGPR != wantedPayloadGPR) {
if (location.recovery().technique() == InPair
&& wantedPayloadGPR == location.recovery().tagGPR()) {
if (verbose)
dataLog(" * Swapping ", payloadGPR, " and ", wantedPayloadGPR, "\n");
m_jit.swap(payloadGPR, wantedPayloadGPR);
updateRecovery(location,
ValueRecovery::inPair(payloadGPR, wantedPayloadGPR));
} else {
if (verbose)
dataLog(" * Moving ", payloadGPR, " into ", wantedPayloadGPR, "\n");
m_jit.move(payloadGPR, wantedPayloadGPR);
if (location.recovery().technique() == InPair) {
updateRecovery(location,
ValueRecovery::inPair(location.recovery().tagGPR(),
wantedPayloadGPR));
} else {
updateRecovery(location,
ValueRecovery::inGPR(wantedPayloadGPR, location.recovery().dataFormat()));
}
}
}
if (wantedTagGPR == InvalidGPRReg)
wantedTagGPR = getFreeGPR();
switch (location.recovery().dataFormat()) {
case DataFormatInt32:
if (verbose)
dataLog(" * Moving int32 tag into ", wantedTagGPR, "\n");
m_jit.move(MacroAssembler::TrustedImm32(JSValue::Int32Tag),
wantedTagGPR);
break;
case DataFormatCell:
if (verbose)
dataLog(" * Moving cell tag into ", wantedTagGPR, "\n");
m_jit.move(MacroAssembler::TrustedImm32(JSValue::CellTag),
wantedTagGPR);
break;
case DataFormatBoolean:
if (verbose)
dataLog(" * Moving boolean tag into ", wantedTagGPR, "\n");
m_jit.move(MacroAssembler::TrustedImm32(JSValue::BooleanTag),
wantedTagGPR);
break;
case DataFormatJS:
ASSERT(wantedTagGPR != location.recovery().payloadGPR());
if (wantedTagGPR != location.recovery().tagGPR()) {
if (verbose)
dataLog(" * Moving ", location.recovery().tagGPR(), " into ", wantedTagGPR, "\n");
m_jit.move(location.recovery().tagGPR(), wantedTagGPR);
}
break;
default:
RELEASE_ASSERT_NOT_REACHED();
}
} else {
ASSERT(location.recovery().isInFPR());
if (wantedTagGPR == InvalidGPRReg) {
ASSERT(wantedPayloadGPR != InvalidGPRReg);
m_lockedRegisters.set(wantedPayloadGPR);
wantedTagGPR = getFreeGPR();
m_lockedRegisters.clear(wantedPayloadGPR);
}
if (wantedPayloadGPR == InvalidGPRReg) {
m_lockedRegisters.set(wantedTagGPR);
wantedPayloadGPR = getFreeGPR();
m_lockedRegisters.clear(wantedTagGPR);
}
m_jit.boxDouble(location.recovery().fpr(), wantedTagGPR, wantedPayloadGPR);
}
updateRecovery(location, ValueRecovery::inPair(wantedTagGPR, wantedPayloadGPR));
}
} // namespace JSC
#endif // ENABLE(JIT) && USE(JSVALUE32_64)