Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /* |
michael@0 | 8 | * Everything needed to build actual MIR instructions: the actual opcodes and |
michael@0 | 9 | * instructions, the instruction interface, and use chains. |
michael@0 | 10 | */ |
michael@0 | 11 | |
michael@0 | 12 | #ifndef jit_MIR_h |
michael@0 | 13 | #define jit_MIR_h |
michael@0 | 14 | |
michael@0 | 15 | #include "mozilla/Array.h" |
michael@0 | 16 | |
michael@0 | 17 | #include "jsinfer.h" |
michael@0 | 18 | |
michael@0 | 19 | #include "jit/CompilerRoot.h" |
michael@0 | 20 | #include "jit/FixedList.h" |
michael@0 | 21 | #include "jit/InlineList.h" |
michael@0 | 22 | #include "jit/IonAllocPolicy.h" |
michael@0 | 23 | #include "jit/IonMacroAssembler.h" |
michael@0 | 24 | #include "jit/MOpcodes.h" |
michael@0 | 25 | #include "jit/TypeDescrSet.h" |
michael@0 | 26 | #include "jit/TypePolicy.h" |
michael@0 | 27 | #include "vm/ScopeObject.h" |
michael@0 | 28 | #include "vm/TypedArrayObject.h" |
michael@0 | 29 | |
michael@0 | 30 | namespace js { |
michael@0 | 31 | |
michael@0 | 32 | class StringObject; |
michael@0 | 33 | |
michael@0 | 34 | namespace jit { |
michael@0 | 35 | |
michael@0 | 36 | class BaselineInspector; |
michael@0 | 37 | class ValueNumberData; |
michael@0 | 38 | class Range; |
michael@0 | 39 | |
michael@0 | 40 | static const inline |
michael@0 | 41 | MIRType MIRTypeFromValue(const js::Value &vp) |
michael@0 | 42 | { |
michael@0 | 43 | if (vp.isDouble()) |
michael@0 | 44 | return MIRType_Double; |
michael@0 | 45 | if (vp.isMagic()) { |
michael@0 | 46 | switch (vp.whyMagic()) { |
michael@0 | 47 | case JS_OPTIMIZED_ARGUMENTS: |
michael@0 | 48 | return MIRType_MagicOptimizedArguments; |
michael@0 | 49 | case JS_OPTIMIZED_OUT: |
michael@0 | 50 | return MIRType_MagicOptimizedOut; |
michael@0 | 51 | case JS_ELEMENTS_HOLE: |
michael@0 | 52 | return MIRType_MagicHole; |
michael@0 | 53 | case JS_IS_CONSTRUCTING: |
michael@0 | 54 | return MIRType_MagicIsConstructing; |
michael@0 | 55 | default: |
michael@0 | 56 | MOZ_ASSERT(!"Unexpected magic constant"); |
michael@0 | 57 | } |
michael@0 | 58 | } |
michael@0 | 59 | return MIRTypeFromValueType(vp.extractNonDoubleType()); |
michael@0 | 60 | } |
michael@0 | 61 | |
michael@0 | 62 | #define MIR_FLAG_LIST(_) \ |
michael@0 | 63 | _(InWorklist) \ |
michael@0 | 64 | _(EmittedAtUses) \ |
michael@0 | 65 | _(LoopInvariant) \ |
michael@0 | 66 | _(Commutative) \ |
michael@0 | 67 | _(Movable) /* Allow LICM and GVN to move this instruction */ \ |
michael@0 | 68 | _(Lowered) /* (Debug only) has a virtual register */ \ |
michael@0 | 69 | _(Guard) /* Not removable if uses == 0 */ \ |
michael@0 | 70 | \ |
michael@0 | 71 | /* Keep the flagged instruction in resume points and do not substitute this |
michael@0 | 72 | * instruction by an UndefinedValue. This might be used by call inlining |
michael@0 | 73 | * when a function argument is not used by the inlined instructions. |
michael@0 | 74 | */ \ |
michael@0 | 75 | _(ImplicitlyUsed) \ |
michael@0 | 76 | \ |
michael@0 | 77 | /* The instruction has been marked dead for lazy removal from resume |
michael@0 | 78 | * points. |
michael@0 | 79 | */ \ |
michael@0 | 80 | _(Unused) \ |
michael@0 | 81 | /* Marks if an instruction has fewer uses than the original code. |
michael@0 | 82 | * E.g. UCE can remove code. |
michael@0 | 83 | * Every instruction where an use is/was removed from an instruction and |
michael@0 | 84 | * as a result the number of operands doesn't equal the original code |
michael@0 | 85 | * need to get marked as UseRemoved. This is important for truncation |
michael@0 | 86 | * analysis to know, since if all original uses are still present, |
michael@0 | 87 | * it can ignore resumepoints. |
michael@0 | 88 | * Currently this is done for every pass after IonBuilder and before |
michael@0 | 89 | * Truncate Doubles. So every time removeUse is called, UseRemoved needs |
michael@0 | 90 | * to get set. |
michael@0 | 91 | */ \ |
michael@0 | 92 | _(UseRemoved) |
michael@0 | 93 | |
michael@0 | 94 | class MDefinition; |
michael@0 | 95 | class MInstruction; |
michael@0 | 96 | class MBasicBlock; |
michael@0 | 97 | class MNode; |
michael@0 | 98 | class MUse; |
michael@0 | 99 | class MIRGraph; |
michael@0 | 100 | class MResumePoint; |
michael@0 | 101 | |
michael@0 | 102 | // Represents a use of a node. |
michael@0 | 103 | class MUse : public TempObject, public InlineListNode<MUse> |
michael@0 | 104 | { |
michael@0 | 105 | friend class MDefinition; |
michael@0 | 106 | |
michael@0 | 107 | MDefinition *producer_; // MDefinition that is being used. |
michael@0 | 108 | MNode *consumer_; // The node that is using this operand. |
michael@0 | 109 | uint32_t index_; // The index of this operand in its consumer. |
michael@0 | 110 | |
michael@0 | 111 | MUse(MDefinition *producer, MNode *consumer, uint32_t index) |
michael@0 | 112 | : producer_(producer), |
michael@0 | 113 | consumer_(consumer), |
michael@0 | 114 | index_(index) |
michael@0 | 115 | { } |
michael@0 | 116 | |
michael@0 | 117 | public: |
michael@0 | 118 | // Default constructor for use in vectors. |
michael@0 | 119 | MUse() |
michael@0 | 120 | : producer_(nullptr), consumer_(nullptr), index_(0) |
michael@0 | 121 | { } |
michael@0 | 122 | |
michael@0 | 123 | // Set data inside the MUse. |
michael@0 | 124 | void set(MDefinition *producer, MNode *consumer, uint32_t index) { |
michael@0 | 125 | producer_ = producer; |
michael@0 | 126 | consumer_ = consumer; |
michael@0 | 127 | index_ = index; |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | MDefinition *producer() const { |
michael@0 | 131 | JS_ASSERT(producer_ != nullptr); |
michael@0 | 132 | return producer_; |
michael@0 | 133 | } |
michael@0 | 134 | bool hasProducer() const { |
michael@0 | 135 | return producer_ != nullptr; |
michael@0 | 136 | } |
michael@0 | 137 | MNode *consumer() const { |
michael@0 | 138 | JS_ASSERT(consumer_ != nullptr); |
michael@0 | 139 | return consumer_; |
michael@0 | 140 | } |
michael@0 | 141 | uint32_t index() const { |
michael@0 | 142 | return index_; |
michael@0 | 143 | } |
michael@0 | 144 | }; |
michael@0 | 145 | |
michael@0 | 146 | typedef InlineList<MUse>::iterator MUseIterator; |
michael@0 | 147 | |
michael@0 | 148 | // A node is an entry in the MIR graph. It has two kinds: |
michael@0 | 149 | // MInstruction: an instruction which appears in the IR stream. |
michael@0 | 150 | // MResumePoint: a list of instructions that correspond to the state of the |
michael@0 | 151 | // interpreter/Baseline stack. |
michael@0 | 152 | // |
michael@0 | 153 | // Nodes can hold references to MDefinitions. Each MDefinition has a list of |
michael@0 | 154 | // nodes holding such a reference (its use chain). |
michael@0 | 155 | class MNode : public TempObject |
michael@0 | 156 | { |
michael@0 | 157 | friend class MDefinition; |
michael@0 | 158 | |
michael@0 | 159 | protected: |
michael@0 | 160 | MBasicBlock *block_; // Containing basic block. |
michael@0 | 161 | |
michael@0 | 162 | public: |
michael@0 | 163 | enum Kind { |
michael@0 | 164 | Definition, |
michael@0 | 165 | ResumePoint |
michael@0 | 166 | }; |
michael@0 | 167 | |
michael@0 | 168 | MNode() |
michael@0 | 169 | : block_(nullptr) |
michael@0 | 170 | { } |
michael@0 | 171 | |
michael@0 | 172 | MNode(MBasicBlock *block) |
michael@0 | 173 | : block_(block) |
michael@0 | 174 | { } |
michael@0 | 175 | |
michael@0 | 176 | virtual Kind kind() const = 0; |
michael@0 | 177 | |
michael@0 | 178 | // Returns the definition at a given operand. |
michael@0 | 179 | virtual MDefinition *getOperand(size_t index) const = 0; |
michael@0 | 180 | virtual size_t numOperands() const = 0; |
michael@0 | 181 | |
michael@0 | 182 | bool isDefinition() const { |
michael@0 | 183 | return kind() == Definition; |
michael@0 | 184 | } |
michael@0 | 185 | bool isResumePoint() const { |
michael@0 | 186 | return kind() == ResumePoint; |
michael@0 | 187 | } |
michael@0 | 188 | MBasicBlock *block() const { |
michael@0 | 189 | return block_; |
michael@0 | 190 | } |
michael@0 | 191 | |
michael@0 | 192 | // Instructions needing to hook into type analysis should return a |
michael@0 | 193 | // TypePolicy. |
michael@0 | 194 | virtual TypePolicy *typePolicy() { |
michael@0 | 195 | return nullptr; |
michael@0 | 196 | } |
michael@0 | 197 | |
michael@0 | 198 | // Replaces an already-set operand during iteration over a use chain. |
michael@0 | 199 | MUseIterator replaceOperand(MUseIterator use, MDefinition *ins); |
michael@0 | 200 | |
michael@0 | 201 | // Replaces an already-set operand, updating use information. |
michael@0 | 202 | void replaceOperand(size_t index, MDefinition *ins); |
michael@0 | 203 | |
michael@0 | 204 | // Resets the operand to an uninitialized state, breaking the link |
michael@0 | 205 | // with the previous operand's producer. |
michael@0 | 206 | void discardOperand(size_t index); |
michael@0 | 207 | |
michael@0 | 208 | inline MDefinition *toDefinition(); |
michael@0 | 209 | inline MResumePoint *toResumePoint(); |
michael@0 | 210 | |
michael@0 | 211 | protected: |
michael@0 | 212 | // Sets an unset operand, updating use information. |
michael@0 | 213 | virtual void setOperand(size_t index, MDefinition *operand) = 0; |
michael@0 | 214 | |
michael@0 | 215 | // Gets the MUse corresponding to given operand. |
michael@0 | 216 | virtual MUse *getUseFor(size_t index) = 0; |
michael@0 | 217 | }; |
michael@0 | 218 | |
michael@0 | 219 | class AliasSet { |
michael@0 | 220 | private: |
michael@0 | 221 | uint32_t flags_; |
michael@0 | 222 | |
michael@0 | 223 | public: |
michael@0 | 224 | enum Flag { |
michael@0 | 225 | None_ = 0, |
michael@0 | 226 | ObjectFields = 1 << 0, // shape, class, slots, length etc. |
michael@0 | 227 | Element = 1 << 1, // A member of obj->elements. |
michael@0 | 228 | DynamicSlot = 1 << 2, // A member of obj->slots. |
michael@0 | 229 | FixedSlot = 1 << 3, // A member of obj->fixedSlots(). |
michael@0 | 230 | TypedArrayElement = 1 << 4, // A typed array element. |
michael@0 | 231 | DOMProperty = 1 << 5, // A DOM property |
michael@0 | 232 | FrameArgument = 1 << 6, // An argument kept on the stack frame |
michael@0 | 233 | AsmJSGlobalVar = 1 << 7, // An asm.js global var |
michael@0 | 234 | AsmJSHeap = 1 << 8, // An asm.js heap load |
michael@0 | 235 | TypedArrayLength = 1 << 9,// A typed array's length |
michael@0 | 236 | Last = TypedArrayLength, |
michael@0 | 237 | Any = Last | (Last - 1), |
michael@0 | 238 | |
michael@0 | 239 | NumCategories = 10, |
michael@0 | 240 | |
michael@0 | 241 | // Indicates load or store. |
michael@0 | 242 | Store_ = 1 << 31 |
michael@0 | 243 | }; |
michael@0 | 244 | |
michael@0 | 245 | static_assert((1 << NumCategories) - 1 == Any, |
michael@0 | 246 | "NumCategories must include all flags present in Any"); |
michael@0 | 247 | |
michael@0 | 248 | AliasSet(uint32_t flags) |
michael@0 | 249 | : flags_(flags) |
michael@0 | 250 | { |
michael@0 | 251 | } |
michael@0 | 252 | |
michael@0 | 253 | public: |
michael@0 | 254 | inline bool isNone() const { |
michael@0 | 255 | return flags_ == None_; |
michael@0 | 256 | } |
michael@0 | 257 | uint32_t flags() const { |
michael@0 | 258 | return flags_ & Any; |
michael@0 | 259 | } |
michael@0 | 260 | inline bool isStore() const { |
michael@0 | 261 | return !!(flags_ & Store_); |
michael@0 | 262 | } |
michael@0 | 263 | inline bool isLoad() const { |
michael@0 | 264 | return !isStore() && !isNone(); |
michael@0 | 265 | } |
michael@0 | 266 | inline AliasSet operator |(const AliasSet &other) const { |
michael@0 | 267 | return AliasSet(flags_ | other.flags_); |
michael@0 | 268 | } |
michael@0 | 269 | inline AliasSet operator &(const AliasSet &other) const { |
michael@0 | 270 | return AliasSet(flags_ & other.flags_); |
michael@0 | 271 | } |
michael@0 | 272 | static AliasSet None() { |
michael@0 | 273 | return AliasSet(None_); |
michael@0 | 274 | } |
michael@0 | 275 | static AliasSet Load(uint32_t flags) { |
michael@0 | 276 | JS_ASSERT(flags && !(flags & Store_)); |
michael@0 | 277 | return AliasSet(flags); |
michael@0 | 278 | } |
michael@0 | 279 | static AliasSet Store(uint32_t flags) { |
michael@0 | 280 | JS_ASSERT(flags && !(flags & Store_)); |
michael@0 | 281 | return AliasSet(flags | Store_); |
michael@0 | 282 | } |
michael@0 | 283 | }; |
michael@0 | 284 | |
michael@0 | 285 | // An MDefinition is an SSA name. |
michael@0 | 286 | class MDefinition : public MNode |
michael@0 | 287 | { |
michael@0 | 288 | friend class MBasicBlock; |
michael@0 | 289 | |
michael@0 | 290 | public: |
michael@0 | 291 | enum Opcode { |
michael@0 | 292 | # define DEFINE_OPCODES(op) Op_##op, |
michael@0 | 293 | MIR_OPCODE_LIST(DEFINE_OPCODES) |
michael@0 | 294 | # undef DEFINE_OPCODES |
michael@0 | 295 | Op_Invalid |
michael@0 | 296 | }; |
michael@0 | 297 | |
michael@0 | 298 | private: |
michael@0 | 299 | InlineList<MUse> uses_; // Use chain. |
michael@0 | 300 | uint32_t id_; // Instruction ID, which after block re-ordering |
michael@0 | 301 | // is sorted within a basic block. |
michael@0 | 302 | ValueNumberData *valueNumber_; // The instruction's value number (see GVN for details in use) |
michael@0 | 303 | Range *range_; // Any computed range for this def. |
michael@0 | 304 | MIRType resultType_; // Representation of result type. |
michael@0 | 305 | types::TemporaryTypeSet *resultTypeSet_; // Optional refinement of the result type. |
michael@0 | 306 | uint32_t flags_; // Bit flags. |
michael@0 | 307 | union { |
michael@0 | 308 | MDefinition *dependency_; // Implicit dependency (store, call, etc.) of this instruction. |
michael@0 | 309 | // Used by alias analysis, GVN and LICM. |
michael@0 | 310 | uint32_t virtualRegister_; // Used by lowering to map definitions to virtual registers. |
michael@0 | 311 | }; |
michael@0 | 312 | |
michael@0 | 313 | // Track bailouts by storing the current pc in MIR instruction. Also used |
michael@0 | 314 | // for profiling and keeping track of what the last known pc was. |
michael@0 | 315 | jsbytecode *trackedPc_; |
michael@0 | 316 | |
michael@0 | 317 | private: |
michael@0 | 318 | enum Flag { |
michael@0 | 319 | None = 0, |
michael@0 | 320 | # define DEFINE_FLAG(flag) flag, |
michael@0 | 321 | MIR_FLAG_LIST(DEFINE_FLAG) |
michael@0 | 322 | # undef DEFINE_FLAG |
michael@0 | 323 | Total |
michael@0 | 324 | }; |
michael@0 | 325 | |
michael@0 | 326 | bool hasFlags(uint32_t flags) const { |
michael@0 | 327 | return (flags_ & flags) == flags; |
michael@0 | 328 | } |
michael@0 | 329 | void removeFlags(uint32_t flags) { |
michael@0 | 330 | flags_ &= ~flags; |
michael@0 | 331 | } |
michael@0 | 332 | void setFlags(uint32_t flags) { |
michael@0 | 333 | flags_ |= flags; |
michael@0 | 334 | } |
michael@0 | 335 | |
michael@0 | 336 | protected: |
michael@0 | 337 | virtual void setBlock(MBasicBlock *block) { |
michael@0 | 338 | block_ = block; |
michael@0 | 339 | } |
michael@0 | 340 | |
michael@0 | 341 | public: |
michael@0 | 342 | MDefinition() |
michael@0 | 343 | : id_(0), |
michael@0 | 344 | valueNumber_(nullptr), |
michael@0 | 345 | range_(nullptr), |
michael@0 | 346 | resultType_(MIRType_None), |
michael@0 | 347 | resultTypeSet_(nullptr), |
michael@0 | 348 | flags_(0), |
michael@0 | 349 | dependency_(nullptr), |
michael@0 | 350 | trackedPc_(nullptr) |
michael@0 | 351 | { } |
michael@0 | 352 | |
michael@0 | 353 | virtual Opcode op() const = 0; |
michael@0 | 354 | virtual const char *opName() const = 0; |
michael@0 | 355 | void printName(FILE *fp) const; |
michael@0 | 356 | static void PrintOpcodeName(FILE *fp, Opcode op); |
michael@0 | 357 | virtual void printOpcode(FILE *fp) const; |
michael@0 | 358 | void dump(FILE *fp) const; |
michael@0 | 359 | void dump() const; |
michael@0 | 360 | |
michael@0 | 361 | // For LICM. |
michael@0 | 362 | virtual bool neverHoist() const { return false; } |
michael@0 | 363 | |
michael@0 | 364 | // Also for LICM. Test whether this definition is likely to be a call, which |
michael@0 | 365 | // would clobber all or many of the floating-point registers, such that |
michael@0 | 366 | // hoisting floating-point constants out of containing loops isn't likely to |
michael@0 | 367 | // be worthwhile. |
michael@0 | 368 | virtual bool possiblyCalls() const { return false; } |
michael@0 | 369 | |
michael@0 | 370 | void setTrackedPc(jsbytecode *pc) { |
michael@0 | 371 | trackedPc_ = pc; |
michael@0 | 372 | } |
michael@0 | 373 | |
michael@0 | 374 | jsbytecode *trackedPc() { |
michael@0 | 375 | return trackedPc_; |
michael@0 | 376 | } |
michael@0 | 377 | |
michael@0 | 378 | // Return the range of this value, *before* any bailout checks. Contrast |
michael@0 | 379 | // this with the type() method, and the Range constructor which takes an |
michael@0 | 380 | // MDefinition*, which describe the value *after* any bailout checks. |
michael@0 | 381 | // |
michael@0 | 382 | // Warning: Range analysis is removing the bit-operations such as '| 0' at |
michael@0 | 383 | // the end of the transformations. Using this function to analyse any |
michael@0 | 384 | // operands after the truncate phase of the range analysis will lead to |
michael@0 | 385 | // errors. Instead, one should define the collectRangeInfoPreTrunc() to set |
michael@0 | 386 | // the right set of flags which are dependent on the range of the inputs. |
michael@0 | 387 | Range *range() const { |
michael@0 | 388 | JS_ASSERT(type() != MIRType_None); |
michael@0 | 389 | return range_; |
michael@0 | 390 | } |
michael@0 | 391 | void setRange(Range *range) { |
michael@0 | 392 | JS_ASSERT(type() != MIRType_None); |
michael@0 | 393 | range_ = range; |
michael@0 | 394 | } |
michael@0 | 395 | |
michael@0 | 396 | virtual HashNumber valueHash() const; |
michael@0 | 397 | virtual bool congruentTo(const MDefinition *ins) const { |
michael@0 | 398 | return false; |
michael@0 | 399 | } |
michael@0 | 400 | bool congruentIfOperandsEqual(const MDefinition *ins) const; |
michael@0 | 401 | virtual MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 402 | virtual void analyzeEdgeCasesForward(); |
michael@0 | 403 | virtual void analyzeEdgeCasesBackward(); |
michael@0 | 404 | |
michael@0 | 405 | virtual bool truncate(); |
michael@0 | 406 | virtual bool isOperandTruncated(size_t index) const; |
michael@0 | 407 | |
michael@0 | 408 | // Compute an absolute or symbolic range for the value of this node. |
michael@0 | 409 | virtual void computeRange(TempAllocator &alloc) { |
michael@0 | 410 | } |
michael@0 | 411 | |
michael@0 | 412 | // Collect information from the pre-truncated ranges. |
michael@0 | 413 | virtual void collectRangeInfoPreTrunc() { |
michael@0 | 414 | } |
michael@0 | 415 | |
michael@0 | 416 | MNode::Kind kind() const { |
michael@0 | 417 | return MNode::Definition; |
michael@0 | 418 | } |
michael@0 | 419 | |
michael@0 | 420 | uint32_t id() const { |
michael@0 | 421 | JS_ASSERT(block_); |
michael@0 | 422 | return id_; |
michael@0 | 423 | } |
michael@0 | 424 | void setId(uint32_t id) { |
michael@0 | 425 | id_ = id; |
michael@0 | 426 | } |
michael@0 | 427 | |
michael@0 | 428 | uint32_t valueNumber() const; |
michael@0 | 429 | void setValueNumber(uint32_t vn); |
michael@0 | 430 | ValueNumberData *valueNumberData() { |
michael@0 | 431 | return valueNumber_; |
michael@0 | 432 | } |
michael@0 | 433 | void clearValueNumberData() { |
michael@0 | 434 | valueNumber_ = nullptr; |
michael@0 | 435 | } |
michael@0 | 436 | void setValueNumberData(ValueNumberData *vn) { |
michael@0 | 437 | JS_ASSERT(valueNumber_ == nullptr); |
michael@0 | 438 | valueNumber_ = vn; |
michael@0 | 439 | } |
michael@0 | 440 | #define FLAG_ACCESSOR(flag) \ |
michael@0 | 441 | bool is##flag() const {\ |
michael@0 | 442 | return hasFlags(1 << flag);\ |
michael@0 | 443 | }\ |
michael@0 | 444 | void set##flag() {\ |
michael@0 | 445 | JS_ASSERT(!hasFlags(1 << flag));\ |
michael@0 | 446 | setFlags(1 << flag);\ |
michael@0 | 447 | }\ |
michael@0 | 448 | void setNot##flag() {\ |
michael@0 | 449 | JS_ASSERT(hasFlags(1 << flag));\ |
michael@0 | 450 | removeFlags(1 << flag);\ |
michael@0 | 451 | }\ |
michael@0 | 452 | void set##flag##Unchecked() {\ |
michael@0 | 453 | setFlags(1 << flag);\ |
michael@0 | 454 | } |
michael@0 | 455 | |
michael@0 | 456 | MIR_FLAG_LIST(FLAG_ACCESSOR) |
michael@0 | 457 | #undef FLAG_ACCESSOR |
michael@0 | 458 | |
michael@0 | 459 | // Return the type of this value. This may be speculative, and enforced |
michael@0 | 460 | // dynamically with the use of bailout checks. If all the bailout checks |
michael@0 | 461 | // pass, the value will have this type. |
michael@0 | 462 | // |
michael@0 | 463 | // Unless this is an MUrsh that has bailouts disabled, which, as a special |
michael@0 | 464 | // case, may return a value in (INT32_MAX,UINT32_MAX] even when its type() |
michael@0 | 465 | // is MIRType_Int32. |
michael@0 | 466 | MIRType type() const { |
michael@0 | 467 | return resultType_; |
michael@0 | 468 | } |
michael@0 | 469 | |
michael@0 | 470 | types::TemporaryTypeSet *resultTypeSet() const { |
michael@0 | 471 | return resultTypeSet_; |
michael@0 | 472 | } |
michael@0 | 473 | bool emptyResultTypeSet() const; |
michael@0 | 474 | |
michael@0 | 475 | bool mightBeType(MIRType type) const { |
michael@0 | 476 | MOZ_ASSERT(type != MIRType_Value); |
michael@0 | 477 | |
michael@0 | 478 | if (type == this->type()) |
michael@0 | 479 | return true; |
michael@0 | 480 | |
michael@0 | 481 | if (MIRType_Value != this->type()) |
michael@0 | 482 | return false; |
michael@0 | 483 | |
michael@0 | 484 | return !resultTypeSet() || resultTypeSet()->mightBeMIRType(type); |
michael@0 | 485 | } |
michael@0 | 486 | |
michael@0 | 487 | // Float32 specialization operations (see big comment in IonAnalysis before the Float32 |
michael@0 | 488 | // specialization algorithm). |
michael@0 | 489 | virtual bool isFloat32Commutative() const { return false; } |
michael@0 | 490 | virtual bool canProduceFloat32() const { return false; } |
michael@0 | 491 | virtual bool canConsumeFloat32(MUse *use) const { return false; } |
michael@0 | 492 | virtual void trySpecializeFloat32(TempAllocator &alloc) {} |
michael@0 | 493 | #ifdef DEBUG |
michael@0 | 494 | // Used during the pass that checks that Float32 flow into valid MDefinitions |
michael@0 | 495 | virtual bool isConsistentFloat32Use(MUse *use) const { |
michael@0 | 496 | return type() == MIRType_Float32 || canConsumeFloat32(use); |
michael@0 | 497 | } |
michael@0 | 498 | #endif |
michael@0 | 499 | |
michael@0 | 500 | // Returns the beginning of this definition's use chain. |
michael@0 | 501 | MUseIterator usesBegin() const { |
michael@0 | 502 | return uses_.begin(); |
michael@0 | 503 | } |
michael@0 | 504 | |
michael@0 | 505 | // Returns the end of this definition's use chain. |
michael@0 | 506 | MUseIterator usesEnd() const { |
michael@0 | 507 | return uses_.end(); |
michael@0 | 508 | } |
michael@0 | 509 | |
michael@0 | 510 | bool canEmitAtUses() const { |
michael@0 | 511 | return !isEmittedAtUses(); |
michael@0 | 512 | } |
michael@0 | 513 | |
michael@0 | 514 | // Removes a use at the given position |
michael@0 | 515 | MUseIterator removeUse(MUseIterator use); |
michael@0 | 516 | void removeUse(MUse *use) { |
michael@0 | 517 | uses_.remove(use); |
michael@0 | 518 | } |
michael@0 | 519 | |
michael@0 | 520 | // Number of uses of this instruction. |
michael@0 | 521 | size_t useCount() const; |
michael@0 | 522 | |
michael@0 | 523 | // Number of uses of this instruction. |
michael@0 | 524 | // (only counting MDefinitions, ignoring MResumePoints) |
michael@0 | 525 | size_t defUseCount() const; |
michael@0 | 526 | |
michael@0 | 527 | // Test whether this MDefinition has exactly one use. |
michael@0 | 528 | bool hasOneUse() const; |
michael@0 | 529 | |
michael@0 | 530 | // Test whether this MDefinition has exactly one use. |
michael@0 | 531 | // (only counting MDefinitions, ignoring MResumePoints) |
michael@0 | 532 | bool hasOneDefUse() const; |
michael@0 | 533 | |
michael@0 | 534 | // Test whether this MDefinition has at least one use. |
michael@0 | 535 | // (only counting MDefinitions, ignoring MResumePoints) |
michael@0 | 536 | bool hasDefUses() const; |
michael@0 | 537 | |
michael@0 | 538 | bool hasUses() const { |
michael@0 | 539 | return !uses_.empty(); |
michael@0 | 540 | } |
michael@0 | 541 | |
michael@0 | 542 | virtual bool isControlInstruction() const { |
michael@0 | 543 | return false; |
michael@0 | 544 | } |
michael@0 | 545 | |
michael@0 | 546 | void addUse(MUse *use) { |
michael@0 | 547 | uses_.pushFront(use); |
michael@0 | 548 | } |
michael@0 | 549 | void replaceAllUsesWith(MDefinition *dom); |
michael@0 | 550 | |
michael@0 | 551 | // Mark this instruction as having replaced all uses of ins, as during GVN, |
michael@0 | 552 | // returning false if the replacement should not be performed. For use when |
michael@0 | 553 | // GVN eliminates instructions which are not equivalent to one another. |
michael@0 | 554 | virtual bool updateForReplacement(MDefinition *ins) { |
michael@0 | 555 | return true; |
michael@0 | 556 | } |
michael@0 | 557 | |
michael@0 | 558 | void setVirtualRegister(uint32_t vreg) { |
michael@0 | 559 | virtualRegister_ = vreg; |
michael@0 | 560 | #ifdef DEBUG |
michael@0 | 561 | setLoweredUnchecked(); |
michael@0 | 562 | #endif |
michael@0 | 563 | } |
michael@0 | 564 | uint32_t virtualRegister() const { |
michael@0 | 565 | JS_ASSERT(isLowered()); |
michael@0 | 566 | return virtualRegister_; |
michael@0 | 567 | } |
michael@0 | 568 | |
michael@0 | 569 | public: |
michael@0 | 570 | // Opcode testing and casts. |
michael@0 | 571 | # define OPCODE_CASTS(opcode) \ |
michael@0 | 572 | bool is##opcode() const { \ |
michael@0 | 573 | return op() == Op_##opcode; \ |
michael@0 | 574 | } \ |
michael@0 | 575 | inline M##opcode *to##opcode(); \ |
michael@0 | 576 | inline const M##opcode *to##opcode() const; |
michael@0 | 577 | MIR_OPCODE_LIST(OPCODE_CASTS) |
michael@0 | 578 | # undef OPCODE_CASTS |
michael@0 | 579 | |
michael@0 | 580 | inline MInstruction *toInstruction(); |
michael@0 | 581 | bool isInstruction() const { |
michael@0 | 582 | return !isPhi(); |
michael@0 | 583 | } |
michael@0 | 584 | |
michael@0 | 585 | void setResultType(MIRType type) { |
michael@0 | 586 | resultType_ = type; |
michael@0 | 587 | } |
michael@0 | 588 | void setResultTypeSet(types::TemporaryTypeSet *types) { |
michael@0 | 589 | resultTypeSet_ = types; |
michael@0 | 590 | } |
michael@0 | 591 | |
michael@0 | 592 | MDefinition *dependency() const { |
michael@0 | 593 | return dependency_; |
michael@0 | 594 | } |
michael@0 | 595 | void setDependency(MDefinition *dependency) { |
michael@0 | 596 | dependency_ = dependency; |
michael@0 | 597 | } |
michael@0 | 598 | virtual AliasSet getAliasSet() const { |
michael@0 | 599 | // Instructions are effectful by default. |
michael@0 | 600 | return AliasSet::Store(AliasSet::Any); |
michael@0 | 601 | } |
michael@0 | 602 | bool isEffectful() const { |
michael@0 | 603 | return getAliasSet().isStore(); |
michael@0 | 604 | } |
michael@0 | 605 | virtual bool mightAlias(const MDefinition *store) const { |
michael@0 | 606 | // Return whether this load may depend on the specified store, given |
michael@0 | 607 | // that the alias sets intersect. This may be refined to exclude |
michael@0 | 608 | // possible aliasing in cases where alias set flags are too imprecise. |
michael@0 | 609 | JS_ASSERT(!isEffectful() && store->isEffectful()); |
michael@0 | 610 | JS_ASSERT(getAliasSet().flags() & store->getAliasSet().flags()); |
michael@0 | 611 | return true; |
michael@0 | 612 | } |
michael@0 | 613 | }; |
michael@0 | 614 | |
michael@0 | 615 | // An MUseDefIterator walks over uses in a definition, skipping any use that is |
michael@0 | 616 | // not a definition. Items from the use list must not be deleted during |
michael@0 | 617 | // iteration. |
michael@0 | 618 | class MUseDefIterator |
michael@0 | 619 | { |
michael@0 | 620 | MDefinition *def_; |
michael@0 | 621 | MUseIterator current_; |
michael@0 | 622 | |
michael@0 | 623 | MUseIterator search(MUseIterator start) { |
michael@0 | 624 | MUseIterator i(start); |
michael@0 | 625 | for (; i != def_->usesEnd(); i++) { |
michael@0 | 626 | if (i->consumer()->isDefinition()) |
michael@0 | 627 | return i; |
michael@0 | 628 | } |
michael@0 | 629 | return def_->usesEnd(); |
michael@0 | 630 | } |
michael@0 | 631 | |
michael@0 | 632 | public: |
michael@0 | 633 | MUseDefIterator(MDefinition *def) |
michael@0 | 634 | : def_(def), |
michael@0 | 635 | current_(search(def->usesBegin())) |
michael@0 | 636 | { } |
michael@0 | 637 | |
michael@0 | 638 | operator bool() const { |
michael@0 | 639 | return current_ != def_->usesEnd(); |
michael@0 | 640 | } |
michael@0 | 641 | MUseDefIterator operator ++(int) { |
michael@0 | 642 | MUseDefIterator old(*this); |
michael@0 | 643 | if (current_ != def_->usesEnd()) |
michael@0 | 644 | current_++; |
michael@0 | 645 | current_ = search(current_); |
michael@0 | 646 | return old; |
michael@0 | 647 | } |
michael@0 | 648 | MUse *use() const { |
michael@0 | 649 | return *current_; |
michael@0 | 650 | } |
michael@0 | 651 | MDefinition *def() const { |
michael@0 | 652 | return current_->consumer()->toDefinition(); |
michael@0 | 653 | } |
michael@0 | 654 | size_t index() const { |
michael@0 | 655 | return current_->index(); |
michael@0 | 656 | } |
michael@0 | 657 | }; |
michael@0 | 658 | |
michael@0 | 659 | // An instruction is an SSA name that is inserted into a basic block's IR |
michael@0 | 660 | // stream. |
michael@0 | 661 | class MInstruction |
michael@0 | 662 | : public MDefinition, |
michael@0 | 663 | public InlineListNode<MInstruction> |
michael@0 | 664 | { |
michael@0 | 665 | MResumePoint *resumePoint_; |
michael@0 | 666 | |
michael@0 | 667 | public: |
michael@0 | 668 | MInstruction() |
michael@0 | 669 | : resumePoint_(nullptr) |
michael@0 | 670 | { } |
michael@0 | 671 | |
michael@0 | 672 | virtual bool accept(MInstructionVisitor *visitor) = 0; |
michael@0 | 673 | |
michael@0 | 674 | void setResumePoint(MResumePoint *resumePoint) { |
michael@0 | 675 | JS_ASSERT(!resumePoint_); |
michael@0 | 676 | resumePoint_ = resumePoint; |
michael@0 | 677 | } |
michael@0 | 678 | MResumePoint *resumePoint() const { |
michael@0 | 679 | return resumePoint_; |
michael@0 | 680 | } |
michael@0 | 681 | }; |
michael@0 | 682 | |
michael@0 | 683 | #define INSTRUCTION_HEADER(opcode) \ |
michael@0 | 684 | Opcode op() const { \ |
michael@0 | 685 | return MDefinition::Op_##opcode; \ |
michael@0 | 686 | } \ |
michael@0 | 687 | const char *opName() const { \ |
michael@0 | 688 | return #opcode; \ |
michael@0 | 689 | } \ |
michael@0 | 690 | bool accept(MInstructionVisitor *visitor) { \ |
michael@0 | 691 | return visitor->visit##opcode(this); \ |
michael@0 | 692 | } |
michael@0 | 693 | |
michael@0 | 694 | template <size_t Arity> |
michael@0 | 695 | class MAryInstruction : public MInstruction |
michael@0 | 696 | { |
michael@0 | 697 | protected: |
michael@0 | 698 | mozilla::Array<MUse, Arity> operands_; |
michael@0 | 699 | |
michael@0 | 700 | void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 701 | operands_[index].set(operand, this, index); |
michael@0 | 702 | operand->addUse(&operands_[index]); |
michael@0 | 703 | } |
michael@0 | 704 | |
michael@0 | 705 | MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 706 | return &operands_[index]; |
michael@0 | 707 | } |
michael@0 | 708 | |
michael@0 | 709 | public: |
michael@0 | 710 | MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 711 | return operands_[index].producer(); |
michael@0 | 712 | } |
michael@0 | 713 | size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 714 | return Arity; |
michael@0 | 715 | } |
michael@0 | 716 | }; |
michael@0 | 717 | |
michael@0 | 718 | class MNullaryInstruction : public MAryInstruction<0> |
michael@0 | 719 | { }; |
michael@0 | 720 | |
michael@0 | 721 | class MUnaryInstruction : public MAryInstruction<1> |
michael@0 | 722 | { |
michael@0 | 723 | protected: |
michael@0 | 724 | MUnaryInstruction(MDefinition *ins) |
michael@0 | 725 | { |
michael@0 | 726 | setOperand(0, ins); |
michael@0 | 727 | } |
michael@0 | 728 | |
michael@0 | 729 | public: |
michael@0 | 730 | MDefinition *input() const { |
michael@0 | 731 | return getOperand(0); |
michael@0 | 732 | } |
michael@0 | 733 | }; |
michael@0 | 734 | |
michael@0 | 735 | class MBinaryInstruction : public MAryInstruction<2> |
michael@0 | 736 | { |
michael@0 | 737 | protected: |
michael@0 | 738 | MBinaryInstruction(MDefinition *left, MDefinition *right) |
michael@0 | 739 | { |
michael@0 | 740 | setOperand(0, left); |
michael@0 | 741 | setOperand(1, right); |
michael@0 | 742 | } |
michael@0 | 743 | |
michael@0 | 744 | public: |
michael@0 | 745 | MDefinition *lhs() const { |
michael@0 | 746 | return getOperand(0); |
michael@0 | 747 | } |
michael@0 | 748 | MDefinition *rhs() const { |
michael@0 | 749 | return getOperand(1); |
michael@0 | 750 | } |
michael@0 | 751 | |
michael@0 | 752 | protected: |
michael@0 | 753 | HashNumber valueHash() const |
michael@0 | 754 | { |
michael@0 | 755 | MDefinition *lhs = getOperand(0); |
michael@0 | 756 | MDefinition *rhs = getOperand(1); |
michael@0 | 757 | |
michael@0 | 758 | return op() ^ lhs->valueNumber() ^ rhs->valueNumber(); |
michael@0 | 759 | } |
michael@0 | 760 | void swapOperands() { |
michael@0 | 761 | MDefinition *temp = getOperand(0); |
michael@0 | 762 | replaceOperand(0, getOperand(1)); |
michael@0 | 763 | replaceOperand(1, temp); |
michael@0 | 764 | } |
michael@0 | 765 | |
michael@0 | 766 | bool binaryCongruentTo(const MDefinition *ins) const |
michael@0 | 767 | { |
michael@0 | 768 | if (op() != ins->op()) |
michael@0 | 769 | return false; |
michael@0 | 770 | |
michael@0 | 771 | if (type() != ins->type()) |
michael@0 | 772 | return false; |
michael@0 | 773 | |
michael@0 | 774 | if (isEffectful() || ins->isEffectful()) |
michael@0 | 775 | return false; |
michael@0 | 776 | |
michael@0 | 777 | const MDefinition *left = getOperand(0); |
michael@0 | 778 | const MDefinition *right = getOperand(1); |
michael@0 | 779 | const MDefinition *tmp; |
michael@0 | 780 | |
michael@0 | 781 | if (isCommutative() && left->valueNumber() > right->valueNumber()) { |
michael@0 | 782 | tmp = right; |
michael@0 | 783 | right = left; |
michael@0 | 784 | left = tmp; |
michael@0 | 785 | } |
michael@0 | 786 | |
michael@0 | 787 | const MBinaryInstruction *bi = static_cast<const MBinaryInstruction *>(ins); |
michael@0 | 788 | const MDefinition *insLeft = bi->getOperand(0); |
michael@0 | 789 | const MDefinition *insRight = bi->getOperand(1); |
michael@0 | 790 | if (isCommutative() && insLeft->valueNumber() > insRight->valueNumber()) { |
michael@0 | 791 | tmp = insRight; |
michael@0 | 792 | insRight = insLeft; |
michael@0 | 793 | insLeft = tmp; |
michael@0 | 794 | } |
michael@0 | 795 | |
michael@0 | 796 | return (left->valueNumber() == insLeft->valueNumber()) && |
michael@0 | 797 | (right->valueNumber() == insRight->valueNumber()); |
michael@0 | 798 | } |
michael@0 | 799 | |
michael@0 | 800 | // Return true if the operands to this instruction are both unsigned, |
michael@0 | 801 | // in which case any wrapping operands were replaced with the underlying |
michael@0 | 802 | // int32 operands. |
michael@0 | 803 | bool tryUseUnsignedOperands(); |
michael@0 | 804 | }; |
michael@0 | 805 | |
michael@0 | 806 | class MTernaryInstruction : public MAryInstruction<3> |
michael@0 | 807 | { |
michael@0 | 808 | protected: |
michael@0 | 809 | MTernaryInstruction(MDefinition *first, MDefinition *second, MDefinition *third) |
michael@0 | 810 | { |
michael@0 | 811 | setOperand(0, first); |
michael@0 | 812 | setOperand(1, second); |
michael@0 | 813 | setOperand(2, third); |
michael@0 | 814 | } |
michael@0 | 815 | |
michael@0 | 816 | protected: |
michael@0 | 817 | HashNumber valueHash() const |
michael@0 | 818 | { |
michael@0 | 819 | MDefinition *first = getOperand(0); |
michael@0 | 820 | MDefinition *second = getOperand(1); |
michael@0 | 821 | MDefinition *third = getOperand(2); |
michael@0 | 822 | |
michael@0 | 823 | return op() ^ first->valueNumber() ^ second->valueNumber() ^ third->valueNumber(); |
michael@0 | 824 | } |
michael@0 | 825 | }; |
michael@0 | 826 | |
michael@0 | 827 | class MQuaternaryInstruction : public MAryInstruction<4> |
michael@0 | 828 | { |
michael@0 | 829 | protected: |
michael@0 | 830 | MQuaternaryInstruction(MDefinition *first, MDefinition *second, |
michael@0 | 831 | MDefinition *third, MDefinition *fourth) |
michael@0 | 832 | { |
michael@0 | 833 | setOperand(0, first); |
michael@0 | 834 | setOperand(1, second); |
michael@0 | 835 | setOperand(2, third); |
michael@0 | 836 | setOperand(3, fourth); |
michael@0 | 837 | } |
michael@0 | 838 | |
michael@0 | 839 | protected: |
michael@0 | 840 | HashNumber valueHash() const |
michael@0 | 841 | { |
michael@0 | 842 | MDefinition *first = getOperand(0); |
michael@0 | 843 | MDefinition *second = getOperand(1); |
michael@0 | 844 | MDefinition *third = getOperand(2); |
michael@0 | 845 | MDefinition *fourth = getOperand(3); |
michael@0 | 846 | |
michael@0 | 847 | return op() ^ first->valueNumber() ^ second->valueNumber() ^ |
michael@0 | 848 | third->valueNumber() ^ fourth->valueNumber(); |
michael@0 | 849 | } |
michael@0 | 850 | }; |
michael@0 | 851 | |
michael@0 | 852 | // Generates an LSnapshot without further effect. |
michael@0 | 853 | class MStart : public MNullaryInstruction |
michael@0 | 854 | { |
michael@0 | 855 | public: |
michael@0 | 856 | enum StartType { |
michael@0 | 857 | StartType_Default, |
michael@0 | 858 | StartType_Osr |
michael@0 | 859 | }; |
michael@0 | 860 | |
michael@0 | 861 | private: |
michael@0 | 862 | StartType startType_; |
michael@0 | 863 | |
michael@0 | 864 | private: |
michael@0 | 865 | MStart(StartType startType) |
michael@0 | 866 | : startType_(startType) |
michael@0 | 867 | { } |
michael@0 | 868 | |
michael@0 | 869 | public: |
michael@0 | 870 | INSTRUCTION_HEADER(Start) |
michael@0 | 871 | static MStart *New(TempAllocator &alloc, StartType startType) { |
michael@0 | 872 | return new(alloc) MStart(startType); |
michael@0 | 873 | } |
michael@0 | 874 | |
michael@0 | 875 | StartType startType() { |
michael@0 | 876 | return startType_; |
michael@0 | 877 | } |
michael@0 | 878 | }; |
michael@0 | 879 | |
michael@0 | 880 | // Instruction marking on entrypoint for on-stack replacement. |
michael@0 | 881 | // OSR may occur at loop headers (at JSOP_TRACE). |
michael@0 | 882 | // There is at most one MOsrEntry per MIRGraph. |
michael@0 | 883 | class MOsrEntry : public MNullaryInstruction |
michael@0 | 884 | { |
michael@0 | 885 | protected: |
michael@0 | 886 | MOsrEntry() { |
michael@0 | 887 | setResultType(MIRType_Pointer); |
michael@0 | 888 | } |
michael@0 | 889 | |
michael@0 | 890 | public: |
michael@0 | 891 | INSTRUCTION_HEADER(OsrEntry) |
michael@0 | 892 | static MOsrEntry *New(TempAllocator &alloc) { |
michael@0 | 893 | return new(alloc) MOsrEntry; |
michael@0 | 894 | } |
michael@0 | 895 | }; |
michael@0 | 896 | |
michael@0 | 897 | // No-op instruction. This cannot be moved or eliminated, and is intended for |
michael@0 | 898 | // anchoring resume points at arbitrary points in a block. |
michael@0 | 899 | class MNop : public MNullaryInstruction |
michael@0 | 900 | { |
michael@0 | 901 | protected: |
michael@0 | 902 | MNop() { |
michael@0 | 903 | } |
michael@0 | 904 | |
michael@0 | 905 | public: |
michael@0 | 906 | INSTRUCTION_HEADER(Nop) |
michael@0 | 907 | static MNop *New(TempAllocator &alloc) { |
michael@0 | 908 | return new(alloc) MNop(); |
michael@0 | 909 | } |
michael@0 | 910 | |
michael@0 | 911 | AliasSet getAliasSet() const { |
michael@0 | 912 | return AliasSet::None(); |
michael@0 | 913 | } |
michael@0 | 914 | }; |
michael@0 | 915 | |
michael@0 | 916 | // A constant js::Value. |
michael@0 | 917 | class MConstant : public MNullaryInstruction |
michael@0 | 918 | { |
michael@0 | 919 | Value value_; |
michael@0 | 920 | |
michael@0 | 921 | protected: |
michael@0 | 922 | MConstant(const Value &v, types::CompilerConstraintList *constraints); |
michael@0 | 923 | |
michael@0 | 924 | public: |
michael@0 | 925 | INSTRUCTION_HEADER(Constant) |
michael@0 | 926 | static MConstant *New(TempAllocator &alloc, const Value &v, |
michael@0 | 927 | types::CompilerConstraintList *constraints = nullptr); |
michael@0 | 928 | static MConstant *NewAsmJS(TempAllocator &alloc, const Value &v, MIRType type); |
michael@0 | 929 | |
michael@0 | 930 | const js::Value &value() const { |
michael@0 | 931 | return value_; |
michael@0 | 932 | } |
michael@0 | 933 | const js::Value *vp() const { |
michael@0 | 934 | return &value_; |
michael@0 | 935 | } |
michael@0 | 936 | const bool valueToBoolean() const { |
michael@0 | 937 | // A hack to avoid this wordy pattern everywhere in the JIT. |
michael@0 | 938 | return ToBoolean(HandleValue::fromMarkedLocation(&value_)); |
michael@0 | 939 | } |
michael@0 | 940 | |
michael@0 | 941 | void printOpcode(FILE *fp) const; |
michael@0 | 942 | |
michael@0 | 943 | HashNumber valueHash() const; |
michael@0 | 944 | bool congruentTo(const MDefinition *ins) const; |
michael@0 | 945 | |
michael@0 | 946 | AliasSet getAliasSet() const { |
michael@0 | 947 | return AliasSet::None(); |
michael@0 | 948 | } |
michael@0 | 949 | |
michael@0 | 950 | bool updateForReplacement(MDefinition *def) { |
michael@0 | 951 | MConstant *c = def->toConstant(); |
michael@0 | 952 | // During constant folding, we don't want to replace a float32 |
michael@0 | 953 | // value by a double value. |
michael@0 | 954 | if (type() == MIRType_Float32) |
michael@0 | 955 | return c->type() == MIRType_Float32; |
michael@0 | 956 | if (type() == MIRType_Double) |
michael@0 | 957 | return c->type() != MIRType_Float32; |
michael@0 | 958 | return true; |
michael@0 | 959 | } |
michael@0 | 960 | |
michael@0 | 961 | void computeRange(TempAllocator &alloc); |
michael@0 | 962 | bool truncate(); |
michael@0 | 963 | |
michael@0 | 964 | bool canProduceFloat32() const; |
michael@0 | 965 | }; |
michael@0 | 966 | |
michael@0 | 967 | // Deep clone a constant JSObject. |
michael@0 | 968 | class MCloneLiteral |
michael@0 | 969 | : public MUnaryInstruction, |
michael@0 | 970 | public ObjectPolicy<0> |
michael@0 | 971 | { |
michael@0 | 972 | protected: |
michael@0 | 973 | MCloneLiteral(MDefinition *obj) |
michael@0 | 974 | : MUnaryInstruction(obj) |
michael@0 | 975 | { |
michael@0 | 976 | setResultType(MIRType_Object); |
michael@0 | 977 | } |
michael@0 | 978 | |
michael@0 | 979 | public: |
michael@0 | 980 | INSTRUCTION_HEADER(CloneLiteral) |
michael@0 | 981 | static MCloneLiteral *New(TempAllocator &alloc, MDefinition *obj); |
michael@0 | 982 | |
michael@0 | 983 | TypePolicy *typePolicy() { |
michael@0 | 984 | return this; |
michael@0 | 985 | } |
michael@0 | 986 | }; |
michael@0 | 987 | |
michael@0 | 988 | class MParameter : public MNullaryInstruction |
michael@0 | 989 | { |
michael@0 | 990 | int32_t index_; |
michael@0 | 991 | |
michael@0 | 992 | public: |
michael@0 | 993 | static const int32_t THIS_SLOT = -1; |
michael@0 | 994 | |
michael@0 | 995 | MParameter(int32_t index, types::TemporaryTypeSet *types) |
michael@0 | 996 | : index_(index) |
michael@0 | 997 | { |
michael@0 | 998 | setResultType(MIRType_Value); |
michael@0 | 999 | setResultTypeSet(types); |
michael@0 | 1000 | } |
michael@0 | 1001 | |
michael@0 | 1002 | public: |
michael@0 | 1003 | INSTRUCTION_HEADER(Parameter) |
michael@0 | 1004 | static MParameter *New(TempAllocator &alloc, int32_t index, types::TemporaryTypeSet *types); |
michael@0 | 1005 | |
michael@0 | 1006 | int32_t index() const { |
michael@0 | 1007 | return index_; |
michael@0 | 1008 | } |
michael@0 | 1009 | void printOpcode(FILE *fp) const; |
michael@0 | 1010 | |
michael@0 | 1011 | HashNumber valueHash() const; |
michael@0 | 1012 | bool congruentTo(const MDefinition *ins) const; |
michael@0 | 1013 | }; |
michael@0 | 1014 | |
michael@0 | 1015 | class MCallee : public MNullaryInstruction |
michael@0 | 1016 | { |
michael@0 | 1017 | public: |
michael@0 | 1018 | MCallee() |
michael@0 | 1019 | { |
michael@0 | 1020 | setResultType(MIRType_Object); |
michael@0 | 1021 | setMovable(); |
michael@0 | 1022 | } |
michael@0 | 1023 | |
michael@0 | 1024 | public: |
michael@0 | 1025 | INSTRUCTION_HEADER(Callee) |
michael@0 | 1026 | |
michael@0 | 1027 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 1028 | return congruentIfOperandsEqual(ins); |
michael@0 | 1029 | } |
michael@0 | 1030 | |
michael@0 | 1031 | static MCallee *New(TempAllocator &alloc) { |
michael@0 | 1032 | return new(alloc) MCallee(); |
michael@0 | 1033 | } |
michael@0 | 1034 | AliasSet getAliasSet() const { |
michael@0 | 1035 | return AliasSet::None(); |
michael@0 | 1036 | } |
michael@0 | 1037 | }; |
michael@0 | 1038 | |
michael@0 | 1039 | class MControlInstruction : public MInstruction |
michael@0 | 1040 | { |
michael@0 | 1041 | public: |
michael@0 | 1042 | MControlInstruction() |
michael@0 | 1043 | { } |
michael@0 | 1044 | |
michael@0 | 1045 | virtual size_t numSuccessors() const = 0; |
michael@0 | 1046 | virtual MBasicBlock *getSuccessor(size_t i) const = 0; |
michael@0 | 1047 | virtual void replaceSuccessor(size_t i, MBasicBlock *successor) = 0; |
michael@0 | 1048 | |
michael@0 | 1049 | bool isControlInstruction() const { |
michael@0 | 1050 | return true; |
michael@0 | 1051 | } |
michael@0 | 1052 | |
michael@0 | 1053 | void printOpcode(FILE *fp) const; |
michael@0 | 1054 | }; |
michael@0 | 1055 | |
michael@0 | 1056 | class MTableSwitch MOZ_FINAL |
michael@0 | 1057 | : public MControlInstruction, |
michael@0 | 1058 | public NoFloatPolicy<0> |
michael@0 | 1059 | { |
michael@0 | 1060 | // The successors of the tableswitch |
michael@0 | 1061 | // - First successor = the default case |
michael@0 | 1062 | // - Successor 2 and higher = the cases sorted on case index. |
michael@0 | 1063 | Vector<MBasicBlock*, 0, IonAllocPolicy> successors_; |
michael@0 | 1064 | Vector<size_t, 0, IonAllocPolicy> cases_; |
michael@0 | 1065 | |
michael@0 | 1066 | // Contains the blocks/cases that still need to get build |
michael@0 | 1067 | Vector<MBasicBlock*, 0, IonAllocPolicy> blocks_; |
michael@0 | 1068 | |
michael@0 | 1069 | MUse operand_; |
michael@0 | 1070 | int32_t low_; |
michael@0 | 1071 | int32_t high_; |
michael@0 | 1072 | |
michael@0 | 1073 | MTableSwitch(TempAllocator &alloc, MDefinition *ins, |
michael@0 | 1074 | int32_t low, int32_t high) |
michael@0 | 1075 | : successors_(alloc), |
michael@0 | 1076 | cases_(alloc), |
michael@0 | 1077 | blocks_(alloc), |
michael@0 | 1078 | low_(low), |
michael@0 | 1079 | high_(high) |
michael@0 | 1080 | { |
michael@0 | 1081 | setOperand(0, ins); |
michael@0 | 1082 | } |
michael@0 | 1083 | |
michael@0 | 1084 | protected: |
michael@0 | 1085 | void setOperand(size_t index, MDefinition *operand) { |
michael@0 | 1086 | JS_ASSERT(index == 0); |
michael@0 | 1087 | operand_.set(operand, this, index); |
michael@0 | 1088 | operand->addUse(&operand_); |
michael@0 | 1089 | } |
michael@0 | 1090 | |
michael@0 | 1091 | MUse *getUseFor(size_t index) { |
michael@0 | 1092 | JS_ASSERT(index == 0); |
michael@0 | 1093 | return &operand_; |
michael@0 | 1094 | } |
michael@0 | 1095 | |
michael@0 | 1096 | public: |
michael@0 | 1097 | INSTRUCTION_HEADER(TableSwitch) |
michael@0 | 1098 | static MTableSwitch *New(TempAllocator &alloc, MDefinition *ins, int32_t low, int32_t high); |
michael@0 | 1099 | |
michael@0 | 1100 | size_t numSuccessors() const { |
michael@0 | 1101 | return successors_.length(); |
michael@0 | 1102 | } |
michael@0 | 1103 | |
michael@0 | 1104 | size_t addSuccessor(MBasicBlock *successor) { |
michael@0 | 1105 | JS_ASSERT(successors_.length() < (size_t)(high_ - low_ + 2)); |
michael@0 | 1106 | JS_ASSERT(!successors_.empty()); |
michael@0 | 1107 | successors_.append(successor); |
michael@0 | 1108 | return successors_.length() - 1; |
michael@0 | 1109 | } |
michael@0 | 1110 | |
michael@0 | 1111 | MBasicBlock *getSuccessor(size_t i) const { |
michael@0 | 1112 | JS_ASSERT(i < numSuccessors()); |
michael@0 | 1113 | return successors_[i]; |
michael@0 | 1114 | } |
michael@0 | 1115 | |
michael@0 | 1116 | void replaceSuccessor(size_t i, MBasicBlock *successor) { |
michael@0 | 1117 | JS_ASSERT(i < numSuccessors()); |
michael@0 | 1118 | successors_[i] = successor; |
michael@0 | 1119 | } |
michael@0 | 1120 | |
michael@0 | 1121 | MBasicBlock** blocks() { |
michael@0 | 1122 | return &blocks_[0]; |
michael@0 | 1123 | } |
michael@0 | 1124 | |
michael@0 | 1125 | size_t numBlocks() const { |
michael@0 | 1126 | return blocks_.length(); |
michael@0 | 1127 | } |
michael@0 | 1128 | |
michael@0 | 1129 | int32_t low() const { |
michael@0 | 1130 | return low_; |
michael@0 | 1131 | } |
michael@0 | 1132 | |
michael@0 | 1133 | int32_t high() const { |
michael@0 | 1134 | return high_; |
michael@0 | 1135 | } |
michael@0 | 1136 | |
michael@0 | 1137 | MBasicBlock *getDefault() const { |
michael@0 | 1138 | return getSuccessor(0); |
michael@0 | 1139 | } |
michael@0 | 1140 | |
michael@0 | 1141 | MBasicBlock *getCase(size_t i) const { |
michael@0 | 1142 | return getSuccessor(cases_[i]); |
michael@0 | 1143 | } |
michael@0 | 1144 | |
michael@0 | 1145 | size_t numCases() const { |
michael@0 | 1146 | return high() - low() + 1; |
michael@0 | 1147 | } |
michael@0 | 1148 | |
michael@0 | 1149 | size_t addDefault(MBasicBlock *block) { |
michael@0 | 1150 | JS_ASSERT(successors_.empty()); |
michael@0 | 1151 | successors_.append(block); |
michael@0 | 1152 | return 0; |
michael@0 | 1153 | } |
michael@0 | 1154 | |
michael@0 | 1155 | void addCase(size_t successorIndex) { |
michael@0 | 1156 | cases_.append(successorIndex); |
michael@0 | 1157 | } |
michael@0 | 1158 | |
michael@0 | 1159 | MBasicBlock *getBlock(size_t i) const { |
michael@0 | 1160 | JS_ASSERT(i < numBlocks()); |
michael@0 | 1161 | return blocks_[i]; |
michael@0 | 1162 | } |
michael@0 | 1163 | |
michael@0 | 1164 | void addBlock(MBasicBlock *block) { |
michael@0 | 1165 | blocks_.append(block); |
michael@0 | 1166 | } |
michael@0 | 1167 | |
michael@0 | 1168 | MDefinition *getOperand(size_t index) const { |
michael@0 | 1169 | JS_ASSERT(index == 0); |
michael@0 | 1170 | return operand_.producer(); |
michael@0 | 1171 | } |
michael@0 | 1172 | |
michael@0 | 1173 | size_t numOperands() const { |
michael@0 | 1174 | return 1; |
michael@0 | 1175 | } |
michael@0 | 1176 | |
michael@0 | 1177 | TypePolicy *typePolicy() { |
michael@0 | 1178 | return this; |
michael@0 | 1179 | } |
michael@0 | 1180 | }; |
michael@0 | 1181 | |
michael@0 | 1182 | template <size_t Arity, size_t Successors> |
michael@0 | 1183 | class MAryControlInstruction : public MControlInstruction |
michael@0 | 1184 | { |
michael@0 | 1185 | mozilla::Array<MUse, Arity> operands_; |
michael@0 | 1186 | mozilla::Array<MBasicBlock *, Successors> successors_; |
michael@0 | 1187 | |
michael@0 | 1188 | protected: |
michael@0 | 1189 | void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 1190 | operands_[index].set(operand, this, index); |
michael@0 | 1191 | operand->addUse(&operands_[index]); |
michael@0 | 1192 | } |
michael@0 | 1193 | void setSuccessor(size_t index, MBasicBlock *successor) { |
michael@0 | 1194 | successors_[index] = successor; |
michael@0 | 1195 | } |
michael@0 | 1196 | |
michael@0 | 1197 | MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 1198 | return &operands_[index]; |
michael@0 | 1199 | } |
michael@0 | 1200 | |
michael@0 | 1201 | public: |
michael@0 | 1202 | MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 1203 | return operands_[index].producer(); |
michael@0 | 1204 | } |
michael@0 | 1205 | size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 1206 | return Arity; |
michael@0 | 1207 | } |
michael@0 | 1208 | size_t numSuccessors() const MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 1209 | return Successors; |
michael@0 | 1210 | } |
michael@0 | 1211 | MBasicBlock *getSuccessor(size_t i) const MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 1212 | return successors_[i]; |
michael@0 | 1213 | } |
michael@0 | 1214 | void replaceSuccessor(size_t i, MBasicBlock *succ) MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 1215 | successors_[i] = succ; |
michael@0 | 1216 | } |
michael@0 | 1217 | }; |
michael@0 | 1218 | |
michael@0 | 1219 | // Jump to the start of another basic block. |
michael@0 | 1220 | class MGoto : public MAryControlInstruction<0, 1> |
michael@0 | 1221 | { |
michael@0 | 1222 | MGoto(MBasicBlock *target) { |
michael@0 | 1223 | setSuccessor(0, target); |
michael@0 | 1224 | } |
michael@0 | 1225 | |
michael@0 | 1226 | public: |
michael@0 | 1227 | INSTRUCTION_HEADER(Goto) |
michael@0 | 1228 | static MGoto *New(TempAllocator &alloc, MBasicBlock *target); |
michael@0 | 1229 | |
michael@0 | 1230 | MBasicBlock *target() { |
michael@0 | 1231 | return getSuccessor(0); |
michael@0 | 1232 | } |
michael@0 | 1233 | AliasSet getAliasSet() const { |
michael@0 | 1234 | return AliasSet::None(); |
michael@0 | 1235 | } |
michael@0 | 1236 | }; |
michael@0 | 1237 | |
michael@0 | 1238 | enum BranchDirection { |
michael@0 | 1239 | FALSE_BRANCH, |
michael@0 | 1240 | TRUE_BRANCH |
michael@0 | 1241 | }; |
michael@0 | 1242 | |
michael@0 | 1243 | static inline BranchDirection |
michael@0 | 1244 | NegateBranchDirection(BranchDirection dir) |
michael@0 | 1245 | { |
michael@0 | 1246 | return (dir == FALSE_BRANCH) ? TRUE_BRANCH : FALSE_BRANCH; |
michael@0 | 1247 | } |
michael@0 | 1248 | |
michael@0 | 1249 | // Tests if the input instruction evaluates to true or false, and jumps to the |
michael@0 | 1250 | // start of a corresponding basic block. |
michael@0 | 1251 | class MTest |
michael@0 | 1252 | : public MAryControlInstruction<1, 2>, |
michael@0 | 1253 | public TestPolicy |
michael@0 | 1254 | { |
michael@0 | 1255 | bool operandMightEmulateUndefined_; |
michael@0 | 1256 | |
michael@0 | 1257 | MTest(MDefinition *ins, MBasicBlock *if_true, MBasicBlock *if_false) |
michael@0 | 1258 | : operandMightEmulateUndefined_(true) |
michael@0 | 1259 | { |
michael@0 | 1260 | setOperand(0, ins); |
michael@0 | 1261 | setSuccessor(0, if_true); |
michael@0 | 1262 | setSuccessor(1, if_false); |
michael@0 | 1263 | } |
michael@0 | 1264 | |
michael@0 | 1265 | public: |
michael@0 | 1266 | INSTRUCTION_HEADER(Test) |
michael@0 | 1267 | static MTest *New(TempAllocator &alloc, MDefinition *ins, |
michael@0 | 1268 | MBasicBlock *ifTrue, MBasicBlock *ifFalse); |
michael@0 | 1269 | |
michael@0 | 1270 | MDefinition *input() const { |
michael@0 | 1271 | return getOperand(0); |
michael@0 | 1272 | } |
michael@0 | 1273 | MBasicBlock *ifTrue() const { |
michael@0 | 1274 | return getSuccessor(0); |
michael@0 | 1275 | } |
michael@0 | 1276 | MBasicBlock *ifFalse() const { |
michael@0 | 1277 | return getSuccessor(1); |
michael@0 | 1278 | } |
michael@0 | 1279 | MBasicBlock *branchSuccessor(BranchDirection dir) const { |
michael@0 | 1280 | return (dir == TRUE_BRANCH) ? ifTrue() : ifFalse(); |
michael@0 | 1281 | } |
michael@0 | 1282 | TypePolicy *typePolicy() { |
michael@0 | 1283 | return this; |
michael@0 | 1284 | } |
michael@0 | 1285 | |
michael@0 | 1286 | AliasSet getAliasSet() const { |
michael@0 | 1287 | return AliasSet::None(); |
michael@0 | 1288 | } |
michael@0 | 1289 | void infer(); |
michael@0 | 1290 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 1291 | void filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined, |
michael@0 | 1292 | bool *filtersNull); |
michael@0 | 1293 | |
michael@0 | 1294 | void markOperandCantEmulateUndefined() { |
michael@0 | 1295 | operandMightEmulateUndefined_ = false; |
michael@0 | 1296 | } |
michael@0 | 1297 | bool operandMightEmulateUndefined() const { |
michael@0 | 1298 | return operandMightEmulateUndefined_; |
michael@0 | 1299 | } |
michael@0 | 1300 | #ifdef DEBUG |
michael@0 | 1301 | bool isConsistentFloat32Use(MUse *use) const { |
michael@0 | 1302 | return true; |
michael@0 | 1303 | } |
michael@0 | 1304 | #endif |
michael@0 | 1305 | }; |
michael@0 | 1306 | |
michael@0 | 1307 | // Returns from this function to the previous caller. |
michael@0 | 1308 | class MReturn |
michael@0 | 1309 | : public MAryControlInstruction<1, 0>, |
michael@0 | 1310 | public BoxInputsPolicy |
michael@0 | 1311 | { |
michael@0 | 1312 | MReturn(MDefinition *ins) { |
michael@0 | 1313 | setOperand(0, ins); |
michael@0 | 1314 | } |
michael@0 | 1315 | |
michael@0 | 1316 | public: |
michael@0 | 1317 | INSTRUCTION_HEADER(Return) |
michael@0 | 1318 | static MReturn *New(TempAllocator &alloc, MDefinition *ins) { |
michael@0 | 1319 | return new(alloc) MReturn(ins); |
michael@0 | 1320 | } |
michael@0 | 1321 | |
michael@0 | 1322 | MDefinition *input() const { |
michael@0 | 1323 | return getOperand(0); |
michael@0 | 1324 | } |
michael@0 | 1325 | TypePolicy *typePolicy() { |
michael@0 | 1326 | return this; |
michael@0 | 1327 | } |
michael@0 | 1328 | AliasSet getAliasSet() const { |
michael@0 | 1329 | return AliasSet::None(); |
michael@0 | 1330 | } |
michael@0 | 1331 | }; |
michael@0 | 1332 | |
michael@0 | 1333 | class MThrow |
michael@0 | 1334 | : public MAryControlInstruction<1, 0>, |
michael@0 | 1335 | public BoxInputsPolicy |
michael@0 | 1336 | { |
michael@0 | 1337 | MThrow(MDefinition *ins) { |
michael@0 | 1338 | setOperand(0, ins); |
michael@0 | 1339 | } |
michael@0 | 1340 | |
michael@0 | 1341 | public: |
michael@0 | 1342 | INSTRUCTION_HEADER(Throw) |
michael@0 | 1343 | static MThrow *New(TempAllocator &alloc, MDefinition *ins) { |
michael@0 | 1344 | return new(alloc) MThrow(ins); |
michael@0 | 1345 | } |
michael@0 | 1346 | |
michael@0 | 1347 | TypePolicy *typePolicy() { |
michael@0 | 1348 | return this; |
michael@0 | 1349 | } |
michael@0 | 1350 | virtual AliasSet getAliasSet() const { |
michael@0 | 1351 | return AliasSet::None(); |
michael@0 | 1352 | } |
michael@0 | 1353 | bool possiblyCalls() const { |
michael@0 | 1354 | return true; |
michael@0 | 1355 | } |
michael@0 | 1356 | }; |
michael@0 | 1357 | |
michael@0 | 1358 | // Fabricate a type set containing only the type of the specified object. |
michael@0 | 1359 | types::TemporaryTypeSet * |
michael@0 | 1360 | MakeSingletonTypeSet(types::CompilerConstraintList *constraints, JSObject *obj); |
michael@0 | 1361 | |
michael@0 | 1362 | bool |
michael@0 | 1363 | MergeTypes(MIRType *ptype, types::TemporaryTypeSet **ptypeSet, |
michael@0 | 1364 | MIRType newType, types::TemporaryTypeSet *newTypeSet); |
michael@0 | 1365 | |
michael@0 | 1366 | class MNewArray : public MNullaryInstruction |
michael@0 | 1367 | { |
michael@0 | 1368 | public: |
michael@0 | 1369 | enum AllocatingBehaviour { |
michael@0 | 1370 | NewArray_Allocating, |
michael@0 | 1371 | NewArray_Unallocating |
michael@0 | 1372 | }; |
michael@0 | 1373 | |
michael@0 | 1374 | private: |
michael@0 | 1375 | // Number of space to allocate for the array. |
michael@0 | 1376 | uint32_t count_; |
michael@0 | 1377 | // Template for the created object. |
michael@0 | 1378 | CompilerRootObject templateObject_; |
michael@0 | 1379 | gc::InitialHeap initialHeap_; |
michael@0 | 1380 | // Allocate space at initialization or not |
michael@0 | 1381 | AllocatingBehaviour allocating_; |
michael@0 | 1382 | |
michael@0 | 1383 | MNewArray(types::CompilerConstraintList *constraints, uint32_t count, JSObject *templateObject, |
michael@0 | 1384 | gc::InitialHeap initialHeap, AllocatingBehaviour allocating) |
michael@0 | 1385 | : count_(count), |
michael@0 | 1386 | templateObject_(templateObject), |
michael@0 | 1387 | initialHeap_(initialHeap), |
michael@0 | 1388 | allocating_(allocating) |
michael@0 | 1389 | { |
michael@0 | 1390 | setResultType(MIRType_Object); |
michael@0 | 1391 | if (!templateObject->hasSingletonType()) |
michael@0 | 1392 | setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject)); |
michael@0 | 1393 | } |
michael@0 | 1394 | |
michael@0 | 1395 | public: |
michael@0 | 1396 | INSTRUCTION_HEADER(NewArray) |
michael@0 | 1397 | |
michael@0 | 1398 | static MNewArray *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
michael@0 | 1399 | uint32_t count, JSObject *templateObject, |
michael@0 | 1400 | gc::InitialHeap initialHeap, AllocatingBehaviour allocating) |
michael@0 | 1401 | { |
michael@0 | 1402 | return new(alloc) MNewArray(constraints, count, templateObject, initialHeap, allocating); |
michael@0 | 1403 | } |
michael@0 | 1404 | |
michael@0 | 1405 | uint32_t count() const { |
michael@0 | 1406 | return count_; |
michael@0 | 1407 | } |
michael@0 | 1408 | |
michael@0 | 1409 | JSObject *templateObject() const { |
michael@0 | 1410 | return templateObject_; |
michael@0 | 1411 | } |
michael@0 | 1412 | |
michael@0 | 1413 | gc::InitialHeap initialHeap() const { |
michael@0 | 1414 | return initialHeap_; |
michael@0 | 1415 | } |
michael@0 | 1416 | |
michael@0 | 1417 | bool isAllocating() const { |
michael@0 | 1418 | return allocating_ == NewArray_Allocating; |
michael@0 | 1419 | } |
michael@0 | 1420 | |
michael@0 | 1421 | // Returns true if the code generator should call through to the |
michael@0 | 1422 | // VM rather than the fast path. |
michael@0 | 1423 | bool shouldUseVM() const; |
michael@0 | 1424 | |
michael@0 | 1425 | // NewArray is marked as non-effectful because all our allocations are |
michael@0 | 1426 | // either lazy when we are using "new Array(length)" or bounded by the |
michael@0 | 1427 | // script or the stack size when we are using "new Array(...)" or "[...]" |
michael@0 | 1428 | // notations. So we might have to allocate the array twice if we bail |
michael@0 | 1429 | // during the computation of the first element of the square braket |
michael@0 | 1430 | // notation. |
michael@0 | 1431 | virtual AliasSet getAliasSet() const { |
michael@0 | 1432 | return AliasSet::None(); |
michael@0 | 1433 | } |
michael@0 | 1434 | }; |
michael@0 | 1435 | |
michael@0 | 1436 | class MNewObject : public MNullaryInstruction |
michael@0 | 1437 | { |
michael@0 | 1438 | CompilerRootObject templateObject_; |
michael@0 | 1439 | gc::InitialHeap initialHeap_; |
michael@0 | 1440 | bool templateObjectIsClassPrototype_; |
michael@0 | 1441 | |
michael@0 | 1442 | MNewObject(types::CompilerConstraintList *constraints, JSObject *templateObject, |
michael@0 | 1443 | gc::InitialHeap initialHeap, bool templateObjectIsClassPrototype) |
michael@0 | 1444 | : templateObject_(templateObject), |
michael@0 | 1445 | initialHeap_(initialHeap), |
michael@0 | 1446 | templateObjectIsClassPrototype_(templateObjectIsClassPrototype) |
michael@0 | 1447 | { |
michael@0 | 1448 | JS_ASSERT_IF(templateObjectIsClassPrototype, !shouldUseVM()); |
michael@0 | 1449 | setResultType(MIRType_Object); |
michael@0 | 1450 | if (!templateObject->hasSingletonType()) |
michael@0 | 1451 | setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject)); |
michael@0 | 1452 | } |
michael@0 | 1453 | |
michael@0 | 1454 | public: |
michael@0 | 1455 | INSTRUCTION_HEADER(NewObject) |
michael@0 | 1456 | |
michael@0 | 1457 | static MNewObject *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
michael@0 | 1458 | JSObject *templateObject, gc::InitialHeap initialHeap, |
michael@0 | 1459 | bool templateObjectIsClassPrototype) |
michael@0 | 1460 | { |
michael@0 | 1461 | return new(alloc) MNewObject(constraints, templateObject, initialHeap, |
michael@0 | 1462 | templateObjectIsClassPrototype); |
michael@0 | 1463 | } |
michael@0 | 1464 | |
michael@0 | 1465 | // Returns true if the code generator should call through to the |
michael@0 | 1466 | // VM rather than the fast path. |
michael@0 | 1467 | bool shouldUseVM() const; |
michael@0 | 1468 | |
michael@0 | 1469 | bool templateObjectIsClassPrototype() const { |
michael@0 | 1470 | return templateObjectIsClassPrototype_; |
michael@0 | 1471 | } |
michael@0 | 1472 | |
michael@0 | 1473 | JSObject *templateObject() const { |
michael@0 | 1474 | return templateObject_; |
michael@0 | 1475 | } |
michael@0 | 1476 | |
michael@0 | 1477 | gc::InitialHeap initialHeap() const { |
michael@0 | 1478 | return initialHeap_; |
michael@0 | 1479 | } |
michael@0 | 1480 | }; |
michael@0 | 1481 | |
michael@0 | 1482 | // Could be allocating either a new array or a new object. |
michael@0 | 1483 | class MNewPar : public MUnaryInstruction |
michael@0 | 1484 | { |
michael@0 | 1485 | CompilerRootObject templateObject_; |
michael@0 | 1486 | |
michael@0 | 1487 | MNewPar(MDefinition *cx, JSObject *templateObject) |
michael@0 | 1488 | : MUnaryInstruction(cx), |
michael@0 | 1489 | templateObject_(templateObject) |
michael@0 | 1490 | { |
michael@0 | 1491 | setResultType(MIRType_Object); |
michael@0 | 1492 | } |
michael@0 | 1493 | |
michael@0 | 1494 | public: |
michael@0 | 1495 | INSTRUCTION_HEADER(NewPar); |
michael@0 | 1496 | |
michael@0 | 1497 | static MNewPar *New(TempAllocator &alloc, MDefinition *cx, JSObject *templateObject) { |
michael@0 | 1498 | return new(alloc) MNewPar(cx, templateObject); |
michael@0 | 1499 | } |
michael@0 | 1500 | |
michael@0 | 1501 | MDefinition *forkJoinContext() const { |
michael@0 | 1502 | return getOperand(0); |
michael@0 | 1503 | } |
michael@0 | 1504 | |
michael@0 | 1505 | JSObject *templateObject() const { |
michael@0 | 1506 | return templateObject_; |
michael@0 | 1507 | } |
michael@0 | 1508 | }; |
michael@0 | 1509 | |
michael@0 | 1510 | // Creates a new derived type object. At runtime, this is just a call |
michael@0 | 1511 | // to `BinaryBlock::createDerived()`. That is, the MIR itself does not |
michael@0 | 1512 | // compile to particularly optimized code. However, using a distinct |
michael@0 | 1513 | // MIR for creating derived type objects allows the compiler to |
michael@0 | 1514 | // optimize ephemeral typed objects as would be created for a |
michael@0 | 1515 | // reference like `a.b.c` -- here, the `a.b` will create an ephemeral |
michael@0 | 1516 | // derived type object that aliases the memory of `a` itself. The |
michael@0 | 1517 | // specific nature of `a.b` is revealed by using |
michael@0 | 1518 | // `MNewDerivedTypedObject` rather than `MGetProperty` or what have |
michael@0 | 1519 | // you. Moreover, the compiler knows that there are no side-effects, |
michael@0 | 1520 | // so `MNewDerivedTypedObject` instructions can be reordered or pruned |
michael@0 | 1521 | // as dead code. |
michael@0 | 1522 | class MNewDerivedTypedObject |
michael@0 | 1523 | : public MTernaryInstruction, |
michael@0 | 1524 | public Mix3Policy<ObjectPolicy<0>, |
michael@0 | 1525 | ObjectPolicy<1>, |
michael@0 | 1526 | IntPolicy<2> > |
michael@0 | 1527 | { |
michael@0 | 1528 | private: |
michael@0 | 1529 | TypeDescrSet set_; |
michael@0 | 1530 | |
michael@0 | 1531 | MNewDerivedTypedObject(TypeDescrSet set, |
michael@0 | 1532 | MDefinition *type, |
michael@0 | 1533 | MDefinition *owner, |
michael@0 | 1534 | MDefinition *offset) |
michael@0 | 1535 | : MTernaryInstruction(type, owner, offset), |
michael@0 | 1536 | set_(set) |
michael@0 | 1537 | { |
michael@0 | 1538 | setMovable(); |
michael@0 | 1539 | setResultType(MIRType_Object); |
michael@0 | 1540 | } |
michael@0 | 1541 | |
michael@0 | 1542 | public: |
michael@0 | 1543 | INSTRUCTION_HEADER(NewDerivedTypedObject); |
michael@0 | 1544 | |
michael@0 | 1545 | static MNewDerivedTypedObject *New(TempAllocator &alloc, TypeDescrSet set, |
michael@0 | 1546 | MDefinition *type, MDefinition *owner, MDefinition *offset) |
michael@0 | 1547 | { |
michael@0 | 1548 | return new(alloc) MNewDerivedTypedObject(set, type, owner, offset); |
michael@0 | 1549 | } |
michael@0 | 1550 | |
michael@0 | 1551 | TypeDescrSet set() const { |
michael@0 | 1552 | return set_; |
michael@0 | 1553 | } |
michael@0 | 1554 | |
michael@0 | 1555 | MDefinition *type() const { |
michael@0 | 1556 | return getOperand(0); |
michael@0 | 1557 | } |
michael@0 | 1558 | |
michael@0 | 1559 | MDefinition *owner() const { |
michael@0 | 1560 | return getOperand(1); |
michael@0 | 1561 | } |
michael@0 | 1562 | |
michael@0 | 1563 | MDefinition *offset() const { |
michael@0 | 1564 | return getOperand(2); |
michael@0 | 1565 | } |
michael@0 | 1566 | |
michael@0 | 1567 | TypePolicy *typePolicy() { |
michael@0 | 1568 | return this; |
michael@0 | 1569 | } |
michael@0 | 1570 | |
michael@0 | 1571 | virtual AliasSet getAliasSet() const { |
michael@0 | 1572 | return AliasSet::None(); |
michael@0 | 1573 | } |
michael@0 | 1574 | }; |
michael@0 | 1575 | |
michael@0 | 1576 | // Abort parallel execution. |
michael@0 | 1577 | class MAbortPar : public MAryControlInstruction<0, 0> |
michael@0 | 1578 | { |
michael@0 | 1579 | MAbortPar() |
michael@0 | 1580 | : MAryControlInstruction<0, 0>() |
michael@0 | 1581 | { |
michael@0 | 1582 | setResultType(MIRType_None); |
michael@0 | 1583 | setGuard(); |
michael@0 | 1584 | } |
michael@0 | 1585 | |
michael@0 | 1586 | public: |
michael@0 | 1587 | INSTRUCTION_HEADER(AbortPar); |
michael@0 | 1588 | |
michael@0 | 1589 | static MAbortPar *New(TempAllocator &alloc) { |
michael@0 | 1590 | return new(alloc) MAbortPar(); |
michael@0 | 1591 | } |
michael@0 | 1592 | }; |
michael@0 | 1593 | |
michael@0 | 1594 | // Setting __proto__ in an object literal. |
michael@0 | 1595 | class MMutateProto |
michael@0 | 1596 | : public MAryInstruction<2>, |
michael@0 | 1597 | public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> > |
michael@0 | 1598 | { |
michael@0 | 1599 | protected: |
michael@0 | 1600 | MMutateProto(MDefinition *obj, MDefinition *value) |
michael@0 | 1601 | { |
michael@0 | 1602 | setOperand(0, obj); |
michael@0 | 1603 | setOperand(1, value); |
michael@0 | 1604 | setResultType(MIRType_None); |
michael@0 | 1605 | } |
michael@0 | 1606 | |
michael@0 | 1607 | public: |
michael@0 | 1608 | INSTRUCTION_HEADER(MutateProto) |
michael@0 | 1609 | |
michael@0 | 1610 | static MMutateProto *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value) |
michael@0 | 1611 | { |
michael@0 | 1612 | return new(alloc) MMutateProto(obj, value); |
michael@0 | 1613 | } |
michael@0 | 1614 | |
michael@0 | 1615 | MDefinition *getObject() const { |
michael@0 | 1616 | return getOperand(0); |
michael@0 | 1617 | } |
michael@0 | 1618 | MDefinition *getValue() const { |
michael@0 | 1619 | return getOperand(1); |
michael@0 | 1620 | } |
michael@0 | 1621 | |
michael@0 | 1622 | TypePolicy *typePolicy() { |
michael@0 | 1623 | return this; |
michael@0 | 1624 | } |
michael@0 | 1625 | bool possiblyCalls() const { |
michael@0 | 1626 | return true; |
michael@0 | 1627 | } |
michael@0 | 1628 | }; |
michael@0 | 1629 | |
michael@0 | 1630 | // Slow path for adding a property to an object without a known base. |
michael@0 | 1631 | class MInitProp |
michael@0 | 1632 | : public MAryInstruction<2>, |
michael@0 | 1633 | public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> > |
michael@0 | 1634 | { |
michael@0 | 1635 | public: |
michael@0 | 1636 | CompilerRootPropertyName name_; |
michael@0 | 1637 | |
michael@0 | 1638 | protected: |
michael@0 | 1639 | MInitProp(MDefinition *obj, PropertyName *name, MDefinition *value) |
michael@0 | 1640 | : name_(name) |
michael@0 | 1641 | { |
michael@0 | 1642 | setOperand(0, obj); |
michael@0 | 1643 | setOperand(1, value); |
michael@0 | 1644 | setResultType(MIRType_None); |
michael@0 | 1645 | } |
michael@0 | 1646 | |
michael@0 | 1647 | public: |
michael@0 | 1648 | INSTRUCTION_HEADER(InitProp) |
michael@0 | 1649 | |
michael@0 | 1650 | static MInitProp *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name, |
michael@0 | 1651 | MDefinition *value) |
michael@0 | 1652 | { |
michael@0 | 1653 | return new(alloc) MInitProp(obj, name, value); |
michael@0 | 1654 | } |
michael@0 | 1655 | |
michael@0 | 1656 | MDefinition *getObject() const { |
michael@0 | 1657 | return getOperand(0); |
michael@0 | 1658 | } |
michael@0 | 1659 | MDefinition *getValue() const { |
michael@0 | 1660 | return getOperand(1); |
michael@0 | 1661 | } |
michael@0 | 1662 | |
michael@0 | 1663 | PropertyName *propertyName() const { |
michael@0 | 1664 | return name_; |
michael@0 | 1665 | } |
michael@0 | 1666 | TypePolicy *typePolicy() { |
michael@0 | 1667 | return this; |
michael@0 | 1668 | } |
michael@0 | 1669 | bool possiblyCalls() const { |
michael@0 | 1670 | return true; |
michael@0 | 1671 | } |
michael@0 | 1672 | }; |
michael@0 | 1673 | |
michael@0 | 1674 | class MInitPropGetterSetter |
michael@0 | 1675 | : public MBinaryInstruction, |
michael@0 | 1676 | public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> > |
michael@0 | 1677 | { |
michael@0 | 1678 | CompilerRootPropertyName name_; |
michael@0 | 1679 | |
michael@0 | 1680 | MInitPropGetterSetter(MDefinition *obj, PropertyName *name, MDefinition *value) |
michael@0 | 1681 | : MBinaryInstruction(obj, value), |
michael@0 | 1682 | name_(name) |
michael@0 | 1683 | { } |
michael@0 | 1684 | |
michael@0 | 1685 | public: |
michael@0 | 1686 | INSTRUCTION_HEADER(InitPropGetterSetter) |
michael@0 | 1687 | |
michael@0 | 1688 | static MInitPropGetterSetter *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name, |
michael@0 | 1689 | MDefinition *value) |
michael@0 | 1690 | { |
michael@0 | 1691 | return new(alloc) MInitPropGetterSetter(obj, name, value); |
michael@0 | 1692 | } |
michael@0 | 1693 | |
michael@0 | 1694 | MDefinition *object() const { |
michael@0 | 1695 | return getOperand(0); |
michael@0 | 1696 | } |
michael@0 | 1697 | MDefinition *value() const { |
michael@0 | 1698 | return getOperand(1); |
michael@0 | 1699 | } |
michael@0 | 1700 | PropertyName *name() const { |
michael@0 | 1701 | return name_; |
michael@0 | 1702 | } |
michael@0 | 1703 | TypePolicy *typePolicy() { |
michael@0 | 1704 | return this; |
michael@0 | 1705 | } |
michael@0 | 1706 | }; |
michael@0 | 1707 | |
michael@0 | 1708 | class MInitElem |
michael@0 | 1709 | : public MAryInstruction<3>, |
michael@0 | 1710 | public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> > |
michael@0 | 1711 | { |
michael@0 | 1712 | MInitElem(MDefinition *obj, MDefinition *id, MDefinition *value) |
michael@0 | 1713 | { |
michael@0 | 1714 | setOperand(0, obj); |
michael@0 | 1715 | setOperand(1, id); |
michael@0 | 1716 | setOperand(2, value); |
michael@0 | 1717 | setResultType(MIRType_None); |
michael@0 | 1718 | } |
michael@0 | 1719 | |
michael@0 | 1720 | public: |
michael@0 | 1721 | INSTRUCTION_HEADER(InitElem) |
michael@0 | 1722 | |
michael@0 | 1723 | static MInitElem *New(TempAllocator &alloc, MDefinition *obj, MDefinition *id, |
michael@0 | 1724 | MDefinition *value) |
michael@0 | 1725 | { |
michael@0 | 1726 | return new(alloc) MInitElem(obj, id, value); |
michael@0 | 1727 | } |
michael@0 | 1728 | |
michael@0 | 1729 | MDefinition *getObject() const { |
michael@0 | 1730 | return getOperand(0); |
michael@0 | 1731 | } |
michael@0 | 1732 | MDefinition *getId() const { |
michael@0 | 1733 | return getOperand(1); |
michael@0 | 1734 | } |
michael@0 | 1735 | MDefinition *getValue() const { |
michael@0 | 1736 | return getOperand(2); |
michael@0 | 1737 | } |
michael@0 | 1738 | TypePolicy *typePolicy() { |
michael@0 | 1739 | return this; |
michael@0 | 1740 | } |
michael@0 | 1741 | bool possiblyCalls() const { |
michael@0 | 1742 | return true; |
michael@0 | 1743 | } |
michael@0 | 1744 | }; |
michael@0 | 1745 | |
michael@0 | 1746 | class MInitElemGetterSetter |
michael@0 | 1747 | : public MTernaryInstruction, |
michael@0 | 1748 | public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2> > |
michael@0 | 1749 | { |
michael@0 | 1750 | MInitElemGetterSetter(MDefinition *obj, MDefinition *id, MDefinition *value) |
michael@0 | 1751 | : MTernaryInstruction(obj, id, value) |
michael@0 | 1752 | { } |
michael@0 | 1753 | |
michael@0 | 1754 | public: |
michael@0 | 1755 | INSTRUCTION_HEADER(InitElemGetterSetter) |
michael@0 | 1756 | |
michael@0 | 1757 | static MInitElemGetterSetter *New(TempAllocator &alloc, MDefinition *obj, MDefinition *id, |
michael@0 | 1758 | MDefinition *value) |
michael@0 | 1759 | { |
michael@0 | 1760 | return new(alloc) MInitElemGetterSetter(obj, id, value); |
michael@0 | 1761 | } |
michael@0 | 1762 | |
michael@0 | 1763 | MDefinition *object() const { |
michael@0 | 1764 | return getOperand(0); |
michael@0 | 1765 | } |
michael@0 | 1766 | MDefinition *idValue() const { |
michael@0 | 1767 | return getOperand(1); |
michael@0 | 1768 | } |
michael@0 | 1769 | MDefinition *value() const { |
michael@0 | 1770 | return getOperand(2); |
michael@0 | 1771 | } |
michael@0 | 1772 | TypePolicy *typePolicy() { |
michael@0 | 1773 | return this; |
michael@0 | 1774 | } |
michael@0 | 1775 | }; |
michael@0 | 1776 | |
michael@0 | 1777 | class MVariadicInstruction : public MInstruction |
michael@0 | 1778 | { |
michael@0 | 1779 | FixedList<MUse> operands_; |
michael@0 | 1780 | |
michael@0 | 1781 | protected: |
michael@0 | 1782 | bool init(TempAllocator &alloc, size_t length) { |
michael@0 | 1783 | return operands_.init(alloc, length); |
michael@0 | 1784 | } |
michael@0 | 1785 | |
michael@0 | 1786 | public: |
michael@0 | 1787 | // Will assert if called before initialization. |
michael@0 | 1788 | MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 1789 | return operands_[index].producer(); |
michael@0 | 1790 | } |
michael@0 | 1791 | size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 1792 | return operands_.length(); |
michael@0 | 1793 | } |
michael@0 | 1794 | void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 1795 | operands_[index].set(operand, this, index); |
michael@0 | 1796 | operand->addUse(&operands_[index]); |
michael@0 | 1797 | } |
michael@0 | 1798 | |
michael@0 | 1799 | MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 1800 | return &operands_[index]; |
michael@0 | 1801 | } |
michael@0 | 1802 | }; |
michael@0 | 1803 | |
michael@0 | 1804 | class MCall |
michael@0 | 1805 | : public MVariadicInstruction, |
michael@0 | 1806 | public CallPolicy |
michael@0 | 1807 | { |
michael@0 | 1808 | private: |
michael@0 | 1809 | // An MCall uses the MPrepareCall, MDefinition for the function, and |
michael@0 | 1810 | // MPassArg instructions. They are stored in the same list. |
michael@0 | 1811 | static const size_t FunctionOperandIndex = 0; |
michael@0 | 1812 | static const size_t NumNonArgumentOperands = 1; |
michael@0 | 1813 | |
michael@0 | 1814 | protected: |
michael@0 | 1815 | // True if the call is for JSOP_NEW. |
michael@0 | 1816 | bool construct_; |
michael@0 | 1817 | // Monomorphic cache of single target from TI, or nullptr. |
michael@0 | 1818 | CompilerRootFunction target_; |
michael@0 | 1819 | // Original value of argc from the bytecode. |
michael@0 | 1820 | uint32_t numActualArgs_; |
michael@0 | 1821 | |
michael@0 | 1822 | bool needsArgCheck_; |
michael@0 | 1823 | |
michael@0 | 1824 | MCall(JSFunction *target, uint32_t numActualArgs, bool construct) |
michael@0 | 1825 | : construct_(construct), |
michael@0 | 1826 | target_(target), |
michael@0 | 1827 | numActualArgs_(numActualArgs), |
michael@0 | 1828 | needsArgCheck_(true) |
michael@0 | 1829 | { |
michael@0 | 1830 | setResultType(MIRType_Value); |
michael@0 | 1831 | } |
michael@0 | 1832 | |
michael@0 | 1833 | public: |
michael@0 | 1834 | INSTRUCTION_HEADER(Call) |
michael@0 | 1835 | static MCall *New(TempAllocator &alloc, JSFunction *target, size_t maxArgc, size_t numActualArgs, |
michael@0 | 1836 | bool construct, bool isDOMCall); |
michael@0 | 1837 | |
michael@0 | 1838 | void initFunction(MDefinition *func) { |
michael@0 | 1839 | return setOperand(FunctionOperandIndex, func); |
michael@0 | 1840 | } |
michael@0 | 1841 | |
michael@0 | 1842 | bool needsArgCheck() const { |
michael@0 | 1843 | return needsArgCheck_; |
michael@0 | 1844 | } |
michael@0 | 1845 | |
michael@0 | 1846 | void disableArgCheck() { |
michael@0 | 1847 | needsArgCheck_ = false; |
michael@0 | 1848 | } |
michael@0 | 1849 | MDefinition *getFunction() const { |
michael@0 | 1850 | return getOperand(FunctionOperandIndex); |
michael@0 | 1851 | } |
michael@0 | 1852 | void replaceFunction(MInstruction *newfunc) { |
michael@0 | 1853 | replaceOperand(FunctionOperandIndex, newfunc); |
michael@0 | 1854 | } |
michael@0 | 1855 | |
michael@0 | 1856 | void addArg(size_t argnum, MDefinition *arg); |
michael@0 | 1857 | |
michael@0 | 1858 | MDefinition *getArg(uint32_t index) const { |
michael@0 | 1859 | return getOperand(NumNonArgumentOperands + index); |
michael@0 | 1860 | } |
michael@0 | 1861 | |
michael@0 | 1862 | static size_t IndexOfThis() { |
michael@0 | 1863 | return NumNonArgumentOperands; |
michael@0 | 1864 | } |
michael@0 | 1865 | static size_t IndexOfArgument(size_t index) { |
michael@0 | 1866 | return NumNonArgumentOperands + index + 1; // +1 to skip |this|. |
michael@0 | 1867 | } |
michael@0 | 1868 | static size_t IndexOfStackArg(size_t index) { |
michael@0 | 1869 | return NumNonArgumentOperands + index; |
michael@0 | 1870 | } |
michael@0 | 1871 | |
michael@0 | 1872 | // For TI-informed monomorphic callsites. |
michael@0 | 1873 | JSFunction *getSingleTarget() const { |
michael@0 | 1874 | return target_; |
michael@0 | 1875 | } |
michael@0 | 1876 | |
michael@0 | 1877 | bool isConstructing() const { |
michael@0 | 1878 | return construct_; |
michael@0 | 1879 | } |
michael@0 | 1880 | |
michael@0 | 1881 | // The number of stack arguments is the max between the number of formal |
michael@0 | 1882 | // arguments and the number of actual arguments. The number of stack |
michael@0 | 1883 | // argument includes the |undefined| padding added in case of underflow. |
michael@0 | 1884 | // Includes |this|. |
michael@0 | 1885 | uint32_t numStackArgs() const { |
michael@0 | 1886 | return numOperands() - NumNonArgumentOperands; |
michael@0 | 1887 | } |
michael@0 | 1888 | |
michael@0 | 1889 | // Does not include |this|. |
michael@0 | 1890 | uint32_t numActualArgs() const { |
michael@0 | 1891 | return numActualArgs_; |
michael@0 | 1892 | } |
michael@0 | 1893 | |
michael@0 | 1894 | TypePolicy *typePolicy() { |
michael@0 | 1895 | return this; |
michael@0 | 1896 | } |
michael@0 | 1897 | |
michael@0 | 1898 | bool possiblyCalls() const { |
michael@0 | 1899 | return true; |
michael@0 | 1900 | } |
michael@0 | 1901 | |
michael@0 | 1902 | virtual bool isCallDOMNative() const { |
michael@0 | 1903 | return false; |
michael@0 | 1904 | } |
michael@0 | 1905 | |
michael@0 | 1906 | // A method that can be called to tell the MCall to figure out whether it's |
michael@0 | 1907 | // movable or not. This can't be done in the constructor, because it |
michael@0 | 1908 | // depends on the arguments to the call, and those aren't passed to the |
michael@0 | 1909 | // constructor but are set up later via addArg. |
michael@0 | 1910 | virtual void computeMovable() { |
michael@0 | 1911 | } |
michael@0 | 1912 | }; |
michael@0 | 1913 | |
michael@0 | 1914 | class MCallDOMNative : public MCall |
michael@0 | 1915 | { |
michael@0 | 1916 | // A helper class for MCalls for DOM natives. Note that this is NOT |
michael@0 | 1917 | // actually a separate MIR op from MCall, because all sorts of places use |
michael@0 | 1918 | // isCall() to check for calls and all we really want is to overload a few |
michael@0 | 1919 | // virtual things from MCall. |
michael@0 | 1920 | protected: |
michael@0 | 1921 | MCallDOMNative(JSFunction *target, uint32_t numActualArgs) |
michael@0 | 1922 | : MCall(target, numActualArgs, false) |
michael@0 | 1923 | { |
michael@0 | 1924 | // If our jitinfo is not marked movable, that means that our C++ |
michael@0 | 1925 | // implementation is fallible or that we have no hope of ever doing the |
michael@0 | 1926 | // sort of argument analysis that would allow us to detemine that we're |
michael@0 | 1927 | // side-effect-free. In the latter case we wouldn't get DCEd no matter |
michael@0 | 1928 | // what, but for the former case we have to explicitly say that we can't |
michael@0 | 1929 | // be DCEd. |
michael@0 | 1930 | if (!getJitInfo()->isMovable) |
michael@0 | 1931 | setGuard(); |
michael@0 | 1932 | } |
michael@0 | 1933 | |
michael@0 | 1934 | friend MCall *MCall::New(TempAllocator &alloc, JSFunction *target, size_t maxArgc, |
michael@0 | 1935 | size_t numActualArgs, bool construct, bool isDOMCall); |
michael@0 | 1936 | |
michael@0 | 1937 | const JSJitInfo *getJitInfo() const; |
michael@0 | 1938 | public: |
michael@0 | 1939 | virtual AliasSet getAliasSet() const MOZ_OVERRIDE; |
michael@0 | 1940 | |
michael@0 | 1941 | virtual bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE; |
michael@0 | 1942 | |
michael@0 | 1943 | virtual bool isCallDOMNative() const MOZ_OVERRIDE { |
michael@0 | 1944 | return true; |
michael@0 | 1945 | } |
michael@0 | 1946 | |
michael@0 | 1947 | virtual void computeMovable() MOZ_OVERRIDE; |
michael@0 | 1948 | }; |
michael@0 | 1949 | |
michael@0 | 1950 | // arr.splice(start, deleteCount) with unused return value. |
michael@0 | 1951 | class MArraySplice |
michael@0 | 1952 | : public MTernaryInstruction, |
michael@0 | 1953 | public Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> > |
michael@0 | 1954 | { |
michael@0 | 1955 | private: |
michael@0 | 1956 | |
michael@0 | 1957 | MArraySplice(MDefinition *object, MDefinition *start, MDefinition *deleteCount) |
michael@0 | 1958 | : MTernaryInstruction(object, start, deleteCount) |
michael@0 | 1959 | { } |
michael@0 | 1960 | |
michael@0 | 1961 | public: |
michael@0 | 1962 | INSTRUCTION_HEADER(ArraySplice) |
michael@0 | 1963 | static MArraySplice *New(TempAllocator &alloc, MDefinition *object, |
michael@0 | 1964 | MDefinition *start, MDefinition *deleteCount) |
michael@0 | 1965 | { |
michael@0 | 1966 | return new(alloc) MArraySplice(object, start, deleteCount); |
michael@0 | 1967 | } |
michael@0 | 1968 | |
michael@0 | 1969 | MDefinition *object() const { |
michael@0 | 1970 | return getOperand(0); |
michael@0 | 1971 | } |
michael@0 | 1972 | |
michael@0 | 1973 | MDefinition *start() const { |
michael@0 | 1974 | return getOperand(1); |
michael@0 | 1975 | } |
michael@0 | 1976 | |
michael@0 | 1977 | MDefinition *deleteCount() const { |
michael@0 | 1978 | return getOperand(2); |
michael@0 | 1979 | } |
michael@0 | 1980 | |
michael@0 | 1981 | bool possiblyCalls() const { |
michael@0 | 1982 | return true; |
michael@0 | 1983 | } |
michael@0 | 1984 | |
michael@0 | 1985 | TypePolicy *typePolicy() { |
michael@0 | 1986 | return this; |
michael@0 | 1987 | } |
michael@0 | 1988 | }; |
michael@0 | 1989 | |
michael@0 | 1990 | // fun.apply(self, arguments) |
michael@0 | 1991 | class MApplyArgs |
michael@0 | 1992 | : public MAryInstruction<3>, |
michael@0 | 1993 | public MixPolicy<ObjectPolicy<0>, MixPolicy<IntPolicy<1>, BoxPolicy<2> > > |
michael@0 | 1994 | { |
michael@0 | 1995 | protected: |
michael@0 | 1996 | // Monomorphic cache of single target from TI, or nullptr. |
michael@0 | 1997 | CompilerRootFunction target_; |
michael@0 | 1998 | |
michael@0 | 1999 | MApplyArgs(JSFunction *target, MDefinition *fun, MDefinition *argc, MDefinition *self) |
michael@0 | 2000 | : target_(target) |
michael@0 | 2001 | { |
michael@0 | 2002 | setOperand(0, fun); |
michael@0 | 2003 | setOperand(1, argc); |
michael@0 | 2004 | setOperand(2, self); |
michael@0 | 2005 | setResultType(MIRType_Value); |
michael@0 | 2006 | } |
michael@0 | 2007 | |
michael@0 | 2008 | public: |
michael@0 | 2009 | INSTRUCTION_HEADER(ApplyArgs) |
michael@0 | 2010 | static MApplyArgs *New(TempAllocator &alloc, JSFunction *target, MDefinition *fun, |
michael@0 | 2011 | MDefinition *argc, MDefinition *self); |
michael@0 | 2012 | |
michael@0 | 2013 | MDefinition *getFunction() const { |
michael@0 | 2014 | return getOperand(0); |
michael@0 | 2015 | } |
michael@0 | 2016 | |
michael@0 | 2017 | // For TI-informed monomorphic callsites. |
michael@0 | 2018 | JSFunction *getSingleTarget() const { |
michael@0 | 2019 | return target_; |
michael@0 | 2020 | } |
michael@0 | 2021 | |
michael@0 | 2022 | MDefinition *getArgc() const { |
michael@0 | 2023 | return getOperand(1); |
michael@0 | 2024 | } |
michael@0 | 2025 | MDefinition *getThis() const { |
michael@0 | 2026 | return getOperand(2); |
michael@0 | 2027 | } |
michael@0 | 2028 | |
michael@0 | 2029 | TypePolicy *typePolicy() { |
michael@0 | 2030 | return this; |
michael@0 | 2031 | } |
michael@0 | 2032 | bool possiblyCalls() const { |
michael@0 | 2033 | return true; |
michael@0 | 2034 | } |
michael@0 | 2035 | }; |
michael@0 | 2036 | |
michael@0 | 2037 | class MBail : public MNullaryInstruction |
michael@0 | 2038 | { |
michael@0 | 2039 | protected: |
michael@0 | 2040 | MBail() |
michael@0 | 2041 | { |
michael@0 | 2042 | setGuard(); |
michael@0 | 2043 | } |
michael@0 | 2044 | |
michael@0 | 2045 | public: |
michael@0 | 2046 | INSTRUCTION_HEADER(Bail) |
michael@0 | 2047 | |
michael@0 | 2048 | static MBail * |
michael@0 | 2049 | New(TempAllocator &alloc) { |
michael@0 | 2050 | return new(alloc) MBail(); |
michael@0 | 2051 | } |
michael@0 | 2052 | |
michael@0 | 2053 | AliasSet getAliasSet() const { |
michael@0 | 2054 | return AliasSet::None(); |
michael@0 | 2055 | } |
michael@0 | 2056 | }; |
michael@0 | 2057 | |
michael@0 | 2058 | class MAssertFloat32 : public MUnaryInstruction |
michael@0 | 2059 | { |
michael@0 | 2060 | protected: |
michael@0 | 2061 | bool mustBeFloat32_; |
michael@0 | 2062 | |
michael@0 | 2063 | MAssertFloat32(MDefinition *value, bool mustBeFloat32) |
michael@0 | 2064 | : MUnaryInstruction(value), mustBeFloat32_(mustBeFloat32) |
michael@0 | 2065 | { |
michael@0 | 2066 | } |
michael@0 | 2067 | |
michael@0 | 2068 | public: |
michael@0 | 2069 | INSTRUCTION_HEADER(AssertFloat32) |
michael@0 | 2070 | |
michael@0 | 2071 | static MAssertFloat32 *New(TempAllocator &alloc, MDefinition *value, bool mustBeFloat32) { |
michael@0 | 2072 | return new(alloc) MAssertFloat32(value, mustBeFloat32); |
michael@0 | 2073 | } |
michael@0 | 2074 | |
michael@0 | 2075 | bool canConsumeFloat32(MUse *use) const { return true; } |
michael@0 | 2076 | |
michael@0 | 2077 | bool mustBeFloat32() const { return mustBeFloat32_; } |
michael@0 | 2078 | }; |
michael@0 | 2079 | |
michael@0 | 2080 | class MGetDynamicName |
michael@0 | 2081 | : public MAryInstruction<2>, |
michael@0 | 2082 | public MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> > |
michael@0 | 2083 | { |
michael@0 | 2084 | protected: |
michael@0 | 2085 | MGetDynamicName(MDefinition *scopeChain, MDefinition *name) |
michael@0 | 2086 | { |
michael@0 | 2087 | setOperand(0, scopeChain); |
michael@0 | 2088 | setOperand(1, name); |
michael@0 | 2089 | setResultType(MIRType_Value); |
michael@0 | 2090 | } |
michael@0 | 2091 | |
michael@0 | 2092 | public: |
michael@0 | 2093 | INSTRUCTION_HEADER(GetDynamicName) |
michael@0 | 2094 | |
michael@0 | 2095 | static MGetDynamicName * |
michael@0 | 2096 | New(TempAllocator &alloc, MDefinition *scopeChain, MDefinition *name) { |
michael@0 | 2097 | return new(alloc) MGetDynamicName(scopeChain, name); |
michael@0 | 2098 | } |
michael@0 | 2099 | |
michael@0 | 2100 | MDefinition *getScopeChain() const { |
michael@0 | 2101 | return getOperand(0); |
michael@0 | 2102 | } |
michael@0 | 2103 | MDefinition *getName() const { |
michael@0 | 2104 | return getOperand(1); |
michael@0 | 2105 | } |
michael@0 | 2106 | |
michael@0 | 2107 | TypePolicy *typePolicy() { |
michael@0 | 2108 | return this; |
michael@0 | 2109 | } |
michael@0 | 2110 | bool possiblyCalls() const { |
michael@0 | 2111 | return true; |
michael@0 | 2112 | } |
michael@0 | 2113 | }; |
michael@0 | 2114 | |
michael@0 | 2115 | // Bailout if the input string contains 'arguments' or 'eval'. |
michael@0 | 2116 | class MFilterArgumentsOrEval |
michael@0 | 2117 | : public MAryInstruction<1>, |
michael@0 | 2118 | public BoxExceptPolicy<0, MIRType_String> |
michael@0 | 2119 | { |
michael@0 | 2120 | protected: |
michael@0 | 2121 | MFilterArgumentsOrEval(MDefinition *string) |
michael@0 | 2122 | { |
michael@0 | 2123 | setOperand(0, string); |
michael@0 | 2124 | setGuard(); |
michael@0 | 2125 | setResultType(MIRType_None); |
michael@0 | 2126 | } |
michael@0 | 2127 | |
michael@0 | 2128 | public: |
michael@0 | 2129 | INSTRUCTION_HEADER(FilterArgumentsOrEval) |
michael@0 | 2130 | |
michael@0 | 2131 | static MFilterArgumentsOrEval *New(TempAllocator &alloc, MDefinition *string) { |
michael@0 | 2132 | return new(alloc) MFilterArgumentsOrEval(string); |
michael@0 | 2133 | } |
michael@0 | 2134 | |
michael@0 | 2135 | MDefinition *getString() const { |
michael@0 | 2136 | return getOperand(0); |
michael@0 | 2137 | } |
michael@0 | 2138 | |
michael@0 | 2139 | TypePolicy *typePolicy() { |
michael@0 | 2140 | return this; |
michael@0 | 2141 | } |
michael@0 | 2142 | bool possiblyCalls() const { |
michael@0 | 2143 | return true; |
michael@0 | 2144 | } |
michael@0 | 2145 | }; |
michael@0 | 2146 | |
michael@0 | 2147 | class MCallDirectEval |
michael@0 | 2148 | : public MAryInstruction<3>, |
michael@0 | 2149 | public MixPolicy<ObjectPolicy<0>, |
michael@0 | 2150 | MixPolicy<BoxExceptPolicy<1, MIRType_String>, BoxPolicy<2> > > |
michael@0 | 2151 | { |
michael@0 | 2152 | protected: |
michael@0 | 2153 | MCallDirectEval(MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue, |
michael@0 | 2154 | jsbytecode *pc) |
michael@0 | 2155 | : pc_(pc) |
michael@0 | 2156 | { |
michael@0 | 2157 | setOperand(0, scopeChain); |
michael@0 | 2158 | setOperand(1, string); |
michael@0 | 2159 | setOperand(2, thisValue); |
michael@0 | 2160 | setResultType(MIRType_Value); |
michael@0 | 2161 | } |
michael@0 | 2162 | |
michael@0 | 2163 | public: |
michael@0 | 2164 | INSTRUCTION_HEADER(CallDirectEval) |
michael@0 | 2165 | |
michael@0 | 2166 | static MCallDirectEval * |
michael@0 | 2167 | New(TempAllocator &alloc, MDefinition *scopeChain, MDefinition *string, MDefinition *thisValue, |
michael@0 | 2168 | jsbytecode *pc) |
michael@0 | 2169 | { |
michael@0 | 2170 | return new(alloc) MCallDirectEval(scopeChain, string, thisValue, pc); |
michael@0 | 2171 | } |
michael@0 | 2172 | |
michael@0 | 2173 | MDefinition *getScopeChain() const { |
michael@0 | 2174 | return getOperand(0); |
michael@0 | 2175 | } |
michael@0 | 2176 | MDefinition *getString() const { |
michael@0 | 2177 | return getOperand(1); |
michael@0 | 2178 | } |
michael@0 | 2179 | MDefinition *getThisValue() const { |
michael@0 | 2180 | return getOperand(2); |
michael@0 | 2181 | } |
michael@0 | 2182 | |
michael@0 | 2183 | jsbytecode *pc() const { |
michael@0 | 2184 | return pc_; |
michael@0 | 2185 | } |
michael@0 | 2186 | |
michael@0 | 2187 | TypePolicy *typePolicy() { |
michael@0 | 2188 | return this; |
michael@0 | 2189 | } |
michael@0 | 2190 | |
michael@0 | 2191 | bool possiblyCalls() const { |
michael@0 | 2192 | return true; |
michael@0 | 2193 | } |
michael@0 | 2194 | |
michael@0 | 2195 | private: |
michael@0 | 2196 | jsbytecode *pc_; |
michael@0 | 2197 | }; |
michael@0 | 2198 | |
michael@0 | 2199 | class MCompare |
michael@0 | 2200 | : public MBinaryInstruction, |
michael@0 | 2201 | public ComparePolicy |
michael@0 | 2202 | { |
michael@0 | 2203 | public: |
michael@0 | 2204 | enum CompareType { |
michael@0 | 2205 | |
michael@0 | 2206 | // Anything compared to Undefined |
michael@0 | 2207 | Compare_Undefined, |
michael@0 | 2208 | |
michael@0 | 2209 | // Anything compared to Null |
michael@0 | 2210 | Compare_Null, |
michael@0 | 2211 | |
michael@0 | 2212 | // Undefined compared to Boolean |
michael@0 | 2213 | // Null compared to Boolean |
michael@0 | 2214 | // Double compared to Boolean |
michael@0 | 2215 | // String compared to Boolean |
michael@0 | 2216 | // Object compared to Boolean |
michael@0 | 2217 | // Value compared to Boolean |
michael@0 | 2218 | Compare_Boolean, |
michael@0 | 2219 | |
michael@0 | 2220 | // Int32 compared to Int32 |
michael@0 | 2221 | // Boolean compared to Boolean |
michael@0 | 2222 | Compare_Int32, |
michael@0 | 2223 | Compare_Int32MaybeCoerceBoth, |
michael@0 | 2224 | Compare_Int32MaybeCoerceLHS, |
michael@0 | 2225 | Compare_Int32MaybeCoerceRHS, |
michael@0 | 2226 | |
michael@0 | 2227 | // Int32 compared as unsigneds |
michael@0 | 2228 | Compare_UInt32, |
michael@0 | 2229 | |
michael@0 | 2230 | // Double compared to Double |
michael@0 | 2231 | Compare_Double, |
michael@0 | 2232 | |
michael@0 | 2233 | Compare_DoubleMaybeCoerceLHS, |
michael@0 | 2234 | Compare_DoubleMaybeCoerceRHS, |
michael@0 | 2235 | |
michael@0 | 2236 | // Float compared to Float |
michael@0 | 2237 | Compare_Float32, |
michael@0 | 2238 | |
michael@0 | 2239 | // String compared to String |
michael@0 | 2240 | Compare_String, |
michael@0 | 2241 | |
michael@0 | 2242 | // Undefined compared to String |
michael@0 | 2243 | // Null compared to String |
michael@0 | 2244 | // Boolean compared to String |
michael@0 | 2245 | // Int32 compared to String |
michael@0 | 2246 | // Double compared to String |
michael@0 | 2247 | // Object compared to String |
michael@0 | 2248 | // Value compared to String |
michael@0 | 2249 | Compare_StrictString, |
michael@0 | 2250 | |
michael@0 | 2251 | // Object compared to Object |
michael@0 | 2252 | Compare_Object, |
michael@0 | 2253 | |
michael@0 | 2254 | // Compare 2 values bitwise |
michael@0 | 2255 | Compare_Value, |
michael@0 | 2256 | |
michael@0 | 2257 | // All other possible compares |
michael@0 | 2258 | Compare_Unknown |
michael@0 | 2259 | }; |
michael@0 | 2260 | |
michael@0 | 2261 | private: |
michael@0 | 2262 | CompareType compareType_; |
michael@0 | 2263 | JSOp jsop_; |
michael@0 | 2264 | bool operandMightEmulateUndefined_; |
michael@0 | 2265 | bool operandsAreNeverNaN_; |
michael@0 | 2266 | |
michael@0 | 2267 | // When a floating-point comparison is converted to an integer comparison |
michael@0 | 2268 | // (when range analysis proves it safe), we need to convert the operands |
michael@0 | 2269 | // to integer as well. |
michael@0 | 2270 | bool truncateOperands_; |
michael@0 | 2271 | |
michael@0 | 2272 | MCompare(MDefinition *left, MDefinition *right, JSOp jsop) |
michael@0 | 2273 | : MBinaryInstruction(left, right), |
michael@0 | 2274 | compareType_(Compare_Unknown), |
michael@0 | 2275 | jsop_(jsop), |
michael@0 | 2276 | operandMightEmulateUndefined_(true), |
michael@0 | 2277 | operandsAreNeverNaN_(false), |
michael@0 | 2278 | truncateOperands_(false) |
michael@0 | 2279 | { |
michael@0 | 2280 | setResultType(MIRType_Boolean); |
michael@0 | 2281 | setMovable(); |
michael@0 | 2282 | } |
michael@0 | 2283 | |
michael@0 | 2284 | public: |
michael@0 | 2285 | INSTRUCTION_HEADER(Compare) |
michael@0 | 2286 | static MCompare *New(TempAllocator &alloc, MDefinition *left, MDefinition *right, JSOp op); |
michael@0 | 2287 | static MCompare *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, JSOp op, |
michael@0 | 2288 | CompareType compareType); |
michael@0 | 2289 | |
michael@0 | 2290 | bool tryFold(bool *result); |
michael@0 | 2291 | bool evaluateConstantOperands(bool *result); |
michael@0 | 2292 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 2293 | void filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined, |
michael@0 | 2294 | bool *filtersNull); |
michael@0 | 2295 | |
michael@0 | 2296 | void infer(BaselineInspector *inspector, jsbytecode *pc); |
michael@0 | 2297 | CompareType compareType() const { |
michael@0 | 2298 | return compareType_; |
michael@0 | 2299 | } |
michael@0 | 2300 | bool isInt32Comparison() const { |
michael@0 | 2301 | return compareType() == Compare_Int32 || |
michael@0 | 2302 | compareType() == Compare_Int32MaybeCoerceBoth || |
michael@0 | 2303 | compareType() == Compare_Int32MaybeCoerceLHS || |
michael@0 | 2304 | compareType() == Compare_Int32MaybeCoerceRHS; |
michael@0 | 2305 | } |
michael@0 | 2306 | bool isDoubleComparison() const { |
michael@0 | 2307 | return compareType() == Compare_Double || |
michael@0 | 2308 | compareType() == Compare_DoubleMaybeCoerceLHS || |
michael@0 | 2309 | compareType() == Compare_DoubleMaybeCoerceRHS; |
michael@0 | 2310 | } |
michael@0 | 2311 | bool isFloat32Comparison() const { |
michael@0 | 2312 | return compareType() == Compare_Float32; |
michael@0 | 2313 | } |
michael@0 | 2314 | void setCompareType(CompareType type) { |
michael@0 | 2315 | compareType_ = type; |
michael@0 | 2316 | } |
michael@0 | 2317 | MIRType inputType(); |
michael@0 | 2318 | |
michael@0 | 2319 | JSOp jsop() const { |
michael@0 | 2320 | return jsop_; |
michael@0 | 2321 | } |
michael@0 | 2322 | TypePolicy *typePolicy() { |
michael@0 | 2323 | return this; |
michael@0 | 2324 | } |
michael@0 | 2325 | void markNoOperandEmulatesUndefined() { |
michael@0 | 2326 | operandMightEmulateUndefined_ = false; |
michael@0 | 2327 | } |
michael@0 | 2328 | bool operandMightEmulateUndefined() const { |
michael@0 | 2329 | return operandMightEmulateUndefined_; |
michael@0 | 2330 | } |
michael@0 | 2331 | bool operandsAreNeverNaN() const { |
michael@0 | 2332 | return operandsAreNeverNaN_; |
michael@0 | 2333 | } |
michael@0 | 2334 | AliasSet getAliasSet() const { |
michael@0 | 2335 | // Strict equality is never effectful. |
michael@0 | 2336 | if (jsop_ == JSOP_STRICTEQ || jsop_ == JSOP_STRICTNE) |
michael@0 | 2337 | return AliasSet::None(); |
michael@0 | 2338 | if (compareType_ == Compare_Unknown) |
michael@0 | 2339 | return AliasSet::Store(AliasSet::Any); |
michael@0 | 2340 | JS_ASSERT(compareType_ <= Compare_Value); |
michael@0 | 2341 | return AliasSet::None(); |
michael@0 | 2342 | } |
michael@0 | 2343 | |
michael@0 | 2344 | void printOpcode(FILE *fp) const; |
michael@0 | 2345 | void collectRangeInfoPreTrunc(); |
michael@0 | 2346 | |
michael@0 | 2347 | void trySpecializeFloat32(TempAllocator &alloc); |
michael@0 | 2348 | bool isFloat32Commutative() const { return true; } |
michael@0 | 2349 | bool truncate(); |
michael@0 | 2350 | bool isOperandTruncated(size_t index) const; |
michael@0 | 2351 | |
michael@0 | 2352 | # ifdef DEBUG |
michael@0 | 2353 | bool isConsistentFloat32Use(MUse *use) const { |
michael@0 | 2354 | // Both sides of the compare can be Float32 |
michael@0 | 2355 | return compareType_ == Compare_Float32; |
michael@0 | 2356 | } |
michael@0 | 2357 | # endif |
michael@0 | 2358 | |
michael@0 | 2359 | protected: |
michael@0 | 2360 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 2361 | if (!binaryCongruentTo(ins)) |
michael@0 | 2362 | return false; |
michael@0 | 2363 | return compareType() == ins->toCompare()->compareType() && |
michael@0 | 2364 | jsop() == ins->toCompare()->jsop(); |
michael@0 | 2365 | } |
michael@0 | 2366 | }; |
michael@0 | 2367 | |
michael@0 | 2368 | // Takes a typed value and returns an untyped value. |
michael@0 | 2369 | class MBox : public MUnaryInstruction |
michael@0 | 2370 | { |
michael@0 | 2371 | MBox(TempAllocator &alloc, MDefinition *ins) |
michael@0 | 2372 | : MUnaryInstruction(ins) |
michael@0 | 2373 | { |
michael@0 | 2374 | setResultType(MIRType_Value); |
michael@0 | 2375 | if (ins->resultTypeSet()) { |
michael@0 | 2376 | setResultTypeSet(ins->resultTypeSet()); |
michael@0 | 2377 | } else if (ins->type() != MIRType_Value) { |
michael@0 | 2378 | types::Type ntype = ins->type() == MIRType_Object |
michael@0 | 2379 | ? types::Type::AnyObjectType() |
michael@0 | 2380 | : types::Type::PrimitiveType(ValueTypeFromMIRType(ins->type())); |
michael@0 | 2381 | setResultTypeSet(alloc.lifoAlloc()->new_<types::TemporaryTypeSet>(ntype)); |
michael@0 | 2382 | } |
michael@0 | 2383 | setMovable(); |
michael@0 | 2384 | } |
michael@0 | 2385 | |
michael@0 | 2386 | public: |
michael@0 | 2387 | INSTRUCTION_HEADER(Box) |
michael@0 | 2388 | static MBox *New(TempAllocator &alloc, MDefinition *ins) |
michael@0 | 2389 | { |
michael@0 | 2390 | // Cannot box a box. |
michael@0 | 2391 | JS_ASSERT(ins->type() != MIRType_Value); |
michael@0 | 2392 | |
michael@0 | 2393 | return new(alloc) MBox(alloc, ins); |
michael@0 | 2394 | } |
michael@0 | 2395 | |
michael@0 | 2396 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 2397 | return congruentIfOperandsEqual(ins); |
michael@0 | 2398 | } |
michael@0 | 2399 | AliasSet getAliasSet() const { |
michael@0 | 2400 | return AliasSet::None(); |
michael@0 | 2401 | } |
michael@0 | 2402 | }; |
michael@0 | 2403 | |
michael@0 | 2404 | // Note: the op may have been inverted during lowering (to put constants in a |
michael@0 | 2405 | // position where they can be immediates), so it is important to use the |
michael@0 | 2406 | // lir->jsop() instead of the mir->jsop() when it is present. |
michael@0 | 2407 | static inline Assembler::Condition |
michael@0 | 2408 | JSOpToCondition(MCompare::CompareType compareType, JSOp op) |
michael@0 | 2409 | { |
michael@0 | 2410 | bool isSigned = (compareType != MCompare::Compare_UInt32); |
michael@0 | 2411 | return JSOpToCondition(op, isSigned); |
michael@0 | 2412 | } |
michael@0 | 2413 | |
michael@0 | 2414 | // Takes a typed value and checks if it is a certain type. If so, the payload |
michael@0 | 2415 | // is unpacked and returned as that type. Otherwise, it is considered a |
michael@0 | 2416 | // deoptimization. |
michael@0 | 2417 | class MUnbox : public MUnaryInstruction, public BoxInputsPolicy |
michael@0 | 2418 | { |
michael@0 | 2419 | public: |
michael@0 | 2420 | enum Mode { |
michael@0 | 2421 | Fallible, // Check the type, and deoptimize if unexpected. |
michael@0 | 2422 | Infallible, // Type guard is not necessary. |
michael@0 | 2423 | TypeBarrier // Guard on the type, and act like a TypeBarrier on failure. |
michael@0 | 2424 | }; |
michael@0 | 2425 | |
michael@0 | 2426 | private: |
michael@0 | 2427 | Mode mode_; |
michael@0 | 2428 | BailoutKind bailoutKind_; |
michael@0 | 2429 | |
michael@0 | 2430 | MUnbox(MDefinition *ins, MIRType type, Mode mode, BailoutKind kind) |
michael@0 | 2431 | : MUnaryInstruction(ins), |
michael@0 | 2432 | mode_(mode) |
michael@0 | 2433 | { |
michael@0 | 2434 | JS_ASSERT(ins->type() == MIRType_Value); |
michael@0 | 2435 | JS_ASSERT(type == MIRType_Boolean || |
michael@0 | 2436 | type == MIRType_Int32 || |
michael@0 | 2437 | type == MIRType_Double || |
michael@0 | 2438 | type == MIRType_String || |
michael@0 | 2439 | type == MIRType_Object); |
michael@0 | 2440 | |
michael@0 | 2441 | setResultType(type); |
michael@0 | 2442 | setResultTypeSet(ins->resultTypeSet()); |
michael@0 | 2443 | setMovable(); |
michael@0 | 2444 | |
michael@0 | 2445 | if (mode_ == TypeBarrier || mode_ == Fallible) |
michael@0 | 2446 | setGuard(); |
michael@0 | 2447 | |
michael@0 | 2448 | bailoutKind_ = kind; |
michael@0 | 2449 | } |
michael@0 | 2450 | public: |
michael@0 | 2451 | INSTRUCTION_HEADER(Unbox) |
michael@0 | 2452 | static MUnbox *New(TempAllocator &alloc, MDefinition *ins, MIRType type, Mode mode) |
michael@0 | 2453 | { |
michael@0 | 2454 | return new(alloc) MUnbox(ins, type, mode, Bailout_Normal); |
michael@0 | 2455 | } |
michael@0 | 2456 | |
michael@0 | 2457 | static MUnbox *New(TempAllocator &alloc, MDefinition *ins, MIRType type, Mode mode, |
michael@0 | 2458 | BailoutKind kind) |
michael@0 | 2459 | { |
michael@0 | 2460 | return new(alloc) MUnbox(ins, type, mode, kind); |
michael@0 | 2461 | } |
michael@0 | 2462 | |
michael@0 | 2463 | TypePolicy *typePolicy() { |
michael@0 | 2464 | return this; |
michael@0 | 2465 | } |
michael@0 | 2466 | |
michael@0 | 2467 | Mode mode() const { |
michael@0 | 2468 | return mode_; |
michael@0 | 2469 | } |
michael@0 | 2470 | BailoutKind bailoutKind() const { |
michael@0 | 2471 | // If infallible, no bailout should be generated. |
michael@0 | 2472 | JS_ASSERT(fallible()); |
michael@0 | 2473 | return bailoutKind_; |
michael@0 | 2474 | } |
michael@0 | 2475 | bool fallible() const { |
michael@0 | 2476 | return mode() != Infallible; |
michael@0 | 2477 | } |
michael@0 | 2478 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 2479 | if (!ins->isUnbox() || ins->toUnbox()->mode() != mode()) |
michael@0 | 2480 | return false; |
michael@0 | 2481 | return congruentIfOperandsEqual(ins); |
michael@0 | 2482 | } |
michael@0 | 2483 | AliasSet getAliasSet() const { |
michael@0 | 2484 | return AliasSet::None(); |
michael@0 | 2485 | } |
michael@0 | 2486 | void printOpcode(FILE *fp) const; |
michael@0 | 2487 | void makeInfallible() { |
michael@0 | 2488 | // Should only be called if we're already Infallible or TypeBarrier |
michael@0 | 2489 | JS_ASSERT(mode() != Fallible); |
michael@0 | 2490 | mode_ = Infallible; |
michael@0 | 2491 | } |
michael@0 | 2492 | }; |
michael@0 | 2493 | |
michael@0 | 2494 | class MGuardObject : public MUnaryInstruction, public SingleObjectPolicy |
michael@0 | 2495 | { |
michael@0 | 2496 | MGuardObject(MDefinition *ins) |
michael@0 | 2497 | : MUnaryInstruction(ins) |
michael@0 | 2498 | { |
michael@0 | 2499 | setGuard(); |
michael@0 | 2500 | setMovable(); |
michael@0 | 2501 | setResultType(MIRType_Object); |
michael@0 | 2502 | } |
michael@0 | 2503 | |
michael@0 | 2504 | public: |
michael@0 | 2505 | INSTRUCTION_HEADER(GuardObject) |
michael@0 | 2506 | |
michael@0 | 2507 | static MGuardObject *New(TempAllocator &alloc, MDefinition *ins) { |
michael@0 | 2508 | return new(alloc) MGuardObject(ins); |
michael@0 | 2509 | } |
michael@0 | 2510 | |
michael@0 | 2511 | TypePolicy *typePolicy() { |
michael@0 | 2512 | return this; |
michael@0 | 2513 | } |
michael@0 | 2514 | AliasSet getAliasSet() const { |
michael@0 | 2515 | return AliasSet::None(); |
michael@0 | 2516 | } |
michael@0 | 2517 | }; |
michael@0 | 2518 | |
michael@0 | 2519 | class MGuardString |
michael@0 | 2520 | : public MUnaryInstruction, |
michael@0 | 2521 | public StringPolicy<0> |
michael@0 | 2522 | { |
michael@0 | 2523 | MGuardString(MDefinition *ins) |
michael@0 | 2524 | : MUnaryInstruction(ins) |
michael@0 | 2525 | { |
michael@0 | 2526 | setGuard(); |
michael@0 | 2527 | setMovable(); |
michael@0 | 2528 | setResultType(MIRType_String); |
michael@0 | 2529 | } |
michael@0 | 2530 | |
michael@0 | 2531 | public: |
michael@0 | 2532 | INSTRUCTION_HEADER(GuardString) |
michael@0 | 2533 | |
michael@0 | 2534 | static MGuardString *New(TempAllocator &alloc, MDefinition *ins) { |
michael@0 | 2535 | return new(alloc) MGuardString(ins); |
michael@0 | 2536 | } |
michael@0 | 2537 | |
michael@0 | 2538 | TypePolicy *typePolicy() { |
michael@0 | 2539 | return this; |
michael@0 | 2540 | } |
michael@0 | 2541 | AliasSet getAliasSet() const { |
michael@0 | 2542 | return AliasSet::None(); |
michael@0 | 2543 | } |
michael@0 | 2544 | }; |
michael@0 | 2545 | |
michael@0 | 2546 | class MAssertRange |
michael@0 | 2547 | : public MUnaryInstruction |
michael@0 | 2548 | { |
michael@0 | 2549 | // This is the range checked by the assertion. Don't confuse this with the |
michael@0 | 2550 | // range_ member or the range() accessor. Since MAssertRange doesn't return |
michael@0 | 2551 | // a value, it doesn't use those. |
michael@0 | 2552 | const Range *assertedRange_; |
michael@0 | 2553 | |
michael@0 | 2554 | MAssertRange(MDefinition *ins, const Range *assertedRange) |
michael@0 | 2555 | : MUnaryInstruction(ins), assertedRange_(assertedRange) |
michael@0 | 2556 | { |
michael@0 | 2557 | setGuard(); |
michael@0 | 2558 | setMovable(); |
michael@0 | 2559 | setResultType(MIRType_None); |
michael@0 | 2560 | } |
michael@0 | 2561 | |
michael@0 | 2562 | public: |
michael@0 | 2563 | INSTRUCTION_HEADER(AssertRange) |
michael@0 | 2564 | |
michael@0 | 2565 | static MAssertRange *New(TempAllocator &alloc, MDefinition *ins, const Range *assertedRange) { |
michael@0 | 2566 | return new(alloc) MAssertRange(ins, assertedRange); |
michael@0 | 2567 | } |
michael@0 | 2568 | |
michael@0 | 2569 | const Range *assertedRange() const { |
michael@0 | 2570 | return assertedRange_; |
michael@0 | 2571 | } |
michael@0 | 2572 | |
michael@0 | 2573 | AliasSet getAliasSet() const { |
michael@0 | 2574 | return AliasSet::None(); |
michael@0 | 2575 | } |
michael@0 | 2576 | |
michael@0 | 2577 | void printOpcode(FILE *fp) const; |
michael@0 | 2578 | }; |
michael@0 | 2579 | |
michael@0 | 2580 | // Caller-side allocation of |this| for |new|: |
michael@0 | 2581 | // Given a templateobject, construct |this| for JSOP_NEW |
michael@0 | 2582 | class MCreateThisWithTemplate |
michael@0 | 2583 | : public MNullaryInstruction |
michael@0 | 2584 | { |
michael@0 | 2585 | // Template for |this|, provided by TI |
michael@0 | 2586 | CompilerRootObject templateObject_; |
michael@0 | 2587 | gc::InitialHeap initialHeap_; |
michael@0 | 2588 | |
michael@0 | 2589 | MCreateThisWithTemplate(types::CompilerConstraintList *constraints, JSObject *templateObject, |
michael@0 | 2590 | gc::InitialHeap initialHeap) |
michael@0 | 2591 | : templateObject_(templateObject), |
michael@0 | 2592 | initialHeap_(initialHeap) |
michael@0 | 2593 | { |
michael@0 | 2594 | setResultType(MIRType_Object); |
michael@0 | 2595 | setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject)); |
michael@0 | 2596 | } |
michael@0 | 2597 | |
michael@0 | 2598 | public: |
michael@0 | 2599 | INSTRUCTION_HEADER(CreateThisWithTemplate); |
michael@0 | 2600 | static MCreateThisWithTemplate *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
michael@0 | 2601 | JSObject *templateObject, gc::InitialHeap initialHeap) |
michael@0 | 2602 | { |
michael@0 | 2603 | return new(alloc) MCreateThisWithTemplate(constraints, templateObject, initialHeap); |
michael@0 | 2604 | } |
michael@0 | 2605 | |
michael@0 | 2606 | JSObject *templateObject() const { |
michael@0 | 2607 | return templateObject_; |
michael@0 | 2608 | } |
michael@0 | 2609 | |
michael@0 | 2610 | gc::InitialHeap initialHeap() const { |
michael@0 | 2611 | return initialHeap_; |
michael@0 | 2612 | } |
michael@0 | 2613 | |
michael@0 | 2614 | // Although creation of |this| modifies global state, it is safely repeatable. |
michael@0 | 2615 | AliasSet getAliasSet() const { |
michael@0 | 2616 | return AliasSet::None(); |
michael@0 | 2617 | } |
michael@0 | 2618 | }; |
michael@0 | 2619 | |
michael@0 | 2620 | // Caller-side allocation of |this| for |new|: |
michael@0 | 2621 | // Given a prototype operand, construct |this| for JSOP_NEW. |
michael@0 | 2622 | class MCreateThisWithProto |
michael@0 | 2623 | : public MBinaryInstruction, |
michael@0 | 2624 | public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> > |
michael@0 | 2625 | { |
michael@0 | 2626 | MCreateThisWithProto(MDefinition *callee, MDefinition *prototype) |
michael@0 | 2627 | : MBinaryInstruction(callee, prototype) |
michael@0 | 2628 | { |
michael@0 | 2629 | setResultType(MIRType_Object); |
michael@0 | 2630 | } |
michael@0 | 2631 | |
michael@0 | 2632 | public: |
michael@0 | 2633 | INSTRUCTION_HEADER(CreateThisWithProto) |
michael@0 | 2634 | static MCreateThisWithProto *New(TempAllocator &alloc, MDefinition *callee, |
michael@0 | 2635 | MDefinition *prototype) |
michael@0 | 2636 | { |
michael@0 | 2637 | return new(alloc) MCreateThisWithProto(callee, prototype); |
michael@0 | 2638 | } |
michael@0 | 2639 | |
michael@0 | 2640 | MDefinition *getCallee() const { |
michael@0 | 2641 | return getOperand(0); |
michael@0 | 2642 | } |
michael@0 | 2643 | MDefinition *getPrototype() const { |
michael@0 | 2644 | return getOperand(1); |
michael@0 | 2645 | } |
michael@0 | 2646 | |
michael@0 | 2647 | // Although creation of |this| modifies global state, it is safely repeatable. |
michael@0 | 2648 | AliasSet getAliasSet() const { |
michael@0 | 2649 | return AliasSet::None(); |
michael@0 | 2650 | } |
michael@0 | 2651 | TypePolicy *typePolicy() { |
michael@0 | 2652 | return this; |
michael@0 | 2653 | } |
michael@0 | 2654 | bool possiblyCalls() const { |
michael@0 | 2655 | return true; |
michael@0 | 2656 | } |
michael@0 | 2657 | }; |
michael@0 | 2658 | |
michael@0 | 2659 | // Caller-side allocation of |this| for |new|: |
michael@0 | 2660 | // Constructs |this| when possible, else MagicValue(JS_IS_CONSTRUCTING). |
michael@0 | 2661 | class MCreateThis |
michael@0 | 2662 | : public MUnaryInstruction, |
michael@0 | 2663 | public ObjectPolicy<0> |
michael@0 | 2664 | { |
michael@0 | 2665 | MCreateThis(MDefinition *callee) |
michael@0 | 2666 | : MUnaryInstruction(callee) |
michael@0 | 2667 | { |
michael@0 | 2668 | setResultType(MIRType_Value); |
michael@0 | 2669 | } |
michael@0 | 2670 | |
michael@0 | 2671 | public: |
michael@0 | 2672 | INSTRUCTION_HEADER(CreateThis) |
michael@0 | 2673 | static MCreateThis *New(TempAllocator &alloc, MDefinition *callee) |
michael@0 | 2674 | { |
michael@0 | 2675 | return new(alloc) MCreateThis(callee); |
michael@0 | 2676 | } |
michael@0 | 2677 | |
michael@0 | 2678 | MDefinition *getCallee() const { |
michael@0 | 2679 | return getOperand(0); |
michael@0 | 2680 | } |
michael@0 | 2681 | |
michael@0 | 2682 | // Although creation of |this| modifies global state, it is safely repeatable. |
michael@0 | 2683 | AliasSet getAliasSet() const { |
michael@0 | 2684 | return AliasSet::None(); |
michael@0 | 2685 | } |
michael@0 | 2686 | TypePolicy *typePolicy() { |
michael@0 | 2687 | return this; |
michael@0 | 2688 | } |
michael@0 | 2689 | bool possiblyCalls() const { |
michael@0 | 2690 | return true; |
michael@0 | 2691 | } |
michael@0 | 2692 | }; |
michael@0 | 2693 | |
michael@0 | 2694 | // Eager initialization of arguments object. |
michael@0 | 2695 | class MCreateArgumentsObject |
michael@0 | 2696 | : public MUnaryInstruction, |
michael@0 | 2697 | public ObjectPolicy<0> |
michael@0 | 2698 | { |
michael@0 | 2699 | MCreateArgumentsObject(MDefinition *callObj) |
michael@0 | 2700 | : MUnaryInstruction(callObj) |
michael@0 | 2701 | { |
michael@0 | 2702 | setResultType(MIRType_Object); |
michael@0 | 2703 | setGuard(); |
michael@0 | 2704 | } |
michael@0 | 2705 | |
michael@0 | 2706 | public: |
michael@0 | 2707 | INSTRUCTION_HEADER(CreateArgumentsObject) |
michael@0 | 2708 | static MCreateArgumentsObject *New(TempAllocator &alloc, MDefinition *callObj) { |
michael@0 | 2709 | return new(alloc) MCreateArgumentsObject(callObj); |
michael@0 | 2710 | } |
michael@0 | 2711 | |
michael@0 | 2712 | MDefinition *getCallObject() const { |
michael@0 | 2713 | return getOperand(0); |
michael@0 | 2714 | } |
michael@0 | 2715 | |
michael@0 | 2716 | AliasSet getAliasSet() const { |
michael@0 | 2717 | return AliasSet::None(); |
michael@0 | 2718 | } |
michael@0 | 2719 | |
michael@0 | 2720 | TypePolicy *typePolicy() { |
michael@0 | 2721 | return this; |
michael@0 | 2722 | } |
michael@0 | 2723 | bool possiblyCalls() const { |
michael@0 | 2724 | return true; |
michael@0 | 2725 | } |
michael@0 | 2726 | }; |
michael@0 | 2727 | |
michael@0 | 2728 | class MGetArgumentsObjectArg |
michael@0 | 2729 | : public MUnaryInstruction, |
michael@0 | 2730 | public ObjectPolicy<0> |
michael@0 | 2731 | { |
michael@0 | 2732 | size_t argno_; |
michael@0 | 2733 | |
michael@0 | 2734 | MGetArgumentsObjectArg(MDefinition *argsObject, size_t argno) |
michael@0 | 2735 | : MUnaryInstruction(argsObject), |
michael@0 | 2736 | argno_(argno) |
michael@0 | 2737 | { |
michael@0 | 2738 | setResultType(MIRType_Value); |
michael@0 | 2739 | } |
michael@0 | 2740 | |
michael@0 | 2741 | public: |
michael@0 | 2742 | INSTRUCTION_HEADER(GetArgumentsObjectArg) |
michael@0 | 2743 | static MGetArgumentsObjectArg *New(TempAllocator &alloc, MDefinition *argsObj, size_t argno) |
michael@0 | 2744 | { |
michael@0 | 2745 | return new(alloc) MGetArgumentsObjectArg(argsObj, argno); |
michael@0 | 2746 | } |
michael@0 | 2747 | |
michael@0 | 2748 | MDefinition *getArgsObject() const { |
michael@0 | 2749 | return getOperand(0); |
michael@0 | 2750 | } |
michael@0 | 2751 | |
michael@0 | 2752 | size_t argno() const { |
michael@0 | 2753 | return argno_; |
michael@0 | 2754 | } |
michael@0 | 2755 | |
michael@0 | 2756 | AliasSet getAliasSet() const { |
michael@0 | 2757 | return AliasSet::Load(AliasSet::Any); |
michael@0 | 2758 | } |
michael@0 | 2759 | |
michael@0 | 2760 | TypePolicy *typePolicy() { |
michael@0 | 2761 | return this; |
michael@0 | 2762 | } |
michael@0 | 2763 | }; |
michael@0 | 2764 | |
michael@0 | 2765 | class MSetArgumentsObjectArg |
michael@0 | 2766 | : public MBinaryInstruction, |
michael@0 | 2767 | public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> > |
michael@0 | 2768 | { |
michael@0 | 2769 | size_t argno_; |
michael@0 | 2770 | |
michael@0 | 2771 | MSetArgumentsObjectArg(MDefinition *argsObj, size_t argno, MDefinition *value) |
michael@0 | 2772 | : MBinaryInstruction(argsObj, value), |
michael@0 | 2773 | argno_(argno) |
michael@0 | 2774 | { |
michael@0 | 2775 | } |
michael@0 | 2776 | |
michael@0 | 2777 | public: |
michael@0 | 2778 | INSTRUCTION_HEADER(SetArgumentsObjectArg) |
michael@0 | 2779 | static MSetArgumentsObjectArg *New(TempAllocator &alloc, MDefinition *argsObj, size_t argno, |
michael@0 | 2780 | MDefinition *value) |
michael@0 | 2781 | { |
michael@0 | 2782 | return new(alloc) MSetArgumentsObjectArg(argsObj, argno, value); |
michael@0 | 2783 | } |
michael@0 | 2784 | |
michael@0 | 2785 | MDefinition *getArgsObject() const { |
michael@0 | 2786 | return getOperand(0); |
michael@0 | 2787 | } |
michael@0 | 2788 | |
michael@0 | 2789 | size_t argno() const { |
michael@0 | 2790 | return argno_; |
michael@0 | 2791 | } |
michael@0 | 2792 | |
michael@0 | 2793 | MDefinition *getValue() const { |
michael@0 | 2794 | return getOperand(1); |
michael@0 | 2795 | } |
michael@0 | 2796 | |
michael@0 | 2797 | AliasSet getAliasSet() const { |
michael@0 | 2798 | return AliasSet::Store(AliasSet::Any); |
michael@0 | 2799 | } |
michael@0 | 2800 | |
michael@0 | 2801 | TypePolicy *typePolicy() { |
michael@0 | 2802 | return this; |
michael@0 | 2803 | } |
michael@0 | 2804 | }; |
michael@0 | 2805 | |
michael@0 | 2806 | class MRunOncePrologue |
michael@0 | 2807 | : public MNullaryInstruction |
michael@0 | 2808 | { |
michael@0 | 2809 | protected: |
michael@0 | 2810 | MRunOncePrologue() |
michael@0 | 2811 | { |
michael@0 | 2812 | setGuard(); |
michael@0 | 2813 | } |
michael@0 | 2814 | |
michael@0 | 2815 | public: |
michael@0 | 2816 | INSTRUCTION_HEADER(RunOncePrologue) |
michael@0 | 2817 | |
michael@0 | 2818 | static MRunOncePrologue *New(TempAllocator &alloc) { |
michael@0 | 2819 | return new(alloc) MRunOncePrologue(); |
michael@0 | 2820 | } |
michael@0 | 2821 | bool possiblyCalls() const { |
michael@0 | 2822 | return true; |
michael@0 | 2823 | } |
michael@0 | 2824 | }; |
michael@0 | 2825 | |
michael@0 | 2826 | // Given a MIRType_Value A and a MIRType_Object B: |
michael@0 | 2827 | // If the Value may be safely unboxed to an Object, return Object(A). |
michael@0 | 2828 | // Otherwise, return B. |
michael@0 | 2829 | // Used to implement return behavior for inlined constructors. |
michael@0 | 2830 | class MReturnFromCtor |
michael@0 | 2831 | : public MAryInstruction<2>, |
michael@0 | 2832 | public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> > |
michael@0 | 2833 | { |
michael@0 | 2834 | MReturnFromCtor(MDefinition *value, MDefinition *object) { |
michael@0 | 2835 | setOperand(0, value); |
michael@0 | 2836 | setOperand(1, object); |
michael@0 | 2837 | setResultType(MIRType_Object); |
michael@0 | 2838 | } |
michael@0 | 2839 | |
michael@0 | 2840 | public: |
michael@0 | 2841 | INSTRUCTION_HEADER(ReturnFromCtor) |
michael@0 | 2842 | static MReturnFromCtor *New(TempAllocator &alloc, MDefinition *value, MDefinition *object) |
michael@0 | 2843 | { |
michael@0 | 2844 | return new(alloc) MReturnFromCtor(value, object); |
michael@0 | 2845 | } |
michael@0 | 2846 | |
michael@0 | 2847 | MDefinition *getValue() const { |
michael@0 | 2848 | return getOperand(0); |
michael@0 | 2849 | } |
michael@0 | 2850 | MDefinition *getObject() const { |
michael@0 | 2851 | return getOperand(1); |
michael@0 | 2852 | } |
michael@0 | 2853 | |
michael@0 | 2854 | AliasSet getAliasSet() const { |
michael@0 | 2855 | return AliasSet::None(); |
michael@0 | 2856 | } |
michael@0 | 2857 | TypePolicy *typePolicy() { |
michael@0 | 2858 | return this; |
michael@0 | 2859 | } |
michael@0 | 2860 | }; |
michael@0 | 2861 | |
michael@0 | 2862 | // Converts a primitive (either typed or untyped) to a double. If the input is |
michael@0 | 2863 | // not primitive at runtime, a bailout occurs. |
michael@0 | 2864 | class MToDouble |
michael@0 | 2865 | : public MUnaryInstruction, |
michael@0 | 2866 | public ToDoublePolicy |
michael@0 | 2867 | { |
michael@0 | 2868 | public: |
michael@0 | 2869 | // Types of values which can be converted. |
michael@0 | 2870 | enum ConversionKind { |
michael@0 | 2871 | NonStringPrimitives, |
michael@0 | 2872 | NonNullNonStringPrimitives, |
michael@0 | 2873 | NumbersOnly |
michael@0 | 2874 | }; |
michael@0 | 2875 | |
michael@0 | 2876 | private: |
michael@0 | 2877 | ConversionKind conversion_; |
michael@0 | 2878 | |
michael@0 | 2879 | MToDouble(MDefinition *def, ConversionKind conversion = NonStringPrimitives) |
michael@0 | 2880 | : MUnaryInstruction(def), conversion_(conversion) |
michael@0 | 2881 | { |
michael@0 | 2882 | setResultType(MIRType_Double); |
michael@0 | 2883 | setMovable(); |
michael@0 | 2884 | |
michael@0 | 2885 | // An object might have "valueOf", which means it is effectful. |
michael@0 | 2886 | if (def->mightBeType(MIRType_Object)) |
michael@0 | 2887 | setGuard(); |
michael@0 | 2888 | } |
michael@0 | 2889 | |
michael@0 | 2890 | public: |
michael@0 | 2891 | INSTRUCTION_HEADER(ToDouble) |
michael@0 | 2892 | static MToDouble *New(TempAllocator &alloc, MDefinition *def, |
michael@0 | 2893 | ConversionKind conversion = NonStringPrimitives) |
michael@0 | 2894 | { |
michael@0 | 2895 | return new(alloc) MToDouble(def, conversion); |
michael@0 | 2896 | } |
michael@0 | 2897 | static MToDouble *NewAsmJS(TempAllocator &alloc, MDefinition *def) { |
michael@0 | 2898 | return new(alloc) MToDouble(def); |
michael@0 | 2899 | } |
michael@0 | 2900 | |
michael@0 | 2901 | ConversionKind conversion() const { |
michael@0 | 2902 | return conversion_; |
michael@0 | 2903 | } |
michael@0 | 2904 | |
michael@0 | 2905 | TypePolicy *typePolicy() { |
michael@0 | 2906 | return this; |
michael@0 | 2907 | } |
michael@0 | 2908 | |
michael@0 | 2909 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 2910 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 2911 | if (!ins->isToDouble() || ins->toToDouble()->conversion() != conversion()) |
michael@0 | 2912 | return false; |
michael@0 | 2913 | return congruentIfOperandsEqual(ins); |
michael@0 | 2914 | } |
michael@0 | 2915 | AliasSet getAliasSet() const { |
michael@0 | 2916 | return AliasSet::None(); |
michael@0 | 2917 | } |
michael@0 | 2918 | |
michael@0 | 2919 | void computeRange(TempAllocator &alloc); |
michael@0 | 2920 | bool truncate(); |
michael@0 | 2921 | bool isOperandTruncated(size_t index) const; |
michael@0 | 2922 | |
michael@0 | 2923 | #ifdef DEBUG |
michael@0 | 2924 | bool isConsistentFloat32Use(MUse *use) const { return true; } |
michael@0 | 2925 | #endif |
michael@0 | 2926 | }; |
michael@0 | 2927 | |
michael@0 | 2928 | // Converts a primitive (either typed or untyped) to a float32. If the input is |
michael@0 | 2929 | // not primitive at runtime, a bailout occurs. |
michael@0 | 2930 | class MToFloat32 |
michael@0 | 2931 | : public MUnaryInstruction, |
michael@0 | 2932 | public ToDoublePolicy |
michael@0 | 2933 | { |
michael@0 | 2934 | public: |
michael@0 | 2935 | // Types of values which can be converted. |
michael@0 | 2936 | enum ConversionKind { |
michael@0 | 2937 | NonStringPrimitives, |
michael@0 | 2938 | NonNullNonStringPrimitives, |
michael@0 | 2939 | NumbersOnly |
michael@0 | 2940 | }; |
michael@0 | 2941 | |
michael@0 | 2942 | protected: |
michael@0 | 2943 | ConversionKind conversion_; |
michael@0 | 2944 | |
michael@0 | 2945 | MToFloat32(MDefinition *def, ConversionKind conversion) |
michael@0 | 2946 | : MUnaryInstruction(def), conversion_(conversion) |
michael@0 | 2947 | { |
michael@0 | 2948 | setResultType(MIRType_Float32); |
michael@0 | 2949 | setMovable(); |
michael@0 | 2950 | |
michael@0 | 2951 | // An object might have "valueOf", which means it is effectful. |
michael@0 | 2952 | if (def->mightBeType(MIRType_Object)) |
michael@0 | 2953 | setGuard(); |
michael@0 | 2954 | } |
michael@0 | 2955 | |
michael@0 | 2956 | public: |
michael@0 | 2957 | INSTRUCTION_HEADER(ToFloat32) |
michael@0 | 2958 | static MToFloat32 *New(TempAllocator &alloc, MDefinition *def, |
michael@0 | 2959 | ConversionKind conversion = NonStringPrimitives) |
michael@0 | 2960 | { |
michael@0 | 2961 | return new(alloc) MToFloat32(def, conversion); |
michael@0 | 2962 | } |
michael@0 | 2963 | static MToFloat32 *NewAsmJS(TempAllocator &alloc, MDefinition *def) { |
michael@0 | 2964 | return new(alloc) MToFloat32(def, NonStringPrimitives); |
michael@0 | 2965 | } |
michael@0 | 2966 | |
michael@0 | 2967 | ConversionKind conversion() const { |
michael@0 | 2968 | return conversion_; |
michael@0 | 2969 | } |
michael@0 | 2970 | |
michael@0 | 2971 | TypePolicy *typePolicy() { |
michael@0 | 2972 | return this; |
michael@0 | 2973 | } |
michael@0 | 2974 | |
michael@0 | 2975 | virtual MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 2976 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 2977 | if (!ins->isToFloat32() || ins->toToFloat32()->conversion() != conversion()) |
michael@0 | 2978 | return false; |
michael@0 | 2979 | return congruentIfOperandsEqual(ins); |
michael@0 | 2980 | } |
michael@0 | 2981 | AliasSet getAliasSet() const { |
michael@0 | 2982 | return AliasSet::None(); |
michael@0 | 2983 | } |
michael@0 | 2984 | |
michael@0 | 2985 | void computeRange(TempAllocator &alloc); |
michael@0 | 2986 | |
michael@0 | 2987 | bool canConsumeFloat32(MUse *use) const { return true; } |
michael@0 | 2988 | bool canProduceFloat32() const { return true; } |
michael@0 | 2989 | }; |
michael@0 | 2990 | |
michael@0 | 2991 | // Converts a uint32 to a double (coming from asm.js). |
michael@0 | 2992 | class MAsmJSUnsignedToDouble |
michael@0 | 2993 | : public MUnaryInstruction |
michael@0 | 2994 | { |
michael@0 | 2995 | MAsmJSUnsignedToDouble(MDefinition *def) |
michael@0 | 2996 | : MUnaryInstruction(def) |
michael@0 | 2997 | { |
michael@0 | 2998 | setResultType(MIRType_Double); |
michael@0 | 2999 | setMovable(); |
michael@0 | 3000 | } |
michael@0 | 3001 | |
michael@0 | 3002 | public: |
michael@0 | 3003 | INSTRUCTION_HEADER(AsmJSUnsignedToDouble); |
michael@0 | 3004 | static MAsmJSUnsignedToDouble *NewAsmJS(TempAllocator &alloc, MDefinition *def) { |
michael@0 | 3005 | return new(alloc) MAsmJSUnsignedToDouble(def); |
michael@0 | 3006 | } |
michael@0 | 3007 | |
michael@0 | 3008 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 3009 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3010 | return congruentIfOperandsEqual(ins); |
michael@0 | 3011 | } |
michael@0 | 3012 | AliasSet getAliasSet() const { |
michael@0 | 3013 | return AliasSet::None(); |
michael@0 | 3014 | } |
michael@0 | 3015 | }; |
michael@0 | 3016 | |
michael@0 | 3017 | // Converts a uint32 to a float32 (coming from asm.js). |
michael@0 | 3018 | class MAsmJSUnsignedToFloat32 |
michael@0 | 3019 | : public MUnaryInstruction |
michael@0 | 3020 | { |
michael@0 | 3021 | MAsmJSUnsignedToFloat32(MDefinition *def) |
michael@0 | 3022 | : MUnaryInstruction(def) |
michael@0 | 3023 | { |
michael@0 | 3024 | setResultType(MIRType_Float32); |
michael@0 | 3025 | setMovable(); |
michael@0 | 3026 | } |
michael@0 | 3027 | |
michael@0 | 3028 | public: |
michael@0 | 3029 | INSTRUCTION_HEADER(AsmJSUnsignedToFloat32); |
michael@0 | 3030 | static MAsmJSUnsignedToFloat32 *NewAsmJS(TempAllocator &alloc, MDefinition *def) { |
michael@0 | 3031 | return new(alloc) MAsmJSUnsignedToFloat32(def); |
michael@0 | 3032 | } |
michael@0 | 3033 | |
michael@0 | 3034 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 3035 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3036 | return congruentIfOperandsEqual(ins); |
michael@0 | 3037 | } |
michael@0 | 3038 | AliasSet getAliasSet() const { |
michael@0 | 3039 | return AliasSet::None(); |
michael@0 | 3040 | } |
michael@0 | 3041 | |
michael@0 | 3042 | bool canProduceFloat32() const { return true; } |
michael@0 | 3043 | }; |
michael@0 | 3044 | |
michael@0 | 3045 | // Converts a primitive (either typed or untyped) to an int32. If the input is |
michael@0 | 3046 | // not primitive at runtime, a bailout occurs. If the input cannot be converted |
michael@0 | 3047 | // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs. |
michael@0 | 3048 | class MToInt32 |
michael@0 | 3049 | : public MUnaryInstruction, |
michael@0 | 3050 | public ToInt32Policy |
michael@0 | 3051 | { |
michael@0 | 3052 | bool canBeNegativeZero_; |
michael@0 | 3053 | MacroAssembler::IntConversionInputKind conversion_; |
michael@0 | 3054 | |
michael@0 | 3055 | MToInt32(MDefinition *def, MacroAssembler::IntConversionInputKind conversion) |
michael@0 | 3056 | : MUnaryInstruction(def), |
michael@0 | 3057 | canBeNegativeZero_(true), |
michael@0 | 3058 | conversion_(conversion) |
michael@0 | 3059 | { |
michael@0 | 3060 | setResultType(MIRType_Int32); |
michael@0 | 3061 | setMovable(); |
michael@0 | 3062 | |
michael@0 | 3063 | // An object might have "valueOf", which means it is effectful. |
michael@0 | 3064 | if (def->mightBeType(MIRType_Object)) |
michael@0 | 3065 | setGuard(); |
michael@0 | 3066 | } |
michael@0 | 3067 | |
michael@0 | 3068 | public: |
michael@0 | 3069 | INSTRUCTION_HEADER(ToInt32) |
michael@0 | 3070 | static MToInt32 *New(TempAllocator &alloc, MDefinition *def, |
michael@0 | 3071 | MacroAssembler::IntConversionInputKind conversion = |
michael@0 | 3072 | MacroAssembler::IntConversion_Any) |
michael@0 | 3073 | { |
michael@0 | 3074 | return new(alloc) MToInt32(def, conversion); |
michael@0 | 3075 | } |
michael@0 | 3076 | |
michael@0 | 3077 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 3078 | |
michael@0 | 3079 | // this only has backwards information flow. |
michael@0 | 3080 | void analyzeEdgeCasesBackward(); |
michael@0 | 3081 | |
michael@0 | 3082 | bool canBeNegativeZero() const { |
michael@0 | 3083 | return canBeNegativeZero_; |
michael@0 | 3084 | } |
michael@0 | 3085 | void setCanBeNegativeZero(bool negativeZero) { |
michael@0 | 3086 | canBeNegativeZero_ = negativeZero; |
michael@0 | 3087 | } |
michael@0 | 3088 | |
michael@0 | 3089 | TypePolicy *typePolicy() { |
michael@0 | 3090 | return this; |
michael@0 | 3091 | } |
michael@0 | 3092 | |
michael@0 | 3093 | MacroAssembler::IntConversionInputKind conversion() const { |
michael@0 | 3094 | return conversion_; |
michael@0 | 3095 | } |
michael@0 | 3096 | |
michael@0 | 3097 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3098 | return congruentIfOperandsEqual(ins); |
michael@0 | 3099 | } |
michael@0 | 3100 | |
michael@0 | 3101 | AliasSet getAliasSet() const { |
michael@0 | 3102 | return AliasSet::None(); |
michael@0 | 3103 | } |
michael@0 | 3104 | void computeRange(TempAllocator &alloc); |
michael@0 | 3105 | |
michael@0 | 3106 | #ifdef DEBUG |
michael@0 | 3107 | bool isConsistentFloat32Use(MUse *use) const { return true; } |
michael@0 | 3108 | #endif |
michael@0 | 3109 | }; |
michael@0 | 3110 | |
michael@0 | 3111 | // Converts a value or typed input to a truncated int32, for use with bitwise |
michael@0 | 3112 | // operations. This is an infallible ValueToECMAInt32. |
michael@0 | 3113 | class MTruncateToInt32 : public MUnaryInstruction |
michael@0 | 3114 | { |
michael@0 | 3115 | MTruncateToInt32(MDefinition *def) |
michael@0 | 3116 | : MUnaryInstruction(def) |
michael@0 | 3117 | { |
michael@0 | 3118 | setResultType(MIRType_Int32); |
michael@0 | 3119 | setMovable(); |
michael@0 | 3120 | |
michael@0 | 3121 | // An object might have "valueOf", which means it is effectful. |
michael@0 | 3122 | if (def->mightBeType(MIRType_Object)) |
michael@0 | 3123 | setGuard(); |
michael@0 | 3124 | } |
michael@0 | 3125 | |
michael@0 | 3126 | public: |
michael@0 | 3127 | INSTRUCTION_HEADER(TruncateToInt32) |
michael@0 | 3128 | static MTruncateToInt32 *New(TempAllocator &alloc, MDefinition *def) { |
michael@0 | 3129 | return new(alloc) MTruncateToInt32(def); |
michael@0 | 3130 | } |
michael@0 | 3131 | static MTruncateToInt32 *NewAsmJS(TempAllocator &alloc, MDefinition *def) { |
michael@0 | 3132 | return new(alloc) MTruncateToInt32(def); |
michael@0 | 3133 | } |
michael@0 | 3134 | |
michael@0 | 3135 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 3136 | |
michael@0 | 3137 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3138 | return congruentIfOperandsEqual(ins); |
michael@0 | 3139 | } |
michael@0 | 3140 | AliasSet getAliasSet() const { |
michael@0 | 3141 | return AliasSet::None(); |
michael@0 | 3142 | } |
michael@0 | 3143 | |
michael@0 | 3144 | void computeRange(TempAllocator &alloc); |
michael@0 | 3145 | bool isOperandTruncated(size_t index) const; |
michael@0 | 3146 | # ifdef DEBUG |
michael@0 | 3147 | bool isConsistentFloat32Use(MUse *use) const { |
michael@0 | 3148 | return true; |
michael@0 | 3149 | } |
michael@0 | 3150 | #endif |
michael@0 | 3151 | }; |
michael@0 | 3152 | |
michael@0 | 3153 | // Converts any type to a string |
michael@0 | 3154 | class MToString : public MUnaryInstruction |
michael@0 | 3155 | { |
michael@0 | 3156 | MToString(MDefinition *def) |
michael@0 | 3157 | : MUnaryInstruction(def) |
michael@0 | 3158 | { |
michael@0 | 3159 | // Converting an object to a string might be effectful. |
michael@0 | 3160 | JS_ASSERT(!def->mightBeType(MIRType_Object)); |
michael@0 | 3161 | |
michael@0 | 3162 | // NOP |
michael@0 | 3163 | JS_ASSERT(def->type() != MIRType_String); |
michael@0 | 3164 | |
michael@0 | 3165 | setResultType(MIRType_String); |
michael@0 | 3166 | setMovable(); |
michael@0 | 3167 | } |
michael@0 | 3168 | |
michael@0 | 3169 | public: |
michael@0 | 3170 | INSTRUCTION_HEADER(ToString) |
michael@0 | 3171 | static MToString *New(TempAllocator &alloc, MDefinition *def) |
michael@0 | 3172 | { |
michael@0 | 3173 | return new(alloc) MToString(def); |
michael@0 | 3174 | } |
michael@0 | 3175 | |
michael@0 | 3176 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 3177 | |
michael@0 | 3178 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3179 | return congruentIfOperandsEqual(ins); |
michael@0 | 3180 | } |
michael@0 | 3181 | AliasSet getAliasSet() const { |
michael@0 | 3182 | JS_ASSERT(!input()->mightBeType(MIRType_Object)); |
michael@0 | 3183 | return AliasSet::None(); |
michael@0 | 3184 | } |
michael@0 | 3185 | }; |
michael@0 | 3186 | |
michael@0 | 3187 | class MBitNot |
michael@0 | 3188 | : public MUnaryInstruction, |
michael@0 | 3189 | public BitwisePolicy |
michael@0 | 3190 | { |
michael@0 | 3191 | protected: |
michael@0 | 3192 | MBitNot(MDefinition *input) |
michael@0 | 3193 | : MUnaryInstruction(input) |
michael@0 | 3194 | { |
michael@0 | 3195 | setResultType(MIRType_Int32); |
michael@0 | 3196 | setMovable(); |
michael@0 | 3197 | } |
michael@0 | 3198 | |
michael@0 | 3199 | public: |
michael@0 | 3200 | INSTRUCTION_HEADER(BitNot) |
michael@0 | 3201 | static MBitNot *New(TempAllocator &alloc, MDefinition *input); |
michael@0 | 3202 | static MBitNot *NewAsmJS(TempAllocator &alloc, MDefinition *input); |
michael@0 | 3203 | |
michael@0 | 3204 | TypePolicy *typePolicy() { |
michael@0 | 3205 | return this; |
michael@0 | 3206 | } |
michael@0 | 3207 | |
michael@0 | 3208 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 3209 | void infer(); |
michael@0 | 3210 | |
michael@0 | 3211 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3212 | return congruentIfOperandsEqual(ins); |
michael@0 | 3213 | } |
michael@0 | 3214 | AliasSet getAliasSet() const { |
michael@0 | 3215 | if (specialization_ == MIRType_None) |
michael@0 | 3216 | return AliasSet::Store(AliasSet::Any); |
michael@0 | 3217 | return AliasSet::None(); |
michael@0 | 3218 | } |
michael@0 | 3219 | void computeRange(TempAllocator &alloc); |
michael@0 | 3220 | }; |
michael@0 | 3221 | |
michael@0 | 3222 | class MTypeOf |
michael@0 | 3223 | : public MUnaryInstruction, |
michael@0 | 3224 | public BoxInputsPolicy |
michael@0 | 3225 | { |
michael@0 | 3226 | MIRType inputType_; |
michael@0 | 3227 | bool inputMaybeCallableOrEmulatesUndefined_; |
michael@0 | 3228 | |
michael@0 | 3229 | MTypeOf(MDefinition *def, MIRType inputType) |
michael@0 | 3230 | : MUnaryInstruction(def), inputType_(inputType), |
michael@0 | 3231 | inputMaybeCallableOrEmulatesUndefined_(true) |
michael@0 | 3232 | { |
michael@0 | 3233 | setResultType(MIRType_String); |
michael@0 | 3234 | setMovable(); |
michael@0 | 3235 | } |
michael@0 | 3236 | |
michael@0 | 3237 | public: |
michael@0 | 3238 | INSTRUCTION_HEADER(TypeOf) |
michael@0 | 3239 | |
michael@0 | 3240 | static MTypeOf *New(TempAllocator &alloc, MDefinition *def, MIRType inputType) { |
michael@0 | 3241 | return new(alloc) MTypeOf(def, inputType); |
michael@0 | 3242 | } |
michael@0 | 3243 | |
michael@0 | 3244 | TypePolicy *typePolicy() { |
michael@0 | 3245 | return this; |
michael@0 | 3246 | } |
michael@0 | 3247 | MIRType inputType() const { |
michael@0 | 3248 | return inputType_; |
michael@0 | 3249 | } |
michael@0 | 3250 | |
michael@0 | 3251 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 3252 | void infer(); |
michael@0 | 3253 | |
michael@0 | 3254 | bool inputMaybeCallableOrEmulatesUndefined() const { |
michael@0 | 3255 | return inputMaybeCallableOrEmulatesUndefined_; |
michael@0 | 3256 | } |
michael@0 | 3257 | void markInputNotCallableOrEmulatesUndefined() { |
michael@0 | 3258 | inputMaybeCallableOrEmulatesUndefined_ = false; |
michael@0 | 3259 | } |
michael@0 | 3260 | |
michael@0 | 3261 | AliasSet getAliasSet() const { |
michael@0 | 3262 | return AliasSet::None(); |
michael@0 | 3263 | } |
michael@0 | 3264 | }; |
michael@0 | 3265 | |
michael@0 | 3266 | class MToId |
michael@0 | 3267 | : public MBinaryInstruction, |
michael@0 | 3268 | public BoxInputsPolicy |
michael@0 | 3269 | { |
michael@0 | 3270 | MToId(MDefinition *object, MDefinition *index) |
michael@0 | 3271 | : MBinaryInstruction(object, index) |
michael@0 | 3272 | { |
michael@0 | 3273 | setResultType(MIRType_Value); |
michael@0 | 3274 | } |
michael@0 | 3275 | |
michael@0 | 3276 | public: |
michael@0 | 3277 | INSTRUCTION_HEADER(ToId) |
michael@0 | 3278 | |
michael@0 | 3279 | static MToId *New(TempAllocator &alloc, MDefinition *object, MDefinition *index) { |
michael@0 | 3280 | return new(alloc) MToId(object, index); |
michael@0 | 3281 | } |
michael@0 | 3282 | |
michael@0 | 3283 | TypePolicy *typePolicy() { |
michael@0 | 3284 | return this; |
michael@0 | 3285 | } |
michael@0 | 3286 | }; |
michael@0 | 3287 | |
michael@0 | 3288 | class MBinaryBitwiseInstruction |
michael@0 | 3289 | : public MBinaryInstruction, |
michael@0 | 3290 | public BitwisePolicy |
michael@0 | 3291 | { |
michael@0 | 3292 | protected: |
michael@0 | 3293 | MBinaryBitwiseInstruction(MDefinition *left, MDefinition *right) |
michael@0 | 3294 | : MBinaryInstruction(left, right) |
michael@0 | 3295 | { |
michael@0 | 3296 | setResultType(MIRType_Int32); |
michael@0 | 3297 | setMovable(); |
michael@0 | 3298 | } |
michael@0 | 3299 | |
michael@0 | 3300 | void specializeAsInt32(); |
michael@0 | 3301 | |
michael@0 | 3302 | public: |
michael@0 | 3303 | TypePolicy *typePolicy() { |
michael@0 | 3304 | return this; |
michael@0 | 3305 | } |
michael@0 | 3306 | |
michael@0 | 3307 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 3308 | MDefinition *foldUnnecessaryBitop(); |
michael@0 | 3309 | virtual MDefinition *foldIfZero(size_t operand) = 0; |
michael@0 | 3310 | virtual MDefinition *foldIfNegOne(size_t operand) = 0; |
michael@0 | 3311 | virtual MDefinition *foldIfEqual() = 0; |
michael@0 | 3312 | virtual void infer(BaselineInspector *inspector, jsbytecode *pc); |
michael@0 | 3313 | |
michael@0 | 3314 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3315 | return binaryCongruentTo(ins); |
michael@0 | 3316 | } |
michael@0 | 3317 | AliasSet getAliasSet() const { |
michael@0 | 3318 | if (specialization_ >= MIRType_Object) |
michael@0 | 3319 | return AliasSet::Store(AliasSet::Any); |
michael@0 | 3320 | return AliasSet::None(); |
michael@0 | 3321 | } |
michael@0 | 3322 | |
michael@0 | 3323 | bool isOperandTruncated(size_t index) const; |
michael@0 | 3324 | }; |
michael@0 | 3325 | |
michael@0 | 3326 | class MBitAnd : public MBinaryBitwiseInstruction |
michael@0 | 3327 | { |
michael@0 | 3328 | MBitAnd(MDefinition *left, MDefinition *right) |
michael@0 | 3329 | : MBinaryBitwiseInstruction(left, right) |
michael@0 | 3330 | { } |
michael@0 | 3331 | |
michael@0 | 3332 | public: |
michael@0 | 3333 | INSTRUCTION_HEADER(BitAnd) |
michael@0 | 3334 | static MBitAnd *New(TempAllocator &alloc, MDefinition *left, MDefinition *right); |
michael@0 | 3335 | static MBitAnd *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right); |
michael@0 | 3336 | |
michael@0 | 3337 | MDefinition *foldIfZero(size_t operand) { |
michael@0 | 3338 | return getOperand(operand); // 0 & x => 0; |
michael@0 | 3339 | } |
michael@0 | 3340 | MDefinition *foldIfNegOne(size_t operand) { |
michael@0 | 3341 | return getOperand(1 - operand); // x & -1 => x |
michael@0 | 3342 | } |
michael@0 | 3343 | MDefinition *foldIfEqual() { |
michael@0 | 3344 | return getOperand(0); // x & x => x; |
michael@0 | 3345 | } |
michael@0 | 3346 | void computeRange(TempAllocator &alloc); |
michael@0 | 3347 | }; |
michael@0 | 3348 | |
michael@0 | 3349 | class MBitOr : public MBinaryBitwiseInstruction |
michael@0 | 3350 | { |
michael@0 | 3351 | MBitOr(MDefinition *left, MDefinition *right) |
michael@0 | 3352 | : MBinaryBitwiseInstruction(left, right) |
michael@0 | 3353 | { } |
michael@0 | 3354 | |
michael@0 | 3355 | public: |
michael@0 | 3356 | INSTRUCTION_HEADER(BitOr) |
michael@0 | 3357 | static MBitOr *New(TempAllocator &alloc, MDefinition *left, MDefinition *right); |
michael@0 | 3358 | static MBitOr *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right); |
michael@0 | 3359 | |
michael@0 | 3360 | MDefinition *foldIfZero(size_t operand) { |
michael@0 | 3361 | return getOperand(1 - operand); // 0 | x => x, so if ith is 0, return (1-i)th |
michael@0 | 3362 | } |
michael@0 | 3363 | MDefinition *foldIfNegOne(size_t operand) { |
michael@0 | 3364 | return getOperand(operand); // x | -1 => -1 |
michael@0 | 3365 | } |
michael@0 | 3366 | MDefinition *foldIfEqual() { |
michael@0 | 3367 | return getOperand(0); // x | x => x |
michael@0 | 3368 | } |
michael@0 | 3369 | void computeRange(TempAllocator &alloc); |
michael@0 | 3370 | }; |
michael@0 | 3371 | |
michael@0 | 3372 | class MBitXor : public MBinaryBitwiseInstruction |
michael@0 | 3373 | { |
michael@0 | 3374 | MBitXor(MDefinition *left, MDefinition *right) |
michael@0 | 3375 | : MBinaryBitwiseInstruction(left, right) |
michael@0 | 3376 | { } |
michael@0 | 3377 | |
michael@0 | 3378 | public: |
michael@0 | 3379 | INSTRUCTION_HEADER(BitXor) |
michael@0 | 3380 | static MBitXor *New(TempAllocator &alloc, MDefinition *left, MDefinition *right); |
michael@0 | 3381 | static MBitXor *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right); |
michael@0 | 3382 | |
michael@0 | 3383 | MDefinition *foldIfZero(size_t operand) { |
michael@0 | 3384 | return getOperand(1 - operand); // 0 ^ x => x |
michael@0 | 3385 | } |
michael@0 | 3386 | MDefinition *foldIfNegOne(size_t operand) { |
michael@0 | 3387 | return this; |
michael@0 | 3388 | } |
michael@0 | 3389 | MDefinition *foldIfEqual() { |
michael@0 | 3390 | return this; |
michael@0 | 3391 | } |
michael@0 | 3392 | void computeRange(TempAllocator &alloc); |
michael@0 | 3393 | }; |
michael@0 | 3394 | |
michael@0 | 3395 | class MShiftInstruction |
michael@0 | 3396 | : public MBinaryBitwiseInstruction |
michael@0 | 3397 | { |
michael@0 | 3398 | protected: |
michael@0 | 3399 | MShiftInstruction(MDefinition *left, MDefinition *right) |
michael@0 | 3400 | : MBinaryBitwiseInstruction(left, right) |
michael@0 | 3401 | { } |
michael@0 | 3402 | |
michael@0 | 3403 | public: |
michael@0 | 3404 | MDefinition *foldIfNegOne(size_t operand) { |
michael@0 | 3405 | return this; |
michael@0 | 3406 | } |
michael@0 | 3407 | MDefinition *foldIfEqual() { |
michael@0 | 3408 | return this; |
michael@0 | 3409 | } |
michael@0 | 3410 | virtual void infer(BaselineInspector *inspector, jsbytecode *pc); |
michael@0 | 3411 | }; |
michael@0 | 3412 | |
michael@0 | 3413 | class MLsh : public MShiftInstruction |
michael@0 | 3414 | { |
michael@0 | 3415 | MLsh(MDefinition *left, MDefinition *right) |
michael@0 | 3416 | : MShiftInstruction(left, right) |
michael@0 | 3417 | { } |
michael@0 | 3418 | |
michael@0 | 3419 | public: |
michael@0 | 3420 | INSTRUCTION_HEADER(Lsh) |
michael@0 | 3421 | static MLsh *New(TempAllocator &alloc, MDefinition *left, MDefinition *right); |
michael@0 | 3422 | static MLsh *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right); |
michael@0 | 3423 | |
michael@0 | 3424 | MDefinition *foldIfZero(size_t operand) { |
michael@0 | 3425 | // 0 << x => 0 |
michael@0 | 3426 | // x << 0 => x |
michael@0 | 3427 | return getOperand(0); |
michael@0 | 3428 | } |
michael@0 | 3429 | |
michael@0 | 3430 | void computeRange(TempAllocator &alloc); |
michael@0 | 3431 | }; |
michael@0 | 3432 | |
michael@0 | 3433 | class MRsh : public MShiftInstruction |
michael@0 | 3434 | { |
michael@0 | 3435 | MRsh(MDefinition *left, MDefinition *right) |
michael@0 | 3436 | : MShiftInstruction(left, right) |
michael@0 | 3437 | { } |
michael@0 | 3438 | |
michael@0 | 3439 | public: |
michael@0 | 3440 | INSTRUCTION_HEADER(Rsh) |
michael@0 | 3441 | static MRsh *New(TempAllocator &alloc, MDefinition *left, MDefinition *right); |
michael@0 | 3442 | static MRsh *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right); |
michael@0 | 3443 | |
michael@0 | 3444 | MDefinition *foldIfZero(size_t operand) { |
michael@0 | 3445 | // 0 >> x => 0 |
michael@0 | 3446 | // x >> 0 => x |
michael@0 | 3447 | return getOperand(0); |
michael@0 | 3448 | } |
michael@0 | 3449 | void computeRange(TempAllocator &alloc); |
michael@0 | 3450 | }; |
michael@0 | 3451 | |
michael@0 | 3452 | class MUrsh : public MShiftInstruction |
michael@0 | 3453 | { |
michael@0 | 3454 | bool bailoutsDisabled_; |
michael@0 | 3455 | |
michael@0 | 3456 | MUrsh(MDefinition *left, MDefinition *right) |
michael@0 | 3457 | : MShiftInstruction(left, right), |
michael@0 | 3458 | bailoutsDisabled_(false) |
michael@0 | 3459 | { } |
michael@0 | 3460 | |
michael@0 | 3461 | public: |
michael@0 | 3462 | INSTRUCTION_HEADER(Ursh) |
michael@0 | 3463 | static MUrsh *New(TempAllocator &alloc, MDefinition *left, MDefinition *right); |
michael@0 | 3464 | static MUrsh *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right); |
michael@0 | 3465 | |
michael@0 | 3466 | MDefinition *foldIfZero(size_t operand) { |
michael@0 | 3467 | // 0 >>> x => 0 |
michael@0 | 3468 | if (operand == 0) |
michael@0 | 3469 | return getOperand(0); |
michael@0 | 3470 | |
michael@0 | 3471 | return this; |
michael@0 | 3472 | } |
michael@0 | 3473 | |
michael@0 | 3474 | void infer(BaselineInspector *inspector, jsbytecode *pc); |
michael@0 | 3475 | |
michael@0 | 3476 | bool bailoutsDisabled() const { |
michael@0 | 3477 | return bailoutsDisabled_; |
michael@0 | 3478 | } |
michael@0 | 3479 | |
michael@0 | 3480 | bool fallible() const; |
michael@0 | 3481 | |
michael@0 | 3482 | void computeRange(TempAllocator &alloc); |
michael@0 | 3483 | void collectRangeInfoPreTrunc(); |
michael@0 | 3484 | }; |
michael@0 | 3485 | |
michael@0 | 3486 | class MBinaryArithInstruction |
michael@0 | 3487 | : public MBinaryInstruction, |
michael@0 | 3488 | public ArithPolicy |
michael@0 | 3489 | { |
michael@0 | 3490 | // Implicit truncate flag is set by the truncate backward range analysis |
michael@0 | 3491 | // optimization phase, and by asm.js pre-processing. It is used in |
michael@0 | 3492 | // NeedNegativeZeroCheck to check if the result of a multiplication needs to |
michael@0 | 3493 | // produce -0 double value, and for avoiding overflow checks. |
michael@0 | 3494 | |
michael@0 | 3495 | // This optimization happens when the multiplication cannot be truncated |
michael@0 | 3496 | // even if all uses are truncating its result, such as when the range |
michael@0 | 3497 | // analysis detect a precision loss in the multiplication. |
michael@0 | 3498 | bool implicitTruncate_; |
michael@0 | 3499 | |
michael@0 | 3500 | void inferFallback(BaselineInspector *inspector, jsbytecode *pc); |
michael@0 | 3501 | |
michael@0 | 3502 | public: |
michael@0 | 3503 | MBinaryArithInstruction(MDefinition *left, MDefinition *right) |
michael@0 | 3504 | : MBinaryInstruction(left, right), |
michael@0 | 3505 | implicitTruncate_(false) |
michael@0 | 3506 | { |
michael@0 | 3507 | setMovable(); |
michael@0 | 3508 | } |
michael@0 | 3509 | |
michael@0 | 3510 | TypePolicy *typePolicy() { |
michael@0 | 3511 | return this; |
michael@0 | 3512 | } |
michael@0 | 3513 | MIRType specialization() const { |
michael@0 | 3514 | return specialization_; |
michael@0 | 3515 | } |
michael@0 | 3516 | |
michael@0 | 3517 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 3518 | |
michael@0 | 3519 | virtual double getIdentity() = 0; |
michael@0 | 3520 | |
michael@0 | 3521 | void infer(TempAllocator &alloc, BaselineInspector *inspector, jsbytecode *pc); |
michael@0 | 3522 | |
michael@0 | 3523 | void setInt32() { |
michael@0 | 3524 | specialization_ = MIRType_Int32; |
michael@0 | 3525 | setResultType(MIRType_Int32); |
michael@0 | 3526 | } |
michael@0 | 3527 | |
michael@0 | 3528 | virtual void trySpecializeFloat32(TempAllocator &alloc); |
michael@0 | 3529 | |
michael@0 | 3530 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3531 | return binaryCongruentTo(ins); |
michael@0 | 3532 | } |
michael@0 | 3533 | AliasSet getAliasSet() const { |
michael@0 | 3534 | if (specialization_ >= MIRType_Object) |
michael@0 | 3535 | return AliasSet::Store(AliasSet::Any); |
michael@0 | 3536 | return AliasSet::None(); |
michael@0 | 3537 | } |
michael@0 | 3538 | |
michael@0 | 3539 | bool isTruncated() const { |
michael@0 | 3540 | return implicitTruncate_; |
michael@0 | 3541 | } |
michael@0 | 3542 | void setTruncated(bool truncate) { |
michael@0 | 3543 | implicitTruncate_ = truncate; |
michael@0 | 3544 | } |
michael@0 | 3545 | }; |
michael@0 | 3546 | |
michael@0 | 3547 | class MMinMax |
michael@0 | 3548 | : public MBinaryInstruction, |
michael@0 | 3549 | public ArithPolicy |
michael@0 | 3550 | { |
michael@0 | 3551 | bool isMax_; |
michael@0 | 3552 | |
michael@0 | 3553 | MMinMax(MDefinition *left, MDefinition *right, MIRType type, bool isMax) |
michael@0 | 3554 | : MBinaryInstruction(left, right), |
michael@0 | 3555 | isMax_(isMax) |
michael@0 | 3556 | { |
michael@0 | 3557 | JS_ASSERT(type == MIRType_Double || type == MIRType_Int32); |
michael@0 | 3558 | setResultType(type); |
michael@0 | 3559 | setMovable(); |
michael@0 | 3560 | specialization_ = type; |
michael@0 | 3561 | } |
michael@0 | 3562 | |
michael@0 | 3563 | public: |
michael@0 | 3564 | INSTRUCTION_HEADER(MinMax) |
michael@0 | 3565 | static MMinMax *New(TempAllocator &alloc, MDefinition *left, MDefinition *right, MIRType type, |
michael@0 | 3566 | bool isMax) |
michael@0 | 3567 | { |
michael@0 | 3568 | return new(alloc) MMinMax(left, right, type, isMax); |
michael@0 | 3569 | } |
michael@0 | 3570 | |
michael@0 | 3571 | bool isMax() const { |
michael@0 | 3572 | return isMax_; |
michael@0 | 3573 | } |
michael@0 | 3574 | MIRType specialization() const { |
michael@0 | 3575 | return specialization_; |
michael@0 | 3576 | } |
michael@0 | 3577 | |
michael@0 | 3578 | TypePolicy *typePolicy() { |
michael@0 | 3579 | return this; |
michael@0 | 3580 | } |
michael@0 | 3581 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3582 | if (!ins->isMinMax()) |
michael@0 | 3583 | return false; |
michael@0 | 3584 | if (isMax() != ins->toMinMax()->isMax()) |
michael@0 | 3585 | return false; |
michael@0 | 3586 | return congruentIfOperandsEqual(ins); |
michael@0 | 3587 | } |
michael@0 | 3588 | |
michael@0 | 3589 | AliasSet getAliasSet() const { |
michael@0 | 3590 | return AliasSet::None(); |
michael@0 | 3591 | } |
michael@0 | 3592 | void computeRange(TempAllocator &alloc); |
michael@0 | 3593 | }; |
michael@0 | 3594 | |
michael@0 | 3595 | class MAbs |
michael@0 | 3596 | : public MUnaryInstruction, |
michael@0 | 3597 | public ArithPolicy |
michael@0 | 3598 | { |
michael@0 | 3599 | bool implicitTruncate_; |
michael@0 | 3600 | |
michael@0 | 3601 | MAbs(MDefinition *num, MIRType type) |
michael@0 | 3602 | : MUnaryInstruction(num), |
michael@0 | 3603 | implicitTruncate_(false) |
michael@0 | 3604 | { |
michael@0 | 3605 | JS_ASSERT(IsNumberType(type)); |
michael@0 | 3606 | setResultType(type); |
michael@0 | 3607 | setMovable(); |
michael@0 | 3608 | specialization_ = type; |
michael@0 | 3609 | } |
michael@0 | 3610 | |
michael@0 | 3611 | public: |
michael@0 | 3612 | INSTRUCTION_HEADER(Abs) |
michael@0 | 3613 | static MAbs *New(TempAllocator &alloc, MDefinition *num, MIRType type) { |
michael@0 | 3614 | return new(alloc) MAbs(num, type); |
michael@0 | 3615 | } |
michael@0 | 3616 | static MAbs *NewAsmJS(TempAllocator &alloc, MDefinition *num, MIRType type) { |
michael@0 | 3617 | MAbs *ins = new(alloc) MAbs(num, type); |
michael@0 | 3618 | if (type == MIRType_Int32) |
michael@0 | 3619 | ins->implicitTruncate_ = true; |
michael@0 | 3620 | return ins; |
michael@0 | 3621 | } |
michael@0 | 3622 | MDefinition *num() const { |
michael@0 | 3623 | return getOperand(0); |
michael@0 | 3624 | } |
michael@0 | 3625 | TypePolicy *typePolicy() { |
michael@0 | 3626 | return this; |
michael@0 | 3627 | } |
michael@0 | 3628 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3629 | return congruentIfOperandsEqual(ins); |
michael@0 | 3630 | } |
michael@0 | 3631 | bool fallible() const; |
michael@0 | 3632 | |
michael@0 | 3633 | AliasSet getAliasSet() const { |
michael@0 | 3634 | return AliasSet::None(); |
michael@0 | 3635 | } |
michael@0 | 3636 | void computeRange(TempAllocator &alloc); |
michael@0 | 3637 | bool isFloat32Commutative() const { return true; } |
michael@0 | 3638 | void trySpecializeFloat32(TempAllocator &alloc); |
michael@0 | 3639 | }; |
michael@0 | 3640 | |
michael@0 | 3641 | // Inline implementation of Math.sqrt(). |
michael@0 | 3642 | class MSqrt |
michael@0 | 3643 | : public MUnaryInstruction, |
michael@0 | 3644 | public FloatingPointPolicy<0> |
michael@0 | 3645 | { |
michael@0 | 3646 | MSqrt(MDefinition *num, MIRType type) |
michael@0 | 3647 | : MUnaryInstruction(num) |
michael@0 | 3648 | { |
michael@0 | 3649 | setResultType(type); |
michael@0 | 3650 | setPolicyType(type); |
michael@0 | 3651 | setMovable(); |
michael@0 | 3652 | } |
michael@0 | 3653 | |
michael@0 | 3654 | public: |
michael@0 | 3655 | INSTRUCTION_HEADER(Sqrt) |
michael@0 | 3656 | static MSqrt *New(TempAllocator &alloc, MDefinition *num) { |
michael@0 | 3657 | return new(alloc) MSqrt(num, MIRType_Double); |
michael@0 | 3658 | } |
michael@0 | 3659 | static MSqrt *NewAsmJS(TempAllocator &alloc, MDefinition *num, MIRType type) { |
michael@0 | 3660 | JS_ASSERT(IsFloatingPointType(type)); |
michael@0 | 3661 | return new(alloc) MSqrt(num, type); |
michael@0 | 3662 | } |
michael@0 | 3663 | MDefinition *num() const { |
michael@0 | 3664 | return getOperand(0); |
michael@0 | 3665 | } |
michael@0 | 3666 | TypePolicy *typePolicy() { |
michael@0 | 3667 | return this; |
michael@0 | 3668 | } |
michael@0 | 3669 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3670 | return congruentIfOperandsEqual(ins); |
michael@0 | 3671 | } |
michael@0 | 3672 | |
michael@0 | 3673 | AliasSet getAliasSet() const { |
michael@0 | 3674 | return AliasSet::None(); |
michael@0 | 3675 | } |
michael@0 | 3676 | void computeRange(TempAllocator &alloc); |
michael@0 | 3677 | |
michael@0 | 3678 | bool isFloat32Commutative() const { return true; } |
michael@0 | 3679 | void trySpecializeFloat32(TempAllocator &alloc); |
michael@0 | 3680 | }; |
michael@0 | 3681 | |
michael@0 | 3682 | // Inline implementation of atan2 (arctangent of y/x). |
michael@0 | 3683 | class MAtan2 |
michael@0 | 3684 | : public MBinaryInstruction, |
michael@0 | 3685 | public MixPolicy<DoublePolicy<0>, DoublePolicy<1> > |
michael@0 | 3686 | { |
michael@0 | 3687 | MAtan2(MDefinition *y, MDefinition *x) |
michael@0 | 3688 | : MBinaryInstruction(y, x) |
michael@0 | 3689 | { |
michael@0 | 3690 | setResultType(MIRType_Double); |
michael@0 | 3691 | setMovable(); |
michael@0 | 3692 | } |
michael@0 | 3693 | |
michael@0 | 3694 | public: |
michael@0 | 3695 | INSTRUCTION_HEADER(Atan2) |
michael@0 | 3696 | static MAtan2 *New(TempAllocator &alloc, MDefinition *y, MDefinition *x) { |
michael@0 | 3697 | return new(alloc) MAtan2(y, x); |
michael@0 | 3698 | } |
michael@0 | 3699 | |
michael@0 | 3700 | MDefinition *y() const { |
michael@0 | 3701 | return getOperand(0); |
michael@0 | 3702 | } |
michael@0 | 3703 | |
michael@0 | 3704 | MDefinition *x() const { |
michael@0 | 3705 | return getOperand(1); |
michael@0 | 3706 | } |
michael@0 | 3707 | |
michael@0 | 3708 | TypePolicy *typePolicy() { |
michael@0 | 3709 | return this; |
michael@0 | 3710 | } |
michael@0 | 3711 | |
michael@0 | 3712 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3713 | return congruentIfOperandsEqual(ins); |
michael@0 | 3714 | } |
michael@0 | 3715 | |
michael@0 | 3716 | AliasSet getAliasSet() const { |
michael@0 | 3717 | return AliasSet::None(); |
michael@0 | 3718 | } |
michael@0 | 3719 | |
michael@0 | 3720 | bool possiblyCalls() const { |
michael@0 | 3721 | return true; |
michael@0 | 3722 | } |
michael@0 | 3723 | }; |
michael@0 | 3724 | |
michael@0 | 3725 | // Inline implementation of Math.hypot(). |
michael@0 | 3726 | class MHypot |
michael@0 | 3727 | : public MBinaryInstruction, |
michael@0 | 3728 | public MixPolicy<DoublePolicy<0>, DoublePolicy<1> > |
michael@0 | 3729 | { |
michael@0 | 3730 | MHypot(MDefinition *y, MDefinition *x) |
michael@0 | 3731 | : MBinaryInstruction(x, y) |
michael@0 | 3732 | { |
michael@0 | 3733 | setResultType(MIRType_Double); |
michael@0 | 3734 | setMovable(); |
michael@0 | 3735 | } |
michael@0 | 3736 | |
michael@0 | 3737 | public: |
michael@0 | 3738 | INSTRUCTION_HEADER(Hypot) |
michael@0 | 3739 | static MHypot *New(TempAllocator &alloc, MDefinition *x, MDefinition *y) { |
michael@0 | 3740 | return new(alloc) MHypot(y, x); |
michael@0 | 3741 | } |
michael@0 | 3742 | |
michael@0 | 3743 | MDefinition *x() const { |
michael@0 | 3744 | return getOperand(0); |
michael@0 | 3745 | } |
michael@0 | 3746 | |
michael@0 | 3747 | MDefinition *y() const { |
michael@0 | 3748 | return getOperand(1); |
michael@0 | 3749 | } |
michael@0 | 3750 | |
michael@0 | 3751 | TypePolicy *typePolicy() { |
michael@0 | 3752 | return this; |
michael@0 | 3753 | } |
michael@0 | 3754 | |
michael@0 | 3755 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3756 | return congruentIfOperandsEqual(ins); |
michael@0 | 3757 | } |
michael@0 | 3758 | |
michael@0 | 3759 | AliasSet getAliasSet() const { |
michael@0 | 3760 | return AliasSet::None(); |
michael@0 | 3761 | } |
michael@0 | 3762 | |
michael@0 | 3763 | bool possiblyCalls() const { |
michael@0 | 3764 | return true; |
michael@0 | 3765 | } |
michael@0 | 3766 | }; |
michael@0 | 3767 | |
michael@0 | 3768 | // Inline implementation of Math.pow(). |
michael@0 | 3769 | class MPow |
michael@0 | 3770 | : public MBinaryInstruction, |
michael@0 | 3771 | public PowPolicy |
michael@0 | 3772 | { |
michael@0 | 3773 | MPow(MDefinition *input, MDefinition *power, MIRType powerType) |
michael@0 | 3774 | : MBinaryInstruction(input, power), |
michael@0 | 3775 | PowPolicy(powerType) |
michael@0 | 3776 | { |
michael@0 | 3777 | setResultType(MIRType_Double); |
michael@0 | 3778 | setMovable(); |
michael@0 | 3779 | } |
michael@0 | 3780 | |
michael@0 | 3781 | public: |
michael@0 | 3782 | INSTRUCTION_HEADER(Pow) |
michael@0 | 3783 | static MPow *New(TempAllocator &alloc, MDefinition *input, MDefinition *power, |
michael@0 | 3784 | MIRType powerType) |
michael@0 | 3785 | { |
michael@0 | 3786 | JS_ASSERT(powerType == MIRType_Double || powerType == MIRType_Int32); |
michael@0 | 3787 | return new(alloc) MPow(input, power, powerType); |
michael@0 | 3788 | } |
michael@0 | 3789 | |
michael@0 | 3790 | MDefinition *input() const { |
michael@0 | 3791 | return lhs(); |
michael@0 | 3792 | } |
michael@0 | 3793 | MDefinition *power() const { |
michael@0 | 3794 | return rhs(); |
michael@0 | 3795 | } |
michael@0 | 3796 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3797 | return congruentIfOperandsEqual(ins); |
michael@0 | 3798 | } |
michael@0 | 3799 | TypePolicy *typePolicy() { |
michael@0 | 3800 | return this; |
michael@0 | 3801 | } |
michael@0 | 3802 | AliasSet getAliasSet() const { |
michael@0 | 3803 | return AliasSet::None(); |
michael@0 | 3804 | } |
michael@0 | 3805 | bool possiblyCalls() const { |
michael@0 | 3806 | return true; |
michael@0 | 3807 | } |
michael@0 | 3808 | }; |
michael@0 | 3809 | |
michael@0 | 3810 | // Inline implementation of Math.pow(x, 0.5), which subtly differs from Math.sqrt(x). |
michael@0 | 3811 | class MPowHalf |
michael@0 | 3812 | : public MUnaryInstruction, |
michael@0 | 3813 | public DoublePolicy<0> |
michael@0 | 3814 | { |
michael@0 | 3815 | bool operandIsNeverNegativeInfinity_; |
michael@0 | 3816 | bool operandIsNeverNegativeZero_; |
michael@0 | 3817 | bool operandIsNeverNaN_; |
michael@0 | 3818 | |
michael@0 | 3819 | MPowHalf(MDefinition *input) |
michael@0 | 3820 | : MUnaryInstruction(input), |
michael@0 | 3821 | operandIsNeverNegativeInfinity_(false), |
michael@0 | 3822 | operandIsNeverNegativeZero_(false), |
michael@0 | 3823 | operandIsNeverNaN_(false) |
michael@0 | 3824 | { |
michael@0 | 3825 | setResultType(MIRType_Double); |
michael@0 | 3826 | setMovable(); |
michael@0 | 3827 | } |
michael@0 | 3828 | |
michael@0 | 3829 | public: |
michael@0 | 3830 | INSTRUCTION_HEADER(PowHalf) |
michael@0 | 3831 | static MPowHalf *New(TempAllocator &alloc, MDefinition *input) { |
michael@0 | 3832 | return new(alloc) MPowHalf(input); |
michael@0 | 3833 | } |
michael@0 | 3834 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3835 | return congruentIfOperandsEqual(ins); |
michael@0 | 3836 | } |
michael@0 | 3837 | bool operandIsNeverNegativeInfinity() const { |
michael@0 | 3838 | return operandIsNeverNegativeInfinity_; |
michael@0 | 3839 | } |
michael@0 | 3840 | bool operandIsNeverNegativeZero() const { |
michael@0 | 3841 | return operandIsNeverNegativeZero_; |
michael@0 | 3842 | } |
michael@0 | 3843 | bool operandIsNeverNaN() const { |
michael@0 | 3844 | return operandIsNeverNaN_; |
michael@0 | 3845 | } |
michael@0 | 3846 | TypePolicy *typePolicy() { |
michael@0 | 3847 | return this; |
michael@0 | 3848 | } |
michael@0 | 3849 | AliasSet getAliasSet() const { |
michael@0 | 3850 | return AliasSet::None(); |
michael@0 | 3851 | } |
michael@0 | 3852 | void collectRangeInfoPreTrunc(); |
michael@0 | 3853 | }; |
michael@0 | 3854 | |
michael@0 | 3855 | // Inline implementation of Math.random(). |
michael@0 | 3856 | class MRandom : public MNullaryInstruction |
michael@0 | 3857 | { |
michael@0 | 3858 | MRandom() |
michael@0 | 3859 | { |
michael@0 | 3860 | setResultType(MIRType_Double); |
michael@0 | 3861 | } |
michael@0 | 3862 | |
michael@0 | 3863 | public: |
michael@0 | 3864 | INSTRUCTION_HEADER(Random) |
michael@0 | 3865 | static MRandom *New(TempAllocator &alloc) { |
michael@0 | 3866 | return new(alloc) MRandom; |
michael@0 | 3867 | } |
michael@0 | 3868 | |
michael@0 | 3869 | AliasSet getAliasSet() const { |
michael@0 | 3870 | return AliasSet::None(); |
michael@0 | 3871 | } |
michael@0 | 3872 | |
michael@0 | 3873 | bool possiblyCalls() const { |
michael@0 | 3874 | return true; |
michael@0 | 3875 | } |
michael@0 | 3876 | |
michael@0 | 3877 | void computeRange(TempAllocator &alloc); |
michael@0 | 3878 | }; |
michael@0 | 3879 | |
michael@0 | 3880 | class MMathFunction |
michael@0 | 3881 | : public MUnaryInstruction, |
michael@0 | 3882 | public FloatingPointPolicy<0> |
michael@0 | 3883 | { |
michael@0 | 3884 | public: |
michael@0 | 3885 | enum Function { |
michael@0 | 3886 | Log, |
michael@0 | 3887 | Sin, |
michael@0 | 3888 | Cos, |
michael@0 | 3889 | Exp, |
michael@0 | 3890 | Tan, |
michael@0 | 3891 | ACos, |
michael@0 | 3892 | ASin, |
michael@0 | 3893 | ATan, |
michael@0 | 3894 | Log10, |
michael@0 | 3895 | Log2, |
michael@0 | 3896 | Log1P, |
michael@0 | 3897 | ExpM1, |
michael@0 | 3898 | CosH, |
michael@0 | 3899 | SinH, |
michael@0 | 3900 | TanH, |
michael@0 | 3901 | ACosH, |
michael@0 | 3902 | ASinH, |
michael@0 | 3903 | ATanH, |
michael@0 | 3904 | Sign, |
michael@0 | 3905 | Trunc, |
michael@0 | 3906 | Cbrt, |
michael@0 | 3907 | Floor, |
michael@0 | 3908 | Ceil, |
michael@0 | 3909 | Round |
michael@0 | 3910 | }; |
michael@0 | 3911 | |
michael@0 | 3912 | private: |
michael@0 | 3913 | Function function_; |
michael@0 | 3914 | const MathCache *cache_; |
michael@0 | 3915 | |
michael@0 | 3916 | MMathFunction(MDefinition *input, Function function, const MathCache *cache) |
michael@0 | 3917 | : MUnaryInstruction(input), function_(function), cache_(cache) |
michael@0 | 3918 | { |
michael@0 | 3919 | setResultType(MIRType_Double); |
michael@0 | 3920 | setPolicyType(MIRType_Double); |
michael@0 | 3921 | setMovable(); |
michael@0 | 3922 | } |
michael@0 | 3923 | |
michael@0 | 3924 | public: |
michael@0 | 3925 | INSTRUCTION_HEADER(MathFunction) |
michael@0 | 3926 | |
michael@0 | 3927 | // A nullptr cache means this function will neither access nor update the cache. |
michael@0 | 3928 | static MMathFunction *New(TempAllocator &alloc, MDefinition *input, Function function, |
michael@0 | 3929 | const MathCache *cache) |
michael@0 | 3930 | { |
michael@0 | 3931 | return new(alloc) MMathFunction(input, function, cache); |
michael@0 | 3932 | } |
michael@0 | 3933 | Function function() const { |
michael@0 | 3934 | return function_; |
michael@0 | 3935 | } |
michael@0 | 3936 | const MathCache *cache() const { |
michael@0 | 3937 | return cache_; |
michael@0 | 3938 | } |
michael@0 | 3939 | TypePolicy *typePolicy() { |
michael@0 | 3940 | return this; |
michael@0 | 3941 | } |
michael@0 | 3942 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 3943 | if (!ins->isMathFunction()) |
michael@0 | 3944 | return false; |
michael@0 | 3945 | if (ins->toMathFunction()->function() != function()) |
michael@0 | 3946 | return false; |
michael@0 | 3947 | return congruentIfOperandsEqual(ins); |
michael@0 | 3948 | } |
michael@0 | 3949 | |
michael@0 | 3950 | AliasSet getAliasSet() const { |
michael@0 | 3951 | return AliasSet::None(); |
michael@0 | 3952 | } |
michael@0 | 3953 | |
michael@0 | 3954 | bool possiblyCalls() const { |
michael@0 | 3955 | return true; |
michael@0 | 3956 | } |
michael@0 | 3957 | |
michael@0 | 3958 | void printOpcode(FILE *fp) const; |
michael@0 | 3959 | |
michael@0 | 3960 | static const char *FunctionName(Function function); |
michael@0 | 3961 | |
michael@0 | 3962 | bool isFloat32Commutative() const { |
michael@0 | 3963 | return function_ == Floor || function_ == Ceil || function_ == Round; |
michael@0 | 3964 | } |
michael@0 | 3965 | void trySpecializeFloat32(TempAllocator &alloc); |
michael@0 | 3966 | void computeRange(TempAllocator &alloc); |
michael@0 | 3967 | }; |
michael@0 | 3968 | |
michael@0 | 3969 | class MAdd : public MBinaryArithInstruction |
michael@0 | 3970 | { |
michael@0 | 3971 | // Is this instruction really an int at heart? |
michael@0 | 3972 | MAdd(MDefinition *left, MDefinition *right) |
michael@0 | 3973 | : MBinaryArithInstruction(left, right) |
michael@0 | 3974 | { |
michael@0 | 3975 | setResultType(MIRType_Value); |
michael@0 | 3976 | } |
michael@0 | 3977 | |
michael@0 | 3978 | public: |
michael@0 | 3979 | INSTRUCTION_HEADER(Add) |
michael@0 | 3980 | static MAdd *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { |
michael@0 | 3981 | return new(alloc) MAdd(left, right); |
michael@0 | 3982 | } |
michael@0 | 3983 | |
michael@0 | 3984 | static MAdd *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, |
michael@0 | 3985 | MIRType type) |
michael@0 | 3986 | { |
michael@0 | 3987 | MAdd *add = new(alloc) MAdd(left, right); |
michael@0 | 3988 | add->specialization_ = type; |
michael@0 | 3989 | add->setResultType(type); |
michael@0 | 3990 | if (type == MIRType_Int32) { |
michael@0 | 3991 | add->setTruncated(true); |
michael@0 | 3992 | add->setCommutative(); |
michael@0 | 3993 | } |
michael@0 | 3994 | return add; |
michael@0 | 3995 | } |
michael@0 | 3996 | |
michael@0 | 3997 | bool isFloat32Commutative() const { return true; } |
michael@0 | 3998 | |
michael@0 | 3999 | double getIdentity() { |
michael@0 | 4000 | return 0; |
michael@0 | 4001 | } |
michael@0 | 4002 | |
michael@0 | 4003 | bool fallible() const; |
michael@0 | 4004 | void computeRange(TempAllocator &alloc); |
michael@0 | 4005 | bool truncate(); |
michael@0 | 4006 | bool isOperandTruncated(size_t index) const; |
michael@0 | 4007 | }; |
michael@0 | 4008 | |
michael@0 | 4009 | class MSub : public MBinaryArithInstruction |
michael@0 | 4010 | { |
michael@0 | 4011 | MSub(MDefinition *left, MDefinition *right) |
michael@0 | 4012 | : MBinaryArithInstruction(left, right) |
michael@0 | 4013 | { |
michael@0 | 4014 | setResultType(MIRType_Value); |
michael@0 | 4015 | } |
michael@0 | 4016 | |
michael@0 | 4017 | public: |
michael@0 | 4018 | INSTRUCTION_HEADER(Sub) |
michael@0 | 4019 | static MSub *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { |
michael@0 | 4020 | return new(alloc) MSub(left, right); |
michael@0 | 4021 | } |
michael@0 | 4022 | static MSub *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, |
michael@0 | 4023 | MIRType type) |
michael@0 | 4024 | { |
michael@0 | 4025 | MSub *sub = new(alloc) MSub(left, right); |
michael@0 | 4026 | sub->specialization_ = type; |
michael@0 | 4027 | sub->setResultType(type); |
michael@0 | 4028 | if (type == MIRType_Int32) |
michael@0 | 4029 | sub->setTruncated(true); |
michael@0 | 4030 | return sub; |
michael@0 | 4031 | } |
michael@0 | 4032 | |
michael@0 | 4033 | double getIdentity() { |
michael@0 | 4034 | return 0; |
michael@0 | 4035 | } |
michael@0 | 4036 | |
michael@0 | 4037 | bool isFloat32Commutative() const { return true; } |
michael@0 | 4038 | |
michael@0 | 4039 | bool fallible() const; |
michael@0 | 4040 | void computeRange(TempAllocator &alloc); |
michael@0 | 4041 | bool truncate(); |
michael@0 | 4042 | bool isOperandTruncated(size_t index) const; |
michael@0 | 4043 | }; |
michael@0 | 4044 | |
michael@0 | 4045 | class MMul : public MBinaryArithInstruction |
michael@0 | 4046 | { |
michael@0 | 4047 | public: |
michael@0 | 4048 | enum Mode { |
michael@0 | 4049 | Normal, |
michael@0 | 4050 | Integer |
michael@0 | 4051 | }; |
michael@0 | 4052 | |
michael@0 | 4053 | private: |
michael@0 | 4054 | // Annotation the result could be a negative zero |
michael@0 | 4055 | // and we need to guard this during execution. |
michael@0 | 4056 | bool canBeNegativeZero_; |
michael@0 | 4057 | |
michael@0 | 4058 | Mode mode_; |
michael@0 | 4059 | |
michael@0 | 4060 | MMul(MDefinition *left, MDefinition *right, MIRType type, Mode mode) |
michael@0 | 4061 | : MBinaryArithInstruction(left, right), |
michael@0 | 4062 | canBeNegativeZero_(true), |
michael@0 | 4063 | mode_(mode) |
michael@0 | 4064 | { |
michael@0 | 4065 | if (mode == Integer) { |
michael@0 | 4066 | // This implements the required behavior for Math.imul, which |
michael@0 | 4067 | // can never fail and always truncates its output to int32. |
michael@0 | 4068 | canBeNegativeZero_ = false; |
michael@0 | 4069 | setTruncated(true); |
michael@0 | 4070 | setCommutative(); |
michael@0 | 4071 | } |
michael@0 | 4072 | JS_ASSERT_IF(mode != Integer, mode == Normal); |
michael@0 | 4073 | |
michael@0 | 4074 | if (type != MIRType_Value) |
michael@0 | 4075 | specialization_ = type; |
michael@0 | 4076 | setResultType(type); |
michael@0 | 4077 | } |
michael@0 | 4078 | |
michael@0 | 4079 | public: |
michael@0 | 4080 | INSTRUCTION_HEADER(Mul) |
michael@0 | 4081 | static MMul *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { |
michael@0 | 4082 | return new(alloc) MMul(left, right, MIRType_Value, MMul::Normal); |
michael@0 | 4083 | } |
michael@0 | 4084 | static MMul *New(TempAllocator &alloc, MDefinition *left, MDefinition *right, MIRType type, |
michael@0 | 4085 | Mode mode = Normal) |
michael@0 | 4086 | { |
michael@0 | 4087 | return new(alloc) MMul(left, right, type, mode); |
michael@0 | 4088 | } |
michael@0 | 4089 | |
michael@0 | 4090 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 4091 | void analyzeEdgeCasesForward(); |
michael@0 | 4092 | void analyzeEdgeCasesBackward(); |
michael@0 | 4093 | |
michael@0 | 4094 | double getIdentity() { |
michael@0 | 4095 | return 1; |
michael@0 | 4096 | } |
michael@0 | 4097 | |
michael@0 | 4098 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 4099 | if (!ins->isMul()) |
michael@0 | 4100 | return false; |
michael@0 | 4101 | |
michael@0 | 4102 | const MMul *mul = ins->toMul(); |
michael@0 | 4103 | if (canBeNegativeZero_ != mul->canBeNegativeZero()) |
michael@0 | 4104 | return false; |
michael@0 | 4105 | |
michael@0 | 4106 | if (mode_ != mul->mode()) |
michael@0 | 4107 | return false; |
michael@0 | 4108 | |
michael@0 | 4109 | return binaryCongruentTo(ins); |
michael@0 | 4110 | } |
michael@0 | 4111 | |
michael@0 | 4112 | bool canOverflow() const; |
michael@0 | 4113 | |
michael@0 | 4114 | bool canBeNegativeZero() const { |
michael@0 | 4115 | return canBeNegativeZero_; |
michael@0 | 4116 | } |
michael@0 | 4117 | void setCanBeNegativeZero(bool negativeZero) { |
michael@0 | 4118 | canBeNegativeZero_ = negativeZero; |
michael@0 | 4119 | } |
michael@0 | 4120 | |
michael@0 | 4121 | bool updateForReplacement(MDefinition *ins); |
michael@0 | 4122 | |
michael@0 | 4123 | bool fallible() const { |
michael@0 | 4124 | return canBeNegativeZero_ || canOverflow(); |
michael@0 | 4125 | } |
michael@0 | 4126 | |
michael@0 | 4127 | void setSpecialization(MIRType type) { |
michael@0 | 4128 | specialization_ = type; |
michael@0 | 4129 | } |
michael@0 | 4130 | |
michael@0 | 4131 | bool isFloat32Commutative() const { return true; } |
michael@0 | 4132 | |
michael@0 | 4133 | void computeRange(TempAllocator &alloc); |
michael@0 | 4134 | bool truncate(); |
michael@0 | 4135 | bool isOperandTruncated(size_t index) const; |
michael@0 | 4136 | |
michael@0 | 4137 | Mode mode() const { return mode_; } |
michael@0 | 4138 | }; |
michael@0 | 4139 | |
michael@0 | 4140 | class MDiv : public MBinaryArithInstruction |
michael@0 | 4141 | { |
michael@0 | 4142 | bool canBeNegativeZero_; |
michael@0 | 4143 | bool canBeNegativeOverflow_; |
michael@0 | 4144 | bool canBeDivideByZero_; |
michael@0 | 4145 | bool canBeNegativeDividend_; |
michael@0 | 4146 | bool unsigned_; |
michael@0 | 4147 | |
michael@0 | 4148 | // A Division can be truncated in 4 differents ways: |
michael@0 | 4149 | // 1. Ignore Infinities (x / 0 --> 0). |
michael@0 | 4150 | // 2. Ignore overflow (INT_MIN / -1 == (INT_MAX + 1) --> INT_MIN) |
michael@0 | 4151 | // 3. Ignore negative zeros. (-0 --> 0) |
michael@0 | 4152 | // 4. Ignore remainder. (3 / 4 --> 0) |
michael@0 | 4153 | // |
michael@0 | 4154 | // isTruncatedIndirectly is used to represent that we are interested in the |
michael@0 | 4155 | // truncated result, but only if they it can safely flow in operations which |
michael@0 | 4156 | // are computed modulo 2^32, such as (2) and (3). |
michael@0 | 4157 | // |
michael@0 | 4158 | // A division can return either Infinities (1) or a remainder (4) when both |
michael@0 | 4159 | // operands are integers. Infinities are not safe, as they would have |
michael@0 | 4160 | // absorbed other math operations. Remainders are not safe, as multiple can |
michael@0 | 4161 | // add up to integers. This implies that we need to distinguish between a |
michael@0 | 4162 | // division which is truncated directly (isTruncated) or which flow into |
michael@0 | 4163 | // truncated operations (isTruncatedIndirectly). |
michael@0 | 4164 | bool isTruncatedIndirectly_; |
michael@0 | 4165 | |
michael@0 | 4166 | MDiv(MDefinition *left, MDefinition *right, MIRType type) |
michael@0 | 4167 | : MBinaryArithInstruction(left, right), |
michael@0 | 4168 | canBeNegativeZero_(true), |
michael@0 | 4169 | canBeNegativeOverflow_(true), |
michael@0 | 4170 | canBeDivideByZero_(true), |
michael@0 | 4171 | canBeNegativeDividend_(true), |
michael@0 | 4172 | unsigned_(false), |
michael@0 | 4173 | isTruncatedIndirectly_(false) |
michael@0 | 4174 | { |
michael@0 | 4175 | if (type != MIRType_Value) |
michael@0 | 4176 | specialization_ = type; |
michael@0 | 4177 | setResultType(type); |
michael@0 | 4178 | } |
michael@0 | 4179 | |
michael@0 | 4180 | public: |
michael@0 | 4181 | INSTRUCTION_HEADER(Div) |
michael@0 | 4182 | static MDiv *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { |
michael@0 | 4183 | return new(alloc) MDiv(left, right, MIRType_Value); |
michael@0 | 4184 | } |
michael@0 | 4185 | static MDiv *New(TempAllocator &alloc, MDefinition *left, MDefinition *right, MIRType type) { |
michael@0 | 4186 | return new(alloc) MDiv(left, right, type); |
michael@0 | 4187 | } |
michael@0 | 4188 | static MDiv *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, |
michael@0 | 4189 | MIRType type, bool unsignd) |
michael@0 | 4190 | { |
michael@0 | 4191 | MDiv *div = new(alloc) MDiv(left, right, type); |
michael@0 | 4192 | div->unsigned_ = unsignd; |
michael@0 | 4193 | if (type == MIRType_Int32) |
michael@0 | 4194 | div->setTruncated(true); |
michael@0 | 4195 | return div; |
michael@0 | 4196 | } |
michael@0 | 4197 | |
michael@0 | 4198 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 4199 | void analyzeEdgeCasesForward(); |
michael@0 | 4200 | void analyzeEdgeCasesBackward(); |
michael@0 | 4201 | |
michael@0 | 4202 | double getIdentity() { |
michael@0 | 4203 | MOZ_ASSUME_UNREACHABLE("not used"); |
michael@0 | 4204 | } |
michael@0 | 4205 | |
michael@0 | 4206 | bool canBeNegativeZero() const { |
michael@0 | 4207 | return canBeNegativeZero_; |
michael@0 | 4208 | } |
michael@0 | 4209 | void setCanBeNegativeZero(bool negativeZero) { |
michael@0 | 4210 | canBeNegativeZero_ = negativeZero; |
michael@0 | 4211 | } |
michael@0 | 4212 | |
michael@0 | 4213 | bool canBeNegativeOverflow() const { |
michael@0 | 4214 | return canBeNegativeOverflow_; |
michael@0 | 4215 | } |
michael@0 | 4216 | |
michael@0 | 4217 | bool canBeDivideByZero() const { |
michael@0 | 4218 | return canBeDivideByZero_; |
michael@0 | 4219 | } |
michael@0 | 4220 | |
michael@0 | 4221 | bool canBeNegativeDividend() const { |
michael@0 | 4222 | return canBeNegativeDividend_; |
michael@0 | 4223 | } |
michael@0 | 4224 | |
michael@0 | 4225 | bool isUnsigned() const { |
michael@0 | 4226 | return unsigned_; |
michael@0 | 4227 | } |
michael@0 | 4228 | |
michael@0 | 4229 | bool isTruncatedIndirectly() const { |
michael@0 | 4230 | return isTruncatedIndirectly_; |
michael@0 | 4231 | } |
michael@0 | 4232 | void setTruncatedIndirectly(bool truncate) { |
michael@0 | 4233 | isTruncatedIndirectly_ = truncate; |
michael@0 | 4234 | } |
michael@0 | 4235 | |
michael@0 | 4236 | bool canTruncateInfinities() const { |
michael@0 | 4237 | return isTruncated(); |
michael@0 | 4238 | } |
michael@0 | 4239 | bool canTruncateRemainder() const { |
michael@0 | 4240 | return isTruncated(); |
michael@0 | 4241 | } |
michael@0 | 4242 | bool canTruncateOverflow() const { |
michael@0 | 4243 | return isTruncated() || isTruncatedIndirectly(); |
michael@0 | 4244 | } |
michael@0 | 4245 | bool canTruncateNegativeZero() const { |
michael@0 | 4246 | return isTruncated() || isTruncatedIndirectly(); |
michael@0 | 4247 | } |
michael@0 | 4248 | |
michael@0 | 4249 | bool isFloat32Commutative() const { return true; } |
michael@0 | 4250 | |
michael@0 | 4251 | void computeRange(TempAllocator &alloc); |
michael@0 | 4252 | bool fallible() const; |
michael@0 | 4253 | bool truncate(); |
michael@0 | 4254 | void collectRangeInfoPreTrunc(); |
michael@0 | 4255 | }; |
michael@0 | 4256 | |
michael@0 | 4257 | class MMod : public MBinaryArithInstruction |
michael@0 | 4258 | { |
michael@0 | 4259 | bool unsigned_; |
michael@0 | 4260 | bool canBeNegativeDividend_; |
michael@0 | 4261 | |
michael@0 | 4262 | MMod(MDefinition *left, MDefinition *right, MIRType type) |
michael@0 | 4263 | : MBinaryArithInstruction(left, right), |
michael@0 | 4264 | unsigned_(false), |
michael@0 | 4265 | canBeNegativeDividend_(true) |
michael@0 | 4266 | { |
michael@0 | 4267 | if (type != MIRType_Value) |
michael@0 | 4268 | specialization_ = type; |
michael@0 | 4269 | setResultType(type); |
michael@0 | 4270 | } |
michael@0 | 4271 | |
michael@0 | 4272 | public: |
michael@0 | 4273 | INSTRUCTION_HEADER(Mod) |
michael@0 | 4274 | static MMod *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { |
michael@0 | 4275 | return new(alloc) MMod(left, right, MIRType_Value); |
michael@0 | 4276 | } |
michael@0 | 4277 | static MMod *NewAsmJS(TempAllocator &alloc, MDefinition *left, MDefinition *right, |
michael@0 | 4278 | MIRType type, bool unsignd) |
michael@0 | 4279 | { |
michael@0 | 4280 | MMod *mod = new(alloc) MMod(left, right, type); |
michael@0 | 4281 | mod->unsigned_ = unsignd; |
michael@0 | 4282 | if (type == MIRType_Int32) |
michael@0 | 4283 | mod->setTruncated(true); |
michael@0 | 4284 | return mod; |
michael@0 | 4285 | } |
michael@0 | 4286 | |
michael@0 | 4287 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 4288 | |
michael@0 | 4289 | double getIdentity() { |
michael@0 | 4290 | MOZ_ASSUME_UNREACHABLE("not used"); |
michael@0 | 4291 | } |
michael@0 | 4292 | |
michael@0 | 4293 | bool canBeNegativeDividend() const { |
michael@0 | 4294 | JS_ASSERT(specialization_ == MIRType_Int32); |
michael@0 | 4295 | return canBeNegativeDividend_; |
michael@0 | 4296 | } |
michael@0 | 4297 | bool canBeDivideByZero() const; |
michael@0 | 4298 | bool canBePowerOfTwoDivisor() const; |
michael@0 | 4299 | |
michael@0 | 4300 | bool isUnsigned() const { |
michael@0 | 4301 | return unsigned_; |
michael@0 | 4302 | } |
michael@0 | 4303 | |
michael@0 | 4304 | bool fallible() const; |
michael@0 | 4305 | |
michael@0 | 4306 | void computeRange(TempAllocator &alloc); |
michael@0 | 4307 | bool truncate(); |
michael@0 | 4308 | void collectRangeInfoPreTrunc(); |
michael@0 | 4309 | }; |
michael@0 | 4310 | |
michael@0 | 4311 | class MConcat |
michael@0 | 4312 | : public MBinaryInstruction, |
michael@0 | 4313 | public MixPolicy<ConvertToStringPolicy<0>, ConvertToStringPolicy<1>> |
michael@0 | 4314 | { |
michael@0 | 4315 | MConcat(MDefinition *left, MDefinition *right) |
michael@0 | 4316 | : MBinaryInstruction(left, right) |
michael@0 | 4317 | { |
michael@0 | 4318 | // At least one input should be definitely string |
michael@0 | 4319 | JS_ASSERT(left->type() == MIRType_String || right->type() == MIRType_String); |
michael@0 | 4320 | |
michael@0 | 4321 | setMovable(); |
michael@0 | 4322 | setResultType(MIRType_String); |
michael@0 | 4323 | } |
michael@0 | 4324 | |
michael@0 | 4325 | public: |
michael@0 | 4326 | INSTRUCTION_HEADER(Concat) |
michael@0 | 4327 | static MConcat *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { |
michael@0 | 4328 | return new(alloc) MConcat(left, right); |
michael@0 | 4329 | } |
michael@0 | 4330 | |
michael@0 | 4331 | TypePolicy *typePolicy() { |
michael@0 | 4332 | return this; |
michael@0 | 4333 | } |
michael@0 | 4334 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 4335 | return congruentIfOperandsEqual(ins); |
michael@0 | 4336 | } |
michael@0 | 4337 | AliasSet getAliasSet() const { |
michael@0 | 4338 | return AliasSet::None(); |
michael@0 | 4339 | } |
michael@0 | 4340 | }; |
michael@0 | 4341 | |
michael@0 | 4342 | class MConcatPar |
michael@0 | 4343 | : public MTernaryInstruction |
michael@0 | 4344 | { |
michael@0 | 4345 | MConcatPar(MDefinition *cx, MDefinition *left, MDefinition *right) |
michael@0 | 4346 | : MTernaryInstruction(cx, left, right) |
michael@0 | 4347 | { |
michael@0 | 4348 | // Type analysis has already run, before replacing with the parallel |
michael@0 | 4349 | // variant. |
michael@0 | 4350 | JS_ASSERT(left->type() == MIRType_String && right->type() == MIRType_String); |
michael@0 | 4351 | |
michael@0 | 4352 | setMovable(); |
michael@0 | 4353 | setResultType(MIRType_String); |
michael@0 | 4354 | } |
michael@0 | 4355 | |
michael@0 | 4356 | public: |
michael@0 | 4357 | INSTRUCTION_HEADER(ConcatPar) |
michael@0 | 4358 | |
michael@0 | 4359 | static MConcatPar *New(TempAllocator &alloc, MDefinition *cx, MConcat *concat) { |
michael@0 | 4360 | return new(alloc) MConcatPar(cx, concat->lhs(), concat->rhs()); |
michael@0 | 4361 | } |
michael@0 | 4362 | |
michael@0 | 4363 | MDefinition *forkJoinContext() const { |
michael@0 | 4364 | return getOperand(0); |
michael@0 | 4365 | } |
michael@0 | 4366 | MDefinition *lhs() const { |
michael@0 | 4367 | return getOperand(1); |
michael@0 | 4368 | } |
michael@0 | 4369 | MDefinition *rhs() const { |
michael@0 | 4370 | return getOperand(2); |
michael@0 | 4371 | } |
michael@0 | 4372 | |
michael@0 | 4373 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 4374 | return congruentIfOperandsEqual(ins); |
michael@0 | 4375 | } |
michael@0 | 4376 | AliasSet getAliasSet() const { |
michael@0 | 4377 | return AliasSet::None(); |
michael@0 | 4378 | } |
michael@0 | 4379 | }; |
michael@0 | 4380 | |
michael@0 | 4381 | class MCharCodeAt |
michael@0 | 4382 | : public MBinaryInstruction, |
michael@0 | 4383 | public MixPolicy<StringPolicy<0>, IntPolicy<1> > |
michael@0 | 4384 | { |
michael@0 | 4385 | MCharCodeAt(MDefinition *str, MDefinition *index) |
michael@0 | 4386 | : MBinaryInstruction(str, index) |
michael@0 | 4387 | { |
michael@0 | 4388 | setMovable(); |
michael@0 | 4389 | setResultType(MIRType_Int32); |
michael@0 | 4390 | } |
michael@0 | 4391 | |
michael@0 | 4392 | public: |
michael@0 | 4393 | INSTRUCTION_HEADER(CharCodeAt) |
michael@0 | 4394 | |
michael@0 | 4395 | static MCharCodeAt *New(TempAllocator &alloc, MDefinition *str, MDefinition *index) { |
michael@0 | 4396 | return new(alloc) MCharCodeAt(str, index); |
michael@0 | 4397 | } |
michael@0 | 4398 | |
michael@0 | 4399 | TypePolicy *typePolicy() { |
michael@0 | 4400 | return this; |
michael@0 | 4401 | } |
michael@0 | 4402 | |
michael@0 | 4403 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 4404 | return congruentIfOperandsEqual(ins); |
michael@0 | 4405 | } |
michael@0 | 4406 | |
michael@0 | 4407 | virtual AliasSet getAliasSet() const { |
michael@0 | 4408 | // Strings are immutable, so there is no implicit dependency. |
michael@0 | 4409 | return AliasSet::None(); |
michael@0 | 4410 | } |
michael@0 | 4411 | |
michael@0 | 4412 | void computeRange(TempAllocator &alloc); |
michael@0 | 4413 | }; |
michael@0 | 4414 | |
michael@0 | 4415 | class MFromCharCode |
michael@0 | 4416 | : public MUnaryInstruction, |
michael@0 | 4417 | public IntPolicy<0> |
michael@0 | 4418 | { |
michael@0 | 4419 | MFromCharCode(MDefinition *code) |
michael@0 | 4420 | : MUnaryInstruction(code) |
michael@0 | 4421 | { |
michael@0 | 4422 | setMovable(); |
michael@0 | 4423 | setResultType(MIRType_String); |
michael@0 | 4424 | } |
michael@0 | 4425 | |
michael@0 | 4426 | public: |
michael@0 | 4427 | INSTRUCTION_HEADER(FromCharCode) |
michael@0 | 4428 | |
michael@0 | 4429 | static MFromCharCode *New(TempAllocator &alloc, MDefinition *code) { |
michael@0 | 4430 | return new(alloc) MFromCharCode(code); |
michael@0 | 4431 | } |
michael@0 | 4432 | |
michael@0 | 4433 | virtual AliasSet getAliasSet() const { |
michael@0 | 4434 | return AliasSet::None(); |
michael@0 | 4435 | } |
michael@0 | 4436 | }; |
michael@0 | 4437 | |
michael@0 | 4438 | class MStringSplit |
michael@0 | 4439 | : public MBinaryInstruction, |
michael@0 | 4440 | public MixPolicy<StringPolicy<0>, StringPolicy<1> > |
michael@0 | 4441 | { |
michael@0 | 4442 | types::TypeObject *typeObject_; |
michael@0 | 4443 | |
michael@0 | 4444 | MStringSplit(types::CompilerConstraintList *constraints, MDefinition *string, MDefinition *sep, |
michael@0 | 4445 | JSObject *templateObject) |
michael@0 | 4446 | : MBinaryInstruction(string, sep), |
michael@0 | 4447 | typeObject_(templateObject->type()) |
michael@0 | 4448 | { |
michael@0 | 4449 | setResultType(MIRType_Object); |
michael@0 | 4450 | setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject)); |
michael@0 | 4451 | } |
michael@0 | 4452 | |
michael@0 | 4453 | public: |
michael@0 | 4454 | INSTRUCTION_HEADER(StringSplit) |
michael@0 | 4455 | |
michael@0 | 4456 | static MStringSplit *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
michael@0 | 4457 | MDefinition *string, MDefinition *sep, |
michael@0 | 4458 | JSObject *templateObject) |
michael@0 | 4459 | { |
michael@0 | 4460 | return new(alloc) MStringSplit(constraints, string, sep, templateObject); |
michael@0 | 4461 | } |
michael@0 | 4462 | types::TypeObject *typeObject() const { |
michael@0 | 4463 | return typeObject_; |
michael@0 | 4464 | } |
michael@0 | 4465 | MDefinition *string() const { |
michael@0 | 4466 | return getOperand(0); |
michael@0 | 4467 | } |
michael@0 | 4468 | MDefinition *separator() const { |
michael@0 | 4469 | return getOperand(1); |
michael@0 | 4470 | } |
michael@0 | 4471 | TypePolicy *typePolicy() { |
michael@0 | 4472 | return this; |
michael@0 | 4473 | } |
michael@0 | 4474 | bool possiblyCalls() const { |
michael@0 | 4475 | return true; |
michael@0 | 4476 | } |
michael@0 | 4477 | virtual AliasSet getAliasSet() const { |
michael@0 | 4478 | // Although this instruction returns a new array, we don't have to mark |
michael@0 | 4479 | // it as store instruction, see also MNewArray. |
michael@0 | 4480 | return AliasSet::None(); |
michael@0 | 4481 | } |
michael@0 | 4482 | }; |
michael@0 | 4483 | |
michael@0 | 4484 | // Returns an object to use as |this| value. See also ComputeThis and |
michael@0 | 4485 | // BoxNonStrictThis in Interpreter.h. |
michael@0 | 4486 | class MComputeThis |
michael@0 | 4487 | : public MUnaryInstruction, |
michael@0 | 4488 | public BoxPolicy<0> |
michael@0 | 4489 | { |
michael@0 | 4490 | MComputeThis(MDefinition *def) |
michael@0 | 4491 | : MUnaryInstruction(def) |
michael@0 | 4492 | { |
michael@0 | 4493 | setResultType(MIRType_Object); |
michael@0 | 4494 | } |
michael@0 | 4495 | |
michael@0 | 4496 | public: |
michael@0 | 4497 | INSTRUCTION_HEADER(ComputeThis) |
michael@0 | 4498 | |
michael@0 | 4499 | static MComputeThis *New(TempAllocator &alloc, MDefinition *def) { |
michael@0 | 4500 | return new(alloc) MComputeThis(def); |
michael@0 | 4501 | } |
michael@0 | 4502 | |
michael@0 | 4503 | MDefinition *input() const { |
michael@0 | 4504 | return getOperand(0); |
michael@0 | 4505 | } |
michael@0 | 4506 | TypePolicy *typePolicy() { |
michael@0 | 4507 | return this; |
michael@0 | 4508 | } |
michael@0 | 4509 | bool possiblyCalls() const { |
michael@0 | 4510 | return true; |
michael@0 | 4511 | } |
michael@0 | 4512 | |
michael@0 | 4513 | // Note: don't override getAliasSet: the thisObject hook can be |
michael@0 | 4514 | // effectful. |
michael@0 | 4515 | }; |
michael@0 | 4516 | |
michael@0 | 4517 | // Load an arrow function's |this| value. |
michael@0 | 4518 | class MLoadArrowThis |
michael@0 | 4519 | : public MUnaryInstruction, |
michael@0 | 4520 | public SingleObjectPolicy |
michael@0 | 4521 | { |
michael@0 | 4522 | MLoadArrowThis(MDefinition *callee) |
michael@0 | 4523 | : MUnaryInstruction(callee) |
michael@0 | 4524 | { |
michael@0 | 4525 | setResultType(MIRType_Value); |
michael@0 | 4526 | setMovable(); |
michael@0 | 4527 | } |
michael@0 | 4528 | |
michael@0 | 4529 | public: |
michael@0 | 4530 | INSTRUCTION_HEADER(LoadArrowThis) |
michael@0 | 4531 | |
michael@0 | 4532 | static MLoadArrowThis *New(TempAllocator &alloc, MDefinition *callee) { |
michael@0 | 4533 | return new(alloc) MLoadArrowThis(callee); |
michael@0 | 4534 | } |
michael@0 | 4535 | MDefinition *callee() const { |
michael@0 | 4536 | return getOperand(0); |
michael@0 | 4537 | } |
michael@0 | 4538 | TypePolicy *typePolicy() { |
michael@0 | 4539 | return this; |
michael@0 | 4540 | } |
michael@0 | 4541 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 4542 | return congruentIfOperandsEqual(ins); |
michael@0 | 4543 | } |
michael@0 | 4544 | AliasSet getAliasSet() const { |
michael@0 | 4545 | // An arrow function's lexical |this| value is immutable. |
michael@0 | 4546 | return AliasSet::None(); |
michael@0 | 4547 | } |
michael@0 | 4548 | }; |
michael@0 | 4549 | |
michael@0 | 4550 | class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi> |
michael@0 | 4551 | { |
michael@0 | 4552 | js::Vector<MUse, 2, IonAllocPolicy> inputs_; |
michael@0 | 4553 | |
michael@0 | 4554 | uint32_t slot_; |
michael@0 | 4555 | bool hasBackedgeType_; |
michael@0 | 4556 | bool triedToSpecialize_; |
michael@0 | 4557 | bool isIterator_; |
michael@0 | 4558 | bool canProduceFloat32_; |
michael@0 | 4559 | bool canConsumeFloat32_; |
michael@0 | 4560 | |
michael@0 | 4561 | #if DEBUG |
michael@0 | 4562 | bool specialized_; |
michael@0 | 4563 | uint32_t capacity_; |
michael@0 | 4564 | #endif |
michael@0 | 4565 | |
michael@0 | 4566 | protected: |
michael@0 | 4567 | MUse *getUseFor(size_t index) { |
michael@0 | 4568 | return &inputs_[index]; |
michael@0 | 4569 | } |
michael@0 | 4570 | |
michael@0 | 4571 | public: |
michael@0 | 4572 | INSTRUCTION_HEADER(Phi) |
michael@0 | 4573 | |
michael@0 | 4574 | MPhi(TempAllocator &alloc, uint32_t slot, MIRType resultType) |
michael@0 | 4575 | : inputs_(alloc), |
michael@0 | 4576 | slot_(slot), |
michael@0 | 4577 | hasBackedgeType_(false), |
michael@0 | 4578 | triedToSpecialize_(false), |
michael@0 | 4579 | isIterator_(false), |
michael@0 | 4580 | canProduceFloat32_(false), |
michael@0 | 4581 | canConsumeFloat32_(false) |
michael@0 | 4582 | #if DEBUG |
michael@0 | 4583 | , specialized_(false) |
michael@0 | 4584 | , capacity_(0) |
michael@0 | 4585 | #endif |
michael@0 | 4586 | { |
michael@0 | 4587 | setResultType(resultType); |
michael@0 | 4588 | } |
michael@0 | 4589 | |
michael@0 | 4590 | static MPhi *New(TempAllocator &alloc, uint32_t slot, MIRType resultType = MIRType_Value) { |
michael@0 | 4591 | return new(alloc) MPhi(alloc, slot, resultType); |
michael@0 | 4592 | } |
michael@0 | 4593 | |
michael@0 | 4594 | void setOperand(size_t index, MDefinition *operand) { |
michael@0 | 4595 | // Note: after the initial IonBuilder pass, it is OK to change phi |
michael@0 | 4596 | // operands such that they do not include the type sets of their |
michael@0 | 4597 | // operands. This can arise during e.g. value numbering, where |
michael@0 | 4598 | // definitions producing the same value may have different type sets. |
michael@0 | 4599 | JS_ASSERT(index < numOperands()); |
michael@0 | 4600 | inputs_[index].set(operand, this, index); |
michael@0 | 4601 | operand->addUse(&inputs_[index]); |
michael@0 | 4602 | } |
michael@0 | 4603 | |
michael@0 | 4604 | void removeOperand(size_t index); |
michael@0 | 4605 | |
michael@0 | 4606 | MDefinition *getOperand(size_t index) const { |
michael@0 | 4607 | return inputs_[index].producer(); |
michael@0 | 4608 | } |
michael@0 | 4609 | size_t numOperands() const { |
michael@0 | 4610 | return inputs_.length(); |
michael@0 | 4611 | } |
michael@0 | 4612 | uint32_t slot() const { |
michael@0 | 4613 | return slot_; |
michael@0 | 4614 | } |
michael@0 | 4615 | bool hasBackedgeType() const { |
michael@0 | 4616 | return hasBackedgeType_; |
michael@0 | 4617 | } |
michael@0 | 4618 | bool triedToSpecialize() const { |
michael@0 | 4619 | return triedToSpecialize_; |
michael@0 | 4620 | } |
michael@0 | 4621 | void specialize(MIRType type) { |
michael@0 | 4622 | triedToSpecialize_ = true; |
michael@0 | 4623 | setResultType(type); |
michael@0 | 4624 | } |
michael@0 | 4625 | bool specializeType(); |
michael@0 | 4626 | |
michael@0 | 4627 | // Whether this phi's type already includes information for def. |
michael@0 | 4628 | bool typeIncludes(MDefinition *def); |
michael@0 | 4629 | |
michael@0 | 4630 | // Add types for this phi which speculate about new inputs that may come in |
michael@0 | 4631 | // via a loop backedge. |
michael@0 | 4632 | bool addBackedgeType(MIRType type, types::TemporaryTypeSet *typeSet); |
michael@0 | 4633 | |
michael@0 | 4634 | // Initializes the operands vector to the given capacity, |
michael@0 | 4635 | // permitting use of addInput() instead of addInputSlow(). |
michael@0 | 4636 | bool reserveLength(size_t length); |
michael@0 | 4637 | |
michael@0 | 4638 | // Use only if capacity has been reserved by reserveLength |
michael@0 | 4639 | void addInput(MDefinition *ins); |
michael@0 | 4640 | |
michael@0 | 4641 | // Appends a new input to the input vector. May call realloc_(). |
michael@0 | 4642 | // Prefer reserveLength() and addInput() instead, where possible. |
michael@0 | 4643 | bool addInputSlow(MDefinition *ins, bool *ptypeChange = nullptr); |
michael@0 | 4644 | |
michael@0 | 4645 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 4646 | |
michael@0 | 4647 | bool congruentTo(const MDefinition *ins) const; |
michael@0 | 4648 | |
michael@0 | 4649 | bool isIterator() const { |
michael@0 | 4650 | return isIterator_; |
michael@0 | 4651 | } |
michael@0 | 4652 | void setIterator() { |
michael@0 | 4653 | isIterator_ = true; |
michael@0 | 4654 | } |
michael@0 | 4655 | |
michael@0 | 4656 | AliasSet getAliasSet() const { |
michael@0 | 4657 | return AliasSet::None(); |
michael@0 | 4658 | } |
michael@0 | 4659 | void computeRange(TempAllocator &alloc); |
michael@0 | 4660 | |
michael@0 | 4661 | MDefinition *operandIfRedundant() { |
michael@0 | 4662 | // If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)), |
michael@0 | 4663 | // returns the operand that it will always be equal to (a, in |
michael@0 | 4664 | // those two cases). |
michael@0 | 4665 | MDefinition *first = getOperand(0); |
michael@0 | 4666 | for (size_t i = 1, e = numOperands(); i < e; i++) { |
michael@0 | 4667 | if (getOperand(i) != first && getOperand(i) != this) |
michael@0 | 4668 | return nullptr; |
michael@0 | 4669 | } |
michael@0 | 4670 | return first; |
michael@0 | 4671 | } |
michael@0 | 4672 | |
michael@0 | 4673 | bool canProduceFloat32() const { |
michael@0 | 4674 | return canProduceFloat32_; |
michael@0 | 4675 | } |
michael@0 | 4676 | |
michael@0 | 4677 | void setCanProduceFloat32(bool can) { |
michael@0 | 4678 | canProduceFloat32_ = can; |
michael@0 | 4679 | } |
michael@0 | 4680 | |
michael@0 | 4681 | bool canConsumeFloat32(MUse *use) const { |
michael@0 | 4682 | return canConsumeFloat32_; |
michael@0 | 4683 | } |
michael@0 | 4684 | |
michael@0 | 4685 | void setCanConsumeFloat32(bool can) { |
michael@0 | 4686 | canConsumeFloat32_ = can; |
michael@0 | 4687 | } |
michael@0 | 4688 | }; |
michael@0 | 4689 | |
michael@0 | 4690 | // The goal of a Beta node is to split a def at a conditionally taken |
michael@0 | 4691 | // branch, so that uses dominated by it have a different name. |
michael@0 | 4692 | class MBeta : public MUnaryInstruction |
michael@0 | 4693 | { |
michael@0 | 4694 | private: |
michael@0 | 4695 | // This is the range induced by a comparison and branch in a preceding |
michael@0 | 4696 | // block. Note that this does not reflect any range constraints from |
michael@0 | 4697 | // the input value itself, so this value may differ from the range() |
michael@0 | 4698 | // range after it is computed. |
michael@0 | 4699 | const Range *comparison_; |
michael@0 | 4700 | |
michael@0 | 4701 | MBeta(MDefinition *val, const Range *comp) |
michael@0 | 4702 | : MUnaryInstruction(val), |
michael@0 | 4703 | comparison_(comp) |
michael@0 | 4704 | { |
michael@0 | 4705 | setResultType(val->type()); |
michael@0 | 4706 | setResultTypeSet(val->resultTypeSet()); |
michael@0 | 4707 | } |
michael@0 | 4708 | |
michael@0 | 4709 | public: |
michael@0 | 4710 | INSTRUCTION_HEADER(Beta) |
michael@0 | 4711 | void printOpcode(FILE *fp) const; |
michael@0 | 4712 | static MBeta *New(TempAllocator &alloc, MDefinition *val, const Range *comp) |
michael@0 | 4713 | { |
michael@0 | 4714 | return new(alloc) MBeta(val, comp); |
michael@0 | 4715 | } |
michael@0 | 4716 | |
michael@0 | 4717 | AliasSet getAliasSet() const { |
michael@0 | 4718 | return AliasSet::None(); |
michael@0 | 4719 | } |
michael@0 | 4720 | |
michael@0 | 4721 | void computeRange(TempAllocator &alloc); |
michael@0 | 4722 | }; |
michael@0 | 4723 | |
michael@0 | 4724 | // MIR representation of a Value on the OSR BaselineFrame. |
michael@0 | 4725 | // The Value is indexed off of OsrFrameReg. |
michael@0 | 4726 | class MOsrValue : public MUnaryInstruction |
michael@0 | 4727 | { |
michael@0 | 4728 | private: |
michael@0 | 4729 | ptrdiff_t frameOffset_; |
michael@0 | 4730 | |
michael@0 | 4731 | MOsrValue(MOsrEntry *entry, ptrdiff_t frameOffset) |
michael@0 | 4732 | : MUnaryInstruction(entry), |
michael@0 | 4733 | frameOffset_(frameOffset) |
michael@0 | 4734 | { |
michael@0 | 4735 | setResultType(MIRType_Value); |
michael@0 | 4736 | } |
michael@0 | 4737 | |
michael@0 | 4738 | public: |
michael@0 | 4739 | INSTRUCTION_HEADER(OsrValue) |
michael@0 | 4740 | static MOsrValue *New(TempAllocator &alloc, MOsrEntry *entry, ptrdiff_t frameOffset) { |
michael@0 | 4741 | return new(alloc) MOsrValue(entry, frameOffset); |
michael@0 | 4742 | } |
michael@0 | 4743 | |
michael@0 | 4744 | ptrdiff_t frameOffset() const { |
michael@0 | 4745 | return frameOffset_; |
michael@0 | 4746 | } |
michael@0 | 4747 | |
michael@0 | 4748 | MOsrEntry *entry() { |
michael@0 | 4749 | return getOperand(0)->toOsrEntry(); |
michael@0 | 4750 | } |
michael@0 | 4751 | |
michael@0 | 4752 | AliasSet getAliasSet() const { |
michael@0 | 4753 | return AliasSet::None(); |
michael@0 | 4754 | } |
michael@0 | 4755 | }; |
michael@0 | 4756 | |
michael@0 | 4757 | // MIR representation of a JSObject scope chain pointer on the OSR BaselineFrame. |
michael@0 | 4758 | // The pointer is indexed off of OsrFrameReg. |
michael@0 | 4759 | class MOsrScopeChain : public MUnaryInstruction |
michael@0 | 4760 | { |
michael@0 | 4761 | private: |
michael@0 | 4762 | MOsrScopeChain(MOsrEntry *entry) |
michael@0 | 4763 | : MUnaryInstruction(entry) |
michael@0 | 4764 | { |
michael@0 | 4765 | setResultType(MIRType_Object); |
michael@0 | 4766 | } |
michael@0 | 4767 | |
michael@0 | 4768 | public: |
michael@0 | 4769 | INSTRUCTION_HEADER(OsrScopeChain) |
michael@0 | 4770 | static MOsrScopeChain *New(TempAllocator &alloc, MOsrEntry *entry) { |
michael@0 | 4771 | return new(alloc) MOsrScopeChain(entry); |
michael@0 | 4772 | } |
michael@0 | 4773 | |
michael@0 | 4774 | MOsrEntry *entry() { |
michael@0 | 4775 | return getOperand(0)->toOsrEntry(); |
michael@0 | 4776 | } |
michael@0 | 4777 | }; |
michael@0 | 4778 | |
michael@0 | 4779 | // MIR representation of a JSObject ArgumentsObject pointer on the OSR BaselineFrame. |
michael@0 | 4780 | // The pointer is indexed off of OsrFrameReg. |
michael@0 | 4781 | class MOsrArgumentsObject : public MUnaryInstruction |
michael@0 | 4782 | { |
michael@0 | 4783 | private: |
michael@0 | 4784 | MOsrArgumentsObject(MOsrEntry *entry) |
michael@0 | 4785 | : MUnaryInstruction(entry) |
michael@0 | 4786 | { |
michael@0 | 4787 | setResultType(MIRType_Object); |
michael@0 | 4788 | } |
michael@0 | 4789 | |
michael@0 | 4790 | public: |
michael@0 | 4791 | INSTRUCTION_HEADER(OsrArgumentsObject) |
michael@0 | 4792 | static MOsrArgumentsObject *New(TempAllocator &alloc, MOsrEntry *entry) { |
michael@0 | 4793 | return new(alloc) MOsrArgumentsObject(entry); |
michael@0 | 4794 | } |
michael@0 | 4795 | |
michael@0 | 4796 | MOsrEntry *entry() { |
michael@0 | 4797 | return getOperand(0)->toOsrEntry(); |
michael@0 | 4798 | } |
michael@0 | 4799 | }; |
michael@0 | 4800 | |
michael@0 | 4801 | // MIR representation of the return value on the OSR BaselineFrame. |
michael@0 | 4802 | // The Value is indexed off of OsrFrameReg. |
michael@0 | 4803 | class MOsrReturnValue : public MUnaryInstruction |
michael@0 | 4804 | { |
michael@0 | 4805 | private: |
michael@0 | 4806 | MOsrReturnValue(MOsrEntry *entry) |
michael@0 | 4807 | : MUnaryInstruction(entry) |
michael@0 | 4808 | { |
michael@0 | 4809 | setResultType(MIRType_Value); |
michael@0 | 4810 | } |
michael@0 | 4811 | |
michael@0 | 4812 | public: |
michael@0 | 4813 | INSTRUCTION_HEADER(OsrReturnValue) |
michael@0 | 4814 | static MOsrReturnValue *New(TempAllocator &alloc, MOsrEntry *entry) { |
michael@0 | 4815 | return new(alloc) MOsrReturnValue(entry); |
michael@0 | 4816 | } |
michael@0 | 4817 | |
michael@0 | 4818 | MOsrEntry *entry() { |
michael@0 | 4819 | return getOperand(0)->toOsrEntry(); |
michael@0 | 4820 | } |
michael@0 | 4821 | }; |
michael@0 | 4822 | |
michael@0 | 4823 | // Check the current frame for over-recursion past the global stack limit. |
michael@0 | 4824 | class MCheckOverRecursed : public MNullaryInstruction |
michael@0 | 4825 | { |
michael@0 | 4826 | public: |
michael@0 | 4827 | INSTRUCTION_HEADER(CheckOverRecursed) |
michael@0 | 4828 | |
michael@0 | 4829 | static MCheckOverRecursed *New(TempAllocator &alloc) { |
michael@0 | 4830 | return new(alloc) MCheckOverRecursed(); |
michael@0 | 4831 | } |
michael@0 | 4832 | }; |
michael@0 | 4833 | |
michael@0 | 4834 | // Check the current frame for over-recursion past the global stack limit. |
michael@0 | 4835 | // Uses the per-thread recursion limit. |
michael@0 | 4836 | class MCheckOverRecursedPar : public MUnaryInstruction |
michael@0 | 4837 | { |
michael@0 | 4838 | MCheckOverRecursedPar(MDefinition *cx) |
michael@0 | 4839 | : MUnaryInstruction(cx) |
michael@0 | 4840 | { |
michael@0 | 4841 | setResultType(MIRType_None); |
michael@0 | 4842 | setGuard(); |
michael@0 | 4843 | setMovable(); |
michael@0 | 4844 | } |
michael@0 | 4845 | |
michael@0 | 4846 | public: |
michael@0 | 4847 | INSTRUCTION_HEADER(CheckOverRecursedPar); |
michael@0 | 4848 | |
michael@0 | 4849 | static MCheckOverRecursedPar *New(TempAllocator &alloc, MDefinition *cx) { |
michael@0 | 4850 | return new(alloc) MCheckOverRecursedPar(cx); |
michael@0 | 4851 | } |
michael@0 | 4852 | |
michael@0 | 4853 | MDefinition *forkJoinContext() const { |
michael@0 | 4854 | return getOperand(0); |
michael@0 | 4855 | } |
michael@0 | 4856 | }; |
michael@0 | 4857 | |
michael@0 | 4858 | // Check for an interrupt (or rendezvous) in parallel mode. |
michael@0 | 4859 | class MInterruptCheckPar : public MUnaryInstruction |
michael@0 | 4860 | { |
michael@0 | 4861 | MInterruptCheckPar(MDefinition *cx) |
michael@0 | 4862 | : MUnaryInstruction(cx) |
michael@0 | 4863 | { |
michael@0 | 4864 | setResultType(MIRType_None); |
michael@0 | 4865 | setGuard(); |
michael@0 | 4866 | setMovable(); |
michael@0 | 4867 | } |
michael@0 | 4868 | |
michael@0 | 4869 | public: |
michael@0 | 4870 | INSTRUCTION_HEADER(InterruptCheckPar); |
michael@0 | 4871 | |
michael@0 | 4872 | static MInterruptCheckPar *New(TempAllocator &alloc, MDefinition *cx) { |
michael@0 | 4873 | return new(alloc) MInterruptCheckPar(cx); |
michael@0 | 4874 | } |
michael@0 | 4875 | |
michael@0 | 4876 | MDefinition *forkJoinContext() const { |
michael@0 | 4877 | return getOperand(0); |
michael@0 | 4878 | } |
michael@0 | 4879 | }; |
michael@0 | 4880 | |
michael@0 | 4881 | // Check whether we need to fire the interrupt handler. |
michael@0 | 4882 | class MInterruptCheck : public MNullaryInstruction |
michael@0 | 4883 | { |
michael@0 | 4884 | MInterruptCheck() { |
michael@0 | 4885 | setGuard(); |
michael@0 | 4886 | } |
michael@0 | 4887 | |
michael@0 | 4888 | public: |
michael@0 | 4889 | INSTRUCTION_HEADER(InterruptCheck) |
michael@0 | 4890 | |
michael@0 | 4891 | static MInterruptCheck *New(TempAllocator &alloc) { |
michael@0 | 4892 | return new(alloc) MInterruptCheck(); |
michael@0 | 4893 | } |
michael@0 | 4894 | AliasSet getAliasSet() const { |
michael@0 | 4895 | return AliasSet::None(); |
michael@0 | 4896 | } |
michael@0 | 4897 | }; |
michael@0 | 4898 | |
michael@0 | 4899 | // If not defined, set a global variable to |undefined|. |
michael@0 | 4900 | class MDefVar : public MUnaryInstruction |
michael@0 | 4901 | { |
michael@0 | 4902 | CompilerRootPropertyName name_; // Target name to be defined. |
michael@0 | 4903 | unsigned attrs_; // Attributes to be set. |
michael@0 | 4904 | |
michael@0 | 4905 | private: |
michael@0 | 4906 | MDefVar(PropertyName *name, unsigned attrs, MDefinition *scopeChain) |
michael@0 | 4907 | : MUnaryInstruction(scopeChain), |
michael@0 | 4908 | name_(name), |
michael@0 | 4909 | attrs_(attrs) |
michael@0 | 4910 | { |
michael@0 | 4911 | } |
michael@0 | 4912 | |
michael@0 | 4913 | public: |
michael@0 | 4914 | INSTRUCTION_HEADER(DefVar) |
michael@0 | 4915 | |
michael@0 | 4916 | static MDefVar *New(TempAllocator &alloc, PropertyName *name, unsigned attrs, |
michael@0 | 4917 | MDefinition *scopeChain) |
michael@0 | 4918 | { |
michael@0 | 4919 | return new(alloc) MDefVar(name, attrs, scopeChain); |
michael@0 | 4920 | } |
michael@0 | 4921 | |
michael@0 | 4922 | PropertyName *name() const { |
michael@0 | 4923 | return name_; |
michael@0 | 4924 | } |
michael@0 | 4925 | unsigned attrs() const { |
michael@0 | 4926 | return attrs_; |
michael@0 | 4927 | } |
michael@0 | 4928 | MDefinition *scopeChain() const { |
michael@0 | 4929 | return getOperand(0); |
michael@0 | 4930 | } |
michael@0 | 4931 | bool possiblyCalls() const { |
michael@0 | 4932 | return true; |
michael@0 | 4933 | } |
michael@0 | 4934 | }; |
michael@0 | 4935 | |
michael@0 | 4936 | class MDefFun : public MUnaryInstruction |
michael@0 | 4937 | { |
michael@0 | 4938 | CompilerRootFunction fun_; |
michael@0 | 4939 | |
michael@0 | 4940 | private: |
michael@0 | 4941 | MDefFun(JSFunction *fun, MDefinition *scopeChain) |
michael@0 | 4942 | : MUnaryInstruction(scopeChain), |
michael@0 | 4943 | fun_(fun) |
michael@0 | 4944 | {} |
michael@0 | 4945 | |
michael@0 | 4946 | public: |
michael@0 | 4947 | INSTRUCTION_HEADER(DefFun) |
michael@0 | 4948 | |
michael@0 | 4949 | static MDefFun *New(TempAllocator &alloc, JSFunction *fun, MDefinition *scopeChain) { |
michael@0 | 4950 | return new(alloc) MDefFun(fun, scopeChain); |
michael@0 | 4951 | } |
michael@0 | 4952 | |
michael@0 | 4953 | JSFunction *fun() const { |
michael@0 | 4954 | return fun_; |
michael@0 | 4955 | } |
michael@0 | 4956 | MDefinition *scopeChain() const { |
michael@0 | 4957 | return getOperand(0); |
michael@0 | 4958 | } |
michael@0 | 4959 | bool possiblyCalls() const { |
michael@0 | 4960 | return true; |
michael@0 | 4961 | } |
michael@0 | 4962 | }; |
michael@0 | 4963 | |
michael@0 | 4964 | class MRegExp : public MNullaryInstruction |
michael@0 | 4965 | { |
michael@0 | 4966 | CompilerRoot<RegExpObject *> source_; |
michael@0 | 4967 | bool mustClone_; |
michael@0 | 4968 | |
michael@0 | 4969 | MRegExp(types::CompilerConstraintList *constraints, RegExpObject *source, bool mustClone) |
michael@0 | 4970 | : source_(source), |
michael@0 | 4971 | mustClone_(mustClone) |
michael@0 | 4972 | { |
michael@0 | 4973 | setResultType(MIRType_Object); |
michael@0 | 4974 | setResultTypeSet(MakeSingletonTypeSet(constraints, source)); |
michael@0 | 4975 | } |
michael@0 | 4976 | |
michael@0 | 4977 | public: |
michael@0 | 4978 | INSTRUCTION_HEADER(RegExp) |
michael@0 | 4979 | |
michael@0 | 4980 | static MRegExp *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
michael@0 | 4981 | RegExpObject *source, bool mustClone) |
michael@0 | 4982 | { |
michael@0 | 4983 | return new(alloc) MRegExp(constraints, source, mustClone); |
michael@0 | 4984 | } |
michael@0 | 4985 | |
michael@0 | 4986 | bool mustClone() const { |
michael@0 | 4987 | return mustClone_; |
michael@0 | 4988 | } |
michael@0 | 4989 | RegExpObject *source() const { |
michael@0 | 4990 | return source_; |
michael@0 | 4991 | } |
michael@0 | 4992 | AliasSet getAliasSet() const { |
michael@0 | 4993 | return AliasSet::None(); |
michael@0 | 4994 | } |
michael@0 | 4995 | bool possiblyCalls() const { |
michael@0 | 4996 | return true; |
michael@0 | 4997 | } |
michael@0 | 4998 | }; |
michael@0 | 4999 | |
michael@0 | 5000 | class MRegExpExec |
michael@0 | 5001 | : public MBinaryInstruction, |
michael@0 | 5002 | public MixPolicy<ConvertToStringPolicy<0>, ObjectPolicy<1>> |
michael@0 | 5003 | { |
michael@0 | 5004 | private: |
michael@0 | 5005 | |
michael@0 | 5006 | MRegExpExec(MDefinition *regexp, MDefinition *string) |
michael@0 | 5007 | : MBinaryInstruction(string, regexp) |
michael@0 | 5008 | { |
michael@0 | 5009 | // May be object or null. |
michael@0 | 5010 | setResultType(MIRType_Value); |
michael@0 | 5011 | } |
michael@0 | 5012 | |
michael@0 | 5013 | public: |
michael@0 | 5014 | INSTRUCTION_HEADER(RegExpExec) |
michael@0 | 5015 | |
michael@0 | 5016 | static MRegExpExec *New(TempAllocator &alloc, MDefinition *regexp, MDefinition *string) { |
michael@0 | 5017 | return new(alloc) MRegExpExec(regexp, string); |
michael@0 | 5018 | } |
michael@0 | 5019 | |
michael@0 | 5020 | MDefinition *string() const { |
michael@0 | 5021 | return getOperand(0); |
michael@0 | 5022 | } |
michael@0 | 5023 | |
michael@0 | 5024 | MDefinition *regexp() const { |
michael@0 | 5025 | return getOperand(1); |
michael@0 | 5026 | } |
michael@0 | 5027 | |
michael@0 | 5028 | TypePolicy *typePolicy() { |
michael@0 | 5029 | return this; |
michael@0 | 5030 | } |
michael@0 | 5031 | |
michael@0 | 5032 | bool possiblyCalls() const { |
michael@0 | 5033 | return true; |
michael@0 | 5034 | } |
michael@0 | 5035 | }; |
michael@0 | 5036 | |
michael@0 | 5037 | class MRegExpTest |
michael@0 | 5038 | : public MBinaryInstruction, |
michael@0 | 5039 | public MixPolicy<ObjectPolicy<1>, ConvertToStringPolicy<0> > |
michael@0 | 5040 | { |
michael@0 | 5041 | private: |
michael@0 | 5042 | |
michael@0 | 5043 | MRegExpTest(MDefinition *regexp, MDefinition *string) |
michael@0 | 5044 | : MBinaryInstruction(string, regexp) |
michael@0 | 5045 | { |
michael@0 | 5046 | setResultType(MIRType_Boolean); |
michael@0 | 5047 | } |
michael@0 | 5048 | |
michael@0 | 5049 | public: |
michael@0 | 5050 | INSTRUCTION_HEADER(RegExpTest) |
michael@0 | 5051 | |
michael@0 | 5052 | static MRegExpTest *New(TempAllocator &alloc, MDefinition *regexp, MDefinition *string) { |
michael@0 | 5053 | return new(alloc) MRegExpTest(regexp, string); |
michael@0 | 5054 | } |
michael@0 | 5055 | |
michael@0 | 5056 | MDefinition *string() const { |
michael@0 | 5057 | return getOperand(0); |
michael@0 | 5058 | } |
michael@0 | 5059 | MDefinition *regexp() const { |
michael@0 | 5060 | return getOperand(1); |
michael@0 | 5061 | } |
michael@0 | 5062 | |
michael@0 | 5063 | TypePolicy *typePolicy() { |
michael@0 | 5064 | return this; |
michael@0 | 5065 | } |
michael@0 | 5066 | |
michael@0 | 5067 | bool possiblyCalls() const { |
michael@0 | 5068 | return true; |
michael@0 | 5069 | } |
michael@0 | 5070 | }; |
michael@0 | 5071 | |
michael@0 | 5072 | template <class Policy1> |
michael@0 | 5073 | class MStrReplace |
michael@0 | 5074 | : public MTernaryInstruction, |
michael@0 | 5075 | public Mix3Policy<StringPolicy<0>, Policy1, StringPolicy<2> > |
michael@0 | 5076 | { |
michael@0 | 5077 | protected: |
michael@0 | 5078 | |
michael@0 | 5079 | MStrReplace(MDefinition *string, MDefinition *pattern, MDefinition *replacement) |
michael@0 | 5080 | : MTernaryInstruction(string, pattern, replacement) |
michael@0 | 5081 | { |
michael@0 | 5082 | setMovable(); |
michael@0 | 5083 | setResultType(MIRType_String); |
michael@0 | 5084 | } |
michael@0 | 5085 | |
michael@0 | 5086 | public: |
michael@0 | 5087 | |
michael@0 | 5088 | MDefinition *string() const { |
michael@0 | 5089 | return getOperand(0); |
michael@0 | 5090 | } |
michael@0 | 5091 | MDefinition *pattern() const { |
michael@0 | 5092 | return getOperand(1); |
michael@0 | 5093 | } |
michael@0 | 5094 | MDefinition *replacement() const { |
michael@0 | 5095 | return getOperand(2); |
michael@0 | 5096 | } |
michael@0 | 5097 | |
michael@0 | 5098 | TypePolicy *typePolicy() { |
michael@0 | 5099 | return this; |
michael@0 | 5100 | } |
michael@0 | 5101 | |
michael@0 | 5102 | bool possiblyCalls() const { |
michael@0 | 5103 | return true; |
michael@0 | 5104 | } |
michael@0 | 5105 | }; |
michael@0 | 5106 | |
michael@0 | 5107 | class MRegExpReplace |
michael@0 | 5108 | : public MStrReplace< ObjectPolicy<1> > |
michael@0 | 5109 | { |
michael@0 | 5110 | private: |
michael@0 | 5111 | |
michael@0 | 5112 | MRegExpReplace(MDefinition *string, MDefinition *pattern, MDefinition *replacement) |
michael@0 | 5113 | : MStrReplace< ObjectPolicy<1> >(string, pattern, replacement) |
michael@0 | 5114 | { |
michael@0 | 5115 | } |
michael@0 | 5116 | |
michael@0 | 5117 | public: |
michael@0 | 5118 | INSTRUCTION_HEADER(RegExpReplace); |
michael@0 | 5119 | |
michael@0 | 5120 | static MRegExpReplace *New(TempAllocator &alloc, MDefinition *string, MDefinition *pattern, MDefinition *replacement) { |
michael@0 | 5121 | return new(alloc) MRegExpReplace(string, pattern, replacement); |
michael@0 | 5122 | } |
michael@0 | 5123 | }; |
michael@0 | 5124 | |
michael@0 | 5125 | class MStringReplace |
michael@0 | 5126 | : public MStrReplace< StringPolicy<1> > |
michael@0 | 5127 | { |
michael@0 | 5128 | private: |
michael@0 | 5129 | |
michael@0 | 5130 | MStringReplace(MDefinition *string, MDefinition *pattern, MDefinition *replacement) |
michael@0 | 5131 | : MStrReplace< StringPolicy<1> >(string, pattern, replacement) |
michael@0 | 5132 | { |
michael@0 | 5133 | } |
michael@0 | 5134 | |
michael@0 | 5135 | public: |
michael@0 | 5136 | INSTRUCTION_HEADER(StringReplace); |
michael@0 | 5137 | |
michael@0 | 5138 | static MStringReplace *New(TempAllocator &alloc, MDefinition *string, MDefinition *pattern, MDefinition *replacement) { |
michael@0 | 5139 | return new(alloc) MStringReplace(string, pattern, replacement); |
michael@0 | 5140 | } |
michael@0 | 5141 | |
michael@0 | 5142 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 5143 | return congruentIfOperandsEqual(ins); |
michael@0 | 5144 | } |
michael@0 | 5145 | AliasSet getAliasSet() const { |
michael@0 | 5146 | return AliasSet::None(); |
michael@0 | 5147 | } |
michael@0 | 5148 | }; |
michael@0 | 5149 | |
michael@0 | 5150 | struct LambdaFunctionInfo |
michael@0 | 5151 | { |
michael@0 | 5152 | // The functions used in lambdas are the canonical original function in |
michael@0 | 5153 | // the script, and are immutable except for delazification. Record this |
michael@0 | 5154 | // information while still on the main thread to avoid races. |
michael@0 | 5155 | CompilerRootFunction fun; |
michael@0 | 5156 | uint16_t flags; |
michael@0 | 5157 | gc::Cell *scriptOrLazyScript; |
michael@0 | 5158 | bool singletonType; |
michael@0 | 5159 | bool useNewTypeForClone; |
michael@0 | 5160 | |
michael@0 | 5161 | LambdaFunctionInfo(JSFunction *fun) |
michael@0 | 5162 | : fun(fun), flags(fun->flags()), |
michael@0 | 5163 | scriptOrLazyScript(fun->hasScript() |
michael@0 | 5164 | ? (gc::Cell *) fun->nonLazyScript() |
michael@0 | 5165 | : (gc::Cell *) fun->lazyScript()), |
michael@0 | 5166 | singletonType(fun->hasSingletonType()), |
michael@0 | 5167 | useNewTypeForClone(types::UseNewTypeForClone(fun)) |
michael@0 | 5168 | {} |
michael@0 | 5169 | |
michael@0 | 5170 | LambdaFunctionInfo(const LambdaFunctionInfo &info) |
michael@0 | 5171 | : fun((JSFunction *) info.fun), flags(info.flags), |
michael@0 | 5172 | scriptOrLazyScript(info.scriptOrLazyScript), |
michael@0 | 5173 | singletonType(info.singletonType), |
michael@0 | 5174 | useNewTypeForClone(info.useNewTypeForClone) |
michael@0 | 5175 | {} |
michael@0 | 5176 | }; |
michael@0 | 5177 | |
michael@0 | 5178 | class MLambda |
michael@0 | 5179 | : public MUnaryInstruction, |
michael@0 | 5180 | public SingleObjectPolicy |
michael@0 | 5181 | { |
michael@0 | 5182 | LambdaFunctionInfo info_; |
michael@0 | 5183 | |
michael@0 | 5184 | MLambda(types::CompilerConstraintList *constraints, MDefinition *scopeChain, JSFunction *fun) |
michael@0 | 5185 | : MUnaryInstruction(scopeChain), info_(fun) |
michael@0 | 5186 | { |
michael@0 | 5187 | setResultType(MIRType_Object); |
michael@0 | 5188 | if (!fun->hasSingletonType() && !types::UseNewTypeForClone(fun)) |
michael@0 | 5189 | setResultTypeSet(MakeSingletonTypeSet(constraints, fun)); |
michael@0 | 5190 | } |
michael@0 | 5191 | |
michael@0 | 5192 | public: |
michael@0 | 5193 | INSTRUCTION_HEADER(Lambda) |
michael@0 | 5194 | |
michael@0 | 5195 | static MLambda *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
michael@0 | 5196 | MDefinition *scopeChain, JSFunction *fun) |
michael@0 | 5197 | { |
michael@0 | 5198 | return new(alloc) MLambda(constraints, scopeChain, fun); |
michael@0 | 5199 | } |
michael@0 | 5200 | MDefinition *scopeChain() const { |
michael@0 | 5201 | return getOperand(0); |
michael@0 | 5202 | } |
michael@0 | 5203 | const LambdaFunctionInfo &info() const { |
michael@0 | 5204 | return info_; |
michael@0 | 5205 | } |
michael@0 | 5206 | TypePolicy *typePolicy() { |
michael@0 | 5207 | return this; |
michael@0 | 5208 | } |
michael@0 | 5209 | }; |
michael@0 | 5210 | |
michael@0 | 5211 | class MLambdaArrow |
michael@0 | 5212 | : public MBinaryInstruction, |
michael@0 | 5213 | public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> > |
michael@0 | 5214 | { |
michael@0 | 5215 | LambdaFunctionInfo info_; |
michael@0 | 5216 | |
michael@0 | 5217 | MLambdaArrow(types::CompilerConstraintList *constraints, MDefinition *scopeChain, |
michael@0 | 5218 | MDefinition *this_, JSFunction *fun) |
michael@0 | 5219 | : MBinaryInstruction(scopeChain, this_), info_(fun) |
michael@0 | 5220 | { |
michael@0 | 5221 | setResultType(MIRType_Object); |
michael@0 | 5222 | MOZ_ASSERT(!types::UseNewTypeForClone(fun)); |
michael@0 | 5223 | if (!fun->hasSingletonType()) |
michael@0 | 5224 | setResultTypeSet(MakeSingletonTypeSet(constraints, fun)); |
michael@0 | 5225 | } |
michael@0 | 5226 | |
michael@0 | 5227 | public: |
michael@0 | 5228 | INSTRUCTION_HEADER(LambdaArrow) |
michael@0 | 5229 | |
michael@0 | 5230 | static MLambdaArrow *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
michael@0 | 5231 | MDefinition *scopeChain, MDefinition *this_, JSFunction *fun) |
michael@0 | 5232 | { |
michael@0 | 5233 | return new(alloc) MLambdaArrow(constraints, scopeChain, this_, fun); |
michael@0 | 5234 | } |
michael@0 | 5235 | MDefinition *scopeChain() const { |
michael@0 | 5236 | return getOperand(0); |
michael@0 | 5237 | } |
michael@0 | 5238 | MDefinition *thisDef() const { |
michael@0 | 5239 | return getOperand(1); |
michael@0 | 5240 | } |
michael@0 | 5241 | const LambdaFunctionInfo &info() const { |
michael@0 | 5242 | return info_; |
michael@0 | 5243 | } |
michael@0 | 5244 | TypePolicy *typePolicy() { |
michael@0 | 5245 | return this; |
michael@0 | 5246 | } |
michael@0 | 5247 | }; |
michael@0 | 5248 | |
michael@0 | 5249 | class MLambdaPar |
michael@0 | 5250 | : public MBinaryInstruction, |
michael@0 | 5251 | public SingleObjectPolicy |
michael@0 | 5252 | { |
michael@0 | 5253 | LambdaFunctionInfo info_; |
michael@0 | 5254 | |
michael@0 | 5255 | MLambdaPar(MDefinition *cx, MDefinition *scopeChain, JSFunction *fun, |
michael@0 | 5256 | types::TemporaryTypeSet *resultTypes, const LambdaFunctionInfo &info) |
michael@0 | 5257 | : MBinaryInstruction(cx, scopeChain), info_(info) |
michael@0 | 5258 | { |
michael@0 | 5259 | JS_ASSERT(!info_.singletonType); |
michael@0 | 5260 | JS_ASSERT(!info_.useNewTypeForClone); |
michael@0 | 5261 | setResultType(MIRType_Object); |
michael@0 | 5262 | setResultTypeSet(resultTypes); |
michael@0 | 5263 | } |
michael@0 | 5264 | |
michael@0 | 5265 | public: |
michael@0 | 5266 | INSTRUCTION_HEADER(LambdaPar); |
michael@0 | 5267 | |
michael@0 | 5268 | static MLambdaPar *New(TempAllocator &alloc, MDefinition *cx, MLambda *lambda) { |
michael@0 | 5269 | return new(alloc) MLambdaPar(cx, lambda->scopeChain(), lambda->info().fun, |
michael@0 | 5270 | lambda->resultTypeSet(), lambda->info()); |
michael@0 | 5271 | } |
michael@0 | 5272 | |
michael@0 | 5273 | MDefinition *forkJoinContext() const { |
michael@0 | 5274 | return getOperand(0); |
michael@0 | 5275 | } |
michael@0 | 5276 | |
michael@0 | 5277 | MDefinition *scopeChain() const { |
michael@0 | 5278 | return getOperand(1); |
michael@0 | 5279 | } |
michael@0 | 5280 | |
michael@0 | 5281 | const LambdaFunctionInfo &info() const { |
michael@0 | 5282 | return info_; |
michael@0 | 5283 | } |
michael@0 | 5284 | }; |
michael@0 | 5285 | |
michael@0 | 5286 | // Determines the implicit |this| value for function calls. |
michael@0 | 5287 | class MImplicitThis |
michael@0 | 5288 | : public MUnaryInstruction, |
michael@0 | 5289 | public SingleObjectPolicy |
michael@0 | 5290 | { |
michael@0 | 5291 | MImplicitThis(MDefinition *callee) |
michael@0 | 5292 | : MUnaryInstruction(callee) |
michael@0 | 5293 | { |
michael@0 | 5294 | setResultType(MIRType_Value); |
michael@0 | 5295 | setMovable(); |
michael@0 | 5296 | } |
michael@0 | 5297 | |
michael@0 | 5298 | public: |
michael@0 | 5299 | INSTRUCTION_HEADER(ImplicitThis) |
michael@0 | 5300 | |
michael@0 | 5301 | static MImplicitThis *New(TempAllocator &alloc, MDefinition *callee) { |
michael@0 | 5302 | return new(alloc) MImplicitThis(callee); |
michael@0 | 5303 | } |
michael@0 | 5304 | |
michael@0 | 5305 | TypePolicy *typePolicy() { |
michael@0 | 5306 | return this; |
michael@0 | 5307 | } |
michael@0 | 5308 | MDefinition *callee() const { |
michael@0 | 5309 | return getOperand(0); |
michael@0 | 5310 | } |
michael@0 | 5311 | AliasSet getAliasSet() const { |
michael@0 | 5312 | return AliasSet::None(); |
michael@0 | 5313 | } |
michael@0 | 5314 | }; |
michael@0 | 5315 | |
michael@0 | 5316 | // Returns obj->slots. |
michael@0 | 5317 | class MSlots |
michael@0 | 5318 | : public MUnaryInstruction, |
michael@0 | 5319 | public SingleObjectPolicy |
michael@0 | 5320 | { |
michael@0 | 5321 | MSlots(MDefinition *object) |
michael@0 | 5322 | : MUnaryInstruction(object) |
michael@0 | 5323 | { |
michael@0 | 5324 | setResultType(MIRType_Slots); |
michael@0 | 5325 | setMovable(); |
michael@0 | 5326 | } |
michael@0 | 5327 | |
michael@0 | 5328 | public: |
michael@0 | 5329 | INSTRUCTION_HEADER(Slots) |
michael@0 | 5330 | |
michael@0 | 5331 | static MSlots *New(TempAllocator &alloc, MDefinition *object) { |
michael@0 | 5332 | return new(alloc) MSlots(object); |
michael@0 | 5333 | } |
michael@0 | 5334 | |
michael@0 | 5335 | TypePolicy *typePolicy() { |
michael@0 | 5336 | return this; |
michael@0 | 5337 | } |
michael@0 | 5338 | MDefinition *object() const { |
michael@0 | 5339 | return getOperand(0); |
michael@0 | 5340 | } |
michael@0 | 5341 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 5342 | return congruentIfOperandsEqual(ins); |
michael@0 | 5343 | } |
michael@0 | 5344 | AliasSet getAliasSet() const { |
michael@0 | 5345 | return AliasSet::Load(AliasSet::ObjectFields); |
michael@0 | 5346 | } |
michael@0 | 5347 | }; |
michael@0 | 5348 | |
michael@0 | 5349 | // Returns obj->elements. |
michael@0 | 5350 | class MElements |
michael@0 | 5351 | : public MUnaryInstruction, |
michael@0 | 5352 | public SingleObjectPolicy |
michael@0 | 5353 | { |
michael@0 | 5354 | MElements(MDefinition *object) |
michael@0 | 5355 | : MUnaryInstruction(object) |
michael@0 | 5356 | { |
michael@0 | 5357 | setResultType(MIRType_Elements); |
michael@0 | 5358 | setMovable(); |
michael@0 | 5359 | } |
michael@0 | 5360 | |
michael@0 | 5361 | public: |
michael@0 | 5362 | INSTRUCTION_HEADER(Elements) |
michael@0 | 5363 | |
michael@0 | 5364 | static MElements *New(TempAllocator &alloc, MDefinition *object) { |
michael@0 | 5365 | return new(alloc) MElements(object); |
michael@0 | 5366 | } |
michael@0 | 5367 | |
michael@0 | 5368 | TypePolicy *typePolicy() { |
michael@0 | 5369 | return this; |
michael@0 | 5370 | } |
michael@0 | 5371 | MDefinition *object() const { |
michael@0 | 5372 | return getOperand(0); |
michael@0 | 5373 | } |
michael@0 | 5374 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 5375 | return congruentIfOperandsEqual(ins); |
michael@0 | 5376 | } |
michael@0 | 5377 | AliasSet getAliasSet() const { |
michael@0 | 5378 | return AliasSet::Load(AliasSet::ObjectFields); |
michael@0 | 5379 | } |
michael@0 | 5380 | }; |
michael@0 | 5381 | |
michael@0 | 5382 | // A constant value for some object's array elements or typed array elements. |
michael@0 | 5383 | class MConstantElements : public MNullaryInstruction |
michael@0 | 5384 | { |
michael@0 | 5385 | void *value_; |
michael@0 | 5386 | |
michael@0 | 5387 | protected: |
michael@0 | 5388 | MConstantElements(void *v) |
michael@0 | 5389 | : value_(v) |
michael@0 | 5390 | { |
michael@0 | 5391 | setResultType(MIRType_Elements); |
michael@0 | 5392 | setMovable(); |
michael@0 | 5393 | } |
michael@0 | 5394 | |
michael@0 | 5395 | public: |
michael@0 | 5396 | INSTRUCTION_HEADER(ConstantElements) |
michael@0 | 5397 | static MConstantElements *New(TempAllocator &alloc, void *v) { |
michael@0 | 5398 | return new(alloc) MConstantElements(v); |
michael@0 | 5399 | } |
michael@0 | 5400 | |
michael@0 | 5401 | void *value() const { |
michael@0 | 5402 | return value_; |
michael@0 | 5403 | } |
michael@0 | 5404 | |
michael@0 | 5405 | void printOpcode(FILE *fp) const; |
michael@0 | 5406 | |
michael@0 | 5407 | HashNumber valueHash() const { |
michael@0 | 5408 | return (HashNumber)(size_t) value_; |
michael@0 | 5409 | } |
michael@0 | 5410 | |
michael@0 | 5411 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 5412 | return ins->isConstantElements() && ins->toConstantElements()->value() == value(); |
michael@0 | 5413 | } |
michael@0 | 5414 | |
michael@0 | 5415 | AliasSet getAliasSet() const { |
michael@0 | 5416 | return AliasSet::None(); |
michael@0 | 5417 | } |
michael@0 | 5418 | }; |
michael@0 | 5419 | |
michael@0 | 5420 | // Passes through an object's elements, after ensuring it is entirely doubles. |
michael@0 | 5421 | class MConvertElementsToDoubles |
michael@0 | 5422 | : public MUnaryInstruction |
michael@0 | 5423 | { |
michael@0 | 5424 | MConvertElementsToDoubles(MDefinition *elements) |
michael@0 | 5425 | : MUnaryInstruction(elements) |
michael@0 | 5426 | { |
michael@0 | 5427 | setGuard(); |
michael@0 | 5428 | setMovable(); |
michael@0 | 5429 | setResultType(MIRType_Elements); |
michael@0 | 5430 | } |
michael@0 | 5431 | |
michael@0 | 5432 | public: |
michael@0 | 5433 | INSTRUCTION_HEADER(ConvertElementsToDoubles) |
michael@0 | 5434 | |
michael@0 | 5435 | static MConvertElementsToDoubles *New(TempAllocator &alloc, MDefinition *elements) { |
michael@0 | 5436 | return new(alloc) MConvertElementsToDoubles(elements); |
michael@0 | 5437 | } |
michael@0 | 5438 | |
michael@0 | 5439 | MDefinition *elements() const { |
michael@0 | 5440 | return getOperand(0); |
michael@0 | 5441 | } |
michael@0 | 5442 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 5443 | return congruentIfOperandsEqual(ins); |
michael@0 | 5444 | } |
michael@0 | 5445 | AliasSet getAliasSet() const { |
michael@0 | 5446 | // This instruction can read and write to the elements' contents. |
michael@0 | 5447 | // However, it is alright to hoist this from loops which explicitly |
michael@0 | 5448 | // read or write to the elements: such reads and writes will use double |
michael@0 | 5449 | // values and can be reordered freely wrt this conversion, except that |
michael@0 | 5450 | // definite double loads must follow the conversion. The latter |
michael@0 | 5451 | // property is ensured by chaining this instruction with the elements |
michael@0 | 5452 | // themselves, in the same manner as MBoundsCheck. |
michael@0 | 5453 | return AliasSet::None(); |
michael@0 | 5454 | } |
michael@0 | 5455 | }; |
michael@0 | 5456 | |
michael@0 | 5457 | // If |elements| has the CONVERT_DOUBLE_ELEMENTS flag, convert value to |
michael@0 | 5458 | // double. Else return the original value. |
michael@0 | 5459 | class MMaybeToDoubleElement |
michael@0 | 5460 | : public MBinaryInstruction, |
michael@0 | 5461 | public IntPolicy<1> |
michael@0 | 5462 | { |
michael@0 | 5463 | MMaybeToDoubleElement(MDefinition *elements, MDefinition *value) |
michael@0 | 5464 | : MBinaryInstruction(elements, value) |
michael@0 | 5465 | { |
michael@0 | 5466 | JS_ASSERT(elements->type() == MIRType_Elements); |
michael@0 | 5467 | setMovable(); |
michael@0 | 5468 | setResultType(MIRType_Value); |
michael@0 | 5469 | } |
michael@0 | 5470 | |
michael@0 | 5471 | public: |
michael@0 | 5472 | INSTRUCTION_HEADER(MaybeToDoubleElement) |
michael@0 | 5473 | |
michael@0 | 5474 | static MMaybeToDoubleElement *New(TempAllocator &alloc, MDefinition *elements, |
michael@0 | 5475 | MDefinition *value) |
michael@0 | 5476 | { |
michael@0 | 5477 | return new(alloc) MMaybeToDoubleElement(elements, value); |
michael@0 | 5478 | } |
michael@0 | 5479 | |
michael@0 | 5480 | TypePolicy *typePolicy() { |
michael@0 | 5481 | return this; |
michael@0 | 5482 | } |
michael@0 | 5483 | |
michael@0 | 5484 | MDefinition *elements() const { |
michael@0 | 5485 | return getOperand(0); |
michael@0 | 5486 | } |
michael@0 | 5487 | MDefinition *value() const { |
michael@0 | 5488 | return getOperand(1); |
michael@0 | 5489 | } |
michael@0 | 5490 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 5491 | return congruentIfOperandsEqual(ins); |
michael@0 | 5492 | } |
michael@0 | 5493 | AliasSet getAliasSet() const { |
michael@0 | 5494 | return AliasSet::Load(AliasSet::ObjectFields); |
michael@0 | 5495 | } |
michael@0 | 5496 | }; |
michael@0 | 5497 | |
michael@0 | 5498 | // Load the initialized length from an elements header. |
michael@0 | 5499 | class MInitializedLength |
michael@0 | 5500 | : public MUnaryInstruction |
michael@0 | 5501 | { |
michael@0 | 5502 | MInitializedLength(MDefinition *elements) |
michael@0 | 5503 | : MUnaryInstruction(elements) |
michael@0 | 5504 | { |
michael@0 | 5505 | setResultType(MIRType_Int32); |
michael@0 | 5506 | setMovable(); |
michael@0 | 5507 | } |
michael@0 | 5508 | |
michael@0 | 5509 | public: |
michael@0 | 5510 | INSTRUCTION_HEADER(InitializedLength) |
michael@0 | 5511 | |
michael@0 | 5512 | static MInitializedLength *New(TempAllocator &alloc, MDefinition *elements) { |
michael@0 | 5513 | return new(alloc) MInitializedLength(elements); |
michael@0 | 5514 | } |
michael@0 | 5515 | |
michael@0 | 5516 | MDefinition *elements() const { |
michael@0 | 5517 | return getOperand(0); |
michael@0 | 5518 | } |
michael@0 | 5519 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 5520 | return congruentIfOperandsEqual(ins); |
michael@0 | 5521 | } |
michael@0 | 5522 | AliasSet getAliasSet() const { |
michael@0 | 5523 | return AliasSet::Load(AliasSet::ObjectFields); |
michael@0 | 5524 | } |
michael@0 | 5525 | |
michael@0 | 5526 | void computeRange(TempAllocator &alloc); |
michael@0 | 5527 | }; |
michael@0 | 5528 | |
michael@0 | 5529 | // Store to the initialized length in an elements header. Note the input is an |
michael@0 | 5530 | // *index*, one less than the desired length. |
michael@0 | 5531 | class MSetInitializedLength |
michael@0 | 5532 | : public MAryInstruction<2> |
michael@0 | 5533 | { |
michael@0 | 5534 | MSetInitializedLength(MDefinition *elements, MDefinition *index) { |
michael@0 | 5535 | setOperand(0, elements); |
michael@0 | 5536 | setOperand(1, index); |
michael@0 | 5537 | } |
michael@0 | 5538 | |
michael@0 | 5539 | public: |
michael@0 | 5540 | INSTRUCTION_HEADER(SetInitializedLength) |
michael@0 | 5541 | |
michael@0 | 5542 | static MSetInitializedLength *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index) { |
michael@0 | 5543 | return new(alloc) MSetInitializedLength(elements, index); |
michael@0 | 5544 | } |
michael@0 | 5545 | |
michael@0 | 5546 | MDefinition *elements() const { |
michael@0 | 5547 | return getOperand(0); |
michael@0 | 5548 | } |
michael@0 | 5549 | MDefinition *index() const { |
michael@0 | 5550 | return getOperand(1); |
michael@0 | 5551 | } |
michael@0 | 5552 | AliasSet getAliasSet() const { |
michael@0 | 5553 | return AliasSet::Store(AliasSet::ObjectFields); |
michael@0 | 5554 | } |
michael@0 | 5555 | }; |
michael@0 | 5556 | |
michael@0 | 5557 | // Load the array length from an elements header. |
michael@0 | 5558 | class MArrayLength |
michael@0 | 5559 | : public MUnaryInstruction |
michael@0 | 5560 | { |
michael@0 | 5561 | MArrayLength(MDefinition *elements) |
michael@0 | 5562 | : MUnaryInstruction(elements) |
michael@0 | 5563 | { |
michael@0 | 5564 | setResultType(MIRType_Int32); |
michael@0 | 5565 | setMovable(); |
michael@0 | 5566 | } |
michael@0 | 5567 | |
michael@0 | 5568 | public: |
michael@0 | 5569 | INSTRUCTION_HEADER(ArrayLength) |
michael@0 | 5570 | |
michael@0 | 5571 | static MArrayLength *New(TempAllocator &alloc, MDefinition *elements) { |
michael@0 | 5572 | return new(alloc) MArrayLength(elements); |
michael@0 | 5573 | } |
michael@0 | 5574 | |
michael@0 | 5575 | MDefinition *elements() const { |
michael@0 | 5576 | return getOperand(0); |
michael@0 | 5577 | } |
michael@0 | 5578 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 5579 | return congruentIfOperandsEqual(ins); |
michael@0 | 5580 | } |
michael@0 | 5581 | AliasSet getAliasSet() const { |
michael@0 | 5582 | return AliasSet::Load(AliasSet::ObjectFields); |
michael@0 | 5583 | } |
michael@0 | 5584 | |
michael@0 | 5585 | void computeRange(TempAllocator &alloc); |
michael@0 | 5586 | }; |
michael@0 | 5587 | |
michael@0 | 5588 | // Store to the length in an elements header. Note the input is an *index*, one |
michael@0 | 5589 | // less than the desired length. |
michael@0 | 5590 | class MSetArrayLength |
michael@0 | 5591 | : public MAryInstruction<2> |
michael@0 | 5592 | { |
michael@0 | 5593 | MSetArrayLength(MDefinition *elements, MDefinition *index) { |
michael@0 | 5594 | setOperand(0, elements); |
michael@0 | 5595 | setOperand(1, index); |
michael@0 | 5596 | } |
michael@0 | 5597 | |
michael@0 | 5598 | public: |
michael@0 | 5599 | INSTRUCTION_HEADER(SetArrayLength) |
michael@0 | 5600 | |
michael@0 | 5601 | static MSetArrayLength *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index) { |
michael@0 | 5602 | return new(alloc) MSetArrayLength(elements, index); |
michael@0 | 5603 | } |
michael@0 | 5604 | |
michael@0 | 5605 | MDefinition *elements() const { |
michael@0 | 5606 | return getOperand(0); |
michael@0 | 5607 | } |
michael@0 | 5608 | MDefinition *index() const { |
michael@0 | 5609 | return getOperand(1); |
michael@0 | 5610 | } |
michael@0 | 5611 | AliasSet getAliasSet() const { |
michael@0 | 5612 | return AliasSet::Store(AliasSet::ObjectFields); |
michael@0 | 5613 | } |
michael@0 | 5614 | }; |
michael@0 | 5615 | |
michael@0 | 5616 | // Read the length of a typed array. |
michael@0 | 5617 | class MTypedArrayLength |
michael@0 | 5618 | : public MUnaryInstruction, |
michael@0 | 5619 | public SingleObjectPolicy |
michael@0 | 5620 | { |
michael@0 | 5621 | MTypedArrayLength(MDefinition *obj) |
michael@0 | 5622 | : MUnaryInstruction(obj) |
michael@0 | 5623 | { |
michael@0 | 5624 | setResultType(MIRType_Int32); |
michael@0 | 5625 | setMovable(); |
michael@0 | 5626 | } |
michael@0 | 5627 | |
michael@0 | 5628 | public: |
michael@0 | 5629 | INSTRUCTION_HEADER(TypedArrayLength) |
michael@0 | 5630 | |
michael@0 | 5631 | static MTypedArrayLength *New(TempAllocator &alloc, MDefinition *obj) { |
michael@0 | 5632 | return new(alloc) MTypedArrayLength(obj); |
michael@0 | 5633 | } |
michael@0 | 5634 | |
michael@0 | 5635 | TypePolicy *typePolicy() { |
michael@0 | 5636 | return this; |
michael@0 | 5637 | } |
michael@0 | 5638 | MDefinition *object() const { |
michael@0 | 5639 | return getOperand(0); |
michael@0 | 5640 | } |
michael@0 | 5641 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 5642 | return congruentIfOperandsEqual(ins); |
michael@0 | 5643 | } |
michael@0 | 5644 | AliasSet getAliasSet() const { |
michael@0 | 5645 | return AliasSet::Load(AliasSet::TypedArrayLength); |
michael@0 | 5646 | } |
michael@0 | 5647 | |
michael@0 | 5648 | void computeRange(TempAllocator &alloc); |
michael@0 | 5649 | }; |
michael@0 | 5650 | |
michael@0 | 5651 | // Load a typed array's elements vector. |
michael@0 | 5652 | class MTypedArrayElements |
michael@0 | 5653 | : public MUnaryInstruction, |
michael@0 | 5654 | public SingleObjectPolicy |
michael@0 | 5655 | { |
michael@0 | 5656 | MTypedArrayElements(MDefinition *object) |
michael@0 | 5657 | : MUnaryInstruction(object) |
michael@0 | 5658 | { |
michael@0 | 5659 | setResultType(MIRType_Elements); |
michael@0 | 5660 | setMovable(); |
michael@0 | 5661 | } |
michael@0 | 5662 | |
michael@0 | 5663 | public: |
michael@0 | 5664 | INSTRUCTION_HEADER(TypedArrayElements) |
michael@0 | 5665 | |
michael@0 | 5666 | static MTypedArrayElements *New(TempAllocator &alloc, MDefinition *object) { |
michael@0 | 5667 | return new(alloc) MTypedArrayElements(object); |
michael@0 | 5668 | } |
michael@0 | 5669 | |
michael@0 | 5670 | TypePolicy *typePolicy() { |
michael@0 | 5671 | return this; |
michael@0 | 5672 | } |
michael@0 | 5673 | MDefinition *object() const { |
michael@0 | 5674 | return getOperand(0); |
michael@0 | 5675 | } |
michael@0 | 5676 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 5677 | return congruentIfOperandsEqual(ins); |
michael@0 | 5678 | } |
michael@0 | 5679 | AliasSet getAliasSet() const { |
michael@0 | 5680 | return AliasSet::Load(AliasSet::ObjectFields); |
michael@0 | 5681 | } |
michael@0 | 5682 | }; |
michael@0 | 5683 | |
michael@0 | 5684 | // Checks whether a typed object is neutered. |
michael@0 | 5685 | class MNeuterCheck |
michael@0 | 5686 | : public MUnaryInstruction |
michael@0 | 5687 | { |
michael@0 | 5688 | private: |
michael@0 | 5689 | MNeuterCheck(MDefinition *object) |
michael@0 | 5690 | : MUnaryInstruction(object) |
michael@0 | 5691 | { |
michael@0 | 5692 | JS_ASSERT(object->type() == MIRType_Object); |
michael@0 | 5693 | setResultType(MIRType_Object); |
michael@0 | 5694 | setResultTypeSet(object->resultTypeSet()); |
michael@0 | 5695 | setGuard(); |
michael@0 | 5696 | setMovable(); |
michael@0 | 5697 | } |
michael@0 | 5698 | |
michael@0 | 5699 | public: |
michael@0 | 5700 | INSTRUCTION_HEADER(NeuterCheck) |
michael@0 | 5701 | |
michael@0 | 5702 | static MNeuterCheck *New(TempAllocator &alloc, MDefinition *object) { |
michael@0 | 5703 | return new(alloc) MNeuterCheck(object); |
michael@0 | 5704 | } |
michael@0 | 5705 | |
michael@0 | 5706 | MDefinition *object() const { |
michael@0 | 5707 | return getOperand(0); |
michael@0 | 5708 | } |
michael@0 | 5709 | |
michael@0 | 5710 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 5711 | return congruentIfOperandsEqual(ins); |
michael@0 | 5712 | } |
michael@0 | 5713 | |
michael@0 | 5714 | AliasSet getAliasSet() const { |
michael@0 | 5715 | return AliasSet::Load(AliasSet::ObjectFields); |
michael@0 | 5716 | } |
michael@0 | 5717 | }; |
michael@0 | 5718 | |
michael@0 | 5719 | // Load a binary data object's "elements", which is just its opaque |
michael@0 | 5720 | // binary data space. Eventually this should probably be |
michael@0 | 5721 | // unified with `MTypedArrayElements`. |
michael@0 | 5722 | class MTypedObjectElements |
michael@0 | 5723 | : public MUnaryInstruction, |
michael@0 | 5724 | public SingleObjectPolicy |
michael@0 | 5725 | { |
michael@0 | 5726 | private: |
michael@0 | 5727 | MTypedObjectElements(MDefinition *object) |
michael@0 | 5728 | : MUnaryInstruction(object) |
michael@0 | 5729 | { |
michael@0 | 5730 | setResultType(MIRType_Elements); |
michael@0 | 5731 | setMovable(); |
michael@0 | 5732 | } |
michael@0 | 5733 | |
michael@0 | 5734 | public: |
michael@0 | 5735 | INSTRUCTION_HEADER(TypedObjectElements) |
michael@0 | 5736 | |
michael@0 | 5737 | static MTypedObjectElements *New(TempAllocator &alloc, MDefinition *object) { |
michael@0 | 5738 | return new(alloc) MTypedObjectElements(object); |
michael@0 | 5739 | } |
michael@0 | 5740 | |
michael@0 | 5741 | TypePolicy *typePolicy() { |
michael@0 | 5742 | return this; |
michael@0 | 5743 | } |
michael@0 | 5744 | MDefinition *object() const { |
michael@0 | 5745 | return getOperand(0); |
michael@0 | 5746 | } |
michael@0 | 5747 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 5748 | return congruentIfOperandsEqual(ins); |
michael@0 | 5749 | } |
michael@0 | 5750 | AliasSet getAliasSet() const { |
michael@0 | 5751 | return AliasSet::Load(AliasSet::ObjectFields); |
michael@0 | 5752 | } |
michael@0 | 5753 | }; |
michael@0 | 5754 | |
michael@0 | 5755 | // Inlined version of the js::SetTypedObjectOffset() intrinsic. |
michael@0 | 5756 | class MSetTypedObjectOffset |
michael@0 | 5757 | : public MBinaryInstruction |
michael@0 | 5758 | { |
michael@0 | 5759 | private: |
michael@0 | 5760 | MSetTypedObjectOffset(MDefinition *object, MDefinition *offset) |
michael@0 | 5761 | : MBinaryInstruction(object, offset) |
michael@0 | 5762 | { |
michael@0 | 5763 | JS_ASSERT(object->type() == MIRType_Object); |
michael@0 | 5764 | JS_ASSERT(offset->type() == MIRType_Int32); |
michael@0 | 5765 | setResultType(MIRType_None); |
michael@0 | 5766 | } |
michael@0 | 5767 | |
michael@0 | 5768 | public: |
michael@0 | 5769 | INSTRUCTION_HEADER(SetTypedObjectOffset) |
michael@0 | 5770 | |
michael@0 | 5771 | static MSetTypedObjectOffset *New(TempAllocator &alloc, |
michael@0 | 5772 | MDefinition *object, |
michael@0 | 5773 | MDefinition *offset) |
michael@0 | 5774 | { |
michael@0 | 5775 | return new(alloc) MSetTypedObjectOffset(object, offset); |
michael@0 | 5776 | } |
michael@0 | 5777 | |
michael@0 | 5778 | MDefinition *object() const { |
michael@0 | 5779 | return getOperand(0); |
michael@0 | 5780 | } |
michael@0 | 5781 | |
michael@0 | 5782 | MDefinition *offset() const { |
michael@0 | 5783 | return getOperand(1); |
michael@0 | 5784 | } |
michael@0 | 5785 | |
michael@0 | 5786 | AliasSet getAliasSet() const { |
michael@0 | 5787 | // This affects the result of MTypedObjectElements, |
michael@0 | 5788 | // which is described as a load of ObjectFields. |
michael@0 | 5789 | return AliasSet::Store(AliasSet::ObjectFields); |
michael@0 | 5790 | } |
michael@0 | 5791 | }; |
michael@0 | 5792 | |
michael@0 | 5793 | // Perform !-operation |
michael@0 | 5794 | class MNot |
michael@0 | 5795 | : public MUnaryInstruction, |
michael@0 | 5796 | public TestPolicy |
michael@0 | 5797 | { |
michael@0 | 5798 | bool operandMightEmulateUndefined_; |
michael@0 | 5799 | bool operandIsNeverNaN_; |
michael@0 | 5800 | |
michael@0 | 5801 | public: |
michael@0 | 5802 | MNot(MDefinition *input) |
michael@0 | 5803 | : MUnaryInstruction(input), |
michael@0 | 5804 | operandMightEmulateUndefined_(true), |
michael@0 | 5805 | operandIsNeverNaN_(false) |
michael@0 | 5806 | { |
michael@0 | 5807 | setResultType(MIRType_Boolean); |
michael@0 | 5808 | setMovable(); |
michael@0 | 5809 | } |
michael@0 | 5810 | |
michael@0 | 5811 | static MNot *New(TempAllocator &alloc, MDefinition *elements) { |
michael@0 | 5812 | return new(alloc) MNot(elements); |
michael@0 | 5813 | } |
michael@0 | 5814 | static MNot *NewAsmJS(TempAllocator &alloc, MDefinition *elements) { |
michael@0 | 5815 | MNot *ins = new(alloc) MNot(elements); |
michael@0 | 5816 | ins->setResultType(MIRType_Int32); |
michael@0 | 5817 | return ins; |
michael@0 | 5818 | } |
michael@0 | 5819 | |
michael@0 | 5820 | INSTRUCTION_HEADER(Not); |
michael@0 | 5821 | |
michael@0 | 5822 | void infer(); |
michael@0 | 5823 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 5824 | |
michael@0 | 5825 | void markOperandCantEmulateUndefined() { |
michael@0 | 5826 | operandMightEmulateUndefined_ = false; |
michael@0 | 5827 | } |
michael@0 | 5828 | bool operandMightEmulateUndefined() const { |
michael@0 | 5829 | return operandMightEmulateUndefined_; |
michael@0 | 5830 | } |
michael@0 | 5831 | bool operandIsNeverNaN() const { |
michael@0 | 5832 | return operandIsNeverNaN_; |
michael@0 | 5833 | } |
michael@0 | 5834 | |
michael@0 | 5835 | MDefinition *operand() const { |
michael@0 | 5836 | return getOperand(0); |
michael@0 | 5837 | } |
michael@0 | 5838 | |
michael@0 | 5839 | virtual AliasSet getAliasSet() const { |
michael@0 | 5840 | return AliasSet::None(); |
michael@0 | 5841 | } |
michael@0 | 5842 | TypePolicy *typePolicy() { |
michael@0 | 5843 | return this; |
michael@0 | 5844 | } |
michael@0 | 5845 | void collectRangeInfoPreTrunc(); |
michael@0 | 5846 | |
michael@0 | 5847 | void trySpecializeFloat32(TempAllocator &alloc); |
michael@0 | 5848 | bool isFloat32Commutative() const { return true; } |
michael@0 | 5849 | #ifdef DEBUG |
michael@0 | 5850 | bool isConsistentFloat32Use(MUse *use) const { |
michael@0 | 5851 | return true; |
michael@0 | 5852 | } |
michael@0 | 5853 | #endif |
michael@0 | 5854 | }; |
michael@0 | 5855 | |
michael@0 | 5856 | // Bailout if index + minimum < 0 or index + maximum >= length. The length used |
michael@0 | 5857 | // in a bounds check must not be negative, or the wrong result may be computed |
michael@0 | 5858 | // (unsigned comparisons may be used). |
michael@0 | 5859 | class MBoundsCheck |
michael@0 | 5860 | : public MBinaryInstruction |
michael@0 | 5861 | { |
michael@0 | 5862 | // Range over which to perform the bounds check, may be modified by GVN. |
michael@0 | 5863 | int32_t minimum_; |
michael@0 | 5864 | int32_t maximum_; |
michael@0 | 5865 | |
michael@0 | 5866 | MBoundsCheck(MDefinition *index, MDefinition *length) |
michael@0 | 5867 | : MBinaryInstruction(index, length), minimum_(0), maximum_(0) |
michael@0 | 5868 | { |
michael@0 | 5869 | setGuard(); |
michael@0 | 5870 | setMovable(); |
michael@0 | 5871 | JS_ASSERT(index->type() == MIRType_Int32); |
michael@0 | 5872 | JS_ASSERT(length->type() == MIRType_Int32); |
michael@0 | 5873 | |
michael@0 | 5874 | // Returns the checked index. |
michael@0 | 5875 | setResultType(MIRType_Int32); |
michael@0 | 5876 | } |
michael@0 | 5877 | |
michael@0 | 5878 | public: |
michael@0 | 5879 | INSTRUCTION_HEADER(BoundsCheck) |
michael@0 | 5880 | |
michael@0 | 5881 | static MBoundsCheck *New(TempAllocator &alloc, MDefinition *index, MDefinition *length) { |
michael@0 | 5882 | return new(alloc) MBoundsCheck(index, length); |
michael@0 | 5883 | } |
michael@0 | 5884 | MDefinition *index() const { |
michael@0 | 5885 | return getOperand(0); |
michael@0 | 5886 | } |
michael@0 | 5887 | MDefinition *length() const { |
michael@0 | 5888 | return getOperand(1); |
michael@0 | 5889 | } |
michael@0 | 5890 | int32_t minimum() const { |
michael@0 | 5891 | return minimum_; |
michael@0 | 5892 | } |
michael@0 | 5893 | void setMinimum(int32_t n) { |
michael@0 | 5894 | minimum_ = n; |
michael@0 | 5895 | } |
michael@0 | 5896 | int32_t maximum() const { |
michael@0 | 5897 | return maximum_; |
michael@0 | 5898 | } |
michael@0 | 5899 | void setMaximum(int32_t n) { |
michael@0 | 5900 | maximum_ = n; |
michael@0 | 5901 | } |
michael@0 | 5902 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 5903 | if (!ins->isBoundsCheck()) |
michael@0 | 5904 | return false; |
michael@0 | 5905 | const MBoundsCheck *other = ins->toBoundsCheck(); |
michael@0 | 5906 | if (minimum() != other->minimum() || maximum() != other->maximum()) |
michael@0 | 5907 | return false; |
michael@0 | 5908 | return congruentIfOperandsEqual(other); |
michael@0 | 5909 | } |
michael@0 | 5910 | virtual AliasSet getAliasSet() const { |
michael@0 | 5911 | return AliasSet::None(); |
michael@0 | 5912 | } |
michael@0 | 5913 | void computeRange(TempAllocator &alloc); |
michael@0 | 5914 | }; |
michael@0 | 5915 | |
michael@0 | 5916 | // Bailout if index < minimum. |
michael@0 | 5917 | class MBoundsCheckLower |
michael@0 | 5918 | : public MUnaryInstruction |
michael@0 | 5919 | { |
michael@0 | 5920 | int32_t minimum_; |
michael@0 | 5921 | bool fallible_; |
michael@0 | 5922 | |
michael@0 | 5923 | MBoundsCheckLower(MDefinition *index) |
michael@0 | 5924 | : MUnaryInstruction(index), minimum_(0), fallible_(true) |
michael@0 | 5925 | { |
michael@0 | 5926 | setGuard(); |
michael@0 | 5927 | setMovable(); |
michael@0 | 5928 | JS_ASSERT(index->type() == MIRType_Int32); |
michael@0 | 5929 | } |
michael@0 | 5930 | |
michael@0 | 5931 | public: |
michael@0 | 5932 | INSTRUCTION_HEADER(BoundsCheckLower) |
michael@0 | 5933 | |
michael@0 | 5934 | static MBoundsCheckLower *New(TempAllocator &alloc, MDefinition *index) { |
michael@0 | 5935 | return new(alloc) MBoundsCheckLower(index); |
michael@0 | 5936 | } |
michael@0 | 5937 | |
michael@0 | 5938 | MDefinition *index() const { |
michael@0 | 5939 | return getOperand(0); |
michael@0 | 5940 | } |
michael@0 | 5941 | int32_t minimum() const { |
michael@0 | 5942 | return minimum_; |
michael@0 | 5943 | } |
michael@0 | 5944 | void setMinimum(int32_t n) { |
michael@0 | 5945 | minimum_ = n; |
michael@0 | 5946 | } |
michael@0 | 5947 | AliasSet getAliasSet() const { |
michael@0 | 5948 | return AliasSet::None(); |
michael@0 | 5949 | } |
michael@0 | 5950 | bool fallible() const { |
michael@0 | 5951 | return fallible_; |
michael@0 | 5952 | } |
michael@0 | 5953 | void collectRangeInfoPreTrunc(); |
michael@0 | 5954 | }; |
michael@0 | 5955 | |
michael@0 | 5956 | // Load a value from a dense array's element vector and does a hole check if the |
michael@0 | 5957 | // array is not known to be packed. |
michael@0 | 5958 | class MLoadElement |
michael@0 | 5959 | : public MBinaryInstruction, |
michael@0 | 5960 | public SingleObjectPolicy |
michael@0 | 5961 | { |
michael@0 | 5962 | bool needsHoleCheck_; |
michael@0 | 5963 | bool loadDoubles_; |
michael@0 | 5964 | |
michael@0 | 5965 | MLoadElement(MDefinition *elements, MDefinition *index, bool needsHoleCheck, bool loadDoubles) |
michael@0 | 5966 | : MBinaryInstruction(elements, index), |
michael@0 | 5967 | needsHoleCheck_(needsHoleCheck), |
michael@0 | 5968 | loadDoubles_(loadDoubles) |
michael@0 | 5969 | { |
michael@0 | 5970 | if (needsHoleCheck) { |
michael@0 | 5971 | // Uses may be optimized away based on this instruction's result |
michael@0 | 5972 | // type. This means it's invalid to DCE this instruction, as we |
michael@0 | 5973 | // have to invalidate when we read a hole. |
michael@0 | 5974 | setGuard(); |
michael@0 | 5975 | } |
michael@0 | 5976 | setResultType(MIRType_Value); |
michael@0 | 5977 | setMovable(); |
michael@0 | 5978 | JS_ASSERT(elements->type() == MIRType_Elements); |
michael@0 | 5979 | JS_ASSERT(index->type() == MIRType_Int32); |
michael@0 | 5980 | } |
michael@0 | 5981 | |
michael@0 | 5982 | public: |
michael@0 | 5983 | INSTRUCTION_HEADER(LoadElement) |
michael@0 | 5984 | |
michael@0 | 5985 | static MLoadElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, |
michael@0 | 5986 | bool needsHoleCheck, bool loadDoubles) { |
michael@0 | 5987 | return new(alloc) MLoadElement(elements, index, needsHoleCheck, loadDoubles); |
michael@0 | 5988 | } |
michael@0 | 5989 | |
michael@0 | 5990 | TypePolicy *typePolicy() { |
michael@0 | 5991 | return this; |
michael@0 | 5992 | } |
michael@0 | 5993 | MDefinition *elements() const { |
michael@0 | 5994 | return getOperand(0); |
michael@0 | 5995 | } |
michael@0 | 5996 | MDefinition *index() const { |
michael@0 | 5997 | return getOperand(1); |
michael@0 | 5998 | } |
michael@0 | 5999 | bool needsHoleCheck() const { |
michael@0 | 6000 | return needsHoleCheck_; |
michael@0 | 6001 | } |
michael@0 | 6002 | bool loadDoubles() const { |
michael@0 | 6003 | return loadDoubles_; |
michael@0 | 6004 | } |
michael@0 | 6005 | bool fallible() const { |
michael@0 | 6006 | return needsHoleCheck(); |
michael@0 | 6007 | } |
michael@0 | 6008 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 6009 | if (!ins->isLoadElement()) |
michael@0 | 6010 | return false; |
michael@0 | 6011 | const MLoadElement *other = ins->toLoadElement(); |
michael@0 | 6012 | if (needsHoleCheck() != other->needsHoleCheck()) |
michael@0 | 6013 | return false; |
michael@0 | 6014 | if (loadDoubles() != other->loadDoubles()) |
michael@0 | 6015 | return false; |
michael@0 | 6016 | return congruentIfOperandsEqual(other); |
michael@0 | 6017 | } |
michael@0 | 6018 | AliasSet getAliasSet() const { |
michael@0 | 6019 | return AliasSet::Load(AliasSet::Element); |
michael@0 | 6020 | } |
michael@0 | 6021 | }; |
michael@0 | 6022 | |
michael@0 | 6023 | // Load a value from a dense array's element vector. If the index is |
michael@0 | 6024 | // out-of-bounds, or the indexed slot has a hole, undefined is returned |
michael@0 | 6025 | // instead. |
michael@0 | 6026 | class MLoadElementHole |
michael@0 | 6027 | : public MTernaryInstruction, |
michael@0 | 6028 | public SingleObjectPolicy |
michael@0 | 6029 | { |
michael@0 | 6030 | bool needsNegativeIntCheck_; |
michael@0 | 6031 | bool needsHoleCheck_; |
michael@0 | 6032 | |
michael@0 | 6033 | MLoadElementHole(MDefinition *elements, MDefinition *index, MDefinition *initLength, bool needsHoleCheck) |
michael@0 | 6034 | : MTernaryInstruction(elements, index, initLength), |
michael@0 | 6035 | needsNegativeIntCheck_(true), |
michael@0 | 6036 | needsHoleCheck_(needsHoleCheck) |
michael@0 | 6037 | { |
michael@0 | 6038 | setResultType(MIRType_Value); |
michael@0 | 6039 | setMovable(); |
michael@0 | 6040 | JS_ASSERT(elements->type() == MIRType_Elements); |
michael@0 | 6041 | JS_ASSERT(index->type() == MIRType_Int32); |
michael@0 | 6042 | JS_ASSERT(initLength->type() == MIRType_Int32); |
michael@0 | 6043 | } |
michael@0 | 6044 | |
michael@0 | 6045 | public: |
michael@0 | 6046 | INSTRUCTION_HEADER(LoadElementHole) |
michael@0 | 6047 | |
michael@0 | 6048 | static MLoadElementHole *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, |
michael@0 | 6049 | MDefinition *initLength, bool needsHoleCheck) { |
michael@0 | 6050 | return new(alloc) MLoadElementHole(elements, index, initLength, needsHoleCheck); |
michael@0 | 6051 | } |
michael@0 | 6052 | |
michael@0 | 6053 | TypePolicy *typePolicy() { |
michael@0 | 6054 | return this; |
michael@0 | 6055 | } |
michael@0 | 6056 | MDefinition *elements() const { |
michael@0 | 6057 | return getOperand(0); |
michael@0 | 6058 | } |
michael@0 | 6059 | MDefinition *index() const { |
michael@0 | 6060 | return getOperand(1); |
michael@0 | 6061 | } |
michael@0 | 6062 | MDefinition *initLength() const { |
michael@0 | 6063 | return getOperand(2); |
michael@0 | 6064 | } |
michael@0 | 6065 | bool needsNegativeIntCheck() const { |
michael@0 | 6066 | return needsNegativeIntCheck_; |
michael@0 | 6067 | } |
michael@0 | 6068 | bool needsHoleCheck() const { |
michael@0 | 6069 | return needsHoleCheck_; |
michael@0 | 6070 | } |
michael@0 | 6071 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 6072 | if (!ins->isLoadElementHole()) |
michael@0 | 6073 | return false; |
michael@0 | 6074 | const MLoadElementHole *other = ins->toLoadElementHole(); |
michael@0 | 6075 | if (needsHoleCheck() != other->needsHoleCheck()) |
michael@0 | 6076 | return false; |
michael@0 | 6077 | if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) |
michael@0 | 6078 | return false; |
michael@0 | 6079 | return congruentIfOperandsEqual(other); |
michael@0 | 6080 | } |
michael@0 | 6081 | AliasSet getAliasSet() const { |
michael@0 | 6082 | return AliasSet::Load(AliasSet::Element); |
michael@0 | 6083 | } |
michael@0 | 6084 | void collectRangeInfoPreTrunc(); |
michael@0 | 6085 | }; |
michael@0 | 6086 | |
michael@0 | 6087 | class MStoreElementCommon |
michael@0 | 6088 | { |
michael@0 | 6089 | bool needsBarrier_; |
michael@0 | 6090 | MIRType elementType_; |
michael@0 | 6091 | bool racy_; // if true, exempted from normal data race req. during par. exec. |
michael@0 | 6092 | |
michael@0 | 6093 | protected: |
michael@0 | 6094 | MStoreElementCommon() |
michael@0 | 6095 | : needsBarrier_(false), |
michael@0 | 6096 | elementType_(MIRType_Value), |
michael@0 | 6097 | racy_(false) |
michael@0 | 6098 | { } |
michael@0 | 6099 | |
michael@0 | 6100 | public: |
michael@0 | 6101 | MIRType elementType() const { |
michael@0 | 6102 | return elementType_; |
michael@0 | 6103 | } |
michael@0 | 6104 | void setElementType(MIRType elementType) { |
michael@0 | 6105 | JS_ASSERT(elementType != MIRType_None); |
michael@0 | 6106 | elementType_ = elementType; |
michael@0 | 6107 | } |
michael@0 | 6108 | bool needsBarrier() const { |
michael@0 | 6109 | return needsBarrier_; |
michael@0 | 6110 | } |
michael@0 | 6111 | void setNeedsBarrier() { |
michael@0 | 6112 | needsBarrier_ = true; |
michael@0 | 6113 | } |
michael@0 | 6114 | bool racy() const { |
michael@0 | 6115 | return racy_; |
michael@0 | 6116 | } |
michael@0 | 6117 | void setRacy() { |
michael@0 | 6118 | racy_ = true; |
michael@0 | 6119 | } |
michael@0 | 6120 | }; |
michael@0 | 6121 | |
michael@0 | 6122 | // Store a value to a dense array slots vector. |
michael@0 | 6123 | class MStoreElement |
michael@0 | 6124 | : public MAryInstruction<3>, |
michael@0 | 6125 | public MStoreElementCommon, |
michael@0 | 6126 | public MixPolicy<SingleObjectPolicy, NoFloatPolicy<2> > |
michael@0 | 6127 | { |
michael@0 | 6128 | bool needsHoleCheck_; |
michael@0 | 6129 | |
michael@0 | 6130 | MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value, bool needsHoleCheck) { |
michael@0 | 6131 | setOperand(0, elements); |
michael@0 | 6132 | setOperand(1, index); |
michael@0 | 6133 | setOperand(2, value); |
michael@0 | 6134 | needsHoleCheck_ = needsHoleCheck; |
michael@0 | 6135 | JS_ASSERT(elements->type() == MIRType_Elements); |
michael@0 | 6136 | JS_ASSERT(index->type() == MIRType_Int32); |
michael@0 | 6137 | } |
michael@0 | 6138 | |
michael@0 | 6139 | public: |
michael@0 | 6140 | INSTRUCTION_HEADER(StoreElement) |
michael@0 | 6141 | |
michael@0 | 6142 | static MStoreElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, |
michael@0 | 6143 | MDefinition *value, bool needsHoleCheck) { |
michael@0 | 6144 | return new(alloc) MStoreElement(elements, index, value, needsHoleCheck); |
michael@0 | 6145 | } |
michael@0 | 6146 | MDefinition *elements() const { |
michael@0 | 6147 | return getOperand(0); |
michael@0 | 6148 | } |
michael@0 | 6149 | MDefinition *index() const { |
michael@0 | 6150 | return getOperand(1); |
michael@0 | 6151 | } |
michael@0 | 6152 | MDefinition *value() const { |
michael@0 | 6153 | return getOperand(2); |
michael@0 | 6154 | } |
michael@0 | 6155 | TypePolicy *typePolicy() { |
michael@0 | 6156 | return this; |
michael@0 | 6157 | } |
michael@0 | 6158 | AliasSet getAliasSet() const { |
michael@0 | 6159 | return AliasSet::Store(AliasSet::Element); |
michael@0 | 6160 | } |
michael@0 | 6161 | bool needsHoleCheck() const { |
michael@0 | 6162 | return needsHoleCheck_; |
michael@0 | 6163 | } |
michael@0 | 6164 | bool fallible() const { |
michael@0 | 6165 | return needsHoleCheck(); |
michael@0 | 6166 | } |
michael@0 | 6167 | }; |
michael@0 | 6168 | |
michael@0 | 6169 | // Like MStoreElement, but supports indexes >= initialized length. The downside |
michael@0 | 6170 | // is that we cannot hoist the elements vector and bounds check, since this |
michael@0 | 6171 | // instruction may update the (initialized) length and reallocate the elements |
michael@0 | 6172 | // vector. |
michael@0 | 6173 | class MStoreElementHole |
michael@0 | 6174 | : public MAryInstruction<4>, |
michael@0 | 6175 | public MStoreElementCommon, |
michael@0 | 6176 | public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> > |
michael@0 | 6177 | { |
michael@0 | 6178 | MStoreElementHole(MDefinition *object, MDefinition *elements, |
michael@0 | 6179 | MDefinition *index, MDefinition *value) { |
michael@0 | 6180 | setOperand(0, object); |
michael@0 | 6181 | setOperand(1, elements); |
michael@0 | 6182 | setOperand(2, index); |
michael@0 | 6183 | setOperand(3, value); |
michael@0 | 6184 | JS_ASSERT(elements->type() == MIRType_Elements); |
michael@0 | 6185 | JS_ASSERT(index->type() == MIRType_Int32); |
michael@0 | 6186 | } |
michael@0 | 6187 | |
michael@0 | 6188 | public: |
michael@0 | 6189 | INSTRUCTION_HEADER(StoreElementHole) |
michael@0 | 6190 | |
michael@0 | 6191 | static MStoreElementHole *New(TempAllocator &alloc, MDefinition *object, MDefinition *elements, |
michael@0 | 6192 | MDefinition *index, MDefinition *value) { |
michael@0 | 6193 | return new(alloc) MStoreElementHole(object, elements, index, value); |
michael@0 | 6194 | } |
michael@0 | 6195 | |
michael@0 | 6196 | MDefinition *object() const { |
michael@0 | 6197 | return getOperand(0); |
michael@0 | 6198 | } |
michael@0 | 6199 | MDefinition *elements() const { |
michael@0 | 6200 | return getOperand(1); |
michael@0 | 6201 | } |
michael@0 | 6202 | MDefinition *index() const { |
michael@0 | 6203 | return getOperand(2); |
michael@0 | 6204 | } |
michael@0 | 6205 | MDefinition *value() const { |
michael@0 | 6206 | return getOperand(3); |
michael@0 | 6207 | } |
michael@0 | 6208 | TypePolicy *typePolicy() { |
michael@0 | 6209 | return this; |
michael@0 | 6210 | } |
michael@0 | 6211 | AliasSet getAliasSet() const { |
michael@0 | 6212 | // StoreElementHole can update the initialized length, the array length |
michael@0 | 6213 | // or reallocate obj->elements. |
michael@0 | 6214 | return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields); |
michael@0 | 6215 | } |
michael@0 | 6216 | }; |
michael@0 | 6217 | |
michael@0 | 6218 | // Array.prototype.pop or Array.prototype.shift on a dense array. |
michael@0 | 6219 | class MArrayPopShift |
michael@0 | 6220 | : public MUnaryInstruction, |
michael@0 | 6221 | public SingleObjectPolicy |
michael@0 | 6222 | { |
michael@0 | 6223 | public: |
michael@0 | 6224 | enum Mode { |
michael@0 | 6225 | Pop, |
michael@0 | 6226 | Shift |
michael@0 | 6227 | }; |
michael@0 | 6228 | |
michael@0 | 6229 | private: |
michael@0 | 6230 | Mode mode_; |
michael@0 | 6231 | bool needsHoleCheck_; |
michael@0 | 6232 | bool maybeUndefined_; |
michael@0 | 6233 | |
michael@0 | 6234 | MArrayPopShift(MDefinition *object, Mode mode, bool needsHoleCheck, bool maybeUndefined) |
michael@0 | 6235 | : MUnaryInstruction(object), mode_(mode), needsHoleCheck_(needsHoleCheck), |
michael@0 | 6236 | maybeUndefined_(maybeUndefined) |
michael@0 | 6237 | { } |
michael@0 | 6238 | |
michael@0 | 6239 | public: |
michael@0 | 6240 | INSTRUCTION_HEADER(ArrayPopShift) |
michael@0 | 6241 | |
michael@0 | 6242 | static MArrayPopShift *New(TempAllocator &alloc, MDefinition *object, Mode mode, |
michael@0 | 6243 | bool needsHoleCheck, bool maybeUndefined) |
michael@0 | 6244 | { |
michael@0 | 6245 | return new(alloc) MArrayPopShift(object, mode, needsHoleCheck, maybeUndefined); |
michael@0 | 6246 | } |
michael@0 | 6247 | |
michael@0 | 6248 | MDefinition *object() const { |
michael@0 | 6249 | return getOperand(0); |
michael@0 | 6250 | } |
michael@0 | 6251 | bool needsHoleCheck() const { |
michael@0 | 6252 | return needsHoleCheck_; |
michael@0 | 6253 | } |
michael@0 | 6254 | bool maybeUndefined() const { |
michael@0 | 6255 | return maybeUndefined_; |
michael@0 | 6256 | } |
michael@0 | 6257 | bool mode() const { |
michael@0 | 6258 | return mode_; |
michael@0 | 6259 | } |
michael@0 | 6260 | TypePolicy *typePolicy() { |
michael@0 | 6261 | return this; |
michael@0 | 6262 | } |
michael@0 | 6263 | AliasSet getAliasSet() const { |
michael@0 | 6264 | return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields); |
michael@0 | 6265 | } |
michael@0 | 6266 | }; |
michael@0 | 6267 | |
michael@0 | 6268 | // Array.prototype.push on a dense array. Returns the new array length. |
michael@0 | 6269 | class MArrayPush |
michael@0 | 6270 | : public MBinaryInstruction, |
michael@0 | 6271 | public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> > |
michael@0 | 6272 | { |
michael@0 | 6273 | MArrayPush(MDefinition *object, MDefinition *value) |
michael@0 | 6274 | : MBinaryInstruction(object, value) |
michael@0 | 6275 | { |
michael@0 | 6276 | setResultType(MIRType_Int32); |
michael@0 | 6277 | } |
michael@0 | 6278 | |
michael@0 | 6279 | public: |
michael@0 | 6280 | INSTRUCTION_HEADER(ArrayPush) |
michael@0 | 6281 | |
michael@0 | 6282 | static MArrayPush *New(TempAllocator &alloc, MDefinition *object, MDefinition *value) { |
michael@0 | 6283 | return new(alloc) MArrayPush(object, value); |
michael@0 | 6284 | } |
michael@0 | 6285 | |
michael@0 | 6286 | MDefinition *object() const { |
michael@0 | 6287 | return getOperand(0); |
michael@0 | 6288 | } |
michael@0 | 6289 | MDefinition *value() const { |
michael@0 | 6290 | return getOperand(1); |
michael@0 | 6291 | } |
michael@0 | 6292 | TypePolicy *typePolicy() { |
michael@0 | 6293 | return this; |
michael@0 | 6294 | } |
michael@0 | 6295 | AliasSet getAliasSet() const { |
michael@0 | 6296 | return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields); |
michael@0 | 6297 | } |
michael@0 | 6298 | void computeRange(TempAllocator &alloc); |
michael@0 | 6299 | }; |
michael@0 | 6300 | |
michael@0 | 6301 | // Array.prototype.concat on two dense arrays. |
michael@0 | 6302 | class MArrayConcat |
michael@0 | 6303 | : public MBinaryInstruction, |
michael@0 | 6304 | public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> > |
michael@0 | 6305 | { |
michael@0 | 6306 | CompilerRootObject templateObj_; |
michael@0 | 6307 | gc::InitialHeap initialHeap_; |
michael@0 | 6308 | |
michael@0 | 6309 | MArrayConcat(types::CompilerConstraintList *constraints, MDefinition *lhs, MDefinition *rhs, |
michael@0 | 6310 | JSObject *templateObj, gc::InitialHeap initialHeap) |
michael@0 | 6311 | : MBinaryInstruction(lhs, rhs), |
michael@0 | 6312 | templateObj_(templateObj), |
michael@0 | 6313 | initialHeap_(initialHeap) |
michael@0 | 6314 | { |
michael@0 | 6315 | setResultType(MIRType_Object); |
michael@0 | 6316 | setResultTypeSet(MakeSingletonTypeSet(constraints, templateObj)); |
michael@0 | 6317 | } |
michael@0 | 6318 | |
michael@0 | 6319 | public: |
michael@0 | 6320 | INSTRUCTION_HEADER(ArrayConcat) |
michael@0 | 6321 | |
michael@0 | 6322 | static MArrayConcat *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
michael@0 | 6323 | MDefinition *lhs, MDefinition *rhs, |
michael@0 | 6324 | JSObject *templateObj, gc::InitialHeap initialHeap) |
michael@0 | 6325 | { |
michael@0 | 6326 | return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj, initialHeap); |
michael@0 | 6327 | } |
michael@0 | 6328 | |
michael@0 | 6329 | JSObject *templateObj() const { |
michael@0 | 6330 | return templateObj_; |
michael@0 | 6331 | } |
michael@0 | 6332 | |
michael@0 | 6333 | gc::InitialHeap initialHeap() const { |
michael@0 | 6334 | return initialHeap_; |
michael@0 | 6335 | } |
michael@0 | 6336 | |
michael@0 | 6337 | TypePolicy *typePolicy() { |
michael@0 | 6338 | return this; |
michael@0 | 6339 | } |
michael@0 | 6340 | AliasSet getAliasSet() const { |
michael@0 | 6341 | return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields); |
michael@0 | 6342 | } |
michael@0 | 6343 | bool possiblyCalls() const { |
michael@0 | 6344 | return true; |
michael@0 | 6345 | } |
michael@0 | 6346 | }; |
michael@0 | 6347 | |
michael@0 | 6348 | class MLoadTypedArrayElement |
michael@0 | 6349 | : public MBinaryInstruction |
michael@0 | 6350 | { |
michael@0 | 6351 | ScalarTypeDescr::Type arrayType_; |
michael@0 | 6352 | |
michael@0 | 6353 | MLoadTypedArrayElement(MDefinition *elements, MDefinition *index, |
michael@0 | 6354 | ScalarTypeDescr::Type arrayType) |
michael@0 | 6355 | : MBinaryInstruction(elements, index), arrayType_(arrayType) |
michael@0 | 6356 | { |
michael@0 | 6357 | setResultType(MIRType_Value); |
michael@0 | 6358 | setMovable(); |
michael@0 | 6359 | JS_ASSERT(elements->type() == MIRType_Elements); |
michael@0 | 6360 | JS_ASSERT(index->type() == MIRType_Int32); |
michael@0 | 6361 | JS_ASSERT(arrayType >= 0 && arrayType < ScalarTypeDescr::TYPE_MAX); |
michael@0 | 6362 | } |
michael@0 | 6363 | |
michael@0 | 6364 | public: |
michael@0 | 6365 | INSTRUCTION_HEADER(LoadTypedArrayElement) |
michael@0 | 6366 | |
michael@0 | 6367 | static MLoadTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, |
michael@0 | 6368 | ScalarTypeDescr::Type arrayType) |
michael@0 | 6369 | { |
michael@0 | 6370 | return new(alloc) MLoadTypedArrayElement(elements, index, arrayType); |
michael@0 | 6371 | } |
michael@0 | 6372 | |
michael@0 | 6373 | ScalarTypeDescr::Type arrayType() const { |
michael@0 | 6374 | return arrayType_; |
michael@0 | 6375 | } |
michael@0 | 6376 | bool fallible() const { |
michael@0 | 6377 | // Bailout if the result does not fit in an int32. |
michael@0 | 6378 | return arrayType_ == ScalarTypeDescr::TYPE_UINT32 && type() == MIRType_Int32; |
michael@0 | 6379 | } |
michael@0 | 6380 | MDefinition *elements() const { |
michael@0 | 6381 | return getOperand(0); |
michael@0 | 6382 | } |
michael@0 | 6383 | MDefinition *index() const { |
michael@0 | 6384 | return getOperand(1); |
michael@0 | 6385 | } |
michael@0 | 6386 | AliasSet getAliasSet() const { |
michael@0 | 6387 | return AliasSet::Load(AliasSet::TypedArrayElement); |
michael@0 | 6388 | } |
michael@0 | 6389 | |
michael@0 | 6390 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 6391 | if (!ins->isLoadTypedArrayElement()) |
michael@0 | 6392 | return false; |
michael@0 | 6393 | const MLoadTypedArrayElement *other = ins->toLoadTypedArrayElement(); |
michael@0 | 6394 | if (arrayType_ != other->arrayType_) |
michael@0 | 6395 | return false; |
michael@0 | 6396 | return congruentIfOperandsEqual(other); |
michael@0 | 6397 | } |
michael@0 | 6398 | |
michael@0 | 6399 | void printOpcode(FILE *fp) const; |
michael@0 | 6400 | |
michael@0 | 6401 | void computeRange(TempAllocator &alloc); |
michael@0 | 6402 | |
michael@0 | 6403 | bool canProduceFloat32() const { return arrayType_ == ScalarTypeDescr::TYPE_FLOAT32; } |
michael@0 | 6404 | }; |
michael@0 | 6405 | |
michael@0 | 6406 | // Load a value from a typed array. Out-of-bounds accesses are handled using |
michael@0 | 6407 | // a VM call. |
michael@0 | 6408 | class MLoadTypedArrayElementHole |
michael@0 | 6409 | : public MBinaryInstruction, |
michael@0 | 6410 | public SingleObjectPolicy |
michael@0 | 6411 | { |
michael@0 | 6412 | int arrayType_; |
michael@0 | 6413 | bool allowDouble_; |
michael@0 | 6414 | |
michael@0 | 6415 | MLoadTypedArrayElementHole(MDefinition *object, MDefinition *index, int arrayType, bool allowDouble) |
michael@0 | 6416 | : MBinaryInstruction(object, index), arrayType_(arrayType), allowDouble_(allowDouble) |
michael@0 | 6417 | { |
michael@0 | 6418 | setResultType(MIRType_Value); |
michael@0 | 6419 | setMovable(); |
michael@0 | 6420 | JS_ASSERT(index->type() == MIRType_Int32); |
michael@0 | 6421 | JS_ASSERT(arrayType >= 0 && arrayType < ScalarTypeDescr::TYPE_MAX); |
michael@0 | 6422 | } |
michael@0 | 6423 | |
michael@0 | 6424 | public: |
michael@0 | 6425 | INSTRUCTION_HEADER(LoadTypedArrayElementHole) |
michael@0 | 6426 | |
michael@0 | 6427 | static MLoadTypedArrayElementHole *New(TempAllocator &alloc, MDefinition *object, MDefinition *index, |
michael@0 | 6428 | int arrayType, bool allowDouble) |
michael@0 | 6429 | { |
michael@0 | 6430 | return new(alloc) MLoadTypedArrayElementHole(object, index, arrayType, allowDouble); |
michael@0 | 6431 | } |
michael@0 | 6432 | |
michael@0 | 6433 | int arrayType() const { |
michael@0 | 6434 | return arrayType_; |
michael@0 | 6435 | } |
michael@0 | 6436 | bool allowDouble() const { |
michael@0 | 6437 | return allowDouble_; |
michael@0 | 6438 | } |
michael@0 | 6439 | bool fallible() const { |
michael@0 | 6440 | return arrayType_ == ScalarTypeDescr::TYPE_UINT32 && !allowDouble_; |
michael@0 | 6441 | } |
michael@0 | 6442 | TypePolicy *typePolicy() { |
michael@0 | 6443 | return this; |
michael@0 | 6444 | } |
michael@0 | 6445 | MDefinition *object() const { |
michael@0 | 6446 | return getOperand(0); |
michael@0 | 6447 | } |
michael@0 | 6448 | MDefinition *index() const { |
michael@0 | 6449 | return getOperand(1); |
michael@0 | 6450 | } |
michael@0 | 6451 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 6452 | if (!ins->isLoadTypedArrayElementHole()) |
michael@0 | 6453 | return false; |
michael@0 | 6454 | const MLoadTypedArrayElementHole *other = ins->toLoadTypedArrayElementHole(); |
michael@0 | 6455 | if (arrayType() != other->arrayType()) |
michael@0 | 6456 | return false; |
michael@0 | 6457 | if (allowDouble() != other->allowDouble()) |
michael@0 | 6458 | return false; |
michael@0 | 6459 | return congruentIfOperandsEqual(other); |
michael@0 | 6460 | } |
michael@0 | 6461 | AliasSet getAliasSet() const { |
michael@0 | 6462 | return AliasSet::Load(AliasSet::TypedArrayElement); |
michael@0 | 6463 | } |
michael@0 | 6464 | bool canProduceFloat32() const { return arrayType_ == ScalarTypeDescr::TYPE_FLOAT32; } |
michael@0 | 6465 | }; |
michael@0 | 6466 | |
michael@0 | 6467 | // Load a value fallibly or infallibly from a statically known typed array. |
michael@0 | 6468 | class MLoadTypedArrayElementStatic |
michael@0 | 6469 | : public MUnaryInstruction, |
michael@0 | 6470 | public ConvertToInt32Policy<0> |
michael@0 | 6471 | { |
michael@0 | 6472 | MLoadTypedArrayElementStatic(TypedArrayObject *typedArray, MDefinition *ptr) |
michael@0 | 6473 | : MUnaryInstruction(ptr), typedArray_(typedArray), fallible_(true) |
michael@0 | 6474 | { |
michael@0 | 6475 | int type = typedArray_->type(); |
michael@0 | 6476 | if (type == ScalarTypeDescr::TYPE_FLOAT32) |
michael@0 | 6477 | setResultType(MIRType_Float32); |
michael@0 | 6478 | else if (type == ScalarTypeDescr::TYPE_FLOAT64) |
michael@0 | 6479 | setResultType(MIRType_Double); |
michael@0 | 6480 | else |
michael@0 | 6481 | setResultType(MIRType_Int32); |
michael@0 | 6482 | } |
michael@0 | 6483 | |
michael@0 | 6484 | CompilerRoot<TypedArrayObject*> typedArray_; |
michael@0 | 6485 | bool fallible_; |
michael@0 | 6486 | |
michael@0 | 6487 | public: |
michael@0 | 6488 | INSTRUCTION_HEADER(LoadTypedArrayElementStatic); |
michael@0 | 6489 | |
michael@0 | 6490 | static MLoadTypedArrayElementStatic *New(TempAllocator &alloc, TypedArrayObject *typedArray, |
michael@0 | 6491 | MDefinition *ptr) |
michael@0 | 6492 | { |
michael@0 | 6493 | return new(alloc) MLoadTypedArrayElementStatic(typedArray, ptr); |
michael@0 | 6494 | } |
michael@0 | 6495 | |
michael@0 | 6496 | ArrayBufferView::ViewType viewType() const { |
michael@0 | 6497 | return (ArrayBufferView::ViewType) typedArray_->type(); |
michael@0 | 6498 | } |
michael@0 | 6499 | void *base() const; |
michael@0 | 6500 | size_t length() const; |
michael@0 | 6501 | |
michael@0 | 6502 | MDefinition *ptr() const { return getOperand(0); } |
michael@0 | 6503 | AliasSet getAliasSet() const { |
michael@0 | 6504 | return AliasSet::Load(AliasSet::TypedArrayElement); |
michael@0 | 6505 | } |
michael@0 | 6506 | |
michael@0 | 6507 | bool fallible() const { |
michael@0 | 6508 | return fallible_; |
michael@0 | 6509 | } |
michael@0 | 6510 | |
michael@0 | 6511 | void setInfallible() { |
michael@0 | 6512 | fallible_ = false; |
michael@0 | 6513 | } |
michael@0 | 6514 | |
michael@0 | 6515 | TypePolicy *typePolicy() { |
michael@0 | 6516 | return this; |
michael@0 | 6517 | } |
michael@0 | 6518 | |
michael@0 | 6519 | void computeRange(TempAllocator &alloc); |
michael@0 | 6520 | bool truncate(); |
michael@0 | 6521 | bool canProduceFloat32() const { return typedArray_->type() == ScalarTypeDescr::TYPE_FLOAT32; } |
michael@0 | 6522 | }; |
michael@0 | 6523 | |
michael@0 | 6524 | class MStoreTypedArrayElement |
michael@0 | 6525 | : public MTernaryInstruction, |
michael@0 | 6526 | public StoreTypedArrayPolicy |
michael@0 | 6527 | { |
michael@0 | 6528 | int arrayType_; |
michael@0 | 6529 | |
michael@0 | 6530 | // See note in MStoreElementCommon. |
michael@0 | 6531 | bool racy_; |
michael@0 | 6532 | |
michael@0 | 6533 | MStoreTypedArrayElement(MDefinition *elements, MDefinition *index, MDefinition *value, |
michael@0 | 6534 | int arrayType) |
michael@0 | 6535 | : MTernaryInstruction(elements, index, value), arrayType_(arrayType), racy_(false) |
michael@0 | 6536 | { |
michael@0 | 6537 | setMovable(); |
michael@0 | 6538 | JS_ASSERT(elements->type() == MIRType_Elements); |
michael@0 | 6539 | JS_ASSERT(index->type() == MIRType_Int32); |
michael@0 | 6540 | JS_ASSERT(arrayType >= 0 && arrayType < ScalarTypeDescr::TYPE_MAX); |
michael@0 | 6541 | } |
michael@0 | 6542 | |
michael@0 | 6543 | public: |
michael@0 | 6544 | INSTRUCTION_HEADER(StoreTypedArrayElement) |
michael@0 | 6545 | |
michael@0 | 6546 | static MStoreTypedArrayElement *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, |
michael@0 | 6547 | MDefinition *value, int arrayType) |
michael@0 | 6548 | { |
michael@0 | 6549 | return new(alloc) MStoreTypedArrayElement(elements, index, value, arrayType); |
michael@0 | 6550 | } |
michael@0 | 6551 | |
michael@0 | 6552 | int arrayType() const { |
michael@0 | 6553 | return arrayType_; |
michael@0 | 6554 | } |
michael@0 | 6555 | bool isByteArray() const { |
michael@0 | 6556 | return (arrayType_ == ScalarTypeDescr::TYPE_INT8 || |
michael@0 | 6557 | arrayType_ == ScalarTypeDescr::TYPE_UINT8 || |
michael@0 | 6558 | arrayType_ == ScalarTypeDescr::TYPE_UINT8_CLAMPED); |
michael@0 | 6559 | } |
michael@0 | 6560 | bool isFloatArray() const { |
michael@0 | 6561 | return (arrayType_ == ScalarTypeDescr::TYPE_FLOAT32 || |
michael@0 | 6562 | arrayType_ == ScalarTypeDescr::TYPE_FLOAT64); |
michael@0 | 6563 | } |
michael@0 | 6564 | TypePolicy *typePolicy() { |
michael@0 | 6565 | return this; |
michael@0 | 6566 | } |
michael@0 | 6567 | MDefinition *elements() const { |
michael@0 | 6568 | return getOperand(0); |
michael@0 | 6569 | } |
michael@0 | 6570 | MDefinition *index() const { |
michael@0 | 6571 | return getOperand(1); |
michael@0 | 6572 | } |
michael@0 | 6573 | MDefinition *value() const { |
michael@0 | 6574 | return getOperand(2); |
michael@0 | 6575 | } |
michael@0 | 6576 | AliasSet getAliasSet() const { |
michael@0 | 6577 | return AliasSet::Store(AliasSet::TypedArrayElement); |
michael@0 | 6578 | } |
michael@0 | 6579 | bool racy() const { |
michael@0 | 6580 | return racy_; |
michael@0 | 6581 | } |
michael@0 | 6582 | void setRacy() { |
michael@0 | 6583 | racy_ = true; |
michael@0 | 6584 | } |
michael@0 | 6585 | bool isOperandTruncated(size_t index) const; |
michael@0 | 6586 | |
michael@0 | 6587 | bool canConsumeFloat32(MUse *use) const { |
michael@0 | 6588 | return use->index() == 2 && arrayType_ == ScalarTypeDescr::TYPE_FLOAT32; |
michael@0 | 6589 | } |
michael@0 | 6590 | }; |
michael@0 | 6591 | |
michael@0 | 6592 | class MStoreTypedArrayElementHole |
michael@0 | 6593 | : public MAryInstruction<4>, |
michael@0 | 6594 | public StoreTypedArrayHolePolicy |
michael@0 | 6595 | { |
michael@0 | 6596 | int arrayType_; |
michael@0 | 6597 | |
michael@0 | 6598 | MStoreTypedArrayElementHole(MDefinition *elements, MDefinition *length, MDefinition *index, |
michael@0 | 6599 | MDefinition *value, int arrayType) |
michael@0 | 6600 | : MAryInstruction<4>(), arrayType_(arrayType) |
michael@0 | 6601 | { |
michael@0 | 6602 | setOperand(0, elements); |
michael@0 | 6603 | setOperand(1, length); |
michael@0 | 6604 | setOperand(2, index); |
michael@0 | 6605 | setOperand(3, value); |
michael@0 | 6606 | setMovable(); |
michael@0 | 6607 | JS_ASSERT(elements->type() == MIRType_Elements); |
michael@0 | 6608 | JS_ASSERT(length->type() == MIRType_Int32); |
michael@0 | 6609 | JS_ASSERT(index->type() == MIRType_Int32); |
michael@0 | 6610 | JS_ASSERT(arrayType >= 0 && arrayType < ScalarTypeDescr::TYPE_MAX); |
michael@0 | 6611 | } |
michael@0 | 6612 | |
michael@0 | 6613 | public: |
michael@0 | 6614 | INSTRUCTION_HEADER(StoreTypedArrayElementHole) |
michael@0 | 6615 | |
michael@0 | 6616 | static MStoreTypedArrayElementHole *New(TempAllocator &alloc, MDefinition *elements, |
michael@0 | 6617 | MDefinition *length, MDefinition *index, |
michael@0 | 6618 | MDefinition *value, int arrayType) |
michael@0 | 6619 | { |
michael@0 | 6620 | return new(alloc) MStoreTypedArrayElementHole(elements, length, index, value, arrayType); |
michael@0 | 6621 | } |
michael@0 | 6622 | |
michael@0 | 6623 | int arrayType() const { |
michael@0 | 6624 | return arrayType_; |
michael@0 | 6625 | } |
michael@0 | 6626 | bool isByteArray() const { |
michael@0 | 6627 | return (arrayType_ == ScalarTypeDescr::TYPE_INT8 || |
michael@0 | 6628 | arrayType_ == ScalarTypeDescr::TYPE_UINT8 || |
michael@0 | 6629 | arrayType_ == ScalarTypeDescr::TYPE_UINT8_CLAMPED); |
michael@0 | 6630 | } |
michael@0 | 6631 | bool isFloatArray() const { |
michael@0 | 6632 | return (arrayType_ == ScalarTypeDescr::TYPE_FLOAT32 || |
michael@0 | 6633 | arrayType_ == ScalarTypeDescr::TYPE_FLOAT64); |
michael@0 | 6634 | } |
michael@0 | 6635 | TypePolicy *typePolicy() { |
michael@0 | 6636 | return this; |
michael@0 | 6637 | } |
michael@0 | 6638 | MDefinition *elements() const { |
michael@0 | 6639 | return getOperand(0); |
michael@0 | 6640 | } |
michael@0 | 6641 | MDefinition *length() const { |
michael@0 | 6642 | return getOperand(1); |
michael@0 | 6643 | } |
michael@0 | 6644 | MDefinition *index() const { |
michael@0 | 6645 | return getOperand(2); |
michael@0 | 6646 | } |
michael@0 | 6647 | MDefinition *value() const { |
michael@0 | 6648 | return getOperand(3); |
michael@0 | 6649 | } |
michael@0 | 6650 | AliasSet getAliasSet() const { |
michael@0 | 6651 | return AliasSet::Store(AliasSet::TypedArrayElement); |
michael@0 | 6652 | } |
michael@0 | 6653 | bool isOperandTruncated(size_t index) const; |
michael@0 | 6654 | |
michael@0 | 6655 | bool canConsumeFloat32(MUse *use) const { |
michael@0 | 6656 | return use->index() == 3 && arrayType_ == ScalarTypeDescr::TYPE_FLOAT32; |
michael@0 | 6657 | } |
michael@0 | 6658 | }; |
michael@0 | 6659 | |
michael@0 | 6660 | // Store a value infallibly to a statically known typed array. |
michael@0 | 6661 | class MStoreTypedArrayElementStatic : |
michael@0 | 6662 | public MBinaryInstruction |
michael@0 | 6663 | , public StoreTypedArrayElementStaticPolicy |
michael@0 | 6664 | { |
michael@0 | 6665 | MStoreTypedArrayElementStatic(TypedArrayObject *typedArray, MDefinition *ptr, MDefinition *v) |
michael@0 | 6666 | : MBinaryInstruction(ptr, v), typedArray_(typedArray) |
michael@0 | 6667 | {} |
michael@0 | 6668 | |
michael@0 | 6669 | CompilerRoot<TypedArrayObject*> typedArray_; |
michael@0 | 6670 | |
michael@0 | 6671 | public: |
michael@0 | 6672 | INSTRUCTION_HEADER(StoreTypedArrayElementStatic); |
michael@0 | 6673 | |
michael@0 | 6674 | static MStoreTypedArrayElementStatic *New(TempAllocator &alloc, TypedArrayObject *typedArray, |
michael@0 | 6675 | MDefinition *ptr, MDefinition *v) |
michael@0 | 6676 | { |
michael@0 | 6677 | return new(alloc) MStoreTypedArrayElementStatic(typedArray, ptr, v); |
michael@0 | 6678 | } |
michael@0 | 6679 | |
michael@0 | 6680 | TypePolicy *typePolicy() { |
michael@0 | 6681 | return this; |
michael@0 | 6682 | } |
michael@0 | 6683 | |
michael@0 | 6684 | ArrayBufferView::ViewType viewType() const { |
michael@0 | 6685 | return (ArrayBufferView::ViewType) typedArray_->type(); |
michael@0 | 6686 | } |
michael@0 | 6687 | bool isFloatArray() const { |
michael@0 | 6688 | return (viewType() == ArrayBufferView::TYPE_FLOAT32 || |
michael@0 | 6689 | viewType() == ArrayBufferView::TYPE_FLOAT64); |
michael@0 | 6690 | } |
michael@0 | 6691 | |
michael@0 | 6692 | void *base() const; |
michael@0 | 6693 | size_t length() const; |
michael@0 | 6694 | |
michael@0 | 6695 | MDefinition *ptr() const { return getOperand(0); } |
michael@0 | 6696 | MDefinition *value() const { return getOperand(1); } |
michael@0 | 6697 | AliasSet getAliasSet() const { |
michael@0 | 6698 | return AliasSet::Store(AliasSet::TypedArrayElement); |
michael@0 | 6699 | } |
michael@0 | 6700 | bool isOperandTruncated(size_t index) const; |
michael@0 | 6701 | |
michael@0 | 6702 | bool canConsumeFloat32(MUse *use) const { |
michael@0 | 6703 | return use->index() == 1 && typedArray_->type() == ScalarTypeDescr::TYPE_FLOAT32; |
michael@0 | 6704 | } |
michael@0 | 6705 | }; |
michael@0 | 6706 | |
michael@0 | 6707 | // Compute an "effective address", i.e., a compound computation of the form: |
michael@0 | 6708 | // base + index * scale + displacement |
michael@0 | 6709 | class MEffectiveAddress : public MBinaryInstruction |
michael@0 | 6710 | { |
michael@0 | 6711 | MEffectiveAddress(MDefinition *base, MDefinition *index, Scale scale, int32_t displacement) |
michael@0 | 6712 | : MBinaryInstruction(base, index), scale_(scale), displacement_(displacement) |
michael@0 | 6713 | { |
michael@0 | 6714 | JS_ASSERT(base->type() == MIRType_Int32); |
michael@0 | 6715 | JS_ASSERT(index->type() == MIRType_Int32); |
michael@0 | 6716 | setMovable(); |
michael@0 | 6717 | setResultType(MIRType_Int32); |
michael@0 | 6718 | } |
michael@0 | 6719 | |
michael@0 | 6720 | Scale scale_; |
michael@0 | 6721 | int32_t displacement_; |
michael@0 | 6722 | |
michael@0 | 6723 | public: |
michael@0 | 6724 | INSTRUCTION_HEADER(EffectiveAddress); |
michael@0 | 6725 | |
michael@0 | 6726 | static MEffectiveAddress *New(TempAllocator &alloc, MDefinition *base, MDefinition *index, |
michael@0 | 6727 | Scale s, int32_t d) |
michael@0 | 6728 | { |
michael@0 | 6729 | return new(alloc) MEffectiveAddress(base, index, s, d); |
michael@0 | 6730 | } |
michael@0 | 6731 | MDefinition *base() const { |
michael@0 | 6732 | return lhs(); |
michael@0 | 6733 | } |
michael@0 | 6734 | MDefinition *index() const { |
michael@0 | 6735 | return rhs(); |
michael@0 | 6736 | } |
michael@0 | 6737 | Scale scale() const { |
michael@0 | 6738 | return scale_; |
michael@0 | 6739 | } |
michael@0 | 6740 | int32_t displacement() const { |
michael@0 | 6741 | return displacement_; |
michael@0 | 6742 | } |
michael@0 | 6743 | }; |
michael@0 | 6744 | |
michael@0 | 6745 | // Clamp input to range [0, 255] for Uint8ClampedArray. |
michael@0 | 6746 | class MClampToUint8 |
michael@0 | 6747 | : public MUnaryInstruction, |
michael@0 | 6748 | public ClampPolicy |
michael@0 | 6749 | { |
michael@0 | 6750 | MClampToUint8(MDefinition *input) |
michael@0 | 6751 | : MUnaryInstruction(input) |
michael@0 | 6752 | { |
michael@0 | 6753 | setResultType(MIRType_Int32); |
michael@0 | 6754 | setMovable(); |
michael@0 | 6755 | } |
michael@0 | 6756 | |
michael@0 | 6757 | public: |
michael@0 | 6758 | INSTRUCTION_HEADER(ClampToUint8) |
michael@0 | 6759 | |
michael@0 | 6760 | static MClampToUint8 *New(TempAllocator &alloc, MDefinition *input) { |
michael@0 | 6761 | return new(alloc) MClampToUint8(input); |
michael@0 | 6762 | } |
michael@0 | 6763 | |
michael@0 | 6764 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 6765 | |
michael@0 | 6766 | TypePolicy *typePolicy() { |
michael@0 | 6767 | return this; |
michael@0 | 6768 | } |
michael@0 | 6769 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 6770 | return congruentIfOperandsEqual(ins); |
michael@0 | 6771 | } |
michael@0 | 6772 | AliasSet getAliasSet() const { |
michael@0 | 6773 | return AliasSet::None(); |
michael@0 | 6774 | } |
michael@0 | 6775 | void computeRange(TempAllocator &alloc); |
michael@0 | 6776 | }; |
michael@0 | 6777 | |
michael@0 | 6778 | class MLoadFixedSlot |
michael@0 | 6779 | : public MUnaryInstruction, |
michael@0 | 6780 | public SingleObjectPolicy |
michael@0 | 6781 | { |
michael@0 | 6782 | size_t slot_; |
michael@0 | 6783 | |
michael@0 | 6784 | protected: |
michael@0 | 6785 | MLoadFixedSlot(MDefinition *obj, size_t slot) |
michael@0 | 6786 | : MUnaryInstruction(obj), slot_(slot) |
michael@0 | 6787 | { |
michael@0 | 6788 | setResultType(MIRType_Value); |
michael@0 | 6789 | setMovable(); |
michael@0 | 6790 | } |
michael@0 | 6791 | |
michael@0 | 6792 | public: |
michael@0 | 6793 | INSTRUCTION_HEADER(LoadFixedSlot) |
michael@0 | 6794 | |
michael@0 | 6795 | static MLoadFixedSlot *New(TempAllocator &alloc, MDefinition *obj, size_t slot) { |
michael@0 | 6796 | return new(alloc) MLoadFixedSlot(obj, slot); |
michael@0 | 6797 | } |
michael@0 | 6798 | |
michael@0 | 6799 | TypePolicy *typePolicy() { |
michael@0 | 6800 | return this; |
michael@0 | 6801 | } |
michael@0 | 6802 | |
michael@0 | 6803 | MDefinition *object() const { |
michael@0 | 6804 | return getOperand(0); |
michael@0 | 6805 | } |
michael@0 | 6806 | size_t slot() const { |
michael@0 | 6807 | return slot_; |
michael@0 | 6808 | } |
michael@0 | 6809 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 6810 | if (!ins->isLoadFixedSlot()) |
michael@0 | 6811 | return false; |
michael@0 | 6812 | if (slot() != ins->toLoadFixedSlot()->slot()) |
michael@0 | 6813 | return false; |
michael@0 | 6814 | return congruentIfOperandsEqual(ins); |
michael@0 | 6815 | } |
michael@0 | 6816 | |
michael@0 | 6817 | AliasSet getAliasSet() const { |
michael@0 | 6818 | return AliasSet::Load(AliasSet::FixedSlot); |
michael@0 | 6819 | } |
michael@0 | 6820 | |
michael@0 | 6821 | bool mightAlias(const MDefinition *store) const; |
michael@0 | 6822 | }; |
michael@0 | 6823 | |
michael@0 | 6824 | class MStoreFixedSlot |
michael@0 | 6825 | : public MBinaryInstruction, |
michael@0 | 6826 | public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> > |
michael@0 | 6827 | { |
michael@0 | 6828 | bool needsBarrier_; |
michael@0 | 6829 | size_t slot_; |
michael@0 | 6830 | |
michael@0 | 6831 | MStoreFixedSlot(MDefinition *obj, MDefinition *rval, size_t slot, bool barrier) |
michael@0 | 6832 | : MBinaryInstruction(obj, rval), |
michael@0 | 6833 | needsBarrier_(barrier), |
michael@0 | 6834 | slot_(slot) |
michael@0 | 6835 | { } |
michael@0 | 6836 | |
michael@0 | 6837 | public: |
michael@0 | 6838 | INSTRUCTION_HEADER(StoreFixedSlot) |
michael@0 | 6839 | |
michael@0 | 6840 | static MStoreFixedSlot *New(TempAllocator &alloc, MDefinition *obj, size_t slot, |
michael@0 | 6841 | MDefinition *rval) |
michael@0 | 6842 | { |
michael@0 | 6843 | return new(alloc) MStoreFixedSlot(obj, rval, slot, false); |
michael@0 | 6844 | } |
michael@0 | 6845 | static MStoreFixedSlot *NewBarriered(TempAllocator &alloc, MDefinition *obj, size_t slot, |
michael@0 | 6846 | MDefinition *rval) |
michael@0 | 6847 | { |
michael@0 | 6848 | return new(alloc) MStoreFixedSlot(obj, rval, slot, true); |
michael@0 | 6849 | } |
michael@0 | 6850 | |
michael@0 | 6851 | TypePolicy *typePolicy() { |
michael@0 | 6852 | return this; |
michael@0 | 6853 | } |
michael@0 | 6854 | |
michael@0 | 6855 | MDefinition *object() const { |
michael@0 | 6856 | return getOperand(0); |
michael@0 | 6857 | } |
michael@0 | 6858 | MDefinition *value() const { |
michael@0 | 6859 | return getOperand(1); |
michael@0 | 6860 | } |
michael@0 | 6861 | size_t slot() const { |
michael@0 | 6862 | return slot_; |
michael@0 | 6863 | } |
michael@0 | 6864 | |
michael@0 | 6865 | AliasSet getAliasSet() const { |
michael@0 | 6866 | return AliasSet::Store(AliasSet::FixedSlot); |
michael@0 | 6867 | } |
michael@0 | 6868 | bool needsBarrier() const { |
michael@0 | 6869 | return needsBarrier_; |
michael@0 | 6870 | } |
michael@0 | 6871 | void setNeedsBarrier() { |
michael@0 | 6872 | needsBarrier_ = true; |
michael@0 | 6873 | } |
michael@0 | 6874 | }; |
michael@0 | 6875 | |
michael@0 | 6876 | typedef Vector<JSObject *, 4, IonAllocPolicy> ObjectVector; |
michael@0 | 6877 | typedef Vector<bool, 4, IonAllocPolicy> BoolVector; |
michael@0 | 6878 | |
michael@0 | 6879 | class InlinePropertyTable : public TempObject |
michael@0 | 6880 | { |
michael@0 | 6881 | struct Entry : public TempObject { |
michael@0 | 6882 | CompilerRoot<types::TypeObject *> typeObj; |
michael@0 | 6883 | CompilerRootFunction func; |
michael@0 | 6884 | |
michael@0 | 6885 | Entry(types::TypeObject *typeObj, JSFunction *func) |
michael@0 | 6886 | : typeObj(typeObj), func(func) |
michael@0 | 6887 | { } |
michael@0 | 6888 | }; |
michael@0 | 6889 | |
michael@0 | 6890 | jsbytecode *pc_; |
michael@0 | 6891 | MResumePoint *priorResumePoint_; |
michael@0 | 6892 | Vector<Entry *, 4, IonAllocPolicy> entries_; |
michael@0 | 6893 | |
michael@0 | 6894 | public: |
michael@0 | 6895 | InlinePropertyTable(TempAllocator &alloc, jsbytecode *pc) |
michael@0 | 6896 | : pc_(pc), priorResumePoint_(nullptr), entries_(alloc) |
michael@0 | 6897 | { } |
michael@0 | 6898 | |
michael@0 | 6899 | void setPriorResumePoint(MResumePoint *resumePoint) { |
michael@0 | 6900 | JS_ASSERT(priorResumePoint_ == nullptr); |
michael@0 | 6901 | priorResumePoint_ = resumePoint; |
michael@0 | 6902 | } |
michael@0 | 6903 | |
michael@0 | 6904 | MResumePoint *priorResumePoint() const { |
michael@0 | 6905 | return priorResumePoint_; |
michael@0 | 6906 | } |
michael@0 | 6907 | |
michael@0 | 6908 | jsbytecode *pc() const { |
michael@0 | 6909 | return pc_; |
michael@0 | 6910 | } |
michael@0 | 6911 | |
michael@0 | 6912 | bool addEntry(TempAllocator &alloc, types::TypeObject *typeObj, JSFunction *func) { |
michael@0 | 6913 | return entries_.append(new(alloc) Entry(typeObj, func)); |
michael@0 | 6914 | } |
michael@0 | 6915 | |
michael@0 | 6916 | size_t numEntries() const { |
michael@0 | 6917 | return entries_.length(); |
michael@0 | 6918 | } |
michael@0 | 6919 | |
michael@0 | 6920 | types::TypeObject *getTypeObject(size_t i) const { |
michael@0 | 6921 | JS_ASSERT(i < numEntries()); |
michael@0 | 6922 | return entries_[i]->typeObj; |
michael@0 | 6923 | } |
michael@0 | 6924 | |
michael@0 | 6925 | JSFunction *getFunction(size_t i) const { |
michael@0 | 6926 | JS_ASSERT(i < numEntries()); |
michael@0 | 6927 | return entries_[i]->func; |
michael@0 | 6928 | } |
michael@0 | 6929 | |
michael@0 | 6930 | bool hasFunction(JSFunction *func) const; |
michael@0 | 6931 | types::TemporaryTypeSet *buildTypeSetForFunction(JSFunction *func) const; |
michael@0 | 6932 | |
michael@0 | 6933 | // Remove targets that vetoed inlining from the InlinePropertyTable. |
michael@0 | 6934 | void trimTo(ObjectVector &targets, BoolVector &choiceSet); |
michael@0 | 6935 | |
michael@0 | 6936 | // Ensure that the InlinePropertyTable's domain is a subset of |targets|. |
michael@0 | 6937 | void trimToTargets(ObjectVector &targets); |
michael@0 | 6938 | }; |
michael@0 | 6939 | |
michael@0 | 6940 | class CacheLocationList : public InlineConcatList<CacheLocationList> |
michael@0 | 6941 | { |
michael@0 | 6942 | public: |
michael@0 | 6943 | CacheLocationList() |
michael@0 | 6944 | : pc(nullptr), |
michael@0 | 6945 | script(nullptr) |
michael@0 | 6946 | { } |
michael@0 | 6947 | |
michael@0 | 6948 | jsbytecode *pc; |
michael@0 | 6949 | JSScript *script; |
michael@0 | 6950 | }; |
michael@0 | 6951 | |
michael@0 | 6952 | class MGetPropertyCache |
michael@0 | 6953 | : public MUnaryInstruction, |
michael@0 | 6954 | public SingleObjectPolicy |
michael@0 | 6955 | { |
michael@0 | 6956 | CompilerRootPropertyName name_; |
michael@0 | 6957 | bool idempotent_; |
michael@0 | 6958 | bool monitoredResult_; |
michael@0 | 6959 | |
michael@0 | 6960 | CacheLocationList location_; |
michael@0 | 6961 | |
michael@0 | 6962 | InlinePropertyTable *inlinePropertyTable_; |
michael@0 | 6963 | |
michael@0 | 6964 | MGetPropertyCache(MDefinition *obj, PropertyName *name, bool monitoredResult) |
michael@0 | 6965 | : MUnaryInstruction(obj), |
michael@0 | 6966 | name_(name), |
michael@0 | 6967 | idempotent_(false), |
michael@0 | 6968 | monitoredResult_(monitoredResult), |
michael@0 | 6969 | location_(), |
michael@0 | 6970 | inlinePropertyTable_(nullptr) |
michael@0 | 6971 | { |
michael@0 | 6972 | setResultType(MIRType_Value); |
michael@0 | 6973 | |
michael@0 | 6974 | // The cache will invalidate if there are objects with e.g. lookup or |
michael@0 | 6975 | // resolve hooks on the proto chain. setGuard ensures this check is not |
michael@0 | 6976 | // eliminated. |
michael@0 | 6977 | setGuard(); |
michael@0 | 6978 | } |
michael@0 | 6979 | |
michael@0 | 6980 | public: |
michael@0 | 6981 | INSTRUCTION_HEADER(GetPropertyCache) |
michael@0 | 6982 | |
michael@0 | 6983 | static MGetPropertyCache *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name, |
michael@0 | 6984 | bool monitoredResult) { |
michael@0 | 6985 | return new(alloc) MGetPropertyCache(obj, name, monitoredResult); |
michael@0 | 6986 | } |
michael@0 | 6987 | |
michael@0 | 6988 | InlinePropertyTable *initInlinePropertyTable(TempAllocator &alloc, jsbytecode *pc) { |
michael@0 | 6989 | JS_ASSERT(inlinePropertyTable_ == nullptr); |
michael@0 | 6990 | inlinePropertyTable_ = new(alloc) InlinePropertyTable(alloc, pc); |
michael@0 | 6991 | return inlinePropertyTable_; |
michael@0 | 6992 | } |
michael@0 | 6993 | |
michael@0 | 6994 | void clearInlinePropertyTable() { |
michael@0 | 6995 | inlinePropertyTable_ = nullptr; |
michael@0 | 6996 | } |
michael@0 | 6997 | |
michael@0 | 6998 | InlinePropertyTable *propTable() const { |
michael@0 | 6999 | return inlinePropertyTable_; |
michael@0 | 7000 | } |
michael@0 | 7001 | |
michael@0 | 7002 | MDefinition *object() const { |
michael@0 | 7003 | return getOperand(0); |
michael@0 | 7004 | } |
michael@0 | 7005 | PropertyName *name() const { |
michael@0 | 7006 | return name_; |
michael@0 | 7007 | } |
michael@0 | 7008 | bool idempotent() const { |
michael@0 | 7009 | return idempotent_; |
michael@0 | 7010 | } |
michael@0 | 7011 | void setIdempotent() { |
michael@0 | 7012 | idempotent_ = true; |
michael@0 | 7013 | setMovable(); |
michael@0 | 7014 | } |
michael@0 | 7015 | bool monitoredResult() const { |
michael@0 | 7016 | return monitoredResult_; |
michael@0 | 7017 | } |
michael@0 | 7018 | CacheLocationList &location() { |
michael@0 | 7019 | return location_; |
michael@0 | 7020 | } |
michael@0 | 7021 | TypePolicy *typePolicy() { return this; } |
michael@0 | 7022 | |
michael@0 | 7023 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 7024 | if (!idempotent_) |
michael@0 | 7025 | return false; |
michael@0 | 7026 | if (!ins->isGetPropertyCache()) |
michael@0 | 7027 | return false; |
michael@0 | 7028 | if (name() != ins->toGetPropertyCache()->name()) |
michael@0 | 7029 | return false; |
michael@0 | 7030 | return congruentIfOperandsEqual(ins); |
michael@0 | 7031 | } |
michael@0 | 7032 | |
michael@0 | 7033 | AliasSet getAliasSet() const { |
michael@0 | 7034 | if (idempotent_) { |
michael@0 | 7035 | return AliasSet::Load(AliasSet::ObjectFields | |
michael@0 | 7036 | AliasSet::FixedSlot | |
michael@0 | 7037 | AliasSet::DynamicSlot); |
michael@0 | 7038 | } |
michael@0 | 7039 | return AliasSet::Store(AliasSet::Any); |
michael@0 | 7040 | } |
michael@0 | 7041 | |
michael@0 | 7042 | void setBlock(MBasicBlock *block); |
michael@0 | 7043 | bool updateForReplacement(MDefinition *ins); |
michael@0 | 7044 | }; |
michael@0 | 7045 | |
michael@0 | 7046 | // Emit code to load a value from an object's slots if its shape matches |
michael@0 | 7047 | // one of the shapes observed by the baseline IC, else bails out. |
michael@0 | 7048 | class MGetPropertyPolymorphic |
michael@0 | 7049 | : public MUnaryInstruction, |
michael@0 | 7050 | public SingleObjectPolicy |
michael@0 | 7051 | { |
michael@0 | 7052 | struct Entry { |
michael@0 | 7053 | // The shape to guard against. |
michael@0 | 7054 | Shape *objShape; |
michael@0 | 7055 | |
michael@0 | 7056 | // The property to laod. |
michael@0 | 7057 | Shape *shape; |
michael@0 | 7058 | }; |
michael@0 | 7059 | |
michael@0 | 7060 | Vector<Entry, 4, IonAllocPolicy> shapes_; |
michael@0 | 7061 | CompilerRootPropertyName name_; |
michael@0 | 7062 | |
michael@0 | 7063 | MGetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, PropertyName *name) |
michael@0 | 7064 | : MUnaryInstruction(obj), |
michael@0 | 7065 | shapes_(alloc), |
michael@0 | 7066 | name_(name) |
michael@0 | 7067 | { |
michael@0 | 7068 | setGuard(); |
michael@0 | 7069 | setMovable(); |
michael@0 | 7070 | setResultType(MIRType_Value); |
michael@0 | 7071 | } |
michael@0 | 7072 | |
michael@0 | 7073 | PropertyName *name() const { |
michael@0 | 7074 | return name_; |
michael@0 | 7075 | } |
michael@0 | 7076 | |
michael@0 | 7077 | public: |
michael@0 | 7078 | INSTRUCTION_HEADER(GetPropertyPolymorphic) |
michael@0 | 7079 | |
michael@0 | 7080 | static MGetPropertyPolymorphic *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name) { |
michael@0 | 7081 | return new(alloc) MGetPropertyPolymorphic(alloc, obj, name); |
michael@0 | 7082 | } |
michael@0 | 7083 | |
michael@0 | 7084 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 7085 | if (!ins->isGetPropertyPolymorphic()) |
michael@0 | 7086 | return false; |
michael@0 | 7087 | if (name() != ins->toGetPropertyPolymorphic()->name()) |
michael@0 | 7088 | return false; |
michael@0 | 7089 | return congruentIfOperandsEqual(ins); |
michael@0 | 7090 | } |
michael@0 | 7091 | |
michael@0 | 7092 | TypePolicy *typePolicy() { |
michael@0 | 7093 | return this; |
michael@0 | 7094 | } |
michael@0 | 7095 | bool addShape(Shape *objShape, Shape *shape) { |
michael@0 | 7096 | Entry entry; |
michael@0 | 7097 | entry.objShape = objShape; |
michael@0 | 7098 | entry.shape = shape; |
michael@0 | 7099 | return shapes_.append(entry); |
michael@0 | 7100 | } |
michael@0 | 7101 | size_t numShapes() const { |
michael@0 | 7102 | return shapes_.length(); |
michael@0 | 7103 | } |
michael@0 | 7104 | Shape *objShape(size_t i) const { |
michael@0 | 7105 | return shapes_[i].objShape; |
michael@0 | 7106 | } |
michael@0 | 7107 | Shape *shape(size_t i) const { |
michael@0 | 7108 | return shapes_[i].shape; |
michael@0 | 7109 | } |
michael@0 | 7110 | MDefinition *obj() const { |
michael@0 | 7111 | return getOperand(0); |
michael@0 | 7112 | } |
michael@0 | 7113 | AliasSet getAliasSet() const { |
michael@0 | 7114 | return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot); |
michael@0 | 7115 | } |
michael@0 | 7116 | |
michael@0 | 7117 | bool mightAlias(const MDefinition *store) const; |
michael@0 | 7118 | }; |
michael@0 | 7119 | |
michael@0 | 7120 | // Emit code to store a value to an object's slots if its shape matches |
michael@0 | 7121 | // one of the shapes observed by the baseline IC, else bails out. |
michael@0 | 7122 | class MSetPropertyPolymorphic |
michael@0 | 7123 | : public MBinaryInstruction, |
michael@0 | 7124 | public SingleObjectPolicy |
michael@0 | 7125 | { |
michael@0 | 7126 | struct Entry { |
michael@0 | 7127 | // The shape to guard against. |
michael@0 | 7128 | Shape *objShape; |
michael@0 | 7129 | |
michael@0 | 7130 | // The property to laod. |
michael@0 | 7131 | Shape *shape; |
michael@0 | 7132 | }; |
michael@0 | 7133 | |
michael@0 | 7134 | Vector<Entry, 4, IonAllocPolicy> shapes_; |
michael@0 | 7135 | bool needsBarrier_; |
michael@0 | 7136 | |
michael@0 | 7137 | MSetPropertyPolymorphic(TempAllocator &alloc, MDefinition *obj, MDefinition *value) |
michael@0 | 7138 | : MBinaryInstruction(obj, value), |
michael@0 | 7139 | shapes_(alloc), |
michael@0 | 7140 | needsBarrier_(false) |
michael@0 | 7141 | { |
michael@0 | 7142 | } |
michael@0 | 7143 | |
michael@0 | 7144 | public: |
michael@0 | 7145 | INSTRUCTION_HEADER(SetPropertyPolymorphic) |
michael@0 | 7146 | |
michael@0 | 7147 | static MSetPropertyPolymorphic *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value) { |
michael@0 | 7148 | return new(alloc) MSetPropertyPolymorphic(alloc, obj, value); |
michael@0 | 7149 | } |
michael@0 | 7150 | |
michael@0 | 7151 | TypePolicy *typePolicy() { |
michael@0 | 7152 | return this; |
michael@0 | 7153 | } |
michael@0 | 7154 | bool addShape(Shape *objShape, Shape *shape) { |
michael@0 | 7155 | Entry entry; |
michael@0 | 7156 | entry.objShape = objShape; |
michael@0 | 7157 | entry.shape = shape; |
michael@0 | 7158 | return shapes_.append(entry); |
michael@0 | 7159 | } |
michael@0 | 7160 | size_t numShapes() const { |
michael@0 | 7161 | return shapes_.length(); |
michael@0 | 7162 | } |
michael@0 | 7163 | Shape *objShape(size_t i) const { |
michael@0 | 7164 | return shapes_[i].objShape; |
michael@0 | 7165 | } |
michael@0 | 7166 | Shape *shape(size_t i) const { |
michael@0 | 7167 | return shapes_[i].shape; |
michael@0 | 7168 | } |
michael@0 | 7169 | MDefinition *obj() const { |
michael@0 | 7170 | return getOperand(0); |
michael@0 | 7171 | } |
michael@0 | 7172 | MDefinition *value() const { |
michael@0 | 7173 | return getOperand(1); |
michael@0 | 7174 | } |
michael@0 | 7175 | bool needsBarrier() const { |
michael@0 | 7176 | return needsBarrier_; |
michael@0 | 7177 | } |
michael@0 | 7178 | void setNeedsBarrier() { |
michael@0 | 7179 | needsBarrier_ = true; |
michael@0 | 7180 | } |
michael@0 | 7181 | AliasSet getAliasSet() const { |
michael@0 | 7182 | return AliasSet::Store(AliasSet::ObjectFields | AliasSet::FixedSlot | AliasSet::DynamicSlot); |
michael@0 | 7183 | } |
michael@0 | 7184 | }; |
michael@0 | 7185 | |
michael@0 | 7186 | class MDispatchInstruction |
michael@0 | 7187 | : public MControlInstruction, |
michael@0 | 7188 | public SingleObjectPolicy |
michael@0 | 7189 | { |
michael@0 | 7190 | // Map from JSFunction* -> MBasicBlock. |
michael@0 | 7191 | struct Entry { |
michael@0 | 7192 | JSFunction *func; |
michael@0 | 7193 | MBasicBlock *block; |
michael@0 | 7194 | |
michael@0 | 7195 | Entry(JSFunction *func, MBasicBlock *block) |
michael@0 | 7196 | : func(func), block(block) |
michael@0 | 7197 | { } |
michael@0 | 7198 | }; |
michael@0 | 7199 | Vector<Entry, 4, IonAllocPolicy> map_; |
michael@0 | 7200 | |
michael@0 | 7201 | // An optional fallback path that uses MCall. |
michael@0 | 7202 | MBasicBlock *fallback_; |
michael@0 | 7203 | MUse operand_; |
michael@0 | 7204 | |
michael@0 | 7205 | public: |
michael@0 | 7206 | MDispatchInstruction(TempAllocator &alloc, MDefinition *input) |
michael@0 | 7207 | : map_(alloc), fallback_(nullptr) |
michael@0 | 7208 | { |
michael@0 | 7209 | setOperand(0, input); |
michael@0 | 7210 | } |
michael@0 | 7211 | |
michael@0 | 7212 | protected: |
michael@0 | 7213 | void setOperand(size_t index, MDefinition *operand) MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 7214 | JS_ASSERT(index == 0); |
michael@0 | 7215 | operand_.set(operand, this, 0); |
michael@0 | 7216 | operand->addUse(&operand_); |
michael@0 | 7217 | } |
michael@0 | 7218 | MUse *getUseFor(size_t index) MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 7219 | JS_ASSERT(index == 0); |
michael@0 | 7220 | return &operand_; |
michael@0 | 7221 | } |
michael@0 | 7222 | MDefinition *getOperand(size_t index) const MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 7223 | JS_ASSERT(index == 0); |
michael@0 | 7224 | return operand_.producer(); |
michael@0 | 7225 | } |
michael@0 | 7226 | size_t numOperands() const MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 7227 | return 1; |
michael@0 | 7228 | } |
michael@0 | 7229 | |
michael@0 | 7230 | public: |
michael@0 | 7231 | void setSuccessor(size_t i, MBasicBlock *successor) { |
michael@0 | 7232 | JS_ASSERT(i < numSuccessors()); |
michael@0 | 7233 | if (i == map_.length()) |
michael@0 | 7234 | fallback_ = successor; |
michael@0 | 7235 | else |
michael@0 | 7236 | map_[i].block = successor; |
michael@0 | 7237 | } |
michael@0 | 7238 | size_t numSuccessors() const MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 7239 | return map_.length() + (fallback_ ? 1 : 0); |
michael@0 | 7240 | } |
michael@0 | 7241 | void replaceSuccessor(size_t i, MBasicBlock *successor) MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 7242 | setSuccessor(i, successor); |
michael@0 | 7243 | } |
michael@0 | 7244 | MBasicBlock *getSuccessor(size_t i) const MOZ_FINAL MOZ_OVERRIDE { |
michael@0 | 7245 | JS_ASSERT(i < numSuccessors()); |
michael@0 | 7246 | if (i == map_.length()) |
michael@0 | 7247 | return fallback_; |
michael@0 | 7248 | return map_[i].block; |
michael@0 | 7249 | } |
michael@0 | 7250 | |
michael@0 | 7251 | public: |
michael@0 | 7252 | void addCase(JSFunction *func, MBasicBlock *block) { |
michael@0 | 7253 | map_.append(Entry(func, block)); |
michael@0 | 7254 | } |
michael@0 | 7255 | uint32_t numCases() const { |
michael@0 | 7256 | return map_.length(); |
michael@0 | 7257 | } |
michael@0 | 7258 | JSFunction *getCase(uint32_t i) const { |
michael@0 | 7259 | return map_[i].func; |
michael@0 | 7260 | } |
michael@0 | 7261 | MBasicBlock *getCaseBlock(uint32_t i) const { |
michael@0 | 7262 | return map_[i].block; |
michael@0 | 7263 | } |
michael@0 | 7264 | |
michael@0 | 7265 | bool hasFallback() const { |
michael@0 | 7266 | return bool(fallback_); |
michael@0 | 7267 | } |
michael@0 | 7268 | void addFallback(MBasicBlock *block) { |
michael@0 | 7269 | JS_ASSERT(!hasFallback()); |
michael@0 | 7270 | fallback_ = block; |
michael@0 | 7271 | } |
michael@0 | 7272 | MBasicBlock *getFallback() const { |
michael@0 | 7273 | JS_ASSERT(hasFallback()); |
michael@0 | 7274 | return fallback_; |
michael@0 | 7275 | } |
michael@0 | 7276 | |
michael@0 | 7277 | public: |
michael@0 | 7278 | MDefinition *input() const { |
michael@0 | 7279 | return getOperand(0); |
michael@0 | 7280 | } |
michael@0 | 7281 | TypePolicy *typePolicy() { |
michael@0 | 7282 | return this; |
michael@0 | 7283 | } |
michael@0 | 7284 | }; |
michael@0 | 7285 | |
michael@0 | 7286 | // Polymorphic dispatch for inlining, keyed off incoming TypeObject. |
michael@0 | 7287 | class MTypeObjectDispatch : public MDispatchInstruction |
michael@0 | 7288 | { |
michael@0 | 7289 | // Map TypeObject (of CallProp's Target Object) -> JSFunction (yielded by the CallProp). |
michael@0 | 7290 | InlinePropertyTable *inlinePropertyTable_; |
michael@0 | 7291 | |
michael@0 | 7292 | MTypeObjectDispatch(TempAllocator &alloc, MDefinition *input, InlinePropertyTable *table) |
michael@0 | 7293 | : MDispatchInstruction(alloc, input), |
michael@0 | 7294 | inlinePropertyTable_(table) |
michael@0 | 7295 | { } |
michael@0 | 7296 | |
michael@0 | 7297 | public: |
michael@0 | 7298 | INSTRUCTION_HEADER(TypeObjectDispatch) |
michael@0 | 7299 | |
michael@0 | 7300 | static MTypeObjectDispatch *New(TempAllocator &alloc, MDefinition *ins, |
michael@0 | 7301 | InlinePropertyTable *table) |
michael@0 | 7302 | { |
michael@0 | 7303 | return new(alloc) MTypeObjectDispatch(alloc, ins, table); |
michael@0 | 7304 | } |
michael@0 | 7305 | |
michael@0 | 7306 | InlinePropertyTable *propTable() const { |
michael@0 | 7307 | return inlinePropertyTable_; |
michael@0 | 7308 | } |
michael@0 | 7309 | }; |
michael@0 | 7310 | |
michael@0 | 7311 | // Polymorphic dispatch for inlining, keyed off incoming JSFunction*. |
michael@0 | 7312 | class MFunctionDispatch : public MDispatchInstruction |
michael@0 | 7313 | { |
michael@0 | 7314 | MFunctionDispatch(TempAllocator &alloc, MDefinition *input) |
michael@0 | 7315 | : MDispatchInstruction(alloc, input) |
michael@0 | 7316 | { } |
michael@0 | 7317 | |
michael@0 | 7318 | public: |
michael@0 | 7319 | INSTRUCTION_HEADER(FunctionDispatch) |
michael@0 | 7320 | |
michael@0 | 7321 | static MFunctionDispatch *New(TempAllocator &alloc, MDefinition *ins) { |
michael@0 | 7322 | return new(alloc) MFunctionDispatch(alloc, ins); |
michael@0 | 7323 | } |
michael@0 | 7324 | }; |
michael@0 | 7325 | |
michael@0 | 7326 | class MGetElementCache |
michael@0 | 7327 | : public MBinaryInstruction |
michael@0 | 7328 | { |
michael@0 | 7329 | MixPolicy<ObjectPolicy<0>, BoxPolicy<1> > PolicyV; |
michael@0 | 7330 | MixPolicy<ObjectPolicy<0>, IntPolicy<1> > PolicyT; |
michael@0 | 7331 | |
michael@0 | 7332 | // See the comment in IonBuilder::jsop_getelem. |
michael@0 | 7333 | bool monitoredResult_; |
michael@0 | 7334 | |
michael@0 | 7335 | MGetElementCache(MDefinition *obj, MDefinition *value, bool monitoredResult) |
michael@0 | 7336 | : MBinaryInstruction(obj, value), monitoredResult_(monitoredResult) |
michael@0 | 7337 | { |
michael@0 | 7338 | setResultType(MIRType_Value); |
michael@0 | 7339 | } |
michael@0 | 7340 | |
michael@0 | 7341 | public: |
michael@0 | 7342 | INSTRUCTION_HEADER(GetElementCache) |
michael@0 | 7343 | |
michael@0 | 7344 | static MGetElementCache *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value, |
michael@0 | 7345 | bool monitoredResult) |
michael@0 | 7346 | { |
michael@0 | 7347 | return new(alloc) MGetElementCache(obj, value, monitoredResult); |
michael@0 | 7348 | } |
michael@0 | 7349 | |
michael@0 | 7350 | MDefinition *object() const { |
michael@0 | 7351 | return getOperand(0); |
michael@0 | 7352 | } |
michael@0 | 7353 | MDefinition *index() const { |
michael@0 | 7354 | return getOperand(1); |
michael@0 | 7355 | } |
michael@0 | 7356 | bool monitoredResult() const { |
michael@0 | 7357 | return monitoredResult_; |
michael@0 | 7358 | } |
michael@0 | 7359 | |
michael@0 | 7360 | bool allowDoubleResult() const; |
michael@0 | 7361 | |
michael@0 | 7362 | TypePolicy *typePolicy() { |
michael@0 | 7363 | if (type() == MIRType_Value) |
michael@0 | 7364 | return &PolicyV; |
michael@0 | 7365 | return &PolicyT; |
michael@0 | 7366 | } |
michael@0 | 7367 | }; |
michael@0 | 7368 | |
michael@0 | 7369 | class MBindNameCache |
michael@0 | 7370 | : public MUnaryInstruction, |
michael@0 | 7371 | public SingleObjectPolicy |
michael@0 | 7372 | { |
michael@0 | 7373 | CompilerRootPropertyName name_; |
michael@0 | 7374 | CompilerRootScript script_; |
michael@0 | 7375 | jsbytecode *pc_; |
michael@0 | 7376 | |
michael@0 | 7377 | MBindNameCache(MDefinition *scopeChain, PropertyName *name, JSScript *script, jsbytecode *pc) |
michael@0 | 7378 | : MUnaryInstruction(scopeChain), name_(name), script_(script), pc_(pc) |
michael@0 | 7379 | { |
michael@0 | 7380 | setResultType(MIRType_Object); |
michael@0 | 7381 | } |
michael@0 | 7382 | |
michael@0 | 7383 | public: |
michael@0 | 7384 | INSTRUCTION_HEADER(BindNameCache) |
michael@0 | 7385 | |
michael@0 | 7386 | static MBindNameCache *New(TempAllocator &alloc, MDefinition *scopeChain, PropertyName *name, |
michael@0 | 7387 | JSScript *script, jsbytecode *pc) |
michael@0 | 7388 | { |
michael@0 | 7389 | return new(alloc) MBindNameCache(scopeChain, name, script, pc); |
michael@0 | 7390 | } |
michael@0 | 7391 | |
michael@0 | 7392 | TypePolicy *typePolicy() { |
michael@0 | 7393 | return this; |
michael@0 | 7394 | } |
michael@0 | 7395 | MDefinition *scopeChain() const { |
michael@0 | 7396 | return getOperand(0); |
michael@0 | 7397 | } |
michael@0 | 7398 | PropertyName *name() const { |
michael@0 | 7399 | return name_; |
michael@0 | 7400 | } |
michael@0 | 7401 | JSScript *script() const { |
michael@0 | 7402 | return script_; |
michael@0 | 7403 | } |
michael@0 | 7404 | jsbytecode *pc() const { |
michael@0 | 7405 | return pc_; |
michael@0 | 7406 | } |
michael@0 | 7407 | }; |
michael@0 | 7408 | |
michael@0 | 7409 | // Guard on an object's shape. |
michael@0 | 7410 | class MGuardShape |
michael@0 | 7411 | : public MUnaryInstruction, |
michael@0 | 7412 | public SingleObjectPolicy |
michael@0 | 7413 | { |
michael@0 | 7414 | CompilerRootShape shape_; |
michael@0 | 7415 | BailoutKind bailoutKind_; |
michael@0 | 7416 | |
michael@0 | 7417 | MGuardShape(MDefinition *obj, Shape *shape, BailoutKind bailoutKind) |
michael@0 | 7418 | : MUnaryInstruction(obj), |
michael@0 | 7419 | shape_(shape), |
michael@0 | 7420 | bailoutKind_(bailoutKind) |
michael@0 | 7421 | { |
michael@0 | 7422 | setGuard(); |
michael@0 | 7423 | setMovable(); |
michael@0 | 7424 | setResultType(MIRType_Object); |
michael@0 | 7425 | } |
michael@0 | 7426 | |
michael@0 | 7427 | public: |
michael@0 | 7428 | INSTRUCTION_HEADER(GuardShape) |
michael@0 | 7429 | |
michael@0 | 7430 | static MGuardShape *New(TempAllocator &alloc, MDefinition *obj, Shape *shape, |
michael@0 | 7431 | BailoutKind bailoutKind) |
michael@0 | 7432 | { |
michael@0 | 7433 | return new(alloc) MGuardShape(obj, shape, bailoutKind); |
michael@0 | 7434 | } |
michael@0 | 7435 | |
michael@0 | 7436 | TypePolicy *typePolicy() { |
michael@0 | 7437 | return this; |
michael@0 | 7438 | } |
michael@0 | 7439 | MDefinition *obj() const { |
michael@0 | 7440 | return getOperand(0); |
michael@0 | 7441 | } |
michael@0 | 7442 | const Shape *shape() const { |
michael@0 | 7443 | return shape_; |
michael@0 | 7444 | } |
michael@0 | 7445 | BailoutKind bailoutKind() const { |
michael@0 | 7446 | return bailoutKind_; |
michael@0 | 7447 | } |
michael@0 | 7448 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 7449 | if (!ins->isGuardShape()) |
michael@0 | 7450 | return false; |
michael@0 | 7451 | if (shape() != ins->toGuardShape()->shape()) |
michael@0 | 7452 | return false; |
michael@0 | 7453 | if (bailoutKind() != ins->toGuardShape()->bailoutKind()) |
michael@0 | 7454 | return false; |
michael@0 | 7455 | return congruentIfOperandsEqual(ins); |
michael@0 | 7456 | } |
michael@0 | 7457 | AliasSet getAliasSet() const { |
michael@0 | 7458 | return AliasSet::Load(AliasSet::ObjectFields); |
michael@0 | 7459 | } |
michael@0 | 7460 | }; |
michael@0 | 7461 | |
michael@0 | 7462 | // Guard on an object's type, inclusively or exclusively. |
michael@0 | 7463 | class MGuardObjectType |
michael@0 | 7464 | : public MUnaryInstruction, |
michael@0 | 7465 | public SingleObjectPolicy |
michael@0 | 7466 | { |
michael@0 | 7467 | CompilerRoot<types::TypeObject *> typeObject_; |
michael@0 | 7468 | bool bailOnEquality_; |
michael@0 | 7469 | |
michael@0 | 7470 | MGuardObjectType(MDefinition *obj, types::TypeObject *typeObject, bool bailOnEquality) |
michael@0 | 7471 | : MUnaryInstruction(obj), |
michael@0 | 7472 | typeObject_(typeObject), |
michael@0 | 7473 | bailOnEquality_(bailOnEquality) |
michael@0 | 7474 | { |
michael@0 | 7475 | setGuard(); |
michael@0 | 7476 | setMovable(); |
michael@0 | 7477 | setResultType(MIRType_Object); |
michael@0 | 7478 | } |
michael@0 | 7479 | |
michael@0 | 7480 | public: |
michael@0 | 7481 | INSTRUCTION_HEADER(GuardObjectType) |
michael@0 | 7482 | |
michael@0 | 7483 | static MGuardObjectType *New(TempAllocator &alloc, MDefinition *obj, types::TypeObject *typeObject, |
michael@0 | 7484 | bool bailOnEquality) { |
michael@0 | 7485 | return new(alloc) MGuardObjectType(obj, typeObject, bailOnEquality); |
michael@0 | 7486 | } |
michael@0 | 7487 | |
michael@0 | 7488 | TypePolicy *typePolicy() { |
michael@0 | 7489 | return this; |
michael@0 | 7490 | } |
michael@0 | 7491 | MDefinition *obj() const { |
michael@0 | 7492 | return getOperand(0); |
michael@0 | 7493 | } |
michael@0 | 7494 | const types::TypeObject *typeObject() const { |
michael@0 | 7495 | return typeObject_; |
michael@0 | 7496 | } |
michael@0 | 7497 | bool bailOnEquality() const { |
michael@0 | 7498 | return bailOnEquality_; |
michael@0 | 7499 | } |
michael@0 | 7500 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 7501 | if (!ins->isGuardObjectType()) |
michael@0 | 7502 | return false; |
michael@0 | 7503 | if (typeObject() != ins->toGuardObjectType()->typeObject()) |
michael@0 | 7504 | return false; |
michael@0 | 7505 | if (bailOnEquality() != ins->toGuardObjectType()->bailOnEquality()) |
michael@0 | 7506 | return false; |
michael@0 | 7507 | return congruentIfOperandsEqual(ins); |
michael@0 | 7508 | } |
michael@0 | 7509 | AliasSet getAliasSet() const { |
michael@0 | 7510 | return AliasSet::Load(AliasSet::ObjectFields); |
michael@0 | 7511 | } |
michael@0 | 7512 | }; |
michael@0 | 7513 | |
michael@0 | 7514 | // Guard on an object's identity, inclusively or exclusively. |
michael@0 | 7515 | class MGuardObjectIdentity |
michael@0 | 7516 | : public MUnaryInstruction, |
michael@0 | 7517 | public SingleObjectPolicy |
michael@0 | 7518 | { |
michael@0 | 7519 | CompilerRoot<JSObject *> singleObject_; |
michael@0 | 7520 | bool bailOnEquality_; |
michael@0 | 7521 | |
michael@0 | 7522 | MGuardObjectIdentity(MDefinition *obj, JSObject *singleObject, bool bailOnEquality) |
michael@0 | 7523 | : MUnaryInstruction(obj), |
michael@0 | 7524 | singleObject_(singleObject), |
michael@0 | 7525 | bailOnEquality_(bailOnEquality) |
michael@0 | 7526 | { |
michael@0 | 7527 | setGuard(); |
michael@0 | 7528 | setMovable(); |
michael@0 | 7529 | setResultType(MIRType_Object); |
michael@0 | 7530 | } |
michael@0 | 7531 | |
michael@0 | 7532 | public: |
michael@0 | 7533 | INSTRUCTION_HEADER(GuardObjectIdentity) |
michael@0 | 7534 | |
michael@0 | 7535 | static MGuardObjectIdentity *New(TempAllocator &alloc, MDefinition *obj, JSObject *singleObject, |
michael@0 | 7536 | bool bailOnEquality) { |
michael@0 | 7537 | return new(alloc) MGuardObjectIdentity(obj, singleObject, bailOnEquality); |
michael@0 | 7538 | } |
michael@0 | 7539 | |
michael@0 | 7540 | TypePolicy *typePolicy() { |
michael@0 | 7541 | return this; |
michael@0 | 7542 | } |
michael@0 | 7543 | MDefinition *obj() const { |
michael@0 | 7544 | return getOperand(0); |
michael@0 | 7545 | } |
michael@0 | 7546 | JSObject *singleObject() const { |
michael@0 | 7547 | return singleObject_; |
michael@0 | 7548 | } |
michael@0 | 7549 | bool bailOnEquality() const { |
michael@0 | 7550 | return bailOnEquality_; |
michael@0 | 7551 | } |
michael@0 | 7552 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 7553 | if (!ins->isGuardObjectIdentity()) |
michael@0 | 7554 | return false; |
michael@0 | 7555 | if (singleObject() != ins->toGuardObjectIdentity()->singleObject()) |
michael@0 | 7556 | return false; |
michael@0 | 7557 | if (bailOnEquality() != ins->toGuardObjectIdentity()->bailOnEquality()) |
michael@0 | 7558 | return false; |
michael@0 | 7559 | return congruentIfOperandsEqual(ins); |
michael@0 | 7560 | } |
michael@0 | 7561 | AliasSet getAliasSet() const { |
michael@0 | 7562 | return AliasSet::Load(AliasSet::ObjectFields); |
michael@0 | 7563 | } |
michael@0 | 7564 | }; |
michael@0 | 7565 | |
michael@0 | 7566 | // Guard on an object's class. |
michael@0 | 7567 | class MGuardClass |
michael@0 | 7568 | : public MUnaryInstruction, |
michael@0 | 7569 | public SingleObjectPolicy |
michael@0 | 7570 | { |
michael@0 | 7571 | const Class *class_; |
michael@0 | 7572 | |
michael@0 | 7573 | MGuardClass(MDefinition *obj, const Class *clasp) |
michael@0 | 7574 | : MUnaryInstruction(obj), |
michael@0 | 7575 | class_(clasp) |
michael@0 | 7576 | { |
michael@0 | 7577 | setGuard(); |
michael@0 | 7578 | setMovable(); |
michael@0 | 7579 | } |
michael@0 | 7580 | |
michael@0 | 7581 | public: |
michael@0 | 7582 | INSTRUCTION_HEADER(GuardClass) |
michael@0 | 7583 | |
michael@0 | 7584 | static MGuardClass *New(TempAllocator &alloc, MDefinition *obj, const Class *clasp) { |
michael@0 | 7585 | return new(alloc) MGuardClass(obj, clasp); |
michael@0 | 7586 | } |
michael@0 | 7587 | |
michael@0 | 7588 | TypePolicy *typePolicy() { |
michael@0 | 7589 | return this; |
michael@0 | 7590 | } |
michael@0 | 7591 | MDefinition *obj() const { |
michael@0 | 7592 | return getOperand(0); |
michael@0 | 7593 | } |
michael@0 | 7594 | const Class *getClass() const { |
michael@0 | 7595 | return class_; |
michael@0 | 7596 | } |
michael@0 | 7597 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 7598 | if (!ins->isGuardClass()) |
michael@0 | 7599 | return false; |
michael@0 | 7600 | if (getClass() != ins->toGuardClass()->getClass()) |
michael@0 | 7601 | return false; |
michael@0 | 7602 | return congruentIfOperandsEqual(ins); |
michael@0 | 7603 | } |
michael@0 | 7604 | AliasSet getAliasSet() const { |
michael@0 | 7605 | return AliasSet::Load(AliasSet::ObjectFields); |
michael@0 | 7606 | } |
michael@0 | 7607 | }; |
michael@0 | 7608 | |
michael@0 | 7609 | // Load from vp[slot] (slots that are not inline in an object). |
michael@0 | 7610 | class MLoadSlot |
michael@0 | 7611 | : public MUnaryInstruction, |
michael@0 | 7612 | public SingleObjectPolicy |
michael@0 | 7613 | { |
michael@0 | 7614 | uint32_t slot_; |
michael@0 | 7615 | |
michael@0 | 7616 | MLoadSlot(MDefinition *slots, uint32_t slot) |
michael@0 | 7617 | : MUnaryInstruction(slots), |
michael@0 | 7618 | slot_(slot) |
michael@0 | 7619 | { |
michael@0 | 7620 | setResultType(MIRType_Value); |
michael@0 | 7621 | setMovable(); |
michael@0 | 7622 | JS_ASSERT(slots->type() == MIRType_Slots); |
michael@0 | 7623 | } |
michael@0 | 7624 | |
michael@0 | 7625 | public: |
michael@0 | 7626 | INSTRUCTION_HEADER(LoadSlot) |
michael@0 | 7627 | |
michael@0 | 7628 | static MLoadSlot *New(TempAllocator &alloc, MDefinition *slots, uint32_t slot) { |
michael@0 | 7629 | return new(alloc) MLoadSlot(slots, slot); |
michael@0 | 7630 | } |
michael@0 | 7631 | |
michael@0 | 7632 | TypePolicy *typePolicy() { |
michael@0 | 7633 | return this; |
michael@0 | 7634 | } |
michael@0 | 7635 | MDefinition *slots() const { |
michael@0 | 7636 | return getOperand(0); |
michael@0 | 7637 | } |
michael@0 | 7638 | uint32_t slot() const { |
michael@0 | 7639 | return slot_; |
michael@0 | 7640 | } |
michael@0 | 7641 | |
michael@0 | 7642 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 7643 | if (!ins->isLoadSlot()) |
michael@0 | 7644 | return false; |
michael@0 | 7645 | if (slot() != ins->toLoadSlot()->slot()) |
michael@0 | 7646 | return false; |
michael@0 | 7647 | return congruentIfOperandsEqual(ins); |
michael@0 | 7648 | } |
michael@0 | 7649 | AliasSet getAliasSet() const { |
michael@0 | 7650 | JS_ASSERT(slots()->type() == MIRType_Slots); |
michael@0 | 7651 | return AliasSet::Load(AliasSet::DynamicSlot); |
michael@0 | 7652 | } |
michael@0 | 7653 | bool mightAlias(const MDefinition *store) const; |
michael@0 | 7654 | }; |
michael@0 | 7655 | |
michael@0 | 7656 | // Inline call to access a function's environment (scope chain). |
michael@0 | 7657 | class MFunctionEnvironment |
michael@0 | 7658 | : public MUnaryInstruction, |
michael@0 | 7659 | public SingleObjectPolicy |
michael@0 | 7660 | { |
michael@0 | 7661 | public: |
michael@0 | 7662 | MFunctionEnvironment(MDefinition *function) |
michael@0 | 7663 | : MUnaryInstruction(function) |
michael@0 | 7664 | { |
michael@0 | 7665 | setResultType(MIRType_Object); |
michael@0 | 7666 | setMovable(); |
michael@0 | 7667 | } |
michael@0 | 7668 | |
michael@0 | 7669 | INSTRUCTION_HEADER(FunctionEnvironment) |
michael@0 | 7670 | |
michael@0 | 7671 | static MFunctionEnvironment *New(TempAllocator &alloc, MDefinition *function) { |
michael@0 | 7672 | return new(alloc) MFunctionEnvironment(function); |
michael@0 | 7673 | } |
michael@0 | 7674 | |
michael@0 | 7675 | MDefinition *function() const { |
michael@0 | 7676 | return getOperand(0); |
michael@0 | 7677 | } |
michael@0 | 7678 | |
michael@0 | 7679 | TypePolicy *typePolicy() { |
michael@0 | 7680 | return this; |
michael@0 | 7681 | } |
michael@0 | 7682 | |
michael@0 | 7683 | // A function's environment is fixed. |
michael@0 | 7684 | AliasSet getAliasSet() const { |
michael@0 | 7685 | return AliasSet::None(); |
michael@0 | 7686 | } |
michael@0 | 7687 | }; |
michael@0 | 7688 | |
michael@0 | 7689 | // Loads the current js::ForkJoinContext*. |
michael@0 | 7690 | // Only applicable in ParallelExecution. |
michael@0 | 7691 | class MForkJoinContext |
michael@0 | 7692 | : public MNullaryInstruction |
michael@0 | 7693 | { |
michael@0 | 7694 | MForkJoinContext() |
michael@0 | 7695 | : MNullaryInstruction() |
michael@0 | 7696 | { |
michael@0 | 7697 | setResultType(MIRType_ForkJoinContext); |
michael@0 | 7698 | } |
michael@0 | 7699 | |
michael@0 | 7700 | public: |
michael@0 | 7701 | INSTRUCTION_HEADER(ForkJoinContext); |
michael@0 | 7702 | |
michael@0 | 7703 | static MForkJoinContext *New(TempAllocator &alloc) { |
michael@0 | 7704 | return new(alloc) MForkJoinContext(); |
michael@0 | 7705 | } |
michael@0 | 7706 | |
michael@0 | 7707 | AliasSet getAliasSet() const { |
michael@0 | 7708 | // Indicate that this instruction reads nothing, stores nothing. |
michael@0 | 7709 | // (For all intents and purposes) |
michael@0 | 7710 | return AliasSet::None(); |
michael@0 | 7711 | } |
michael@0 | 7712 | |
michael@0 | 7713 | bool possiblyCalls() const { |
michael@0 | 7714 | return true; |
michael@0 | 7715 | } |
michael@0 | 7716 | }; |
michael@0 | 7717 | |
michael@0 | 7718 | // Calls the ForkJoinGetSlice stub, used for inlining the eponymous intrinsic. |
michael@0 | 7719 | // Only applicable in ParallelExecution. |
michael@0 | 7720 | class MForkJoinGetSlice |
michael@0 | 7721 | : public MUnaryInstruction |
michael@0 | 7722 | { |
michael@0 | 7723 | MForkJoinGetSlice(MDefinition *cx) |
michael@0 | 7724 | : MUnaryInstruction(cx) |
michael@0 | 7725 | { |
michael@0 | 7726 | setResultType(MIRType_Int32); |
michael@0 | 7727 | } |
michael@0 | 7728 | |
michael@0 | 7729 | public: |
michael@0 | 7730 | INSTRUCTION_HEADER(ForkJoinGetSlice); |
michael@0 | 7731 | |
michael@0 | 7732 | static MForkJoinGetSlice *New(TempAllocator &alloc, MDefinition *cx) { |
michael@0 | 7733 | return new(alloc) MForkJoinGetSlice(cx); |
michael@0 | 7734 | } |
michael@0 | 7735 | |
michael@0 | 7736 | MDefinition *forkJoinContext() { |
michael@0 | 7737 | return getOperand(0); |
michael@0 | 7738 | } |
michael@0 | 7739 | |
michael@0 | 7740 | bool possiblyCalls() const { |
michael@0 | 7741 | return true; |
michael@0 | 7742 | } |
michael@0 | 7743 | }; |
michael@0 | 7744 | |
michael@0 | 7745 | // Store to vp[slot] (slots that are not inline in an object). |
michael@0 | 7746 | class MStoreSlot |
michael@0 | 7747 | : public MBinaryInstruction, |
michael@0 | 7748 | public MixPolicy<ObjectPolicy<0>, NoFloatPolicy<1> > |
michael@0 | 7749 | { |
michael@0 | 7750 | uint32_t slot_; |
michael@0 | 7751 | MIRType slotType_; |
michael@0 | 7752 | bool needsBarrier_; |
michael@0 | 7753 | |
michael@0 | 7754 | MStoreSlot(MDefinition *slots, uint32_t slot, MDefinition *value, bool barrier) |
michael@0 | 7755 | : MBinaryInstruction(slots, value), |
michael@0 | 7756 | slot_(slot), |
michael@0 | 7757 | slotType_(MIRType_Value), |
michael@0 | 7758 | needsBarrier_(barrier) |
michael@0 | 7759 | { |
michael@0 | 7760 | JS_ASSERT(slots->type() == MIRType_Slots); |
michael@0 | 7761 | } |
michael@0 | 7762 | |
michael@0 | 7763 | public: |
michael@0 | 7764 | INSTRUCTION_HEADER(StoreSlot) |
michael@0 | 7765 | |
michael@0 | 7766 | static MStoreSlot *New(TempAllocator &alloc, MDefinition *slots, uint32_t slot, |
michael@0 | 7767 | MDefinition *value) |
michael@0 | 7768 | { |
michael@0 | 7769 | return new(alloc) MStoreSlot(slots, slot, value, false); |
michael@0 | 7770 | } |
michael@0 | 7771 | static MStoreSlot *NewBarriered(TempAllocator &alloc, MDefinition *slots, uint32_t slot, |
michael@0 | 7772 | MDefinition *value) |
michael@0 | 7773 | { |
michael@0 | 7774 | return new(alloc) MStoreSlot(slots, slot, value, true); |
michael@0 | 7775 | } |
michael@0 | 7776 | |
michael@0 | 7777 | TypePolicy *typePolicy() { |
michael@0 | 7778 | return this; |
michael@0 | 7779 | } |
michael@0 | 7780 | MDefinition *slots() const { |
michael@0 | 7781 | return getOperand(0); |
michael@0 | 7782 | } |
michael@0 | 7783 | MDefinition *value() const { |
michael@0 | 7784 | return getOperand(1); |
michael@0 | 7785 | } |
michael@0 | 7786 | uint32_t slot() const { |
michael@0 | 7787 | return slot_; |
michael@0 | 7788 | } |
michael@0 | 7789 | MIRType slotType() const { |
michael@0 | 7790 | return slotType_; |
michael@0 | 7791 | } |
michael@0 | 7792 | void setSlotType(MIRType slotType) { |
michael@0 | 7793 | JS_ASSERT(slotType != MIRType_None); |
michael@0 | 7794 | slotType_ = slotType; |
michael@0 | 7795 | } |
michael@0 | 7796 | bool needsBarrier() const { |
michael@0 | 7797 | return needsBarrier_; |
michael@0 | 7798 | } |
michael@0 | 7799 | void setNeedsBarrier() { |
michael@0 | 7800 | needsBarrier_ = true; |
michael@0 | 7801 | } |
michael@0 | 7802 | AliasSet getAliasSet() const { |
michael@0 | 7803 | return AliasSet::Store(AliasSet::DynamicSlot); |
michael@0 | 7804 | } |
michael@0 | 7805 | }; |
michael@0 | 7806 | |
michael@0 | 7807 | class MGetNameCache |
michael@0 | 7808 | : public MUnaryInstruction, |
michael@0 | 7809 | public SingleObjectPolicy |
michael@0 | 7810 | { |
michael@0 | 7811 | public: |
michael@0 | 7812 | enum AccessKind { |
michael@0 | 7813 | NAMETYPEOF, |
michael@0 | 7814 | NAME |
michael@0 | 7815 | }; |
michael@0 | 7816 | |
michael@0 | 7817 | private: |
michael@0 | 7818 | CompilerRootPropertyName name_; |
michael@0 | 7819 | AccessKind kind_; |
michael@0 | 7820 | |
michael@0 | 7821 | MGetNameCache(MDefinition *obj, PropertyName *name, AccessKind kind) |
michael@0 | 7822 | : MUnaryInstruction(obj), |
michael@0 | 7823 | name_(name), |
michael@0 | 7824 | kind_(kind) |
michael@0 | 7825 | { |
michael@0 | 7826 | setResultType(MIRType_Value); |
michael@0 | 7827 | } |
michael@0 | 7828 | |
michael@0 | 7829 | public: |
michael@0 | 7830 | INSTRUCTION_HEADER(GetNameCache) |
michael@0 | 7831 | |
michael@0 | 7832 | static MGetNameCache *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name, |
michael@0 | 7833 | AccessKind kind) |
michael@0 | 7834 | { |
michael@0 | 7835 | return new(alloc) MGetNameCache(obj, name, kind); |
michael@0 | 7836 | } |
michael@0 | 7837 | TypePolicy *typePolicy() { |
michael@0 | 7838 | return this; |
michael@0 | 7839 | } |
michael@0 | 7840 | MDefinition *scopeObj() const { |
michael@0 | 7841 | return getOperand(0); |
michael@0 | 7842 | } |
michael@0 | 7843 | PropertyName *name() const { |
michael@0 | 7844 | return name_; |
michael@0 | 7845 | } |
michael@0 | 7846 | AccessKind accessKind() const { |
michael@0 | 7847 | return kind_; |
michael@0 | 7848 | } |
michael@0 | 7849 | }; |
michael@0 | 7850 | |
michael@0 | 7851 | class MCallGetIntrinsicValue : public MNullaryInstruction |
michael@0 | 7852 | { |
michael@0 | 7853 | CompilerRootPropertyName name_; |
michael@0 | 7854 | |
michael@0 | 7855 | MCallGetIntrinsicValue(PropertyName *name) |
michael@0 | 7856 | : name_(name) |
michael@0 | 7857 | { |
michael@0 | 7858 | setResultType(MIRType_Value); |
michael@0 | 7859 | } |
michael@0 | 7860 | |
michael@0 | 7861 | public: |
michael@0 | 7862 | INSTRUCTION_HEADER(CallGetIntrinsicValue) |
michael@0 | 7863 | |
michael@0 | 7864 | static MCallGetIntrinsicValue *New(TempAllocator &alloc, PropertyName *name) { |
michael@0 | 7865 | return new(alloc) MCallGetIntrinsicValue(name); |
michael@0 | 7866 | } |
michael@0 | 7867 | PropertyName *name() const { |
michael@0 | 7868 | return name_; |
michael@0 | 7869 | } |
michael@0 | 7870 | AliasSet getAliasSet() const { |
michael@0 | 7871 | return AliasSet::None(); |
michael@0 | 7872 | } |
michael@0 | 7873 | bool possiblyCalls() const { |
michael@0 | 7874 | return true; |
michael@0 | 7875 | } |
michael@0 | 7876 | }; |
michael@0 | 7877 | |
michael@0 | 7878 | class MCallsiteCloneCache |
michael@0 | 7879 | : public MUnaryInstruction, |
michael@0 | 7880 | public SingleObjectPolicy |
michael@0 | 7881 | { |
michael@0 | 7882 | jsbytecode *callPc_; |
michael@0 | 7883 | |
michael@0 | 7884 | MCallsiteCloneCache(MDefinition *callee, jsbytecode *callPc) |
michael@0 | 7885 | : MUnaryInstruction(callee), |
michael@0 | 7886 | callPc_(callPc) |
michael@0 | 7887 | { |
michael@0 | 7888 | setResultType(MIRType_Object); |
michael@0 | 7889 | } |
michael@0 | 7890 | |
michael@0 | 7891 | public: |
michael@0 | 7892 | INSTRUCTION_HEADER(CallsiteCloneCache); |
michael@0 | 7893 | |
michael@0 | 7894 | static MCallsiteCloneCache *New(TempAllocator &alloc, MDefinition *callee, jsbytecode *callPc) { |
michael@0 | 7895 | return new(alloc) MCallsiteCloneCache(callee, callPc); |
michael@0 | 7896 | } |
michael@0 | 7897 | TypePolicy *typePolicy() { |
michael@0 | 7898 | return this; |
michael@0 | 7899 | } |
michael@0 | 7900 | MDefinition *callee() const { |
michael@0 | 7901 | return getOperand(0); |
michael@0 | 7902 | } |
michael@0 | 7903 | jsbytecode *callPc() const { |
michael@0 | 7904 | return callPc_; |
michael@0 | 7905 | } |
michael@0 | 7906 | |
michael@0 | 7907 | // Callsite cloning is idempotent. |
michael@0 | 7908 | AliasSet getAliasSet() const { |
michael@0 | 7909 | return AliasSet::None(); |
michael@0 | 7910 | } |
michael@0 | 7911 | }; |
michael@0 | 7912 | |
michael@0 | 7913 | class MSetPropertyInstruction : public MBinaryInstruction |
michael@0 | 7914 | { |
michael@0 | 7915 | CompilerRootPropertyName name_; |
michael@0 | 7916 | bool strict_; |
michael@0 | 7917 | bool needsBarrier_; |
michael@0 | 7918 | |
michael@0 | 7919 | protected: |
michael@0 | 7920 | MSetPropertyInstruction(MDefinition *obj, MDefinition *value, PropertyName *name, |
michael@0 | 7921 | bool strict) |
michael@0 | 7922 | : MBinaryInstruction(obj, value), |
michael@0 | 7923 | name_(name), strict_(strict), needsBarrier_(true) |
michael@0 | 7924 | {} |
michael@0 | 7925 | |
michael@0 | 7926 | public: |
michael@0 | 7927 | MDefinition *object() const { |
michael@0 | 7928 | return getOperand(0); |
michael@0 | 7929 | } |
michael@0 | 7930 | MDefinition *value() const { |
michael@0 | 7931 | return getOperand(1); |
michael@0 | 7932 | } |
michael@0 | 7933 | PropertyName *name() const { |
michael@0 | 7934 | return name_; |
michael@0 | 7935 | } |
michael@0 | 7936 | bool strict() const { |
michael@0 | 7937 | return strict_; |
michael@0 | 7938 | } |
michael@0 | 7939 | bool needsBarrier() const { |
michael@0 | 7940 | return needsBarrier_; |
michael@0 | 7941 | } |
michael@0 | 7942 | void setNeedsBarrier() { |
michael@0 | 7943 | needsBarrier_ = true; |
michael@0 | 7944 | } |
michael@0 | 7945 | }; |
michael@0 | 7946 | |
michael@0 | 7947 | class MSetElementInstruction |
michael@0 | 7948 | : public MTernaryInstruction |
michael@0 | 7949 | { |
michael@0 | 7950 | protected: |
michael@0 | 7951 | MSetElementInstruction(MDefinition *object, MDefinition *index, MDefinition *value) |
michael@0 | 7952 | : MTernaryInstruction(object, index, value) |
michael@0 | 7953 | { |
michael@0 | 7954 | } |
michael@0 | 7955 | |
michael@0 | 7956 | public: |
michael@0 | 7957 | MDefinition *object() const { |
michael@0 | 7958 | return getOperand(0); |
michael@0 | 7959 | } |
michael@0 | 7960 | MDefinition *index() const { |
michael@0 | 7961 | return getOperand(1); |
michael@0 | 7962 | } |
michael@0 | 7963 | MDefinition *value() const { |
michael@0 | 7964 | return getOperand(2); |
michael@0 | 7965 | } |
michael@0 | 7966 | }; |
michael@0 | 7967 | |
michael@0 | 7968 | class MDeleteProperty |
michael@0 | 7969 | : public MUnaryInstruction, |
michael@0 | 7970 | public BoxInputsPolicy |
michael@0 | 7971 | { |
michael@0 | 7972 | CompilerRootPropertyName name_; |
michael@0 | 7973 | |
michael@0 | 7974 | protected: |
michael@0 | 7975 | MDeleteProperty(MDefinition *val, PropertyName *name) |
michael@0 | 7976 | : MUnaryInstruction(val), |
michael@0 | 7977 | name_(name) |
michael@0 | 7978 | { |
michael@0 | 7979 | setResultType(MIRType_Boolean); |
michael@0 | 7980 | } |
michael@0 | 7981 | |
michael@0 | 7982 | public: |
michael@0 | 7983 | INSTRUCTION_HEADER(DeleteProperty) |
michael@0 | 7984 | |
michael@0 | 7985 | static MDeleteProperty *New(TempAllocator &alloc, MDefinition *obj, PropertyName *name) { |
michael@0 | 7986 | return new(alloc) MDeleteProperty(obj, name); |
michael@0 | 7987 | } |
michael@0 | 7988 | MDefinition *value() const { |
michael@0 | 7989 | return getOperand(0); |
michael@0 | 7990 | } |
michael@0 | 7991 | PropertyName *name() const { |
michael@0 | 7992 | return name_; |
michael@0 | 7993 | } |
michael@0 | 7994 | virtual TypePolicy *typePolicy() { |
michael@0 | 7995 | return this; |
michael@0 | 7996 | } |
michael@0 | 7997 | }; |
michael@0 | 7998 | |
michael@0 | 7999 | class MDeleteElement |
michael@0 | 8000 | : public MBinaryInstruction, |
michael@0 | 8001 | public BoxInputsPolicy |
michael@0 | 8002 | { |
michael@0 | 8003 | MDeleteElement(MDefinition *value, MDefinition *index) |
michael@0 | 8004 | : MBinaryInstruction(value, index) |
michael@0 | 8005 | { |
michael@0 | 8006 | setResultType(MIRType_Boolean); |
michael@0 | 8007 | } |
michael@0 | 8008 | |
michael@0 | 8009 | public: |
michael@0 | 8010 | INSTRUCTION_HEADER(DeleteElement) |
michael@0 | 8011 | |
michael@0 | 8012 | static MDeleteElement *New(TempAllocator &alloc, MDefinition *value, MDefinition *index) { |
michael@0 | 8013 | return new(alloc) MDeleteElement(value, index); |
michael@0 | 8014 | } |
michael@0 | 8015 | MDefinition *value() const { |
michael@0 | 8016 | return getOperand(0); |
michael@0 | 8017 | } |
michael@0 | 8018 | MDefinition *index() const { |
michael@0 | 8019 | return getOperand(1); |
michael@0 | 8020 | } |
michael@0 | 8021 | virtual TypePolicy *typePolicy() { |
michael@0 | 8022 | return this; |
michael@0 | 8023 | } |
michael@0 | 8024 | }; |
michael@0 | 8025 | |
michael@0 | 8026 | // Note: This uses CallSetElementPolicy to always box its second input, |
michael@0 | 8027 | // ensuring we don't need two LIR instructions to lower this. |
michael@0 | 8028 | class MCallSetProperty |
michael@0 | 8029 | : public MSetPropertyInstruction, |
michael@0 | 8030 | public CallSetElementPolicy |
michael@0 | 8031 | { |
michael@0 | 8032 | MCallSetProperty(MDefinition *obj, MDefinition *value, PropertyName *name, bool strict) |
michael@0 | 8033 | : MSetPropertyInstruction(obj, value, name, strict) |
michael@0 | 8034 | { |
michael@0 | 8035 | } |
michael@0 | 8036 | |
michael@0 | 8037 | public: |
michael@0 | 8038 | INSTRUCTION_HEADER(CallSetProperty) |
michael@0 | 8039 | |
michael@0 | 8040 | static MCallSetProperty *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value, |
michael@0 | 8041 | PropertyName *name, bool strict) |
michael@0 | 8042 | { |
michael@0 | 8043 | return new(alloc) MCallSetProperty(obj, value, name, strict); |
michael@0 | 8044 | } |
michael@0 | 8045 | |
michael@0 | 8046 | TypePolicy *typePolicy() { |
michael@0 | 8047 | return this; |
michael@0 | 8048 | } |
michael@0 | 8049 | bool possiblyCalls() const { |
michael@0 | 8050 | return true; |
michael@0 | 8051 | } |
michael@0 | 8052 | }; |
michael@0 | 8053 | |
michael@0 | 8054 | class MSetPropertyCache |
michael@0 | 8055 | : public MSetPropertyInstruction, |
michael@0 | 8056 | public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> > |
michael@0 | 8057 | { |
michael@0 | 8058 | bool needsTypeBarrier_; |
michael@0 | 8059 | |
michael@0 | 8060 | MSetPropertyCache(MDefinition *obj, MDefinition *value, PropertyName *name, bool strict, |
michael@0 | 8061 | bool typeBarrier) |
michael@0 | 8062 | : MSetPropertyInstruction(obj, value, name, strict), |
michael@0 | 8063 | needsTypeBarrier_(typeBarrier) |
michael@0 | 8064 | { |
michael@0 | 8065 | } |
michael@0 | 8066 | |
michael@0 | 8067 | public: |
michael@0 | 8068 | INSTRUCTION_HEADER(SetPropertyCache) |
michael@0 | 8069 | |
michael@0 | 8070 | static MSetPropertyCache *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value, |
michael@0 | 8071 | PropertyName *name, bool strict, bool typeBarrier) |
michael@0 | 8072 | { |
michael@0 | 8073 | return new(alloc) MSetPropertyCache(obj, value, name, strict, typeBarrier); |
michael@0 | 8074 | } |
michael@0 | 8075 | |
michael@0 | 8076 | TypePolicy *typePolicy() { |
michael@0 | 8077 | return this; |
michael@0 | 8078 | } |
michael@0 | 8079 | |
michael@0 | 8080 | bool needsTypeBarrier() const { |
michael@0 | 8081 | return needsTypeBarrier_; |
michael@0 | 8082 | } |
michael@0 | 8083 | }; |
michael@0 | 8084 | |
michael@0 | 8085 | class MSetElementCache |
michael@0 | 8086 | : public MSetElementInstruction, |
michael@0 | 8087 | public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> > |
michael@0 | 8088 | { |
michael@0 | 8089 | bool strict_; |
michael@0 | 8090 | bool guardHoles_; |
michael@0 | 8091 | |
michael@0 | 8092 | MSetElementCache(MDefinition *obj, MDefinition *index, MDefinition *value, bool strict, |
michael@0 | 8093 | bool guardHoles) |
michael@0 | 8094 | : MSetElementInstruction(obj, index, value), |
michael@0 | 8095 | strict_(strict), |
michael@0 | 8096 | guardHoles_(guardHoles) |
michael@0 | 8097 | { |
michael@0 | 8098 | } |
michael@0 | 8099 | |
michael@0 | 8100 | public: |
michael@0 | 8101 | INSTRUCTION_HEADER(SetElementCache); |
michael@0 | 8102 | |
michael@0 | 8103 | static MSetElementCache *New(TempAllocator &alloc, MDefinition *obj, MDefinition *index, |
michael@0 | 8104 | MDefinition *value, bool strict, bool guardHoles) |
michael@0 | 8105 | { |
michael@0 | 8106 | return new(alloc) MSetElementCache(obj, index, value, strict, guardHoles); |
michael@0 | 8107 | } |
michael@0 | 8108 | |
michael@0 | 8109 | bool strict() const { |
michael@0 | 8110 | return strict_; |
michael@0 | 8111 | } |
michael@0 | 8112 | bool guardHoles() const { |
michael@0 | 8113 | return guardHoles_; |
michael@0 | 8114 | } |
michael@0 | 8115 | |
michael@0 | 8116 | TypePolicy *typePolicy() { |
michael@0 | 8117 | return this; |
michael@0 | 8118 | } |
michael@0 | 8119 | |
michael@0 | 8120 | bool canConsumeFloat32(MUse *use) const { return use->index() == 2; } |
michael@0 | 8121 | }; |
michael@0 | 8122 | |
michael@0 | 8123 | class MCallGetProperty |
michael@0 | 8124 | : public MUnaryInstruction, |
michael@0 | 8125 | public BoxInputsPolicy |
michael@0 | 8126 | { |
michael@0 | 8127 | CompilerRootPropertyName name_; |
michael@0 | 8128 | bool idempotent_; |
michael@0 | 8129 | bool callprop_; |
michael@0 | 8130 | |
michael@0 | 8131 | MCallGetProperty(MDefinition *value, PropertyName *name, bool callprop) |
michael@0 | 8132 | : MUnaryInstruction(value), name_(name), |
michael@0 | 8133 | idempotent_(false), |
michael@0 | 8134 | callprop_(callprop) |
michael@0 | 8135 | { |
michael@0 | 8136 | setResultType(MIRType_Value); |
michael@0 | 8137 | } |
michael@0 | 8138 | |
michael@0 | 8139 | public: |
michael@0 | 8140 | INSTRUCTION_HEADER(CallGetProperty) |
michael@0 | 8141 | |
michael@0 | 8142 | static MCallGetProperty *New(TempAllocator &alloc, MDefinition *value, PropertyName *name, |
michael@0 | 8143 | bool callprop) |
michael@0 | 8144 | { |
michael@0 | 8145 | return new(alloc) MCallGetProperty(value, name, callprop); |
michael@0 | 8146 | } |
michael@0 | 8147 | MDefinition *value() const { |
michael@0 | 8148 | return getOperand(0); |
michael@0 | 8149 | } |
michael@0 | 8150 | PropertyName *name() const { |
michael@0 | 8151 | return name_; |
michael@0 | 8152 | } |
michael@0 | 8153 | bool callprop() const { |
michael@0 | 8154 | return callprop_; |
michael@0 | 8155 | } |
michael@0 | 8156 | TypePolicy *typePolicy() { |
michael@0 | 8157 | return this; |
michael@0 | 8158 | } |
michael@0 | 8159 | |
michael@0 | 8160 | // Constructors need to perform a GetProp on the function prototype. |
michael@0 | 8161 | // Since getters cannot be set on the prototype, fetching is non-effectful. |
michael@0 | 8162 | // The operation may be safely repeated in case of bailout. |
michael@0 | 8163 | void setIdempotent() { |
michael@0 | 8164 | idempotent_ = true; |
michael@0 | 8165 | } |
michael@0 | 8166 | AliasSet getAliasSet() const { |
michael@0 | 8167 | if (!idempotent_) |
michael@0 | 8168 | return AliasSet::Store(AliasSet::Any); |
michael@0 | 8169 | return AliasSet::None(); |
michael@0 | 8170 | } |
michael@0 | 8171 | bool possiblyCalls() const { |
michael@0 | 8172 | return true; |
michael@0 | 8173 | } |
michael@0 | 8174 | }; |
michael@0 | 8175 | |
michael@0 | 8176 | // Inline call to handle lhs[rhs]. The first input is a Value so that this |
michael@0 | 8177 | // instruction can handle both objects and strings. |
michael@0 | 8178 | class MCallGetElement |
michael@0 | 8179 | : public MBinaryInstruction, |
michael@0 | 8180 | public BoxInputsPolicy |
michael@0 | 8181 | { |
michael@0 | 8182 | MCallGetElement(MDefinition *lhs, MDefinition *rhs) |
michael@0 | 8183 | : MBinaryInstruction(lhs, rhs) |
michael@0 | 8184 | { |
michael@0 | 8185 | setResultType(MIRType_Value); |
michael@0 | 8186 | } |
michael@0 | 8187 | |
michael@0 | 8188 | public: |
michael@0 | 8189 | INSTRUCTION_HEADER(CallGetElement) |
michael@0 | 8190 | |
michael@0 | 8191 | static MCallGetElement *New(TempAllocator &alloc, MDefinition *lhs, MDefinition *rhs) { |
michael@0 | 8192 | return new(alloc) MCallGetElement(lhs, rhs); |
michael@0 | 8193 | } |
michael@0 | 8194 | TypePolicy *typePolicy() { |
michael@0 | 8195 | return this; |
michael@0 | 8196 | } |
michael@0 | 8197 | bool possiblyCalls() const { |
michael@0 | 8198 | return true; |
michael@0 | 8199 | } |
michael@0 | 8200 | }; |
michael@0 | 8201 | |
michael@0 | 8202 | class MCallSetElement |
michael@0 | 8203 | : public MSetElementInstruction, |
michael@0 | 8204 | public CallSetElementPolicy |
michael@0 | 8205 | { |
michael@0 | 8206 | MCallSetElement(MDefinition *object, MDefinition *index, MDefinition *value) |
michael@0 | 8207 | : MSetElementInstruction(object, index, value) |
michael@0 | 8208 | { |
michael@0 | 8209 | } |
michael@0 | 8210 | |
michael@0 | 8211 | public: |
michael@0 | 8212 | INSTRUCTION_HEADER(CallSetElement) |
michael@0 | 8213 | |
michael@0 | 8214 | static MCallSetElement *New(TempAllocator &alloc, MDefinition *object, MDefinition *index, |
michael@0 | 8215 | MDefinition *value) |
michael@0 | 8216 | { |
michael@0 | 8217 | return new(alloc) MCallSetElement(object, index, value); |
michael@0 | 8218 | } |
michael@0 | 8219 | |
michael@0 | 8220 | TypePolicy *typePolicy() { |
michael@0 | 8221 | return this; |
michael@0 | 8222 | } |
michael@0 | 8223 | bool possiblyCalls() const { |
michael@0 | 8224 | return true; |
michael@0 | 8225 | } |
michael@0 | 8226 | }; |
michael@0 | 8227 | |
michael@0 | 8228 | class MCallInitElementArray |
michael@0 | 8229 | : public MAryInstruction<2>, |
michael@0 | 8230 | public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> > |
michael@0 | 8231 | { |
michael@0 | 8232 | uint32_t index_; |
michael@0 | 8233 | |
michael@0 | 8234 | MCallInitElementArray(MDefinition *obj, uint32_t index, MDefinition *val) |
michael@0 | 8235 | : index_(index) |
michael@0 | 8236 | { |
michael@0 | 8237 | setOperand(0, obj); |
michael@0 | 8238 | setOperand(1, val); |
michael@0 | 8239 | } |
michael@0 | 8240 | |
michael@0 | 8241 | public: |
michael@0 | 8242 | INSTRUCTION_HEADER(CallInitElementArray) |
michael@0 | 8243 | |
michael@0 | 8244 | static MCallInitElementArray *New(TempAllocator &alloc, MDefinition *obj, uint32_t index, |
michael@0 | 8245 | MDefinition *val) |
michael@0 | 8246 | { |
michael@0 | 8247 | return new(alloc) MCallInitElementArray(obj, index, val); |
michael@0 | 8248 | } |
michael@0 | 8249 | |
michael@0 | 8250 | MDefinition *object() const { |
michael@0 | 8251 | return getOperand(0); |
michael@0 | 8252 | } |
michael@0 | 8253 | |
michael@0 | 8254 | uint32_t index() const { |
michael@0 | 8255 | return index_; |
michael@0 | 8256 | } |
michael@0 | 8257 | |
michael@0 | 8258 | MDefinition *value() const { |
michael@0 | 8259 | return getOperand(1); |
michael@0 | 8260 | } |
michael@0 | 8261 | |
michael@0 | 8262 | TypePolicy *typePolicy() { |
michael@0 | 8263 | return this; |
michael@0 | 8264 | } |
michael@0 | 8265 | bool possiblyCalls() const { |
michael@0 | 8266 | return true; |
michael@0 | 8267 | } |
michael@0 | 8268 | }; |
michael@0 | 8269 | |
michael@0 | 8270 | class MSetDOMProperty |
michael@0 | 8271 | : public MAryInstruction<2>, |
michael@0 | 8272 | public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> > |
michael@0 | 8273 | { |
michael@0 | 8274 | const JSJitSetterOp func_; |
michael@0 | 8275 | |
michael@0 | 8276 | MSetDOMProperty(const JSJitSetterOp func, MDefinition *obj, MDefinition *val) |
michael@0 | 8277 | : func_(func) |
michael@0 | 8278 | { |
michael@0 | 8279 | setOperand(0, obj); |
michael@0 | 8280 | setOperand(1, val); |
michael@0 | 8281 | } |
michael@0 | 8282 | |
michael@0 | 8283 | public: |
michael@0 | 8284 | INSTRUCTION_HEADER(SetDOMProperty) |
michael@0 | 8285 | |
michael@0 | 8286 | static MSetDOMProperty *New(TempAllocator &alloc, const JSJitSetterOp func, MDefinition *obj, |
michael@0 | 8287 | MDefinition *val) |
michael@0 | 8288 | { |
michael@0 | 8289 | return new(alloc) MSetDOMProperty(func, obj, val); |
michael@0 | 8290 | } |
michael@0 | 8291 | |
michael@0 | 8292 | const JSJitSetterOp fun() { |
michael@0 | 8293 | return func_; |
michael@0 | 8294 | } |
michael@0 | 8295 | |
michael@0 | 8296 | MDefinition *object() { |
michael@0 | 8297 | return getOperand(0); |
michael@0 | 8298 | } |
michael@0 | 8299 | |
michael@0 | 8300 | MDefinition *value() |
michael@0 | 8301 | { |
michael@0 | 8302 | return getOperand(1); |
michael@0 | 8303 | } |
michael@0 | 8304 | |
michael@0 | 8305 | TypePolicy *typePolicy() { |
michael@0 | 8306 | return this; |
michael@0 | 8307 | } |
michael@0 | 8308 | |
michael@0 | 8309 | bool possiblyCalls() const { |
michael@0 | 8310 | return true; |
michael@0 | 8311 | } |
michael@0 | 8312 | }; |
michael@0 | 8313 | |
michael@0 | 8314 | class MGetDOMProperty |
michael@0 | 8315 | : public MAryInstruction<2>, |
michael@0 | 8316 | public ObjectPolicy<0> |
michael@0 | 8317 | { |
michael@0 | 8318 | const JSJitInfo *info_; |
michael@0 | 8319 | |
michael@0 | 8320 | protected: |
michael@0 | 8321 | MGetDOMProperty(const JSJitInfo *jitinfo, MDefinition *obj, MDefinition *guard) |
michael@0 | 8322 | : info_(jitinfo) |
michael@0 | 8323 | { |
michael@0 | 8324 | JS_ASSERT(jitinfo); |
michael@0 | 8325 | JS_ASSERT(jitinfo->type() == JSJitInfo::Getter); |
michael@0 | 8326 | |
michael@0 | 8327 | setOperand(0, obj); |
michael@0 | 8328 | |
michael@0 | 8329 | // Pin the guard as an operand if we want to hoist later |
michael@0 | 8330 | setOperand(1, guard); |
michael@0 | 8331 | |
michael@0 | 8332 | // We are movable iff the jitinfo says we can be. |
michael@0 | 8333 | if (isDomMovable()) { |
michael@0 | 8334 | JS_ASSERT(jitinfo->aliasSet() != JSJitInfo::AliasEverything); |
michael@0 | 8335 | setMovable(); |
michael@0 | 8336 | } else { |
michael@0 | 8337 | // If we're not movable, that means we shouldn't be DCEd either, |
michael@0 | 8338 | // because we might throw an exception when called, and getting rid |
michael@0 | 8339 | // of that is observable. |
michael@0 | 8340 | setGuard(); |
michael@0 | 8341 | } |
michael@0 | 8342 | |
michael@0 | 8343 | setResultType(MIRType_Value); |
michael@0 | 8344 | } |
michael@0 | 8345 | |
michael@0 | 8346 | const JSJitInfo *info() const { |
michael@0 | 8347 | return info_; |
michael@0 | 8348 | } |
michael@0 | 8349 | |
michael@0 | 8350 | public: |
michael@0 | 8351 | INSTRUCTION_HEADER(GetDOMProperty) |
michael@0 | 8352 | |
michael@0 | 8353 | static MGetDOMProperty *New(TempAllocator &alloc, const JSJitInfo *info, MDefinition *obj, |
michael@0 | 8354 | MDefinition *guard) |
michael@0 | 8355 | { |
michael@0 | 8356 | return new(alloc) MGetDOMProperty(info, obj, guard); |
michael@0 | 8357 | } |
michael@0 | 8358 | |
michael@0 | 8359 | const JSJitGetterOp fun() { |
michael@0 | 8360 | return info_->getter; |
michael@0 | 8361 | } |
michael@0 | 8362 | bool isInfallible() const { |
michael@0 | 8363 | return info_->isInfallible; |
michael@0 | 8364 | } |
michael@0 | 8365 | bool isDomMovable() const { |
michael@0 | 8366 | return info_->isMovable; |
michael@0 | 8367 | } |
michael@0 | 8368 | JSJitInfo::AliasSet domAliasSet() const { |
michael@0 | 8369 | return info_->aliasSet(); |
michael@0 | 8370 | } |
michael@0 | 8371 | size_t domMemberSlotIndex() const { |
michael@0 | 8372 | MOZ_ASSERT(info_->isInSlot); |
michael@0 | 8373 | return info_->slotIndex; |
michael@0 | 8374 | } |
michael@0 | 8375 | MDefinition *object() { |
michael@0 | 8376 | return getOperand(0); |
michael@0 | 8377 | } |
michael@0 | 8378 | |
michael@0 | 8379 | TypePolicy *typePolicy() { |
michael@0 | 8380 | return this; |
michael@0 | 8381 | } |
michael@0 | 8382 | |
michael@0 | 8383 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 8384 | if (!isDomMovable()) |
michael@0 | 8385 | return false; |
michael@0 | 8386 | |
michael@0 | 8387 | if (!ins->isGetDOMProperty()) |
michael@0 | 8388 | return false; |
michael@0 | 8389 | |
michael@0 | 8390 | // Checking the jitinfo is the same as checking the constant function |
michael@0 | 8391 | if (!(info() == ins->toGetDOMProperty()->info())) |
michael@0 | 8392 | return false; |
michael@0 | 8393 | |
michael@0 | 8394 | return congruentIfOperandsEqual(ins); |
michael@0 | 8395 | } |
michael@0 | 8396 | |
michael@0 | 8397 | AliasSet getAliasSet() const { |
michael@0 | 8398 | JSJitInfo::AliasSet aliasSet = domAliasSet(); |
michael@0 | 8399 | if (aliasSet == JSJitInfo::AliasNone) |
michael@0 | 8400 | return AliasSet::None(); |
michael@0 | 8401 | if (aliasSet == JSJitInfo::AliasDOMSets) |
michael@0 | 8402 | return AliasSet::Load(AliasSet::DOMProperty); |
michael@0 | 8403 | JS_ASSERT(aliasSet == JSJitInfo::AliasEverything); |
michael@0 | 8404 | return AliasSet::Store(AliasSet::Any); |
michael@0 | 8405 | } |
michael@0 | 8406 | |
michael@0 | 8407 | bool possiblyCalls() const { |
michael@0 | 8408 | return true; |
michael@0 | 8409 | } |
michael@0 | 8410 | }; |
michael@0 | 8411 | |
michael@0 | 8412 | class MGetDOMMember : public MGetDOMProperty |
michael@0 | 8413 | { |
michael@0 | 8414 | // We inherit everything from MGetDOMProperty except our possiblyCalls value |
michael@0 | 8415 | MGetDOMMember(const JSJitInfo *jitinfo, MDefinition *obj, MDefinition *guard) |
michael@0 | 8416 | : MGetDOMProperty(jitinfo, obj, guard) |
michael@0 | 8417 | { |
michael@0 | 8418 | } |
michael@0 | 8419 | |
michael@0 | 8420 | public: |
michael@0 | 8421 | INSTRUCTION_HEADER(GetDOMMember) |
michael@0 | 8422 | |
michael@0 | 8423 | static MGetDOMMember *New(TempAllocator &alloc, const JSJitInfo *info, MDefinition *obj, |
michael@0 | 8424 | MDefinition *guard) |
michael@0 | 8425 | { |
michael@0 | 8426 | return new(alloc) MGetDOMMember(info, obj, guard); |
michael@0 | 8427 | } |
michael@0 | 8428 | |
michael@0 | 8429 | bool possiblyCalls() const { |
michael@0 | 8430 | return false; |
michael@0 | 8431 | } |
michael@0 | 8432 | }; |
michael@0 | 8433 | |
michael@0 | 8434 | class MStringLength |
michael@0 | 8435 | : public MUnaryInstruction, |
michael@0 | 8436 | public StringPolicy<0> |
michael@0 | 8437 | { |
michael@0 | 8438 | MStringLength(MDefinition *string) |
michael@0 | 8439 | : MUnaryInstruction(string) |
michael@0 | 8440 | { |
michael@0 | 8441 | setResultType(MIRType_Int32); |
michael@0 | 8442 | setMovable(); |
michael@0 | 8443 | } |
michael@0 | 8444 | public: |
michael@0 | 8445 | INSTRUCTION_HEADER(StringLength) |
michael@0 | 8446 | |
michael@0 | 8447 | static MStringLength *New(TempAllocator &alloc, MDefinition *string) { |
michael@0 | 8448 | return new(alloc) MStringLength(string); |
michael@0 | 8449 | } |
michael@0 | 8450 | |
michael@0 | 8451 | MDefinition *foldsTo(TempAllocator &alloc, bool useValueNumbers); |
michael@0 | 8452 | |
michael@0 | 8453 | TypePolicy *typePolicy() { |
michael@0 | 8454 | return this; |
michael@0 | 8455 | } |
michael@0 | 8456 | |
michael@0 | 8457 | MDefinition *string() const { |
michael@0 | 8458 | return getOperand(0); |
michael@0 | 8459 | } |
michael@0 | 8460 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 8461 | return congruentIfOperandsEqual(ins); |
michael@0 | 8462 | } |
michael@0 | 8463 | AliasSet getAliasSet() const { |
michael@0 | 8464 | // The string |length| property is immutable, so there is no |
michael@0 | 8465 | // implicit dependency. |
michael@0 | 8466 | return AliasSet::None(); |
michael@0 | 8467 | } |
michael@0 | 8468 | |
michael@0 | 8469 | void computeRange(TempAllocator &alloc); |
michael@0 | 8470 | }; |
michael@0 | 8471 | |
michael@0 | 8472 | // Inlined version of Math.floor(). |
michael@0 | 8473 | class MFloor |
michael@0 | 8474 | : public MUnaryInstruction, |
michael@0 | 8475 | public FloatingPointPolicy<0> |
michael@0 | 8476 | { |
michael@0 | 8477 | MFloor(MDefinition *num) |
michael@0 | 8478 | : MUnaryInstruction(num) |
michael@0 | 8479 | { |
michael@0 | 8480 | setResultType(MIRType_Int32); |
michael@0 | 8481 | setPolicyType(MIRType_Double); |
michael@0 | 8482 | setMovable(); |
michael@0 | 8483 | } |
michael@0 | 8484 | |
michael@0 | 8485 | public: |
michael@0 | 8486 | INSTRUCTION_HEADER(Floor) |
michael@0 | 8487 | |
michael@0 | 8488 | static MFloor *New(TempAllocator &alloc, MDefinition *num) { |
michael@0 | 8489 | return new(alloc) MFloor(num); |
michael@0 | 8490 | } |
michael@0 | 8491 | |
michael@0 | 8492 | MDefinition *num() const { |
michael@0 | 8493 | return getOperand(0); |
michael@0 | 8494 | } |
michael@0 | 8495 | AliasSet getAliasSet() const { |
michael@0 | 8496 | return AliasSet::None(); |
michael@0 | 8497 | } |
michael@0 | 8498 | TypePolicy *typePolicy() { |
michael@0 | 8499 | return this; |
michael@0 | 8500 | } |
michael@0 | 8501 | bool isFloat32Commutative() const { |
michael@0 | 8502 | return true; |
michael@0 | 8503 | } |
michael@0 | 8504 | void trySpecializeFloat32(TempAllocator &alloc); |
michael@0 | 8505 | #ifdef DEBUG |
michael@0 | 8506 | bool isConsistentFloat32Use(MUse *use) const { |
michael@0 | 8507 | return true; |
michael@0 | 8508 | } |
michael@0 | 8509 | #endif |
michael@0 | 8510 | }; |
michael@0 | 8511 | |
michael@0 | 8512 | // Inlined version of Math.round(). |
michael@0 | 8513 | class MRound |
michael@0 | 8514 | : public MUnaryInstruction, |
michael@0 | 8515 | public FloatingPointPolicy<0> |
michael@0 | 8516 | { |
michael@0 | 8517 | MRound(MDefinition *num) |
michael@0 | 8518 | : MUnaryInstruction(num) |
michael@0 | 8519 | { |
michael@0 | 8520 | setResultType(MIRType_Int32); |
michael@0 | 8521 | setPolicyType(MIRType_Double); |
michael@0 | 8522 | setMovable(); |
michael@0 | 8523 | } |
michael@0 | 8524 | |
michael@0 | 8525 | public: |
michael@0 | 8526 | INSTRUCTION_HEADER(Round) |
michael@0 | 8527 | |
michael@0 | 8528 | static MRound *New(TempAllocator &alloc, MDefinition *num) { |
michael@0 | 8529 | return new(alloc) MRound(num); |
michael@0 | 8530 | } |
michael@0 | 8531 | |
michael@0 | 8532 | MDefinition *num() const { |
michael@0 | 8533 | return getOperand(0); |
michael@0 | 8534 | } |
michael@0 | 8535 | AliasSet getAliasSet() const { |
michael@0 | 8536 | return AliasSet::None(); |
michael@0 | 8537 | } |
michael@0 | 8538 | TypePolicy *typePolicy() { |
michael@0 | 8539 | return this; |
michael@0 | 8540 | } |
michael@0 | 8541 | |
michael@0 | 8542 | bool isFloat32Commutative() const { |
michael@0 | 8543 | return true; |
michael@0 | 8544 | } |
michael@0 | 8545 | void trySpecializeFloat32(TempAllocator &alloc); |
michael@0 | 8546 | #ifdef DEBUG |
michael@0 | 8547 | bool isConsistentFloat32Use(MUse *use) const { |
michael@0 | 8548 | return true; |
michael@0 | 8549 | } |
michael@0 | 8550 | #endif |
michael@0 | 8551 | }; |
michael@0 | 8552 | |
michael@0 | 8553 | class MIteratorStart |
michael@0 | 8554 | : public MUnaryInstruction, |
michael@0 | 8555 | public SingleObjectPolicy |
michael@0 | 8556 | { |
michael@0 | 8557 | uint8_t flags_; |
michael@0 | 8558 | |
michael@0 | 8559 | MIteratorStart(MDefinition *obj, uint8_t flags) |
michael@0 | 8560 | : MUnaryInstruction(obj), flags_(flags) |
michael@0 | 8561 | { |
michael@0 | 8562 | setResultType(MIRType_Object); |
michael@0 | 8563 | } |
michael@0 | 8564 | |
michael@0 | 8565 | public: |
michael@0 | 8566 | INSTRUCTION_HEADER(IteratorStart) |
michael@0 | 8567 | |
michael@0 | 8568 | static MIteratorStart *New(TempAllocator &alloc, MDefinition *obj, uint8_t flags) { |
michael@0 | 8569 | return new(alloc) MIteratorStart(obj, flags); |
michael@0 | 8570 | } |
michael@0 | 8571 | |
michael@0 | 8572 | TypePolicy *typePolicy() { |
michael@0 | 8573 | return this; |
michael@0 | 8574 | } |
michael@0 | 8575 | MDefinition *object() const { |
michael@0 | 8576 | return getOperand(0); |
michael@0 | 8577 | } |
michael@0 | 8578 | uint8_t flags() const { |
michael@0 | 8579 | return flags_; |
michael@0 | 8580 | } |
michael@0 | 8581 | }; |
michael@0 | 8582 | |
michael@0 | 8583 | class MIteratorNext |
michael@0 | 8584 | : public MUnaryInstruction, |
michael@0 | 8585 | public SingleObjectPolicy |
michael@0 | 8586 | { |
michael@0 | 8587 | MIteratorNext(MDefinition *iter) |
michael@0 | 8588 | : MUnaryInstruction(iter) |
michael@0 | 8589 | { |
michael@0 | 8590 | setResultType(MIRType_Value); |
michael@0 | 8591 | } |
michael@0 | 8592 | |
michael@0 | 8593 | public: |
michael@0 | 8594 | INSTRUCTION_HEADER(IteratorNext) |
michael@0 | 8595 | |
michael@0 | 8596 | static MIteratorNext *New(TempAllocator &alloc, MDefinition *iter) { |
michael@0 | 8597 | return new(alloc) MIteratorNext(iter); |
michael@0 | 8598 | } |
michael@0 | 8599 | |
michael@0 | 8600 | TypePolicy *typePolicy() { |
michael@0 | 8601 | return this; |
michael@0 | 8602 | } |
michael@0 | 8603 | MDefinition *iterator() const { |
michael@0 | 8604 | return getOperand(0); |
michael@0 | 8605 | } |
michael@0 | 8606 | }; |
michael@0 | 8607 | |
michael@0 | 8608 | class MIteratorMore |
michael@0 | 8609 | : public MUnaryInstruction, |
michael@0 | 8610 | public SingleObjectPolicy |
michael@0 | 8611 | { |
michael@0 | 8612 | MIteratorMore(MDefinition *iter) |
michael@0 | 8613 | : MUnaryInstruction(iter) |
michael@0 | 8614 | { |
michael@0 | 8615 | setResultType(MIRType_Boolean); |
michael@0 | 8616 | } |
michael@0 | 8617 | |
michael@0 | 8618 | public: |
michael@0 | 8619 | INSTRUCTION_HEADER(IteratorMore) |
michael@0 | 8620 | |
michael@0 | 8621 | static MIteratorMore *New(TempAllocator &alloc, MDefinition *iter) { |
michael@0 | 8622 | return new(alloc) MIteratorMore(iter); |
michael@0 | 8623 | } |
michael@0 | 8624 | |
michael@0 | 8625 | TypePolicy *typePolicy() { |
michael@0 | 8626 | return this; |
michael@0 | 8627 | } |
michael@0 | 8628 | MDefinition *iterator() const { |
michael@0 | 8629 | return getOperand(0); |
michael@0 | 8630 | } |
michael@0 | 8631 | }; |
michael@0 | 8632 | |
michael@0 | 8633 | class MIteratorEnd |
michael@0 | 8634 | : public MUnaryInstruction, |
michael@0 | 8635 | public SingleObjectPolicy |
michael@0 | 8636 | { |
michael@0 | 8637 | MIteratorEnd(MDefinition *iter) |
michael@0 | 8638 | : MUnaryInstruction(iter) |
michael@0 | 8639 | { } |
michael@0 | 8640 | |
michael@0 | 8641 | public: |
michael@0 | 8642 | INSTRUCTION_HEADER(IteratorEnd) |
michael@0 | 8643 | |
michael@0 | 8644 | static MIteratorEnd *New(TempAllocator &alloc, MDefinition *iter) { |
michael@0 | 8645 | return new(alloc) MIteratorEnd(iter); |
michael@0 | 8646 | } |
michael@0 | 8647 | |
michael@0 | 8648 | TypePolicy *typePolicy() { |
michael@0 | 8649 | return this; |
michael@0 | 8650 | } |
michael@0 | 8651 | MDefinition *iterator() const { |
michael@0 | 8652 | return getOperand(0); |
michael@0 | 8653 | } |
michael@0 | 8654 | }; |
michael@0 | 8655 | |
michael@0 | 8656 | // Implementation for 'in' operator. |
michael@0 | 8657 | class MIn |
michael@0 | 8658 | : public MBinaryInstruction, |
michael@0 | 8659 | public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> > |
michael@0 | 8660 | { |
michael@0 | 8661 | MIn(MDefinition *key, MDefinition *obj) |
michael@0 | 8662 | : MBinaryInstruction(key, obj) |
michael@0 | 8663 | { |
michael@0 | 8664 | setResultType(MIRType_Boolean); |
michael@0 | 8665 | } |
michael@0 | 8666 | |
michael@0 | 8667 | public: |
michael@0 | 8668 | INSTRUCTION_HEADER(In) |
michael@0 | 8669 | |
michael@0 | 8670 | static MIn *New(TempAllocator &alloc, MDefinition *key, MDefinition *obj) { |
michael@0 | 8671 | return new(alloc) MIn(key, obj); |
michael@0 | 8672 | } |
michael@0 | 8673 | |
michael@0 | 8674 | TypePolicy *typePolicy() { |
michael@0 | 8675 | return this; |
michael@0 | 8676 | } |
michael@0 | 8677 | bool possiblyCalls() const { |
michael@0 | 8678 | return true; |
michael@0 | 8679 | } |
michael@0 | 8680 | }; |
michael@0 | 8681 | |
michael@0 | 8682 | |
michael@0 | 8683 | // Test whether the index is in the array bounds or a hole. |
michael@0 | 8684 | class MInArray |
michael@0 | 8685 | : public MQuaternaryInstruction, |
michael@0 | 8686 | public ObjectPolicy<3> |
michael@0 | 8687 | { |
michael@0 | 8688 | bool needsHoleCheck_; |
michael@0 | 8689 | bool needsNegativeIntCheck_; |
michael@0 | 8690 | |
michael@0 | 8691 | MInArray(MDefinition *elements, MDefinition *index, |
michael@0 | 8692 | MDefinition *initLength, MDefinition *object, |
michael@0 | 8693 | bool needsHoleCheck) |
michael@0 | 8694 | : MQuaternaryInstruction(elements, index, initLength, object), |
michael@0 | 8695 | needsHoleCheck_(needsHoleCheck), |
michael@0 | 8696 | needsNegativeIntCheck_(true) |
michael@0 | 8697 | { |
michael@0 | 8698 | setResultType(MIRType_Boolean); |
michael@0 | 8699 | setMovable(); |
michael@0 | 8700 | JS_ASSERT(elements->type() == MIRType_Elements); |
michael@0 | 8701 | JS_ASSERT(index->type() == MIRType_Int32); |
michael@0 | 8702 | JS_ASSERT(initLength->type() == MIRType_Int32); |
michael@0 | 8703 | } |
michael@0 | 8704 | |
michael@0 | 8705 | public: |
michael@0 | 8706 | INSTRUCTION_HEADER(InArray) |
michael@0 | 8707 | |
michael@0 | 8708 | static MInArray *New(TempAllocator &alloc, MDefinition *elements, MDefinition *index, |
michael@0 | 8709 | MDefinition *initLength, MDefinition *object, |
michael@0 | 8710 | bool needsHoleCheck) |
michael@0 | 8711 | { |
michael@0 | 8712 | return new(alloc) MInArray(elements, index, initLength, object, needsHoleCheck); |
michael@0 | 8713 | } |
michael@0 | 8714 | |
michael@0 | 8715 | MDefinition *elements() const { |
michael@0 | 8716 | return getOperand(0); |
michael@0 | 8717 | } |
michael@0 | 8718 | MDefinition *index() const { |
michael@0 | 8719 | return getOperand(1); |
michael@0 | 8720 | } |
michael@0 | 8721 | MDefinition *initLength() const { |
michael@0 | 8722 | return getOperand(2); |
michael@0 | 8723 | } |
michael@0 | 8724 | MDefinition *object() const { |
michael@0 | 8725 | return getOperand(3); |
michael@0 | 8726 | } |
michael@0 | 8727 | bool needsHoleCheck() const { |
michael@0 | 8728 | return needsHoleCheck_; |
michael@0 | 8729 | } |
michael@0 | 8730 | bool needsNegativeIntCheck() const { |
michael@0 | 8731 | return needsNegativeIntCheck_; |
michael@0 | 8732 | } |
michael@0 | 8733 | void collectRangeInfoPreTrunc(); |
michael@0 | 8734 | AliasSet getAliasSet() const { |
michael@0 | 8735 | return AliasSet::Load(AliasSet::Element); |
michael@0 | 8736 | } |
michael@0 | 8737 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 8738 | if (!ins->isInArray()) |
michael@0 | 8739 | return false; |
michael@0 | 8740 | const MInArray *other = ins->toInArray(); |
michael@0 | 8741 | if (needsHoleCheck() != other->needsHoleCheck()) |
michael@0 | 8742 | return false; |
michael@0 | 8743 | if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) |
michael@0 | 8744 | return false; |
michael@0 | 8745 | return congruentIfOperandsEqual(other); |
michael@0 | 8746 | } |
michael@0 | 8747 | TypePolicy *typePolicy() { |
michael@0 | 8748 | return this; |
michael@0 | 8749 | } |
michael@0 | 8750 | |
michael@0 | 8751 | }; |
michael@0 | 8752 | |
michael@0 | 8753 | // Implementation for instanceof operator with specific rhs. |
michael@0 | 8754 | class MInstanceOf |
michael@0 | 8755 | : public MUnaryInstruction, |
michael@0 | 8756 | public InstanceOfPolicy |
michael@0 | 8757 | { |
michael@0 | 8758 | CompilerRootObject protoObj_; |
michael@0 | 8759 | |
michael@0 | 8760 | MInstanceOf(MDefinition *obj, JSObject *proto) |
michael@0 | 8761 | : MUnaryInstruction(obj), |
michael@0 | 8762 | protoObj_(proto) |
michael@0 | 8763 | { |
michael@0 | 8764 | setResultType(MIRType_Boolean); |
michael@0 | 8765 | } |
michael@0 | 8766 | |
michael@0 | 8767 | public: |
michael@0 | 8768 | INSTRUCTION_HEADER(InstanceOf) |
michael@0 | 8769 | |
michael@0 | 8770 | static MInstanceOf *New(TempAllocator &alloc, MDefinition *obj, JSObject *proto) { |
michael@0 | 8771 | return new(alloc) MInstanceOf(obj, proto); |
michael@0 | 8772 | } |
michael@0 | 8773 | |
michael@0 | 8774 | TypePolicy *typePolicy() { |
michael@0 | 8775 | return this; |
michael@0 | 8776 | } |
michael@0 | 8777 | |
michael@0 | 8778 | JSObject *prototypeObject() { |
michael@0 | 8779 | return protoObj_; |
michael@0 | 8780 | } |
michael@0 | 8781 | }; |
michael@0 | 8782 | |
michael@0 | 8783 | // Implementation for instanceof operator with unknown rhs. |
michael@0 | 8784 | class MCallInstanceOf |
michael@0 | 8785 | : public MBinaryInstruction, |
michael@0 | 8786 | public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> > |
michael@0 | 8787 | { |
michael@0 | 8788 | MCallInstanceOf(MDefinition *obj, MDefinition *proto) |
michael@0 | 8789 | : MBinaryInstruction(obj, proto) |
michael@0 | 8790 | { |
michael@0 | 8791 | setResultType(MIRType_Boolean); |
michael@0 | 8792 | } |
michael@0 | 8793 | |
michael@0 | 8794 | public: |
michael@0 | 8795 | INSTRUCTION_HEADER(CallInstanceOf) |
michael@0 | 8796 | |
michael@0 | 8797 | static MCallInstanceOf *New(TempAllocator &alloc, MDefinition *obj, MDefinition *proto) { |
michael@0 | 8798 | return new(alloc) MCallInstanceOf(obj, proto); |
michael@0 | 8799 | } |
michael@0 | 8800 | |
michael@0 | 8801 | TypePolicy *typePolicy() { |
michael@0 | 8802 | return this; |
michael@0 | 8803 | } |
michael@0 | 8804 | }; |
michael@0 | 8805 | |
michael@0 | 8806 | class MArgumentsLength : public MNullaryInstruction |
michael@0 | 8807 | { |
michael@0 | 8808 | MArgumentsLength() |
michael@0 | 8809 | { |
michael@0 | 8810 | setResultType(MIRType_Int32); |
michael@0 | 8811 | setMovable(); |
michael@0 | 8812 | } |
michael@0 | 8813 | |
michael@0 | 8814 | public: |
michael@0 | 8815 | INSTRUCTION_HEADER(ArgumentsLength) |
michael@0 | 8816 | |
michael@0 | 8817 | static MArgumentsLength *New(TempAllocator &alloc) { |
michael@0 | 8818 | return new(alloc) MArgumentsLength(); |
michael@0 | 8819 | } |
michael@0 | 8820 | |
michael@0 | 8821 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 8822 | return congruentIfOperandsEqual(ins); |
michael@0 | 8823 | } |
michael@0 | 8824 | AliasSet getAliasSet() const { |
michael@0 | 8825 | // Arguments |length| cannot be mutated by Ion Code. |
michael@0 | 8826 | return AliasSet::None(); |
michael@0 | 8827 | } |
michael@0 | 8828 | |
michael@0 | 8829 | void computeRange(TempAllocator &alloc); |
michael@0 | 8830 | }; |
michael@0 | 8831 | |
michael@0 | 8832 | // This MIR instruction is used to get an argument from the actual arguments. |
michael@0 | 8833 | class MGetFrameArgument |
michael@0 | 8834 | : public MUnaryInstruction, |
michael@0 | 8835 | public IntPolicy<0> |
michael@0 | 8836 | { |
michael@0 | 8837 | bool scriptHasSetArg_; |
michael@0 | 8838 | |
michael@0 | 8839 | MGetFrameArgument(MDefinition *idx, bool scriptHasSetArg) |
michael@0 | 8840 | : MUnaryInstruction(idx), |
michael@0 | 8841 | scriptHasSetArg_(scriptHasSetArg) |
michael@0 | 8842 | { |
michael@0 | 8843 | setResultType(MIRType_Value); |
michael@0 | 8844 | setMovable(); |
michael@0 | 8845 | } |
michael@0 | 8846 | |
michael@0 | 8847 | public: |
michael@0 | 8848 | INSTRUCTION_HEADER(GetFrameArgument) |
michael@0 | 8849 | |
michael@0 | 8850 | static MGetFrameArgument *New(TempAllocator &alloc, MDefinition *idx, bool scriptHasSetArg) { |
michael@0 | 8851 | return new(alloc) MGetFrameArgument(idx, scriptHasSetArg); |
michael@0 | 8852 | } |
michael@0 | 8853 | |
michael@0 | 8854 | MDefinition *index() const { |
michael@0 | 8855 | return getOperand(0); |
michael@0 | 8856 | } |
michael@0 | 8857 | |
michael@0 | 8858 | TypePolicy *typePolicy() { |
michael@0 | 8859 | return this; |
michael@0 | 8860 | } |
michael@0 | 8861 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 8862 | return congruentIfOperandsEqual(ins); |
michael@0 | 8863 | } |
michael@0 | 8864 | AliasSet getAliasSet() const { |
michael@0 | 8865 | // If the script doesn't have any JSOP_SETARG ops, then this instruction is never |
michael@0 | 8866 | // aliased. |
michael@0 | 8867 | if (scriptHasSetArg_) |
michael@0 | 8868 | return AliasSet::Load(AliasSet::FrameArgument); |
michael@0 | 8869 | return AliasSet::None(); |
michael@0 | 8870 | } |
michael@0 | 8871 | }; |
michael@0 | 8872 | |
michael@0 | 8873 | // This MIR instruction is used to set an argument value in the frame. |
michael@0 | 8874 | class MSetFrameArgument |
michael@0 | 8875 | : public MUnaryInstruction, |
michael@0 | 8876 | public NoFloatPolicy<0> |
michael@0 | 8877 | { |
michael@0 | 8878 | uint32_t argno_; |
michael@0 | 8879 | |
michael@0 | 8880 | MSetFrameArgument(uint32_t argno, MDefinition *value) |
michael@0 | 8881 | : MUnaryInstruction(value), |
michael@0 | 8882 | argno_(argno) |
michael@0 | 8883 | { |
michael@0 | 8884 | setMovable(); |
michael@0 | 8885 | } |
michael@0 | 8886 | |
michael@0 | 8887 | public: |
michael@0 | 8888 | INSTRUCTION_HEADER(SetFrameArgument) |
michael@0 | 8889 | |
michael@0 | 8890 | static MSetFrameArgument *New(TempAllocator &alloc, uint32_t argno, MDefinition *value) { |
michael@0 | 8891 | return new(alloc) MSetFrameArgument(argno, value); |
michael@0 | 8892 | } |
michael@0 | 8893 | |
michael@0 | 8894 | uint32_t argno() const { |
michael@0 | 8895 | return argno_; |
michael@0 | 8896 | } |
michael@0 | 8897 | |
michael@0 | 8898 | MDefinition *value() const { |
michael@0 | 8899 | return getOperand(0); |
michael@0 | 8900 | } |
michael@0 | 8901 | |
michael@0 | 8902 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 8903 | return false; |
michael@0 | 8904 | } |
michael@0 | 8905 | AliasSet getAliasSet() const { |
michael@0 | 8906 | return AliasSet::Store(AliasSet::FrameArgument); |
michael@0 | 8907 | } |
michael@0 | 8908 | TypePolicy *typePolicy() { |
michael@0 | 8909 | return this; |
michael@0 | 8910 | } |
michael@0 | 8911 | }; |
michael@0 | 8912 | |
michael@0 | 8913 | class MRestCommon |
michael@0 | 8914 | { |
michael@0 | 8915 | unsigned numFormals_; |
michael@0 | 8916 | CompilerRootObject templateObject_; |
michael@0 | 8917 | |
michael@0 | 8918 | protected: |
michael@0 | 8919 | MRestCommon(unsigned numFormals, JSObject *templateObject) |
michael@0 | 8920 | : numFormals_(numFormals), |
michael@0 | 8921 | templateObject_(templateObject) |
michael@0 | 8922 | { } |
michael@0 | 8923 | |
michael@0 | 8924 | public: |
michael@0 | 8925 | unsigned numFormals() const { |
michael@0 | 8926 | return numFormals_; |
michael@0 | 8927 | } |
michael@0 | 8928 | JSObject *templateObject() const { |
michael@0 | 8929 | return templateObject_; |
michael@0 | 8930 | } |
michael@0 | 8931 | }; |
michael@0 | 8932 | |
michael@0 | 8933 | class MRest |
michael@0 | 8934 | : public MUnaryInstruction, |
michael@0 | 8935 | public MRestCommon, |
michael@0 | 8936 | public IntPolicy<0> |
michael@0 | 8937 | { |
michael@0 | 8938 | MRest(types::CompilerConstraintList *constraints, MDefinition *numActuals, unsigned numFormals, |
michael@0 | 8939 | JSObject *templateObject) |
michael@0 | 8940 | : MUnaryInstruction(numActuals), |
michael@0 | 8941 | MRestCommon(numFormals, templateObject) |
michael@0 | 8942 | { |
michael@0 | 8943 | setResultType(MIRType_Object); |
michael@0 | 8944 | setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject)); |
michael@0 | 8945 | } |
michael@0 | 8946 | |
michael@0 | 8947 | public: |
michael@0 | 8948 | INSTRUCTION_HEADER(Rest); |
michael@0 | 8949 | |
michael@0 | 8950 | static MRest *New(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
michael@0 | 8951 | MDefinition *numActuals, unsigned numFormals, |
michael@0 | 8952 | JSObject *templateObject) |
michael@0 | 8953 | { |
michael@0 | 8954 | return new(alloc) MRest(constraints, numActuals, numFormals, templateObject); |
michael@0 | 8955 | } |
michael@0 | 8956 | |
michael@0 | 8957 | MDefinition *numActuals() const { |
michael@0 | 8958 | return getOperand(0); |
michael@0 | 8959 | } |
michael@0 | 8960 | |
michael@0 | 8961 | TypePolicy *typePolicy() { |
michael@0 | 8962 | return this; |
michael@0 | 8963 | } |
michael@0 | 8964 | AliasSet getAliasSet() const { |
michael@0 | 8965 | return AliasSet::None(); |
michael@0 | 8966 | } |
michael@0 | 8967 | bool possiblyCalls() const { |
michael@0 | 8968 | return true; |
michael@0 | 8969 | } |
michael@0 | 8970 | }; |
michael@0 | 8971 | |
michael@0 | 8972 | class MRestPar |
michael@0 | 8973 | : public MBinaryInstruction, |
michael@0 | 8974 | public MRestCommon, |
michael@0 | 8975 | public IntPolicy<1> |
michael@0 | 8976 | { |
michael@0 | 8977 | MRestPar(MDefinition *cx, MDefinition *numActuals, unsigned numFormals, |
michael@0 | 8978 | JSObject *templateObject, types::TemporaryTypeSet *resultTypes) |
michael@0 | 8979 | : MBinaryInstruction(cx, numActuals), |
michael@0 | 8980 | MRestCommon(numFormals, templateObject) |
michael@0 | 8981 | { |
michael@0 | 8982 | setResultType(MIRType_Object); |
michael@0 | 8983 | setResultTypeSet(resultTypes); |
michael@0 | 8984 | } |
michael@0 | 8985 | |
michael@0 | 8986 | public: |
michael@0 | 8987 | INSTRUCTION_HEADER(RestPar); |
michael@0 | 8988 | |
michael@0 | 8989 | static MRestPar *New(TempAllocator &alloc, MDefinition *cx, MRest *rest) { |
michael@0 | 8990 | return new(alloc) MRestPar(cx, rest->numActuals(), rest->numFormals(), |
michael@0 | 8991 | rest->templateObject(), rest->resultTypeSet()); |
michael@0 | 8992 | } |
michael@0 | 8993 | |
michael@0 | 8994 | MDefinition *forkJoinContext() const { |
michael@0 | 8995 | return getOperand(0); |
michael@0 | 8996 | } |
michael@0 | 8997 | MDefinition *numActuals() const { |
michael@0 | 8998 | return getOperand(1); |
michael@0 | 8999 | } |
michael@0 | 9000 | |
michael@0 | 9001 | TypePolicy *typePolicy() { |
michael@0 | 9002 | return this; |
michael@0 | 9003 | } |
michael@0 | 9004 | AliasSet getAliasSet() const { |
michael@0 | 9005 | return AliasSet::None(); |
michael@0 | 9006 | } |
michael@0 | 9007 | bool possiblyCalls() const { |
michael@0 | 9008 | return true; |
michael@0 | 9009 | } |
michael@0 | 9010 | }; |
michael@0 | 9011 | |
michael@0 | 9012 | // Guard on an object being safe for writes by current parallel cx. |
michael@0 | 9013 | // Must be either thread-local or else a handle into the destination array. |
michael@0 | 9014 | class MGuardThreadExclusive |
michael@0 | 9015 | : public MBinaryInstruction, |
michael@0 | 9016 | public ObjectPolicy<1> |
michael@0 | 9017 | { |
michael@0 | 9018 | MGuardThreadExclusive(MDefinition *cx, MDefinition *obj) |
michael@0 | 9019 | : MBinaryInstruction(cx, obj) |
michael@0 | 9020 | { |
michael@0 | 9021 | setResultType(MIRType_None); |
michael@0 | 9022 | setGuard(); |
michael@0 | 9023 | } |
michael@0 | 9024 | |
michael@0 | 9025 | public: |
michael@0 | 9026 | INSTRUCTION_HEADER(GuardThreadExclusive); |
michael@0 | 9027 | |
michael@0 | 9028 | static MGuardThreadExclusive *New(TempAllocator &alloc, MDefinition *cx, MDefinition *obj) { |
michael@0 | 9029 | return new(alloc) MGuardThreadExclusive(cx, obj); |
michael@0 | 9030 | } |
michael@0 | 9031 | MDefinition *forkJoinContext() const { |
michael@0 | 9032 | return getOperand(0); |
michael@0 | 9033 | } |
michael@0 | 9034 | MDefinition *object() const { |
michael@0 | 9035 | return getOperand(1); |
michael@0 | 9036 | } |
michael@0 | 9037 | BailoutKind bailoutKind() const { |
michael@0 | 9038 | return Bailout_Normal; |
michael@0 | 9039 | } |
michael@0 | 9040 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 9041 | return congruentIfOperandsEqual(ins); |
michael@0 | 9042 | } |
michael@0 | 9043 | AliasSet getAliasSet() const { |
michael@0 | 9044 | return AliasSet::None(); |
michael@0 | 9045 | } |
michael@0 | 9046 | bool possiblyCalls() const { |
michael@0 | 9047 | return true; |
michael@0 | 9048 | } |
michael@0 | 9049 | }; |
michael@0 | 9050 | |
michael@0 | 9051 | class MFilterTypeSet |
michael@0 | 9052 | : public MUnaryInstruction, |
michael@0 | 9053 | public FilterTypeSetPolicy |
michael@0 | 9054 | { |
michael@0 | 9055 | MFilterTypeSet(MDefinition *def, types::TemporaryTypeSet *types) |
michael@0 | 9056 | : MUnaryInstruction(def) |
michael@0 | 9057 | { |
michael@0 | 9058 | JS_ASSERT(!types->unknown()); |
michael@0 | 9059 | setResultType(types->getKnownMIRType()); |
michael@0 | 9060 | setResultTypeSet(types); |
michael@0 | 9061 | } |
michael@0 | 9062 | |
michael@0 | 9063 | public: |
michael@0 | 9064 | INSTRUCTION_HEADER(FilterTypeSet) |
michael@0 | 9065 | |
michael@0 | 9066 | static MFilterTypeSet *New(TempAllocator &alloc, MDefinition *def, types::TemporaryTypeSet *types) { |
michael@0 | 9067 | return new(alloc) MFilterTypeSet(def, types); |
michael@0 | 9068 | } |
michael@0 | 9069 | |
michael@0 | 9070 | TypePolicy *typePolicy() { |
michael@0 | 9071 | return this; |
michael@0 | 9072 | } |
michael@0 | 9073 | bool congruentTo(const MDefinition *def) const { |
michael@0 | 9074 | return false; |
michael@0 | 9075 | } |
michael@0 | 9076 | AliasSet getAliasSet() const { |
michael@0 | 9077 | return AliasSet::None(); |
michael@0 | 9078 | } |
michael@0 | 9079 | virtual bool neverHoist() const { |
michael@0 | 9080 | return resultTypeSet()->empty(); |
michael@0 | 9081 | } |
michael@0 | 9082 | }; |
michael@0 | 9083 | |
michael@0 | 9084 | // Given a value, guard that the value is in a particular TypeSet, then returns |
michael@0 | 9085 | // that value. |
michael@0 | 9086 | class MTypeBarrier |
michael@0 | 9087 | : public MUnaryInstruction, |
michael@0 | 9088 | public TypeBarrierPolicy |
michael@0 | 9089 | { |
michael@0 | 9090 | MTypeBarrier(MDefinition *def, types::TemporaryTypeSet *types) |
michael@0 | 9091 | : MUnaryInstruction(def) |
michael@0 | 9092 | { |
michael@0 | 9093 | JS_ASSERT(!types->unknown()); |
michael@0 | 9094 | setResultType(types->getKnownMIRType()); |
michael@0 | 9095 | setResultTypeSet(types); |
michael@0 | 9096 | |
michael@0 | 9097 | setGuard(); |
michael@0 | 9098 | setMovable(); |
michael@0 | 9099 | } |
michael@0 | 9100 | |
michael@0 | 9101 | public: |
michael@0 | 9102 | INSTRUCTION_HEADER(TypeBarrier) |
michael@0 | 9103 | |
michael@0 | 9104 | static MTypeBarrier *New(TempAllocator &alloc, MDefinition *def, types::TemporaryTypeSet *types) { |
michael@0 | 9105 | return new(alloc) MTypeBarrier(def, types); |
michael@0 | 9106 | } |
michael@0 | 9107 | |
michael@0 | 9108 | void printOpcode(FILE *fp) const; |
michael@0 | 9109 | |
michael@0 | 9110 | TypePolicy *typePolicy() { |
michael@0 | 9111 | return this; |
michael@0 | 9112 | } |
michael@0 | 9113 | |
michael@0 | 9114 | bool congruentTo(const MDefinition *def) const { |
michael@0 | 9115 | return false; |
michael@0 | 9116 | } |
michael@0 | 9117 | AliasSet getAliasSet() const { |
michael@0 | 9118 | return AliasSet::None(); |
michael@0 | 9119 | } |
michael@0 | 9120 | virtual bool neverHoist() const { |
michael@0 | 9121 | return resultTypeSet()->empty(); |
michael@0 | 9122 | } |
michael@0 | 9123 | |
michael@0 | 9124 | bool alwaysBails() const { |
michael@0 | 9125 | // If mirtype of input doesn't agree with mirtype of barrier, |
michael@0 | 9126 | // we will definitely bail. |
michael@0 | 9127 | MIRType type = resultTypeSet()->getKnownMIRType(); |
michael@0 | 9128 | if (type == MIRType_Value) |
michael@0 | 9129 | return false; |
michael@0 | 9130 | if (input()->type() == MIRType_Value) |
michael@0 | 9131 | return false; |
michael@0 | 9132 | return input()->type() != type; |
michael@0 | 9133 | } |
michael@0 | 9134 | }; |
michael@0 | 9135 | |
michael@0 | 9136 | // Like MTypeBarrier, guard that the value is in the given type set. This is |
michael@0 | 9137 | // used before property writes to ensure the value being written is represented |
michael@0 | 9138 | // in the property types for the object. |
michael@0 | 9139 | class MMonitorTypes : public MUnaryInstruction, public BoxInputsPolicy |
michael@0 | 9140 | { |
michael@0 | 9141 | const types::TemporaryTypeSet *typeSet_; |
michael@0 | 9142 | |
michael@0 | 9143 | MMonitorTypes(MDefinition *def, const types::TemporaryTypeSet *types) |
michael@0 | 9144 | : MUnaryInstruction(def), |
michael@0 | 9145 | typeSet_(types) |
michael@0 | 9146 | { |
michael@0 | 9147 | setGuard(); |
michael@0 | 9148 | JS_ASSERT(!types->unknown()); |
michael@0 | 9149 | } |
michael@0 | 9150 | |
michael@0 | 9151 | public: |
michael@0 | 9152 | INSTRUCTION_HEADER(MonitorTypes) |
michael@0 | 9153 | |
michael@0 | 9154 | static MMonitorTypes *New(TempAllocator &alloc, MDefinition *def, const types::TemporaryTypeSet *types) { |
michael@0 | 9155 | return new(alloc) MMonitorTypes(def, types); |
michael@0 | 9156 | } |
michael@0 | 9157 | |
michael@0 | 9158 | TypePolicy *typePolicy() { |
michael@0 | 9159 | return this; |
michael@0 | 9160 | } |
michael@0 | 9161 | |
michael@0 | 9162 | const types::TemporaryTypeSet *typeSet() const { |
michael@0 | 9163 | return typeSet_; |
michael@0 | 9164 | } |
michael@0 | 9165 | AliasSet getAliasSet() const { |
michael@0 | 9166 | return AliasSet::None(); |
michael@0 | 9167 | } |
michael@0 | 9168 | }; |
michael@0 | 9169 | |
michael@0 | 9170 | // Given a value being written to another object, update the generational store |
michael@0 | 9171 | // buffer if the value is in the nursery and object is in the tenured heap. |
michael@0 | 9172 | class MPostWriteBarrier : public MBinaryInstruction, public ObjectPolicy<0> |
michael@0 | 9173 | { |
michael@0 | 9174 | MPostWriteBarrier(MDefinition *obj, MDefinition *value) |
michael@0 | 9175 | : MBinaryInstruction(obj, value) |
michael@0 | 9176 | { |
michael@0 | 9177 | setGuard(); |
michael@0 | 9178 | } |
michael@0 | 9179 | |
michael@0 | 9180 | public: |
michael@0 | 9181 | INSTRUCTION_HEADER(PostWriteBarrier) |
michael@0 | 9182 | |
michael@0 | 9183 | static MPostWriteBarrier *New(TempAllocator &alloc, MDefinition *obj, MDefinition *value) { |
michael@0 | 9184 | return new(alloc) MPostWriteBarrier(obj, value); |
michael@0 | 9185 | } |
michael@0 | 9186 | |
michael@0 | 9187 | TypePolicy *typePolicy() { |
michael@0 | 9188 | return this; |
michael@0 | 9189 | } |
michael@0 | 9190 | |
michael@0 | 9191 | MDefinition *object() const { |
michael@0 | 9192 | return getOperand(0); |
michael@0 | 9193 | } |
michael@0 | 9194 | |
michael@0 | 9195 | MDefinition *value() const { |
michael@0 | 9196 | return getOperand(1); |
michael@0 | 9197 | } |
michael@0 | 9198 | |
michael@0 | 9199 | AliasSet getAliasSet() const { |
michael@0 | 9200 | return AliasSet::None(); |
michael@0 | 9201 | } |
michael@0 | 9202 | |
michael@0 | 9203 | #ifdef DEBUG |
michael@0 | 9204 | bool isConsistentFloat32Use(MUse *use) const { |
michael@0 | 9205 | // During lowering, values that neither have object nor value MIR type |
michael@0 | 9206 | // are ignored, thus Float32 can show up at this point without any issue. |
michael@0 | 9207 | return use->index() == 1; |
michael@0 | 9208 | } |
michael@0 | 9209 | #endif |
michael@0 | 9210 | }; |
michael@0 | 9211 | |
michael@0 | 9212 | class MNewSlots : public MNullaryInstruction |
michael@0 | 9213 | { |
michael@0 | 9214 | unsigned nslots_; |
michael@0 | 9215 | |
michael@0 | 9216 | MNewSlots(unsigned nslots) |
michael@0 | 9217 | : nslots_(nslots) |
michael@0 | 9218 | { |
michael@0 | 9219 | setResultType(MIRType_Slots); |
michael@0 | 9220 | } |
michael@0 | 9221 | |
michael@0 | 9222 | public: |
michael@0 | 9223 | INSTRUCTION_HEADER(NewSlots) |
michael@0 | 9224 | |
michael@0 | 9225 | static MNewSlots *New(TempAllocator &alloc, unsigned nslots) { |
michael@0 | 9226 | return new(alloc) MNewSlots(nslots); |
michael@0 | 9227 | } |
michael@0 | 9228 | unsigned nslots() const { |
michael@0 | 9229 | return nslots_; |
michael@0 | 9230 | } |
michael@0 | 9231 | AliasSet getAliasSet() const { |
michael@0 | 9232 | return AliasSet::None(); |
michael@0 | 9233 | } |
michael@0 | 9234 | bool possiblyCalls() const { |
michael@0 | 9235 | return true; |
michael@0 | 9236 | } |
michael@0 | 9237 | }; |
michael@0 | 9238 | |
michael@0 | 9239 | class MNewDeclEnvObject : public MNullaryInstruction |
michael@0 | 9240 | { |
michael@0 | 9241 | CompilerRootObject templateObj_; |
michael@0 | 9242 | |
michael@0 | 9243 | MNewDeclEnvObject(JSObject *templateObj) |
michael@0 | 9244 | : MNullaryInstruction(), |
michael@0 | 9245 | templateObj_(templateObj) |
michael@0 | 9246 | { |
michael@0 | 9247 | setResultType(MIRType_Object); |
michael@0 | 9248 | } |
michael@0 | 9249 | |
michael@0 | 9250 | public: |
michael@0 | 9251 | INSTRUCTION_HEADER(NewDeclEnvObject); |
michael@0 | 9252 | |
michael@0 | 9253 | static MNewDeclEnvObject *New(TempAllocator &alloc, JSObject *templateObj) { |
michael@0 | 9254 | return new(alloc) MNewDeclEnvObject(templateObj); |
michael@0 | 9255 | } |
michael@0 | 9256 | |
michael@0 | 9257 | JSObject *templateObj() { |
michael@0 | 9258 | return templateObj_; |
michael@0 | 9259 | } |
michael@0 | 9260 | AliasSet getAliasSet() const { |
michael@0 | 9261 | return AliasSet::None(); |
michael@0 | 9262 | } |
michael@0 | 9263 | }; |
michael@0 | 9264 | |
michael@0 | 9265 | class MNewCallObjectBase : public MUnaryInstruction |
michael@0 | 9266 | { |
michael@0 | 9267 | CompilerRootObject templateObj_; |
michael@0 | 9268 | |
michael@0 | 9269 | protected: |
michael@0 | 9270 | MNewCallObjectBase(JSObject *templateObj, MDefinition *slots) |
michael@0 | 9271 | : MUnaryInstruction(slots), |
michael@0 | 9272 | templateObj_(templateObj) |
michael@0 | 9273 | { |
michael@0 | 9274 | setResultType(MIRType_Object); |
michael@0 | 9275 | } |
michael@0 | 9276 | |
michael@0 | 9277 | public: |
michael@0 | 9278 | MDefinition *slots() { |
michael@0 | 9279 | return getOperand(0); |
michael@0 | 9280 | } |
michael@0 | 9281 | JSObject *templateObject() { |
michael@0 | 9282 | return templateObj_; |
michael@0 | 9283 | } |
michael@0 | 9284 | AliasSet getAliasSet() const { |
michael@0 | 9285 | return AliasSet::None(); |
michael@0 | 9286 | } |
michael@0 | 9287 | }; |
michael@0 | 9288 | |
michael@0 | 9289 | class MNewCallObject : public MNewCallObjectBase |
michael@0 | 9290 | { |
michael@0 | 9291 | public: |
michael@0 | 9292 | INSTRUCTION_HEADER(NewCallObject) |
michael@0 | 9293 | |
michael@0 | 9294 | MNewCallObject(JSObject *templateObj, MDefinition *slots) |
michael@0 | 9295 | : MNewCallObjectBase(templateObj, slots) |
michael@0 | 9296 | {} |
michael@0 | 9297 | |
michael@0 | 9298 | static MNewCallObject * |
michael@0 | 9299 | New(TempAllocator &alloc, JSObject *templateObj, MDefinition *slots) |
michael@0 | 9300 | { |
michael@0 | 9301 | return new(alloc) MNewCallObject(templateObj, slots); |
michael@0 | 9302 | } |
michael@0 | 9303 | }; |
michael@0 | 9304 | |
michael@0 | 9305 | class MNewRunOnceCallObject : public MNewCallObjectBase |
michael@0 | 9306 | { |
michael@0 | 9307 | public: |
michael@0 | 9308 | INSTRUCTION_HEADER(NewRunOnceCallObject) |
michael@0 | 9309 | |
michael@0 | 9310 | MNewRunOnceCallObject(JSObject *templateObj, MDefinition *slots) |
michael@0 | 9311 | : MNewCallObjectBase(templateObj, slots) |
michael@0 | 9312 | {} |
michael@0 | 9313 | |
michael@0 | 9314 | static MNewRunOnceCallObject * |
michael@0 | 9315 | New(TempAllocator &alloc, JSObject *templateObj, MDefinition *slots) |
michael@0 | 9316 | { |
michael@0 | 9317 | return new(alloc) MNewRunOnceCallObject(templateObj, slots); |
michael@0 | 9318 | } |
michael@0 | 9319 | }; |
michael@0 | 9320 | |
michael@0 | 9321 | class MNewCallObjectPar : public MBinaryInstruction |
michael@0 | 9322 | { |
michael@0 | 9323 | CompilerRootObject templateObj_; |
michael@0 | 9324 | |
michael@0 | 9325 | MNewCallObjectPar(MDefinition *cx, JSObject *templateObj, MDefinition *slots) |
michael@0 | 9326 | : MBinaryInstruction(cx, slots), |
michael@0 | 9327 | templateObj_(templateObj) |
michael@0 | 9328 | { |
michael@0 | 9329 | setResultType(MIRType_Object); |
michael@0 | 9330 | } |
michael@0 | 9331 | |
michael@0 | 9332 | public: |
michael@0 | 9333 | INSTRUCTION_HEADER(NewCallObjectPar); |
michael@0 | 9334 | |
michael@0 | 9335 | static MNewCallObjectPar *New(TempAllocator &alloc, MDefinition *cx, MNewCallObjectBase *callObj) { |
michael@0 | 9336 | return new(alloc) MNewCallObjectPar(cx, callObj->templateObject(), callObj->slots()); |
michael@0 | 9337 | } |
michael@0 | 9338 | |
michael@0 | 9339 | MDefinition *forkJoinContext() const { |
michael@0 | 9340 | return getOperand(0); |
michael@0 | 9341 | } |
michael@0 | 9342 | |
michael@0 | 9343 | MDefinition *slots() const { |
michael@0 | 9344 | return getOperand(1); |
michael@0 | 9345 | } |
michael@0 | 9346 | |
michael@0 | 9347 | JSObject *templateObj() const { |
michael@0 | 9348 | return templateObj_; |
michael@0 | 9349 | } |
michael@0 | 9350 | |
michael@0 | 9351 | AliasSet getAliasSet() const { |
michael@0 | 9352 | return AliasSet::None(); |
michael@0 | 9353 | } |
michael@0 | 9354 | }; |
michael@0 | 9355 | |
michael@0 | 9356 | class MNewStringObject : |
michael@0 | 9357 | public MUnaryInstruction, |
michael@0 | 9358 | public ConvertToStringPolicy<0> |
michael@0 | 9359 | { |
michael@0 | 9360 | CompilerRootObject templateObj_; |
michael@0 | 9361 | |
michael@0 | 9362 | MNewStringObject(MDefinition *input, JSObject *templateObj) |
michael@0 | 9363 | : MUnaryInstruction(input), |
michael@0 | 9364 | templateObj_(templateObj) |
michael@0 | 9365 | { |
michael@0 | 9366 | setResultType(MIRType_Object); |
michael@0 | 9367 | } |
michael@0 | 9368 | |
michael@0 | 9369 | public: |
michael@0 | 9370 | INSTRUCTION_HEADER(NewStringObject) |
michael@0 | 9371 | |
michael@0 | 9372 | static MNewStringObject *New(TempAllocator &alloc, MDefinition *input, JSObject *templateObj) { |
michael@0 | 9373 | return new(alloc) MNewStringObject(input, templateObj); |
michael@0 | 9374 | } |
michael@0 | 9375 | |
michael@0 | 9376 | StringObject *templateObj() const; |
michael@0 | 9377 | |
michael@0 | 9378 | TypePolicy *typePolicy() { |
michael@0 | 9379 | return this; |
michael@0 | 9380 | } |
michael@0 | 9381 | }; |
michael@0 | 9382 | |
michael@0 | 9383 | // Node that represents that a script has begun executing. This comes at the |
michael@0 | 9384 | // start of the function and is called once per function (including inline |
michael@0 | 9385 | // ones) |
michael@0 | 9386 | class MProfilerStackOp : public MNullaryInstruction |
michael@0 | 9387 | { |
michael@0 | 9388 | public: |
michael@0 | 9389 | enum Type { |
michael@0 | 9390 | Enter, // a function has begun executing and it is not inline |
michael@0 | 9391 | Exit, // any function has exited (inlined or normal) |
michael@0 | 9392 | InlineEnter, // an inline function has begun executing |
michael@0 | 9393 | |
michael@0 | 9394 | InlineExit // all instructions of an inline function are done, a |
michael@0 | 9395 | // return from the inline function could have occurred |
michael@0 | 9396 | // before this boundary |
michael@0 | 9397 | }; |
michael@0 | 9398 | |
michael@0 | 9399 | private: |
michael@0 | 9400 | JSScript *script_; |
michael@0 | 9401 | Type type_; |
michael@0 | 9402 | unsigned inlineLevel_; |
michael@0 | 9403 | |
michael@0 | 9404 | MProfilerStackOp(JSScript *script, Type type, unsigned inlineLevel) |
michael@0 | 9405 | : script_(script), type_(type), inlineLevel_(inlineLevel) |
michael@0 | 9406 | { |
michael@0 | 9407 | JS_ASSERT_IF(type != InlineExit, script != nullptr); |
michael@0 | 9408 | JS_ASSERT_IF(type == InlineEnter, inlineLevel != 0); |
michael@0 | 9409 | setGuard(); |
michael@0 | 9410 | } |
michael@0 | 9411 | |
michael@0 | 9412 | public: |
michael@0 | 9413 | INSTRUCTION_HEADER(ProfilerStackOp) |
michael@0 | 9414 | |
michael@0 | 9415 | static MProfilerStackOp *New(TempAllocator &alloc, JSScript *script, Type type, |
michael@0 | 9416 | unsigned inlineLevel = 0) { |
michael@0 | 9417 | return new(alloc) MProfilerStackOp(script, type, inlineLevel); |
michael@0 | 9418 | } |
michael@0 | 9419 | |
michael@0 | 9420 | JSScript *script() { |
michael@0 | 9421 | return script_; |
michael@0 | 9422 | } |
michael@0 | 9423 | |
michael@0 | 9424 | Type type() { |
michael@0 | 9425 | return type_; |
michael@0 | 9426 | } |
michael@0 | 9427 | |
michael@0 | 9428 | unsigned inlineLevel() { |
michael@0 | 9429 | return inlineLevel_; |
michael@0 | 9430 | } |
michael@0 | 9431 | |
michael@0 | 9432 | AliasSet getAliasSet() const { |
michael@0 | 9433 | return AliasSet::None(); |
michael@0 | 9434 | } |
michael@0 | 9435 | }; |
michael@0 | 9436 | |
michael@0 | 9437 | // This is an alias for MLoadFixedSlot. |
michael@0 | 9438 | class MEnclosingScope : public MLoadFixedSlot |
michael@0 | 9439 | { |
michael@0 | 9440 | MEnclosingScope(MDefinition *obj) |
michael@0 | 9441 | : MLoadFixedSlot(obj, ScopeObject::enclosingScopeSlot()) |
michael@0 | 9442 | { |
michael@0 | 9443 | setResultType(MIRType_Object); |
michael@0 | 9444 | } |
michael@0 | 9445 | |
michael@0 | 9446 | public: |
michael@0 | 9447 | static MEnclosingScope *New(TempAllocator &alloc, MDefinition *obj) { |
michael@0 | 9448 | return new(alloc) MEnclosingScope(obj); |
michael@0 | 9449 | } |
michael@0 | 9450 | |
michael@0 | 9451 | AliasSet getAliasSet() const { |
michael@0 | 9452 | // ScopeObject reserved slots are immutable. |
michael@0 | 9453 | return AliasSet::None(); |
michael@0 | 9454 | } |
michael@0 | 9455 | }; |
michael@0 | 9456 | |
michael@0 | 9457 | // Creates a dense array of the given length. |
michael@0 | 9458 | // |
michael@0 | 9459 | // Note: the template object should be an *empty* dense array! |
michael@0 | 9460 | class MNewDenseArrayPar : public MBinaryInstruction |
michael@0 | 9461 | { |
michael@0 | 9462 | CompilerRootObject templateObject_; |
michael@0 | 9463 | |
michael@0 | 9464 | MNewDenseArrayPar(MDefinition *cx, MDefinition *length, JSObject *templateObject) |
michael@0 | 9465 | : MBinaryInstruction(cx, length), |
michael@0 | 9466 | templateObject_(templateObject) |
michael@0 | 9467 | { |
michael@0 | 9468 | setResultType(MIRType_Object); |
michael@0 | 9469 | } |
michael@0 | 9470 | |
michael@0 | 9471 | public: |
michael@0 | 9472 | INSTRUCTION_HEADER(NewDenseArrayPar); |
michael@0 | 9473 | |
michael@0 | 9474 | static MNewDenseArrayPar *New(TempAllocator &alloc, MDefinition *cx, MDefinition *length, |
michael@0 | 9475 | JSObject *templateObject) |
michael@0 | 9476 | { |
michael@0 | 9477 | return new(alloc) MNewDenseArrayPar(cx, length, templateObject); |
michael@0 | 9478 | } |
michael@0 | 9479 | |
michael@0 | 9480 | MDefinition *forkJoinContext() const { |
michael@0 | 9481 | return getOperand(0); |
michael@0 | 9482 | } |
michael@0 | 9483 | |
michael@0 | 9484 | MDefinition *length() const { |
michael@0 | 9485 | return getOperand(1); |
michael@0 | 9486 | } |
michael@0 | 9487 | |
michael@0 | 9488 | JSObject *templateObject() const { |
michael@0 | 9489 | return templateObject_; |
michael@0 | 9490 | } |
michael@0 | 9491 | |
michael@0 | 9492 | bool possiblyCalls() const { |
michael@0 | 9493 | return true; |
michael@0 | 9494 | } |
michael@0 | 9495 | }; |
michael@0 | 9496 | |
michael@0 | 9497 | // A resume point contains the information needed to reconstruct the Baseline |
michael@0 | 9498 | // state from a position in the JIT. See the big comment near resumeAfter() in |
michael@0 | 9499 | // IonBuilder.cpp. |
michael@0 | 9500 | class MResumePoint MOZ_FINAL : public MNode, public InlineForwardListNode<MResumePoint> |
michael@0 | 9501 | { |
michael@0 | 9502 | public: |
michael@0 | 9503 | enum Mode { |
michael@0 | 9504 | ResumeAt, // Resume until before the current instruction |
michael@0 | 9505 | ResumeAfter, // Resume after the current instruction |
michael@0 | 9506 | Outer // State before inlining. |
michael@0 | 9507 | }; |
michael@0 | 9508 | |
michael@0 | 9509 | private: |
michael@0 | 9510 | friend class MBasicBlock; |
michael@0 | 9511 | friend void AssertBasicGraphCoherency(MIRGraph &graph); |
michael@0 | 9512 | |
michael@0 | 9513 | FixedList<MUse> operands_; |
michael@0 | 9514 | uint32_t stackDepth_; |
michael@0 | 9515 | jsbytecode *pc_; |
michael@0 | 9516 | MResumePoint *caller_; |
michael@0 | 9517 | MInstruction *instruction_; |
michael@0 | 9518 | Mode mode_; |
michael@0 | 9519 | |
michael@0 | 9520 | MResumePoint(MBasicBlock *block, jsbytecode *pc, MResumePoint *parent, Mode mode); |
michael@0 | 9521 | void inherit(MBasicBlock *state); |
michael@0 | 9522 | |
michael@0 | 9523 | protected: |
michael@0 | 9524 | // Initializes operands_ to an empty array of a fixed length. |
michael@0 | 9525 | // The array may then be filled in by inherit(). |
michael@0 | 9526 | bool init(TempAllocator &alloc) { |
michael@0 | 9527 | return operands_.init(alloc, stackDepth_); |
michael@0 | 9528 | } |
michael@0 | 9529 | |
michael@0 | 9530 | // Overwrites an operand without updating its Uses. |
michael@0 | 9531 | void setOperand(size_t index, MDefinition *operand) { |
michael@0 | 9532 | JS_ASSERT(index < stackDepth_); |
michael@0 | 9533 | operands_[index].set(operand, this, index); |
michael@0 | 9534 | operand->addUse(&operands_[index]); |
michael@0 | 9535 | } |
michael@0 | 9536 | |
michael@0 | 9537 | void clearOperand(size_t index) { |
michael@0 | 9538 | JS_ASSERT(index < stackDepth_); |
michael@0 | 9539 | operands_[index].set(nullptr, this, index); |
michael@0 | 9540 | } |
michael@0 | 9541 | |
michael@0 | 9542 | MUse *getUseFor(size_t index) { |
michael@0 | 9543 | return &operands_[index]; |
michael@0 | 9544 | } |
michael@0 | 9545 | |
michael@0 | 9546 | public: |
michael@0 | 9547 | static MResumePoint *New(TempAllocator &alloc, MBasicBlock *block, jsbytecode *pc, |
michael@0 | 9548 | MResumePoint *parent, Mode mode); |
michael@0 | 9549 | |
michael@0 | 9550 | MNode::Kind kind() const { |
michael@0 | 9551 | return MNode::ResumePoint; |
michael@0 | 9552 | } |
michael@0 | 9553 | size_t numOperands() const { |
michael@0 | 9554 | return stackDepth_; |
michael@0 | 9555 | } |
michael@0 | 9556 | MDefinition *getOperand(size_t index) const { |
michael@0 | 9557 | JS_ASSERT(index < stackDepth_); |
michael@0 | 9558 | return operands_[index].producer(); |
michael@0 | 9559 | } |
michael@0 | 9560 | jsbytecode *pc() const { |
michael@0 | 9561 | return pc_; |
michael@0 | 9562 | } |
michael@0 | 9563 | uint32_t stackDepth() const { |
michael@0 | 9564 | return stackDepth_; |
michael@0 | 9565 | } |
michael@0 | 9566 | MResumePoint *caller() { |
michael@0 | 9567 | return caller_; |
michael@0 | 9568 | } |
michael@0 | 9569 | void setCaller(MResumePoint *caller) { |
michael@0 | 9570 | caller_ = caller; |
michael@0 | 9571 | } |
michael@0 | 9572 | uint32_t frameCount() const { |
michael@0 | 9573 | uint32_t count = 1; |
michael@0 | 9574 | for (MResumePoint *it = caller_; it; it = it->caller_) |
michael@0 | 9575 | count++; |
michael@0 | 9576 | return count; |
michael@0 | 9577 | } |
michael@0 | 9578 | MInstruction *instruction() { |
michael@0 | 9579 | return instruction_; |
michael@0 | 9580 | } |
michael@0 | 9581 | void setInstruction(MInstruction *ins) { |
michael@0 | 9582 | instruction_ = ins; |
michael@0 | 9583 | } |
michael@0 | 9584 | Mode mode() const { |
michael@0 | 9585 | return mode_; |
michael@0 | 9586 | } |
michael@0 | 9587 | |
michael@0 | 9588 | void discardUses() { |
michael@0 | 9589 | for (size_t i = 0; i < stackDepth_; i++) { |
michael@0 | 9590 | if (operands_[i].hasProducer()) |
michael@0 | 9591 | operands_[i].producer()->removeUse(&operands_[i]); |
michael@0 | 9592 | } |
michael@0 | 9593 | } |
michael@0 | 9594 | |
michael@0 | 9595 | bool writeRecoverData(CompactBufferWriter &writer) const; |
michael@0 | 9596 | }; |
michael@0 | 9597 | |
michael@0 | 9598 | class MIsCallable |
michael@0 | 9599 | : public MUnaryInstruction, |
michael@0 | 9600 | public SingleObjectPolicy |
michael@0 | 9601 | { |
michael@0 | 9602 | MIsCallable(MDefinition *object) |
michael@0 | 9603 | : MUnaryInstruction(object) |
michael@0 | 9604 | { |
michael@0 | 9605 | setResultType(MIRType_Boolean); |
michael@0 | 9606 | setMovable(); |
michael@0 | 9607 | } |
michael@0 | 9608 | |
michael@0 | 9609 | public: |
michael@0 | 9610 | INSTRUCTION_HEADER(IsCallable); |
michael@0 | 9611 | |
michael@0 | 9612 | static MIsCallable *New(TempAllocator &alloc, MDefinition *obj) { |
michael@0 | 9613 | return new(alloc) MIsCallable(obj); |
michael@0 | 9614 | } |
michael@0 | 9615 | |
michael@0 | 9616 | MDefinition *object() const { |
michael@0 | 9617 | return getOperand(0); |
michael@0 | 9618 | } |
michael@0 | 9619 | AliasSet getAliasSet() const { |
michael@0 | 9620 | return AliasSet::None(); |
michael@0 | 9621 | } |
michael@0 | 9622 | }; |
michael@0 | 9623 | |
michael@0 | 9624 | class MHaveSameClass |
michael@0 | 9625 | : public MBinaryInstruction, |
michael@0 | 9626 | public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> > |
michael@0 | 9627 | { |
michael@0 | 9628 | MHaveSameClass(MDefinition *left, MDefinition *right) |
michael@0 | 9629 | : MBinaryInstruction(left, right) |
michael@0 | 9630 | { |
michael@0 | 9631 | setResultType(MIRType_Boolean); |
michael@0 | 9632 | setMovable(); |
michael@0 | 9633 | } |
michael@0 | 9634 | |
michael@0 | 9635 | public: |
michael@0 | 9636 | INSTRUCTION_HEADER(HaveSameClass); |
michael@0 | 9637 | |
michael@0 | 9638 | static MHaveSameClass *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) { |
michael@0 | 9639 | return new(alloc) MHaveSameClass(left, right); |
michael@0 | 9640 | } |
michael@0 | 9641 | |
michael@0 | 9642 | TypePolicy *typePolicy() { |
michael@0 | 9643 | return this; |
michael@0 | 9644 | } |
michael@0 | 9645 | bool congruentTo(const MDefinition *ins) const { |
michael@0 | 9646 | return congruentIfOperandsEqual(ins); |
michael@0 | 9647 | } |
michael@0 | 9648 | AliasSet getAliasSet() const { |
michael@0 | 9649 | return AliasSet::None(); |
michael@0 | 9650 | } |
michael@0 | 9651 | }; |
michael@0 | 9652 | |
michael@0 | 9653 | class MHasClass |
michael@0 | 9654 | : public MUnaryInstruction, |
michael@0 | 9655 | public SingleObjectPolicy |
michael@0 | 9656 | { |
michael@0 | 9657 | const Class *class_; |
michael@0 | 9658 | |
michael@0 | 9659 | MHasClass(MDefinition *object, const Class *clasp) |
michael@0 | 9660 | : MUnaryInstruction(object) |
michael@0 | 9661 | , class_(clasp) |
michael@0 | 9662 | { |
michael@0 | 9663 | JS_ASSERT(object->type() == MIRType_Object); |
michael@0 | 9664 | setResultType(MIRType_Boolean); |
michael@0 | 9665 | setMovable(); |
michael@0 | 9666 | } |
michael@0 | 9667 | |
michael@0 | 9668 | public: |
michael@0 | 9669 | INSTRUCTION_HEADER(HasClass); |
michael@0 | 9670 | |
michael@0 | 9671 | static MHasClass *New(TempAllocator &alloc, MDefinition *obj, const Class *clasp) { |
michael@0 | 9672 | return new(alloc) MHasClass(obj, clasp); |
michael@0 | 9673 | } |
michael@0 | 9674 | |
michael@0 | 9675 | MDefinition *object() const { |
michael@0 | 9676 | return getOperand(0); |
michael@0 | 9677 | } |
michael@0 | 9678 | const Class *getClass() const { |
michael@0 | 9679 | return class_; |
michael@0 | 9680 | } |
michael@0 | 9681 | AliasSet getAliasSet() const { |
michael@0 | 9682 | return AliasSet::None(); |
michael@0 | 9683 | } |
michael@0 | 9684 | }; |
michael@0 | 9685 | |
michael@0 | 9686 | // Increase the usecount of the provided script upon execution and test if |
michael@0 | 9687 | // the usecount surpasses the threshold. Upon hit it will recompile the |
michael@0 | 9688 | // outermost script (i.e. not the inlined script). |
michael@0 | 9689 | class MRecompileCheck : public MNullaryInstruction |
michael@0 | 9690 | { |
michael@0 | 9691 | JSScript *script_; |
michael@0 | 9692 | uint32_t recompileThreshold_; |
michael@0 | 9693 | |
michael@0 | 9694 | MRecompileCheck(JSScript *script, uint32_t recompileThreshold) |
michael@0 | 9695 | : script_(script), |
michael@0 | 9696 | recompileThreshold_(recompileThreshold) |
michael@0 | 9697 | { |
michael@0 | 9698 | setGuard(); |
michael@0 | 9699 | } |
michael@0 | 9700 | |
michael@0 | 9701 | public: |
michael@0 | 9702 | INSTRUCTION_HEADER(RecompileCheck); |
michael@0 | 9703 | |
michael@0 | 9704 | static MRecompileCheck *New(TempAllocator &alloc, JSScript *script_, uint32_t useCount) { |
michael@0 | 9705 | return new(alloc) MRecompileCheck(script_, useCount); |
michael@0 | 9706 | } |
michael@0 | 9707 | |
michael@0 | 9708 | JSScript *script() const { |
michael@0 | 9709 | return script_; |
michael@0 | 9710 | } |
michael@0 | 9711 | |
michael@0 | 9712 | uint32_t recompileThreshold() const { |
michael@0 | 9713 | return recompileThreshold_; |
michael@0 | 9714 | } |
michael@0 | 9715 | |
michael@0 | 9716 | AliasSet getAliasSet() const { |
michael@0 | 9717 | return AliasSet::None(); |
michael@0 | 9718 | } |
michael@0 | 9719 | }; |
michael@0 | 9720 | |
michael@0 | 9721 | class MAsmJSNeg : public MUnaryInstruction |
michael@0 | 9722 | { |
michael@0 | 9723 | MAsmJSNeg(MDefinition *op, MIRType type) |
michael@0 | 9724 | : MUnaryInstruction(op) |
michael@0 | 9725 | { |
michael@0 | 9726 | setResultType(type); |
michael@0 | 9727 | setMovable(); |
michael@0 | 9728 | } |
michael@0 | 9729 | |
michael@0 | 9730 | public: |
michael@0 | 9731 | INSTRUCTION_HEADER(AsmJSNeg); |
michael@0 | 9732 | static MAsmJSNeg *NewAsmJS(TempAllocator &alloc, MDefinition *op, MIRType type) { |
michael@0 | 9733 | return new(alloc) MAsmJSNeg(op, type); |
michael@0 | 9734 | } |
michael@0 | 9735 | }; |
michael@0 | 9736 | |
michael@0 | 9737 | class MAsmJSHeapAccess |
michael@0 | 9738 | { |
michael@0 | 9739 | ArrayBufferView::ViewType viewType_; |
michael@0 | 9740 | bool skipBoundsCheck_; |
michael@0 | 9741 | |
michael@0 | 9742 | public: |
michael@0 | 9743 | MAsmJSHeapAccess(ArrayBufferView::ViewType vt, bool s) |
michael@0 | 9744 | : viewType_(vt), skipBoundsCheck_(s) |
michael@0 | 9745 | {} |
michael@0 | 9746 | |
michael@0 | 9747 | ArrayBufferView::ViewType viewType() const { return viewType_; } |
michael@0 | 9748 | bool skipBoundsCheck() const { return skipBoundsCheck_; } |
michael@0 | 9749 | void setSkipBoundsCheck(bool v) { skipBoundsCheck_ = v; } |
michael@0 | 9750 | }; |
michael@0 | 9751 | |
michael@0 | 9752 | class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess |
michael@0 | 9753 | { |
michael@0 | 9754 | MAsmJSLoadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr) |
michael@0 | 9755 | : MUnaryInstruction(ptr), MAsmJSHeapAccess(vt, false) |
michael@0 | 9756 | { |
michael@0 | 9757 | setMovable(); |
michael@0 | 9758 | if (vt == ArrayBufferView::TYPE_FLOAT32) |
michael@0 | 9759 | setResultType(MIRType_Float32); |
michael@0 | 9760 | else if (vt == ArrayBufferView::TYPE_FLOAT64) |
michael@0 | 9761 | setResultType(MIRType_Double); |
michael@0 | 9762 | else |
michael@0 | 9763 | setResultType(MIRType_Int32); |
michael@0 | 9764 | } |
michael@0 | 9765 | |
michael@0 | 9766 | public: |
michael@0 | 9767 | INSTRUCTION_HEADER(AsmJSLoadHeap); |
michael@0 | 9768 | |
michael@0 | 9769 | static MAsmJSLoadHeap *New(TempAllocator &alloc, ArrayBufferView::ViewType vt, MDefinition *ptr) { |
michael@0 | 9770 | return new(alloc) MAsmJSLoadHeap(vt, ptr); |
michael@0 | 9771 | } |
michael@0 | 9772 | |
michael@0 | 9773 | MDefinition *ptr() const { return getOperand(0); } |
michael@0 | 9774 | |
michael@0 | 9775 | bool congruentTo(const MDefinition *ins) const; |
michael@0 | 9776 | AliasSet getAliasSet() const { |
michael@0 | 9777 | return AliasSet::Load(AliasSet::AsmJSHeap); |
michael@0 | 9778 | } |
michael@0 | 9779 | bool mightAlias(const MDefinition *def) const; |
michael@0 | 9780 | }; |
michael@0 | 9781 | |
michael@0 | 9782 | class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess |
michael@0 | 9783 | { |
michael@0 | 9784 | MAsmJSStoreHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v) |
michael@0 | 9785 | : MBinaryInstruction(ptr, v) , MAsmJSHeapAccess(vt, false) |
michael@0 | 9786 | {} |
michael@0 | 9787 | |
michael@0 | 9788 | public: |
michael@0 | 9789 | INSTRUCTION_HEADER(AsmJSStoreHeap); |
michael@0 | 9790 | |
michael@0 | 9791 | static MAsmJSStoreHeap *New(TempAllocator &alloc, ArrayBufferView::ViewType vt, |
michael@0 | 9792 | MDefinition *ptr, MDefinition *v) |
michael@0 | 9793 | { |
michael@0 | 9794 | return new(alloc) MAsmJSStoreHeap(vt, ptr, v); |
michael@0 | 9795 | } |
michael@0 | 9796 | |
michael@0 | 9797 | MDefinition *ptr() const { return getOperand(0); } |
michael@0 | 9798 | MDefinition *value() const { return getOperand(1); } |
michael@0 | 9799 | |
michael@0 | 9800 | AliasSet getAliasSet() const { |
michael@0 | 9801 | return AliasSet::Store(AliasSet::AsmJSHeap); |
michael@0 | 9802 | } |
michael@0 | 9803 | }; |
michael@0 | 9804 | |
michael@0 | 9805 | class MAsmJSLoadGlobalVar : public MNullaryInstruction |
michael@0 | 9806 | { |
michael@0 | 9807 | MAsmJSLoadGlobalVar(MIRType type, unsigned globalDataOffset, bool isConstant) |
michael@0 | 9808 | : globalDataOffset_(globalDataOffset), isConstant_(isConstant) |
michael@0 | 9809 | { |
michael@0 | 9810 | JS_ASSERT(IsNumberType(type)); |
michael@0 | 9811 | setResultType(type); |
michael@0 | 9812 | setMovable(); |
michael@0 | 9813 | } |
michael@0 | 9814 | |
michael@0 | 9815 | unsigned globalDataOffset_; |
michael@0 | 9816 | bool isConstant_; |
michael@0 | 9817 | |
michael@0 | 9818 | public: |
michael@0 | 9819 | INSTRUCTION_HEADER(AsmJSLoadGlobalVar); |
michael@0 | 9820 | |
michael@0 | 9821 | static MAsmJSLoadGlobalVar *New(TempAllocator &alloc, MIRType type, unsigned globalDataOffset, |
michael@0 | 9822 | bool isConstant) |
michael@0 | 9823 | { |
michael@0 | 9824 | return new(alloc) MAsmJSLoadGlobalVar(type, globalDataOffset, isConstant); |
michael@0 | 9825 | } |
michael@0 | 9826 | |
michael@0 | 9827 | unsigned globalDataOffset() const { return globalDataOffset_; } |
michael@0 | 9828 | |
michael@0 | 9829 | bool congruentTo(const MDefinition *ins) const; |
michael@0 | 9830 | |
michael@0 | 9831 | AliasSet getAliasSet() const { |
michael@0 | 9832 | return isConstant_ ? AliasSet::None() : AliasSet::Load(AliasSet::AsmJSGlobalVar); |
michael@0 | 9833 | } |
michael@0 | 9834 | |
michael@0 | 9835 | bool mightAlias(const MDefinition *def) const; |
michael@0 | 9836 | }; |
michael@0 | 9837 | |
michael@0 | 9838 | class MAsmJSStoreGlobalVar : public MUnaryInstruction |
michael@0 | 9839 | { |
michael@0 | 9840 | MAsmJSStoreGlobalVar(unsigned globalDataOffset, MDefinition *v) |
michael@0 | 9841 | : MUnaryInstruction(v), globalDataOffset_(globalDataOffset) |
michael@0 | 9842 | {} |
michael@0 | 9843 | |
michael@0 | 9844 | unsigned globalDataOffset_; |
michael@0 | 9845 | |
michael@0 | 9846 | public: |
michael@0 | 9847 | INSTRUCTION_HEADER(AsmJSStoreGlobalVar); |
michael@0 | 9848 | |
michael@0 | 9849 | static MAsmJSStoreGlobalVar *New(TempAllocator &alloc, unsigned globalDataOffset, MDefinition *v) { |
michael@0 | 9850 | return new(alloc) MAsmJSStoreGlobalVar(globalDataOffset, v); |
michael@0 | 9851 | } |
michael@0 | 9852 | |
michael@0 | 9853 | unsigned globalDataOffset() const { return globalDataOffset_; } |
michael@0 | 9854 | MDefinition *value() const { return getOperand(0); } |
michael@0 | 9855 | |
michael@0 | 9856 | AliasSet getAliasSet() const { |
michael@0 | 9857 | return AliasSet::Store(AliasSet::AsmJSGlobalVar); |
michael@0 | 9858 | } |
michael@0 | 9859 | }; |
michael@0 | 9860 | |
michael@0 | 9861 | class MAsmJSLoadFuncPtr : public MUnaryInstruction |
michael@0 | 9862 | { |
michael@0 | 9863 | MAsmJSLoadFuncPtr(unsigned globalDataOffset, MDefinition *index) |
michael@0 | 9864 | : MUnaryInstruction(index), globalDataOffset_(globalDataOffset) |
michael@0 | 9865 | { |
michael@0 | 9866 | setResultType(MIRType_Pointer); |
michael@0 | 9867 | } |
michael@0 | 9868 | |
michael@0 | 9869 | unsigned globalDataOffset_; |
michael@0 | 9870 | |
michael@0 | 9871 | public: |
michael@0 | 9872 | INSTRUCTION_HEADER(AsmJSLoadFuncPtr); |
michael@0 | 9873 | |
michael@0 | 9874 | static MAsmJSLoadFuncPtr *New(TempAllocator &alloc, unsigned globalDataOffset, |
michael@0 | 9875 | MDefinition *index) |
michael@0 | 9876 | { |
michael@0 | 9877 | return new(alloc) MAsmJSLoadFuncPtr(globalDataOffset, index); |
michael@0 | 9878 | } |
michael@0 | 9879 | |
michael@0 | 9880 | unsigned globalDataOffset() const { return globalDataOffset_; } |
michael@0 | 9881 | MDefinition *index() const { return getOperand(0); } |
michael@0 | 9882 | }; |
michael@0 | 9883 | |
michael@0 | 9884 | class MAsmJSLoadFFIFunc : public MNullaryInstruction |
michael@0 | 9885 | { |
michael@0 | 9886 | MAsmJSLoadFFIFunc(unsigned globalDataOffset) |
michael@0 | 9887 | : globalDataOffset_(globalDataOffset) |
michael@0 | 9888 | { |
michael@0 | 9889 | setResultType(MIRType_Pointer); |
michael@0 | 9890 | } |
michael@0 | 9891 | |
michael@0 | 9892 | unsigned globalDataOffset_; |
michael@0 | 9893 | |
michael@0 | 9894 | public: |
michael@0 | 9895 | INSTRUCTION_HEADER(AsmJSLoadFFIFunc); |
michael@0 | 9896 | |
michael@0 | 9897 | static MAsmJSLoadFFIFunc *New(TempAllocator &alloc, unsigned globalDataOffset) |
michael@0 | 9898 | { |
michael@0 | 9899 | return new(alloc) MAsmJSLoadFFIFunc(globalDataOffset); |
michael@0 | 9900 | } |
michael@0 | 9901 | |
michael@0 | 9902 | unsigned globalDataOffset() const { return globalDataOffset_; } |
michael@0 | 9903 | }; |
michael@0 | 9904 | |
michael@0 | 9905 | class MAsmJSParameter : public MNullaryInstruction |
michael@0 | 9906 | { |
michael@0 | 9907 | ABIArg abi_; |
michael@0 | 9908 | |
michael@0 | 9909 | MAsmJSParameter(ABIArg abi, MIRType mirType) |
michael@0 | 9910 | : abi_(abi) |
michael@0 | 9911 | { |
michael@0 | 9912 | setResultType(mirType); |
michael@0 | 9913 | } |
michael@0 | 9914 | |
michael@0 | 9915 | public: |
michael@0 | 9916 | INSTRUCTION_HEADER(AsmJSParameter); |
michael@0 | 9917 | |
michael@0 | 9918 | static MAsmJSParameter *New(TempAllocator &alloc, ABIArg abi, MIRType mirType) { |
michael@0 | 9919 | return new(alloc) MAsmJSParameter(abi, mirType); |
michael@0 | 9920 | } |
michael@0 | 9921 | |
michael@0 | 9922 | ABIArg abi() const { return abi_; } |
michael@0 | 9923 | }; |
michael@0 | 9924 | |
michael@0 | 9925 | class MAsmJSReturn : public MAryControlInstruction<1, 0> |
michael@0 | 9926 | { |
michael@0 | 9927 | MAsmJSReturn(MDefinition *ins) { |
michael@0 | 9928 | setOperand(0, ins); |
michael@0 | 9929 | } |
michael@0 | 9930 | |
michael@0 | 9931 | public: |
michael@0 | 9932 | INSTRUCTION_HEADER(AsmJSReturn); |
michael@0 | 9933 | static MAsmJSReturn *New(TempAllocator &alloc, MDefinition *ins) { |
michael@0 | 9934 | return new(alloc) MAsmJSReturn(ins); |
michael@0 | 9935 | } |
michael@0 | 9936 | }; |
michael@0 | 9937 | |
michael@0 | 9938 | class MAsmJSVoidReturn : public MAryControlInstruction<0, 0> |
michael@0 | 9939 | { |
michael@0 | 9940 | public: |
michael@0 | 9941 | INSTRUCTION_HEADER(AsmJSVoidReturn); |
michael@0 | 9942 | static MAsmJSVoidReturn *New(TempAllocator &alloc) { |
michael@0 | 9943 | return new(alloc) MAsmJSVoidReturn(); |
michael@0 | 9944 | } |
michael@0 | 9945 | }; |
michael@0 | 9946 | |
michael@0 | 9947 | class MAsmJSPassStackArg : public MUnaryInstruction |
michael@0 | 9948 | { |
michael@0 | 9949 | MAsmJSPassStackArg(uint32_t spOffset, MDefinition *ins) |
michael@0 | 9950 | : MUnaryInstruction(ins), |
michael@0 | 9951 | spOffset_(spOffset) |
michael@0 | 9952 | {} |
michael@0 | 9953 | |
michael@0 | 9954 | uint32_t spOffset_; |
michael@0 | 9955 | |
michael@0 | 9956 | public: |
michael@0 | 9957 | INSTRUCTION_HEADER(AsmJSPassStackArg); |
michael@0 | 9958 | static MAsmJSPassStackArg *New(TempAllocator &alloc, uint32_t spOffset, MDefinition *ins) { |
michael@0 | 9959 | return new(alloc) MAsmJSPassStackArg(spOffset, ins); |
michael@0 | 9960 | } |
michael@0 | 9961 | uint32_t spOffset() const { |
michael@0 | 9962 | return spOffset_; |
michael@0 | 9963 | } |
michael@0 | 9964 | void incrementOffset(uint32_t inc) { |
michael@0 | 9965 | spOffset_ += inc; |
michael@0 | 9966 | } |
michael@0 | 9967 | MDefinition *arg() const { |
michael@0 | 9968 | return getOperand(0); |
michael@0 | 9969 | } |
michael@0 | 9970 | }; |
michael@0 | 9971 | |
michael@0 | 9972 | class MAsmJSCall MOZ_FINAL : public MInstruction |
michael@0 | 9973 | { |
michael@0 | 9974 | public: |
michael@0 | 9975 | class Callee { |
michael@0 | 9976 | public: |
michael@0 | 9977 | enum Which { Internal, Dynamic, Builtin }; |
michael@0 | 9978 | private: |
michael@0 | 9979 | Which which_; |
michael@0 | 9980 | union { |
michael@0 | 9981 | Label *internal_; |
michael@0 | 9982 | MDefinition *dynamic_; |
michael@0 | 9983 | AsmJSImmKind builtin_; |
michael@0 | 9984 | } u; |
michael@0 | 9985 | public: |
michael@0 | 9986 | Callee() {} |
michael@0 | 9987 | Callee(Label *callee) : which_(Internal) { u.internal_ = callee; } |
michael@0 | 9988 | Callee(MDefinition *callee) : which_(Dynamic) { u.dynamic_ = callee; } |
michael@0 | 9989 | Callee(AsmJSImmKind callee) : which_(Builtin) { u.builtin_ = callee; } |
michael@0 | 9990 | Which which() const { return which_; } |
michael@0 | 9991 | Label *internal() const { JS_ASSERT(which_ == Internal); return u.internal_; } |
michael@0 | 9992 | MDefinition *dynamic() const { JS_ASSERT(which_ == Dynamic); return u.dynamic_; } |
michael@0 | 9993 | AsmJSImmKind builtin() const { JS_ASSERT(which_ == Builtin); return u.builtin_; } |
michael@0 | 9994 | }; |
michael@0 | 9995 | |
michael@0 | 9996 | private: |
michael@0 | 9997 | struct Operand { |
michael@0 | 9998 | AnyRegister reg; |
michael@0 | 9999 | MUse use; |
michael@0 | 10000 | }; |
michael@0 | 10001 | |
michael@0 | 10002 | CallSiteDesc desc_; |
michael@0 | 10003 | Callee callee_; |
michael@0 | 10004 | FixedList<MUse> operands_; |
michael@0 | 10005 | FixedList<AnyRegister> argRegs_; |
michael@0 | 10006 | size_t spIncrement_; |
michael@0 | 10007 | |
michael@0 | 10008 | MAsmJSCall(const CallSiteDesc &desc, Callee callee, size_t spIncrement) |
michael@0 | 10009 | : desc_(desc), callee_(callee), spIncrement_(spIncrement) |
michael@0 | 10010 | { } |
michael@0 | 10011 | |
michael@0 | 10012 | protected: |
michael@0 | 10013 | void setOperand(size_t index, MDefinition *operand) { |
michael@0 | 10014 | operands_[index].set(operand, this, index); |
michael@0 | 10015 | operand->addUse(&operands_[index]); |
michael@0 | 10016 | } |
michael@0 | 10017 | MUse *getUseFor(size_t index) { |
michael@0 | 10018 | return &operands_[index]; |
michael@0 | 10019 | } |
michael@0 | 10020 | |
michael@0 | 10021 | public: |
michael@0 | 10022 | INSTRUCTION_HEADER(AsmJSCall); |
michael@0 | 10023 | |
michael@0 | 10024 | struct Arg { |
michael@0 | 10025 | AnyRegister reg; |
michael@0 | 10026 | MDefinition *def; |
michael@0 | 10027 | Arg(AnyRegister reg, MDefinition *def) : reg(reg), def(def) {} |
michael@0 | 10028 | }; |
michael@0 | 10029 | typedef Vector<Arg, 8> Args; |
michael@0 | 10030 | |
michael@0 | 10031 | static MAsmJSCall *New(TempAllocator &alloc, const CallSiteDesc &desc, Callee callee, |
michael@0 | 10032 | const Args &args, MIRType resultType, size_t spIncrement); |
michael@0 | 10033 | |
michael@0 | 10034 | size_t numOperands() const { |
michael@0 | 10035 | return operands_.length(); |
michael@0 | 10036 | } |
michael@0 | 10037 | MDefinition *getOperand(size_t index) const { |
michael@0 | 10038 | JS_ASSERT(index < numOperands()); |
michael@0 | 10039 | return operands_[index].producer(); |
michael@0 | 10040 | } |
michael@0 | 10041 | size_t numArgs() const { |
michael@0 | 10042 | return argRegs_.length(); |
michael@0 | 10043 | } |
michael@0 | 10044 | AnyRegister registerForArg(size_t index) const { |
michael@0 | 10045 | JS_ASSERT(index < numArgs()); |
michael@0 | 10046 | return argRegs_[index]; |
michael@0 | 10047 | } |
michael@0 | 10048 | const CallSiteDesc &desc() const { |
michael@0 | 10049 | return desc_; |
michael@0 | 10050 | } |
michael@0 | 10051 | Callee callee() const { |
michael@0 | 10052 | return callee_; |
michael@0 | 10053 | } |
michael@0 | 10054 | size_t dynamicCalleeOperandIndex() const { |
michael@0 | 10055 | JS_ASSERT(callee_.which() == Callee::Dynamic); |
michael@0 | 10056 | JS_ASSERT(numArgs() == numOperands() - 1); |
michael@0 | 10057 | return numArgs(); |
michael@0 | 10058 | } |
michael@0 | 10059 | size_t spIncrement() const { |
michael@0 | 10060 | return spIncrement_; |
michael@0 | 10061 | } |
michael@0 | 10062 | |
michael@0 | 10063 | bool possiblyCalls() const { |
michael@0 | 10064 | return true; |
michael@0 | 10065 | } |
michael@0 | 10066 | }; |
michael@0 | 10067 | |
michael@0 | 10068 | #undef INSTRUCTION_HEADER |
michael@0 | 10069 | |
michael@0 | 10070 | // Implement opcode casts now that the compiler can see the inheritance. |
michael@0 | 10071 | #define OPCODE_CASTS(opcode) \ |
michael@0 | 10072 | M##opcode *MDefinition::to##opcode() \ |
michael@0 | 10073 | { \ |
michael@0 | 10074 | JS_ASSERT(is##opcode()); \ |
michael@0 | 10075 | return static_cast<M##opcode *>(this); \ |
michael@0 | 10076 | } \ |
michael@0 | 10077 | const M##opcode *MDefinition::to##opcode() const \ |
michael@0 | 10078 | { \ |
michael@0 | 10079 | JS_ASSERT(is##opcode()); \ |
michael@0 | 10080 | return static_cast<const M##opcode *>(this); \ |
michael@0 | 10081 | } |
michael@0 | 10082 | MIR_OPCODE_LIST(OPCODE_CASTS) |
michael@0 | 10083 | #undef OPCODE_CASTS |
michael@0 | 10084 | |
michael@0 | 10085 | MDefinition *MNode::toDefinition() |
michael@0 | 10086 | { |
michael@0 | 10087 | JS_ASSERT(isDefinition()); |
michael@0 | 10088 | return (MDefinition *)this; |
michael@0 | 10089 | } |
michael@0 | 10090 | |
michael@0 | 10091 | MResumePoint *MNode::toResumePoint() |
michael@0 | 10092 | { |
michael@0 | 10093 | JS_ASSERT(isResumePoint()); |
michael@0 | 10094 | return (MResumePoint *)this; |
michael@0 | 10095 | } |
michael@0 | 10096 | |
michael@0 | 10097 | MInstruction *MDefinition::toInstruction() |
michael@0 | 10098 | { |
michael@0 | 10099 | JS_ASSERT(!isPhi()); |
michael@0 | 10100 | return (MInstruction *)this; |
michael@0 | 10101 | } |
michael@0 | 10102 | |
michael@0 | 10103 | typedef Vector<MDefinition *, 8, IonAllocPolicy> MDefinitionVector; |
michael@0 | 10104 | |
michael@0 | 10105 | // Helper functions used to decide how to build MIR. |
michael@0 | 10106 | |
michael@0 | 10107 | bool ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id); |
michael@0 | 10108 | bool ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id, |
michael@0 | 10109 | ScalarTypeDescr::Type *arrayType); |
michael@0 | 10110 | bool ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj); |
michael@0 | 10111 | bool ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints, |
michael@0 | 10112 | MDefinition *obj); |
michael@0 | 10113 | MIRType DenseNativeElementType(types::CompilerConstraintList *constraints, MDefinition *obj); |
michael@0 | 10114 | bool PropertyReadNeedsTypeBarrier(JSContext *propertycx, |
michael@0 | 10115 | types::CompilerConstraintList *constraints, |
michael@0 | 10116 | types::TypeObjectKey *object, PropertyName *name, |
michael@0 | 10117 | types::TemporaryTypeSet *observed, bool updateObserved); |
michael@0 | 10118 | bool PropertyReadNeedsTypeBarrier(JSContext *propertycx, |
michael@0 | 10119 | types::CompilerConstraintList *constraints, |
michael@0 | 10120 | MDefinition *obj, PropertyName *name, |
michael@0 | 10121 | types::TemporaryTypeSet *observed); |
michael@0 | 10122 | bool PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints, |
michael@0 | 10123 | MDefinition *obj, PropertyName *name, |
michael@0 | 10124 | types::TemporaryTypeSet *observed); |
michael@0 | 10125 | bool PropertyReadIsIdempotent(types::CompilerConstraintList *constraints, |
michael@0 | 10126 | MDefinition *obj, PropertyName *name); |
michael@0 | 10127 | void AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name, |
michael@0 | 10128 | types::TemporaryTypeSet *observed); |
michael@0 | 10129 | bool PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstraintList *constraints, |
michael@0 | 10130 | MBasicBlock *current, MDefinition **pobj, |
michael@0 | 10131 | PropertyName *name, MDefinition **pvalue, |
michael@0 | 10132 | bool canModify); |
michael@0 | 10133 | |
michael@0 | 10134 | } // namespace jit |
michael@0 | 10135 | } // namespace js |
michael@0 | 10136 | |
michael@0 | 10137 | #endif /* jit_MIR_h */ |