| 1 | /* |
| 2 | * Copyright (C) 2011, 2013 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 | #pragma once |
| 27 | |
| 28 | #if ENABLE(DFG_JIT) |
| 29 | |
| 30 | #include "DFGMinifiedIDInlines.h" |
| 31 | #include "DFGVariableEvent.h" |
| 32 | #include "DFGVariableEventStream.h" |
| 33 | #include "DataFormat.h" |
| 34 | |
| 35 | namespace JSC { namespace DFG { |
| 36 | |
| 37 | // === GenerationInfo === |
| 38 | // |
| 39 | // This class is used to track the current status of live values during code generation. |
| 40 | // Can provide information as to whether a value is in machine registers, and if so which, |
| 41 | // whether a value has been spilled to the RegisterFile, and if so may be able to provide |
| 42 | // details of the format in memory (all values are spilled in a boxed form, but we may be |
| 43 | // able to track the type of box), and tracks how many outstanding uses of a value remain, |
| 44 | // so that we know when the value is dead and the machine registers associated with it |
| 45 | // may be released. |
| 46 | class GenerationInfo { |
| 47 | public: |
| 48 | GenerationInfo() |
| 49 | : m_node(0) |
| 50 | , m_useCount(0) |
| 51 | , m_registerFormat(DataFormatNone) |
| 52 | , m_spillFormat(DataFormatNone) |
| 53 | , m_canFill(false) |
| 54 | , m_bornForOSR(false) |
| 55 | , m_isConstant(false) |
| 56 | { |
| 57 | } |
| 58 | |
| 59 | void initConstant(Node* node, uint32_t useCount) |
| 60 | { |
| 61 | m_node = node; |
| 62 | m_useCount = useCount; |
| 63 | m_registerFormat = DataFormatNone; |
| 64 | m_spillFormat = DataFormatNone; |
| 65 | m_canFill = true; |
| 66 | m_bornForOSR = false; |
| 67 | m_isConstant = true; |
| 68 | ASSERT(m_useCount); |
| 69 | } |
| 70 | void initGPR(Node* node, uint32_t useCount, GPRReg gpr, DataFormat format) |
| 71 | { |
| 72 | ASSERT(gpr != InvalidGPRReg); |
| 73 | m_node = node; |
| 74 | m_useCount = useCount; |
| 75 | m_registerFormat = format; |
| 76 | m_spillFormat = DataFormatNone; |
| 77 | m_canFill = false; |
| 78 | u.gpr = gpr; |
| 79 | m_bornForOSR = false; |
| 80 | m_isConstant = false; |
| 81 | ASSERT(m_useCount); |
| 82 | } |
| 83 | void initInt32(Node* node, uint32_t useCount, GPRReg gpr) |
| 84 | { |
| 85 | initGPR(node, useCount, gpr, DataFormatInt32); |
| 86 | } |
| 87 | void initInt52(Node* node, uint32_t useCount, GPRReg reg, DataFormat format) |
| 88 | { |
| 89 | ASSERT(format == DataFormatInt52 || format == DataFormatStrictInt52); |
| 90 | initGPR(node, useCount, reg, format); |
| 91 | } |
| 92 | void initInt52(Node* node, uint32_t useCount, GPRReg reg) |
| 93 | { |
| 94 | initGPR(node, useCount, reg, DataFormatInt52); |
| 95 | } |
| 96 | void initStrictInt52(Node* node, uint32_t useCount, GPRReg reg) |
| 97 | { |
| 98 | initGPR(node, useCount, reg, DataFormatStrictInt52); |
| 99 | } |
| 100 | #if USE(JSVALUE64) |
| 101 | void initJSValue(Node* node, uint32_t useCount, GPRReg gpr, DataFormat format = DataFormatJS) |
| 102 | { |
| 103 | ASSERT(format & DataFormatJS); |
| 104 | initGPR(node, useCount, gpr, format); |
| 105 | } |
| 106 | #elif USE(JSVALUE32_64) |
| 107 | void initJSValue(Node* node, uint32_t useCount, GPRReg tagGPR, GPRReg payloadGPR, DataFormat format = DataFormatJS) |
| 108 | { |
| 109 | ASSERT(format & DataFormatJS); |
| 110 | |
| 111 | m_node = node; |
| 112 | m_useCount = useCount; |
| 113 | m_registerFormat = format; |
| 114 | m_spillFormat = DataFormatNone; |
| 115 | m_canFill = false; |
| 116 | u.v.tagGPR = tagGPR; |
| 117 | u.v.payloadGPR = payloadGPR; |
| 118 | m_bornForOSR = false; |
| 119 | m_isConstant = false; |
| 120 | ASSERT(m_useCount); |
| 121 | } |
| 122 | #endif |
| 123 | void initCell(Node* node, uint32_t useCount, GPRReg gpr) |
| 124 | { |
| 125 | initGPR(node, useCount, gpr, DataFormatCell); |
| 126 | } |
| 127 | void initBoolean(Node* node, uint32_t useCount, GPRReg gpr) |
| 128 | { |
| 129 | initGPR(node, useCount, gpr, DataFormatBoolean); |
| 130 | } |
| 131 | void initDouble(Node* node, uint32_t useCount, FPRReg fpr) |
| 132 | { |
| 133 | ASSERT(fpr != InvalidFPRReg); |
| 134 | m_node = node; |
| 135 | m_useCount = useCount; |
| 136 | m_registerFormat = DataFormatDouble; |
| 137 | m_spillFormat = DataFormatNone; |
| 138 | m_canFill = false; |
| 139 | u.fpr = fpr; |
| 140 | m_bornForOSR = false; |
| 141 | m_isConstant = false; |
| 142 | ASSERT(m_useCount); |
| 143 | } |
| 144 | void initStorage(Node* node, uint32_t useCount, GPRReg gpr) |
| 145 | { |
| 146 | initGPR(node, useCount, gpr, DataFormatStorage); |
| 147 | } |
| 148 | |
| 149 | // Get the node that produced this value. |
| 150 | Node* node() { return m_node; } |
| 151 | |
| 152 | void noticeOSRBirth(VariableEventStream& stream, Node* node, VirtualRegister virtualRegister) |
| 153 | { |
| 154 | if (m_node != node) |
| 155 | return; |
| 156 | if (!alive()) |
| 157 | return; |
| 158 | if (m_bornForOSR) |
| 159 | return; |
| 160 | |
| 161 | m_bornForOSR = true; |
| 162 | |
| 163 | if (m_isConstant) |
| 164 | appendBirth(stream); |
| 165 | else if (m_registerFormat != DataFormatNone) |
| 166 | appendFill(BirthToFill, stream); |
| 167 | else if (m_spillFormat != DataFormatNone) |
| 168 | appendSpill(BirthToSpill, stream, virtualRegister); |
| 169 | } |
| 170 | |
| 171 | // Mark the value as having been used (decrement the useCount). |
| 172 | // Returns true if this was the last use of the value, and any |
| 173 | // associated machine registers may be freed. |
| 174 | bool use(VariableEventStream& stream) |
| 175 | { |
| 176 | ASSERT(m_useCount); |
| 177 | bool result = !--m_useCount; |
| 178 | |
| 179 | if (result && m_bornForOSR) { |
| 180 | ASSERT(m_node); |
| 181 | stream.appendAndLog(VariableEvent::death(MinifiedID(m_node))); |
| 182 | } |
| 183 | |
| 184 | return result; |
| 185 | } |
| 186 | |
| 187 | // Used to check the operands of operations to see if they are on |
| 188 | // their last use; in some cases it may be safe to reuse the same |
| 189 | // machine register for the result of the operation. |
| 190 | uint32_t useCount() |
| 191 | { |
| 192 | ASSERT(m_useCount); |
| 193 | return m_useCount; |
| 194 | } |
| 195 | |
| 196 | // Get the format of the value in machine registers (or 'none'). |
| 197 | DataFormat registerFormat() { return m_registerFormat; } |
| 198 | // Get the format of the value as it is spilled in the JSStack (or 'none'). |
| 199 | DataFormat spillFormat() { return m_spillFormat; } |
| 200 | |
| 201 | bool isFormat(DataFormat expectedFormat) |
| 202 | { |
| 203 | return registerFormat() == expectedFormat || spillFormat() == expectedFormat; |
| 204 | } |
| 205 | |
| 206 | bool isJSFormat(DataFormat expectedFormat) |
| 207 | { |
| 208 | return JSC::isJSFormat(registerFormat(), expectedFormat) || JSC::isJSFormat(spillFormat(), expectedFormat); |
| 209 | } |
| 210 | |
| 211 | bool isJSInt32() |
| 212 | { |
| 213 | return isJSFormat(DataFormatJSInt32); |
| 214 | } |
| 215 | |
| 216 | bool isInt52() |
| 217 | { |
| 218 | return isFormat(DataFormatInt52); |
| 219 | } |
| 220 | |
| 221 | bool isStrictInt52() |
| 222 | { |
| 223 | return isFormat(DataFormatStrictInt52); |
| 224 | } |
| 225 | |
| 226 | bool isJSDouble() |
| 227 | { |
| 228 | return isJSFormat(DataFormatJSDouble); |
| 229 | } |
| 230 | |
| 231 | bool isJSCell() |
| 232 | { |
| 233 | return isJSFormat(DataFormatJSCell); |
| 234 | } |
| 235 | |
| 236 | bool isJSBoolean() |
| 237 | { |
| 238 | return isJSFormat(DataFormatJSBoolean); |
| 239 | } |
| 240 | |
| 241 | bool isUnknownJS() |
| 242 | { |
| 243 | return spillFormat() == DataFormatNone |
| 244 | ? registerFormat() == DataFormatJS || registerFormat() == DataFormatNone |
| 245 | : spillFormat() == DataFormatJS; |
| 246 | } |
| 247 | |
| 248 | // Get the machine resister currently holding the value. |
| 249 | #if USE(JSVALUE64) |
| 250 | GPRReg gpr() { ASSERT(m_registerFormat && m_registerFormat != DataFormatDouble); return u.gpr; } |
| 251 | FPRReg fpr() { ASSERT(m_registerFormat == DataFormatDouble); return u.fpr; } |
| 252 | JSValueRegs jsValueRegs() { ASSERT(m_registerFormat & DataFormatJS); return JSValueRegs(u.gpr); } |
| 253 | #elif USE(JSVALUE32_64) |
| 254 | GPRReg gpr() { ASSERT(!(m_registerFormat & DataFormatJS) && m_registerFormat != DataFormatDouble); return u.gpr; } |
| 255 | GPRReg tagGPR() { ASSERT(m_registerFormat & DataFormatJS); return u.v.tagGPR; } |
| 256 | GPRReg payloadGPR() { ASSERT(m_registerFormat & DataFormatJS); return u.v.payloadGPR; } |
| 257 | FPRReg fpr() { ASSERT(m_registerFormat == DataFormatDouble || m_registerFormat == DataFormatJSDouble); return u.fpr; } |
| 258 | JSValueRegs jsValueRegs() { ASSERT(m_registerFormat & DataFormatJS); return JSValueRegs(u.v.tagGPR, u.v.payloadGPR); } |
| 259 | #endif |
| 260 | |
| 261 | // Check whether a value needs spilling in order to free up any associated machine registers. |
| 262 | bool needsSpill() |
| 263 | { |
| 264 | // This should only be called on values that are currently in a register. |
| 265 | ASSERT(m_registerFormat != DataFormatNone); |
| 266 | // Constants do not need spilling, nor do values that have already been |
| 267 | // spilled to the JSStack. |
| 268 | return !m_canFill; |
| 269 | } |
| 270 | |
| 271 | // Called when a VirtualRegister is being spilled to the JSStack for the first time. |
| 272 | void spill(VariableEventStream& stream, VirtualRegister virtualRegister, DataFormat spillFormat) |
| 273 | { |
| 274 | // We shouldn't be spill values that don't need spilling. |
| 275 | ASSERT(!m_canFill); |
| 276 | ASSERT(m_spillFormat == DataFormatNone); |
| 277 | // We should only be spilling values that are currently in machine registers. |
| 278 | ASSERT(m_registerFormat != DataFormatNone); |
| 279 | |
| 280 | m_registerFormat = DataFormatNone; |
| 281 | m_spillFormat = spillFormat; |
| 282 | m_canFill = true; |
| 283 | |
| 284 | if (m_bornForOSR) |
| 285 | appendSpill(Spill, stream, virtualRegister); |
| 286 | } |
| 287 | |
| 288 | // Called on values that don't need spilling (constants and values that have |
| 289 | // already been spilled), to mark them as no longer being in machine registers. |
| 290 | void setSpilled(VariableEventStream& stream, VirtualRegister virtualRegister) |
| 291 | { |
| 292 | // Should only be called on values that don't need spilling, and are currently in registers. |
| 293 | ASSERT(m_canFill && m_registerFormat != DataFormatNone); |
| 294 | m_registerFormat = DataFormatNone; |
| 295 | |
| 296 | if (m_bornForOSR) |
| 297 | appendSpill(Spill, stream, virtualRegister); |
| 298 | } |
| 299 | |
| 300 | void killSpilled() |
| 301 | { |
| 302 | m_spillFormat = DataFormatNone; |
| 303 | m_canFill = false; |
| 304 | } |
| 305 | |
| 306 | void fillGPR(VariableEventStream& stream, GPRReg gpr, DataFormat format) |
| 307 | { |
| 308 | ASSERT(gpr != InvalidGPRReg); |
| 309 | m_registerFormat = format; |
| 310 | u.gpr = gpr; |
| 311 | if (m_bornForOSR) |
| 312 | appendFill(Fill, stream); |
| 313 | } |
| 314 | |
| 315 | // Record that this value is filled into machine registers, |
| 316 | // tracking which registers, and what format the value has. |
| 317 | #if USE(JSVALUE64) |
| 318 | void fillJSValue(VariableEventStream& stream, GPRReg gpr, DataFormat format = DataFormatJS) |
| 319 | { |
| 320 | ASSERT(format & DataFormatJS); |
| 321 | fillGPR(stream, gpr, format); |
| 322 | } |
| 323 | #elif USE(JSVALUE32_64) |
| 324 | void fillJSValue(VariableEventStream& stream, GPRReg tagGPR, GPRReg payloadGPR, DataFormat format = DataFormatJS) |
| 325 | { |
| 326 | ASSERT(format & DataFormatJS); |
| 327 | m_registerFormat = format; |
| 328 | u.v.tagGPR = tagGPR; // FIXME: for JSValues with known type (boolean, integer, cell etc.) no tagGPR is needed? |
| 329 | u.v.payloadGPR = payloadGPR; |
| 330 | |
| 331 | if (m_bornForOSR) |
| 332 | appendFill(Fill, stream); |
| 333 | } |
| 334 | void fillCell(VariableEventStream& stream, GPRReg gpr) |
| 335 | { |
| 336 | fillGPR(stream, gpr, DataFormatCell); |
| 337 | } |
| 338 | #endif |
| 339 | void fillInt32(VariableEventStream& stream, GPRReg gpr) |
| 340 | { |
| 341 | fillGPR(stream, gpr, DataFormatInt32); |
| 342 | } |
| 343 | void fillInt52(VariableEventStream& stream, GPRReg gpr, DataFormat format) |
| 344 | { |
| 345 | ASSERT(format == DataFormatInt52 || format == DataFormatStrictInt52); |
| 346 | fillGPR(stream, gpr, format); |
| 347 | } |
| 348 | void fillInt52(VariableEventStream& stream, GPRReg gpr) |
| 349 | { |
| 350 | fillGPR(stream, gpr, DataFormatInt52); |
| 351 | } |
| 352 | void fillStrictInt52(VariableEventStream& stream, GPRReg gpr) |
| 353 | { |
| 354 | fillGPR(stream, gpr, DataFormatStrictInt52); |
| 355 | } |
| 356 | void fillBoolean(VariableEventStream& stream, GPRReg gpr) |
| 357 | { |
| 358 | fillGPR(stream, gpr, DataFormatBoolean); |
| 359 | } |
| 360 | void fillDouble(VariableEventStream& stream, FPRReg fpr) |
| 361 | { |
| 362 | ASSERT(fpr != InvalidFPRReg); |
| 363 | m_registerFormat = DataFormatDouble; |
| 364 | u.fpr = fpr; |
| 365 | |
| 366 | if (m_bornForOSR) |
| 367 | appendFill(Fill, stream); |
| 368 | } |
| 369 | void fillStorage(VariableEventStream& stream, GPRReg gpr) |
| 370 | { |
| 371 | fillGPR(stream, gpr, DataFormatStorage); |
| 372 | } |
| 373 | |
| 374 | bool alive() |
| 375 | { |
| 376 | return m_useCount; |
| 377 | } |
| 378 | |
| 379 | ValueRecovery recovery(VirtualRegister spillSlot) const |
| 380 | { |
| 381 | if (m_isConstant) |
| 382 | return ValueRecovery::constant(m_node->constant()->value()); |
| 383 | |
| 384 | if (m_registerFormat == DataFormatDouble) |
| 385 | return ValueRecovery::inFPR(u.fpr, DataFormatDouble); |
| 386 | |
| 387 | #if USE(JSVALUE32_64) |
| 388 | if (m_registerFormat & DataFormatJS) { |
| 389 | if (m_registerFormat == DataFormatJS) |
| 390 | return ValueRecovery::inPair(u.v.tagGPR, u.v.payloadGPR); |
| 391 | return ValueRecovery::inGPR(u.v.payloadGPR, static_cast<DataFormat>(m_registerFormat & ~DataFormatJS)); |
| 392 | } |
| 393 | #endif |
| 394 | if (m_registerFormat) |
| 395 | return ValueRecovery::inGPR(u.gpr, m_registerFormat); |
| 396 | |
| 397 | ASSERT(m_spillFormat); |
| 398 | |
| 399 | return ValueRecovery::displacedInJSStack(spillSlot, m_spillFormat); |
| 400 | } |
| 401 | |
| 402 | private: |
| 403 | void appendBirth(VariableEventStream& stream) |
| 404 | { |
| 405 | stream.appendAndLog(VariableEvent::birth(MinifiedID(m_node))); |
| 406 | } |
| 407 | |
| 408 | void appendFill(VariableEventKind kind, VariableEventStream& stream) |
| 409 | { |
| 410 | ASSERT(m_bornForOSR); |
| 411 | |
| 412 | if (m_registerFormat == DataFormatDouble) { |
| 413 | stream.appendAndLog(VariableEvent::fillFPR(kind, MinifiedID(m_node), u.fpr)); |
| 414 | return; |
| 415 | } |
| 416 | #if USE(JSVALUE32_64) |
| 417 | if (m_registerFormat & DataFormatJS) { |
| 418 | stream.appendAndLog(VariableEvent::fillPair(kind, MinifiedID(m_node), u.v.tagGPR, u.v.payloadGPR)); |
| 419 | return; |
| 420 | } |
| 421 | #endif |
| 422 | stream.appendAndLog(VariableEvent::fillGPR(kind, MinifiedID(m_node), u.gpr, m_registerFormat)); |
| 423 | } |
| 424 | |
| 425 | void appendSpill(VariableEventKind kind, VariableEventStream& stream, VirtualRegister virtualRegister) |
| 426 | { |
| 427 | stream.appendAndLog(VariableEvent::spill(kind, MinifiedID(m_node), virtualRegister, m_spillFormat)); |
| 428 | } |
| 429 | |
| 430 | // The node whose result is stored in this virtual register. |
| 431 | Node* m_node; |
| 432 | uint32_t m_useCount; |
| 433 | DataFormat m_registerFormat; |
| 434 | DataFormat m_spillFormat; |
| 435 | bool m_canFill; |
| 436 | bool m_bornForOSR; |
| 437 | bool m_isConstant; |
| 438 | union { |
| 439 | GPRReg gpr; |
| 440 | FPRReg fpr; |
| 441 | #if USE(JSVALUE32_64) |
| 442 | struct { |
| 443 | GPRReg tagGPR; |
| 444 | GPRReg payloadGPR; |
| 445 | } v; |
| 446 | #endif |
| 447 | } u; |
| 448 | }; |
| 449 | |
| 450 | } } // namespace JSC::DFG |
| 451 | |
| 452 | #endif |
| 453 | |