| /* |
| * 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) |