| 1 | /* |
| 2 | * Copyright (C) 2015 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * 1. Redistributions of source code must retain the above copyright |
| 8 | * notice, this list of conditions and the following disclaimer. |
| 9 | * 2. Redistributions in binary form must reproduce the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer in the |
| 11 | * documentation and/or other materials provided with the distribution. |
| 12 | * |
| 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| 14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| 17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | #include "config.h" |
| 27 | #include "CallFrameShuffler.h" |
| 28 | |
| 29 | #if ENABLE(JIT) && USE(JSVALUE32_64) |
| 30 | |
| 31 | #include "CCallHelpers.h" |
| 32 | #include "DataFormat.h" |
| 33 | #include "JSCInlines.h" |
| 34 | |
| 35 | namespace JSC { |
| 36 | |
| 37 | DataFormat CallFrameShuffler::emitStore(CachedRecovery& location, MacroAssembler::Address address) |
| 38 | { |
| 39 | ASSERT(!location.recovery().isInJSStack()); |
| 40 | |
| 41 | switch (location.recovery().technique()) { |
| 42 | case UnboxedInt32InGPR: |
| 43 | m_jit.store32(MacroAssembler::TrustedImm32(JSValue::Int32Tag), |
| 44 | address.withOffset(TagOffset)); |
| 45 | m_jit.store32(location.recovery().gpr(), address.withOffset(PayloadOffset)); |
| 46 | return DataFormatInt32; |
| 47 | case UnboxedCellInGPR: |
| 48 | m_jit.store32(MacroAssembler::TrustedImm32(JSValue::CellTag), |
| 49 | address.withOffset(TagOffset)); |
| 50 | m_jit.store32(location.recovery().gpr(), address.withOffset(PayloadOffset)); |
| 51 | return DataFormatCell; |
| 52 | case Constant: |
| 53 | m_jit.storeTrustedValue(location.recovery().constant(), address); |
| 54 | return DataFormatJS; |
| 55 | case InPair: |
| 56 | m_jit.storeValue(location.recovery().jsValueRegs(), address); |
| 57 | return DataFormatJS; |
| 58 | case UnboxedBooleanInGPR: |
| 59 | m_jit.store32(MacroAssembler::TrustedImm32(JSValue::BooleanTag), |
| 60 | address.withOffset(TagOffset)); |
| 61 | m_jit.store32(location.recovery().gpr(), address.withOffset(PayloadOffset)); |
| 62 | return DataFormatBoolean; |
| 63 | case InFPR: |
| 64 | case UnboxedDoubleInFPR: |
| 65 | m_jit.storeDouble(location.recovery().fpr(), address); |
| 66 | return DataFormatJS; |
| 67 | default: |
| 68 | RELEASE_ASSERT_NOT_REACHED(); |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | void CallFrameShuffler::emitBox(CachedRecovery& location) |
| 73 | { |
| 74 | // Nothing to do, we're good! JSValues and doubles can be stored |
| 75 | // immediately, and other formats don't need any transformation - |
| 76 | // just storing a constant tag separately. |
| 77 | ASSERT_UNUSED(location, canBox(location)); |
| 78 | } |
| 79 | |
| 80 | void CallFrameShuffler::emitLoad(CachedRecovery& location) |
| 81 | { |
| 82 | if (!location.recovery().isInJSStack()) |
| 83 | return; |
| 84 | |
| 85 | if (verbose) |
| 86 | dataLog(" * Loading " , location.recovery(), " into " ); |
| 87 | VirtualRegister reg { location.recovery().virtualRegister() }; |
| 88 | MacroAssembler::Address address { addressForOld(reg) }; |
| 89 | |
| 90 | bool tryFPR { true }; |
| 91 | JSValueRegs wantedJSValueRegs { location.wantedJSValueRegs() }; |
| 92 | if (wantedJSValueRegs) { |
| 93 | if (wantedJSValueRegs.payloadGPR() != InvalidGPRReg |
| 94 | && !m_registers[wantedJSValueRegs.payloadGPR()] |
| 95 | && !m_lockedRegisters.get(wantedJSValueRegs.payloadGPR())) |
| 96 | tryFPR = false; |
| 97 | if (wantedJSValueRegs.tagGPR() != InvalidGPRReg |
| 98 | && !m_registers[wantedJSValueRegs.tagGPR()] |
| 99 | && !m_lockedRegisters.get(wantedJSValueRegs.tagGPR())) |
| 100 | tryFPR = false; |
| 101 | } |
| 102 | |
| 103 | if (tryFPR && location.loadsIntoFPR()) { |
| 104 | FPRReg resultFPR = location.wantedFPR(); |
| 105 | if (resultFPR == InvalidFPRReg || m_registers[resultFPR] || m_lockedRegisters.get(resultFPR)) |
| 106 | resultFPR = getFreeFPR(); |
| 107 | if (resultFPR != InvalidFPRReg) { |
| 108 | m_jit.loadDouble(address, resultFPR); |
| 109 | DataFormat dataFormat = DataFormatJS; |
| 110 | if (location.recovery().dataFormat() == DataFormatDouble) |
| 111 | dataFormat = DataFormatDouble; |
| 112 | updateRecovery(location, |
| 113 | ValueRecovery::inFPR(resultFPR, dataFormat)); |
| 114 | if (verbose) |
| 115 | dataLog(location.recovery(), "\n" ); |
| 116 | if (reg == newAsOld(dangerFrontier())) |
| 117 | updateDangerFrontier(); |
| 118 | return; |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | if (location.loadsIntoGPR()) { |
| 123 | GPRReg resultGPR { wantedJSValueRegs.payloadGPR() }; |
| 124 | if (resultGPR == InvalidGPRReg || m_registers[resultGPR] || m_lockedRegisters.get(resultGPR)) |
| 125 | resultGPR = getFreeGPR(); |
| 126 | ASSERT(resultGPR != InvalidGPRReg); |
| 127 | m_jit.loadPtr(address.withOffset(PayloadOffset), resultGPR); |
| 128 | updateRecovery(location, |
| 129 | ValueRecovery::inGPR(resultGPR, location.recovery().dataFormat())); |
| 130 | if (verbose) |
| 131 | dataLog(location.recovery(), "\n" ); |
| 132 | if (reg == newAsOld(dangerFrontier())) |
| 133 | updateDangerFrontier(); |
| 134 | return; |
| 135 | } |
| 136 | |
| 137 | ASSERT(location.recovery().technique() == DisplacedInJSStack); |
| 138 | GPRReg payloadGPR { wantedJSValueRegs.payloadGPR() }; |
| 139 | GPRReg tagGPR { wantedJSValueRegs.tagGPR() }; |
| 140 | if (payloadGPR == InvalidGPRReg || m_registers[payloadGPR] || m_lockedRegisters.get(payloadGPR)) |
| 141 | payloadGPR = getFreeGPR(); |
| 142 | m_lockedRegisters.set(payloadGPR); |
| 143 | if (tagGPR == InvalidGPRReg || m_registers[tagGPR] || m_lockedRegisters.get(tagGPR)) |
| 144 | tagGPR = getFreeGPR(); |
| 145 | m_lockedRegisters.clear(payloadGPR); |
| 146 | ASSERT(payloadGPR != InvalidGPRReg && tagGPR != InvalidGPRReg && tagGPR != payloadGPR); |
| 147 | m_jit.loadPtr(address.withOffset(PayloadOffset), payloadGPR); |
| 148 | m_jit.loadPtr(address.withOffset(TagOffset), tagGPR); |
| 149 | updateRecovery(location, |
| 150 | ValueRecovery::inPair(tagGPR, payloadGPR)); |
| 151 | if (verbose) |
| 152 | dataLog(location.recovery(), "\n" ); |
| 153 | if (reg == newAsOld(dangerFrontier())) |
| 154 | updateDangerFrontier(); |
| 155 | } |
| 156 | |
| 157 | bool CallFrameShuffler::canLoad(CachedRecovery& location) |
| 158 | { |
| 159 | if (!location.recovery().isInJSStack()) |
| 160 | return true; |
| 161 | |
| 162 | if (location.loadsIntoFPR() && getFreeFPR() != InvalidFPRReg) |
| 163 | return true; |
| 164 | |
| 165 | if (location.loadsIntoGPR() && getFreeGPR() != InvalidGPRReg) |
| 166 | return true; |
| 167 | |
| 168 | if (location.recovery().technique() == DisplacedInJSStack) { |
| 169 | GPRReg payloadGPR { getFreeGPR() }; |
| 170 | if (payloadGPR == InvalidGPRReg) |
| 171 | return false; |
| 172 | m_lockedRegisters.set(payloadGPR); |
| 173 | GPRReg tagGPR { getFreeGPR() }; |
| 174 | m_lockedRegisters.clear(payloadGPR); |
| 175 | return tagGPR != InvalidGPRReg; |
| 176 | } |
| 177 | |
| 178 | return false; |
| 179 | } |
| 180 | |
| 181 | void CallFrameShuffler::emitDisplace(CachedRecovery& location) |
| 182 | { |
| 183 | ASSERT(location.recovery().isInRegisters()); |
| 184 | JSValueRegs wantedJSValueRegs { location.wantedJSValueRegs() }; |
| 185 | ASSERT(wantedJSValueRegs); // We don't support wanted FPRs on 32bit platforms |
| 186 | |
| 187 | GPRReg wantedTagGPR { wantedJSValueRegs.tagGPR() }; |
| 188 | GPRReg wantedPayloadGPR { wantedJSValueRegs.payloadGPR() }; |
| 189 | |
| 190 | if (wantedTagGPR != InvalidGPRReg) { |
| 191 | ASSERT(!m_lockedRegisters.get(wantedTagGPR)); |
| 192 | if (CachedRecovery* currentTag { m_registers[wantedTagGPR] }) { |
| 193 | if (currentTag == &location) { |
| 194 | if (verbose) |
| 195 | dataLog(" + " , wantedTagGPR, " is OK\n" ); |
| 196 | } else { |
| 197 | // This can never happen on 32bit platforms since we |
| 198 | // have at most one wanted JSValueRegs, for the |
| 199 | // callee, and no callee-save registers. |
| 200 | RELEASE_ASSERT_NOT_REACHED(); |
| 201 | } |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | if (wantedPayloadGPR != InvalidGPRReg) { |
| 206 | ASSERT(!m_lockedRegisters.get(wantedPayloadGPR)); |
| 207 | if (CachedRecovery* currentPayload { m_registers[wantedPayloadGPR] }) { |
| 208 | if (currentPayload == &location) { |
| 209 | if (verbose) |
| 210 | dataLog(" + " , wantedPayloadGPR, " is OK\n" ); |
| 211 | } else { |
| 212 | // See above |
| 213 | RELEASE_ASSERT_NOT_REACHED(); |
| 214 | } |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | if (location.recovery().technique() == InPair |
| 219 | || location.recovery().isInGPR()) { |
| 220 | GPRReg payloadGPR; |
| 221 | if (location.recovery().technique() == InPair) |
| 222 | payloadGPR = location.recovery().payloadGPR(); |
| 223 | else |
| 224 | payloadGPR = location.recovery().gpr(); |
| 225 | |
| 226 | if (wantedPayloadGPR == InvalidGPRReg) |
| 227 | wantedPayloadGPR = payloadGPR; |
| 228 | |
| 229 | if (payloadGPR != wantedPayloadGPR) { |
| 230 | if (location.recovery().technique() == InPair |
| 231 | && wantedPayloadGPR == location.recovery().tagGPR()) { |
| 232 | if (verbose) |
| 233 | dataLog(" * Swapping " , payloadGPR, " and " , wantedPayloadGPR, "\n" ); |
| 234 | m_jit.swap(payloadGPR, wantedPayloadGPR); |
| 235 | updateRecovery(location, |
| 236 | ValueRecovery::inPair(payloadGPR, wantedPayloadGPR)); |
| 237 | } else { |
| 238 | if (verbose) |
| 239 | dataLog(" * Moving " , payloadGPR, " into " , wantedPayloadGPR, "\n" ); |
| 240 | m_jit.move(payloadGPR, wantedPayloadGPR); |
| 241 | if (location.recovery().technique() == InPair) { |
| 242 | updateRecovery(location, |
| 243 | ValueRecovery::inPair(location.recovery().tagGPR(), |
| 244 | wantedPayloadGPR)); |
| 245 | } else { |
| 246 | updateRecovery(location, |
| 247 | ValueRecovery::inGPR(wantedPayloadGPR, location.recovery().dataFormat())); |
| 248 | } |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | if (wantedTagGPR == InvalidGPRReg) |
| 253 | wantedTagGPR = getFreeGPR(); |
| 254 | switch (location.recovery().dataFormat()) { |
| 255 | case DataFormatInt32: |
| 256 | if (verbose) |
| 257 | dataLog(" * Moving int32 tag into " , wantedTagGPR, "\n" ); |
| 258 | m_jit.move(MacroAssembler::TrustedImm32(JSValue::Int32Tag), |
| 259 | wantedTagGPR); |
| 260 | break; |
| 261 | case DataFormatCell: |
| 262 | if (verbose) |
| 263 | dataLog(" * Moving cell tag into " , wantedTagGPR, "\n" ); |
| 264 | m_jit.move(MacroAssembler::TrustedImm32(JSValue::CellTag), |
| 265 | wantedTagGPR); |
| 266 | break; |
| 267 | case DataFormatBoolean: |
| 268 | if (verbose) |
| 269 | dataLog(" * Moving boolean tag into " , wantedTagGPR, "\n" ); |
| 270 | m_jit.move(MacroAssembler::TrustedImm32(JSValue::BooleanTag), |
| 271 | wantedTagGPR); |
| 272 | break; |
| 273 | case DataFormatJS: |
| 274 | ASSERT(wantedTagGPR != location.recovery().payloadGPR()); |
| 275 | if (wantedTagGPR != location.recovery().tagGPR()) { |
| 276 | if (verbose) |
| 277 | dataLog(" * Moving " , location.recovery().tagGPR(), " into " , wantedTagGPR, "\n" ); |
| 278 | m_jit.move(location.recovery().tagGPR(), wantedTagGPR); |
| 279 | } |
| 280 | break; |
| 281 | |
| 282 | default: |
| 283 | RELEASE_ASSERT_NOT_REACHED(); |
| 284 | } |
| 285 | } else { |
| 286 | ASSERT(location.recovery().isInFPR()); |
| 287 | if (wantedTagGPR == InvalidGPRReg) { |
| 288 | ASSERT(wantedPayloadGPR != InvalidGPRReg); |
| 289 | m_lockedRegisters.set(wantedPayloadGPR); |
| 290 | wantedTagGPR = getFreeGPR(); |
| 291 | m_lockedRegisters.clear(wantedPayloadGPR); |
| 292 | } |
| 293 | if (wantedPayloadGPR == InvalidGPRReg) { |
| 294 | m_lockedRegisters.set(wantedTagGPR); |
| 295 | wantedPayloadGPR = getFreeGPR(); |
| 296 | m_lockedRegisters.clear(wantedTagGPR); |
| 297 | } |
| 298 | m_jit.boxDouble(location.recovery().fpr(), wantedTagGPR, wantedPayloadGPR); |
| 299 | } |
| 300 | updateRecovery(location, ValueRecovery::inPair(wantedTagGPR, wantedPayloadGPR)); |
| 301 | } |
| 302 | |
| 303 | } // namespace JSC |
| 304 | |
| 305 | #endif // ENABLE(JIT) && USE(JSVALUE32_64) |
| 306 | |