1 /* 2 * Copyright (C) 2011 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 #ifndef DFGSpeculativeJIT_h 27 #define DFGSpeculativeJIT_h 28 29 #if ENABLE(DFG_JIT) 30 31 #include <dfg/DFGJITCodeGenerator.h> 32 33 namespace JSC { namespace DFG { 34 35 class SpeculativeJIT; 36 37 // This enum describes the types of additional recovery that 38 // may need be performed should a speculation check fail. 39 enum SpeculationRecoveryType { 40 SpeculativeAdd 41 }; 42 43 // === SpeculationRecovery === 44 // 45 // This class provides additional information that may be associated with a 46 // speculation check - for example 47 class SpeculationRecovery { 48 public: 49 SpeculationRecovery(SpeculationRecoveryType type, GPRReg dest, GPRReg src) 50 : m_type(type) 51 , m_dest(dest) 52 , m_src(src) 53 { 54 } 55 56 SpeculationRecoveryType type() { return m_type; } 57 GPRReg dest() { return m_dest; } 58 GPRReg src() { return m_src; } 59 60 private: 61 // Indicates the type of additional recovery to be performed. 62 SpeculationRecoveryType m_type; 63 // different recovery types may required different additional information here. 64 GPRReg m_dest; 65 GPRReg m_src; 66 }; 67 68 // === SpeculationCheck === 69 // 70 // This structure records a bail-out from the speculative path, 71 // which will need to be linked in to the non-speculative one. 72 struct SpeculationCheck { 73 SpeculationCheck(MacroAssembler::Jump, SpeculativeJIT*, unsigned recoveryIndex = 0); 74 75 // The location of the jump out from the speculative path, 76 // and the node we were generating code for. 77 MacroAssembler::Jump m_check; 78 NodeIndex m_nodeIndex; 79 // Used to record any additional recovery to be performed; this 80 // value is an index into the SpeculativeJIT's m_speculationRecoveryList 81 // array, offset by 1. (m_recoveryIndex == 0) means no recovery. 82 unsigned m_recoveryIndex; 83 84 struct RegisterInfo { 85 NodeIndex nodeIndex; 86 DataFormat format; 87 }; 88 RegisterInfo m_gprInfo[numberOfGPRs]; 89 NodeIndex m_fprInfo[numberOfFPRs]; 90 }; 91 typedef SegmentedVector<SpeculationCheck, 16> SpeculationCheckVector; 92 93 94 // === SpeculativeJIT === 95 // 96 // The SpeculativeJIT is used to generate a fast, but potentially 97 // incomplete code path for the dataflow. When code generating 98 // we may make assumptions about operand types, dynamically check, 99 // and bail-out to an alternate code path if these checks fail. 100 // Importantly, the speculative code path cannot be reentered once 101 // a speculative check has failed. This allows the SpeculativeJIT 102 // to propagate type information (including information that has 103 // only speculatively been asserted) through the dataflow. 104 class SpeculativeJIT : public JITCodeGenerator { 105 friend struct SpeculationCheck; 106 public: 107 SpeculativeJIT(JITCompiler& jit) 108 : JITCodeGenerator(jit, true) 109 , m_didTerminate(false) 110 { 111 } 112 113 bool compile(); 114 115 // Retrieve the list of bail-outs from the speculative path, 116 // and additional recovery information. 117 SpeculationCheckVector& speculationChecks() 118 { 119 return m_speculationChecks; 120 } 121 SpeculationRecovery* speculationRecovery(size_t index) 122 { 123 // SpeculationCheck::m_recoveryIndex is offset by 1, 124 // 0 means no recovery. 125 return index ? &m_speculationRecoveryList[index - 1] : 0; 126 } 127 128 // Called by the speculative operand types, below, to fill operand to 129 // machine registers, implicitly generating speculation checks as needed. 130 GPRReg fillSpeculateInt(NodeIndex, DataFormat& returnFormat); 131 GPRReg fillSpeculateIntStrict(NodeIndex); 132 GPRReg fillSpeculateCell(NodeIndex); 133 134 private: 135 bool compile(Node&); 136 bool compile(BasicBlock&); 137 138 bool isDoubleConstantWithInt32Value(NodeIndex nodeIndex, int32_t& out) 139 { 140 if (!m_jit.isDoubleConstant(nodeIndex)) 141 return false; 142 double value = m_jit.valueOfDoubleConstant(nodeIndex); 143 144 int32_t asInt32 = static_cast<int32_t>(value); 145 if (value != asInt32) 146 return false; 147 if (!asInt32 && signbit(value)) 148 return false; 149 150 out = asInt32; 151 return true; 152 } 153 154 // Add a speculation check without additional recovery. 155 void speculationCheck(MacroAssembler::Jump jumpToFail) 156 { 157 m_speculationChecks.append(SpeculationCheck(jumpToFail, this)); 158 } 159 // Add a speculation check with additional recovery. 160 void speculationCheck(MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) 161 { 162 m_speculationRecoveryList.append(recovery); 163 m_speculationChecks.append(SpeculationCheck(jumpToFail, this, m_speculationRecoveryList.size())); 164 } 165 166 // Called when we statically determine that a speculation will fail. 167 void terminateSpeculativeExecution() 168 { 169 // FIXME: in cases where we can statically determine we're going to bail out from the speculative 170 // JIT we should probably rewind code generation and only produce the non-speculative path. 171 m_didTerminate = true; 172 speculationCheck(m_jit.jump()); 173 } 174 175 template<bool strict> 176 GPRReg fillSpeculateIntInternal(NodeIndex, DataFormat& returnFormat); 177 178 // It is possible, during speculative generation, to reach a situation in which we 179 // can statically determine a speculation will fail (for example, when two nodes 180 // will make conflicting speculations about the same operand). In such cases this 181 // flag is set, indicating no further code generation should take place. 182 bool m_didTerminate; 183 // This vector tracks bail-outs from the speculative path to the non-speculative one. 184 SpeculationCheckVector m_speculationChecks; 185 // Some bail-outs need to record additional information recording specific recovery 186 // to be performed (for example, on detected overflow from an add, we may need to 187 // reverse the addition if an operand is being overwritten). 188 Vector<SpeculationRecovery, 16> m_speculationRecoveryList; 189 }; 190 191 192 // === Speculative Operand types === 193 // 194 // SpeculateIntegerOperand, SpeculateStrictInt32Operand and SpeculateCellOperand. 195 // 196 // These are used to lock the operands to a node into machine registers within the 197 // SpeculativeJIT. The classes operate like those provided by the JITCodeGenerator, 198 // however these will perform a speculative check for a more restrictive type than 199 // we can statically determine the operand to have. If the operand does not have 200 // the requested type, a bail-out to the non-speculative path will be taken. 201 202 class SpeculateIntegerOperand { 203 public: 204 explicit SpeculateIntegerOperand(SpeculativeJIT* jit, NodeIndex index) 205 : m_jit(jit) 206 , m_index(index) 207 , m_gprOrInvalid(InvalidGPRReg) 208 #ifndef NDEBUG 209 , m_format(DataFormatNone) 210 #endif 211 { 212 ASSERT(m_jit); 213 if (jit->isFilled(index)) 214 gpr(); 215 } 216 217 ~SpeculateIntegerOperand() 218 { 219 ASSERT(m_gprOrInvalid != InvalidGPRReg); 220 m_jit->unlock(m_gprOrInvalid); 221 } 222 223 NodeIndex index() const 224 { 225 return m_index; 226 } 227 228 GPRReg gpr() 229 { 230 if (m_gprOrInvalid == InvalidGPRReg) 231 m_gprOrInvalid = m_jit->fillSpeculateInt(index(), m_format); 232 return m_gprOrInvalid; 233 } 234 235 DataFormat format() 236 { 237 gpr(); // m_format is set when m_gpr is locked. 238 ASSERT(m_format == DataFormatInteger || m_format == DataFormatJSInteger); 239 return m_format; 240 } 241 242 MacroAssembler::RegisterID registerID() 243 { 244 return JITCompiler::gprToRegisterID(gpr()); 245 } 246 247 private: 248 SpeculativeJIT* m_jit; 249 NodeIndex m_index; 250 GPRReg m_gprOrInvalid; 251 DataFormat m_format; 252 }; 253 254 class SpeculateStrictInt32Operand { 255 public: 256 explicit SpeculateStrictInt32Operand(SpeculativeJIT* jit, NodeIndex index) 257 : m_jit(jit) 258 , m_index(index) 259 , m_gprOrInvalid(InvalidGPRReg) 260 { 261 ASSERT(m_jit); 262 if (jit->isFilled(index)) 263 gpr(); 264 } 265 266 ~SpeculateStrictInt32Operand() 267 { 268 ASSERT(m_gprOrInvalid != InvalidGPRReg); 269 m_jit->unlock(m_gprOrInvalid); 270 } 271 272 NodeIndex index() const 273 { 274 return m_index; 275 } 276 277 GPRReg gpr() 278 { 279 if (m_gprOrInvalid == InvalidGPRReg) 280 m_gprOrInvalid = m_jit->fillSpeculateIntStrict(index()); 281 return m_gprOrInvalid; 282 } 283 284 MacroAssembler::RegisterID registerID() 285 { 286 return JITCompiler::gprToRegisterID(gpr()); 287 } 288 289 private: 290 SpeculativeJIT* m_jit; 291 NodeIndex m_index; 292 GPRReg m_gprOrInvalid; 293 }; 294 295 class SpeculateCellOperand { 296 public: 297 explicit SpeculateCellOperand(SpeculativeJIT* jit, NodeIndex index) 298 : m_jit(jit) 299 , m_index(index) 300 , m_gprOrInvalid(InvalidGPRReg) 301 { 302 ASSERT(m_jit); 303 if (jit->isFilled(index)) 304 gpr(); 305 } 306 307 ~SpeculateCellOperand() 308 { 309 ASSERT(m_gprOrInvalid != InvalidGPRReg); 310 m_jit->unlock(m_gprOrInvalid); 311 } 312 313 NodeIndex index() const 314 { 315 return m_index; 316 } 317 318 GPRReg gpr() 319 { 320 if (m_gprOrInvalid == InvalidGPRReg) 321 m_gprOrInvalid = m_jit->fillSpeculateCell(index()); 322 return m_gprOrInvalid; 323 } 324 325 MacroAssembler::RegisterID registerID() 326 { 327 return JITCompiler::gprToRegisterID(gpr()); 328 } 329 330 private: 331 SpeculativeJIT* m_jit; 332 NodeIndex m_index; 333 GPRReg m_gprOrInvalid; 334 }; 335 336 337 // === SpeculationCheckIndexIterator === 338 // 339 // This class is used by the non-speculative JIT to check which 340 // nodes require entry points from the speculative path. 341 class SpeculationCheckIndexIterator { 342 public: 343 SpeculationCheckIndexIterator(SpeculationCheckVector& speculationChecks) 344 : m_speculationChecks(speculationChecks) 345 , m_iter(m_speculationChecks.begin()) 346 , m_end(m_speculationChecks.end()) 347 { 348 } 349 350 bool hasCheckAtIndex(NodeIndex nodeIndex) 351 { 352 while (m_iter != m_end) { 353 NodeIndex current = m_iter->m_nodeIndex; 354 if (current >= nodeIndex) 355 return current == nodeIndex; 356 ++m_iter; 357 } 358 return false; 359 } 360 361 private: 362 SpeculationCheckVector& m_speculationChecks; 363 SpeculationCheckVector::Iterator m_iter; 364 SpeculationCheckVector::Iterator m_end; 365 }; 366 367 368 } } // namespace JSC::DFG 369 370 #endif 371 #endif 372 373