Source/JavaScriptCore/ChangeLog

 12013-11-18 Filip Pizlo <fpizlo@apple.com>
 2
 3 Infer constant global variables
 4 https://bugs.webkit.org/show_bug.cgi?id=124464
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 All global variables that are candidates for watchpoint-based constant inference (i.e.
 9 not 'const' variables) will now have WatchpointSet's associated with them and those
 10 are used to drive the inference by tracking three states of each variable:
 11
 12 Uninitialized: the variable's value is Undefined and the WatchpointSet state is
 13 ClearWatchpoint.
 14
 15 Initialized: the variable's value was set to something (could even be explicitly set
 16 to Undefined) and the WatchpointSet state is IsWatching.
 17
 18 Invalidated: the variable's value was set to something else (could even be the same
 19 thing as before but the point is that a put operation did execute again) and the
 20 WatchpointSet is IsInvalidated.
 21
 22 If the compiler tries to compile a GetGlobalVar and the WatchpointSet state is
 23 IsWatching, then the current value of the variable can be folded in place of the get,
 24 and a watchpoint on the variable can be registered.
 25
 26 We handle race conditions between the mutator and compiler by mandating that:
 27
 28 - The mutator changes the WatchpointSet state after executing the put.
 29
 30 - The compiler checks the WatchpointSet state prior to reading the value.
 31
 32 The concrete algorithm used by the mutator is:
 33
 34 1. Store the new value into the variable.
 35 --- Execute a store-store fence.
 36 2. Bump the state (ClearWatchpoing becomes IsWatching, IsWatching becomes
 37 IsInvalidated)
 38
 39 The concrete algorithm that the compiler uses is:
 40
 41 1. Load the state. If it's *not* IsWatching, then give up on constant inference.
 42 --- Execute a load-load fence.
 43 2. Load the value of the variable and use that for folding, while also registering
 44 a DesiredWatchpoint. The various parts of this step can be done in any order.
 45
 46 The desired watchpoint registration will fail if the watchpoint set is already
 47 invalidated. Now consider the following interesting interleavings; note that the
 48 fences are omitted since they are just there to ensure sequential consistency:
 49
 50 Uninitialized->M1->M2->C1->C2: Compiler sees IsWatching because of the mutator's store
 51 operation, and the variable is folded. The fencing ensures that C2 sees the value
 52 stored in M1 - i.e. we fold on the value that will actually be watchpointed. If
 53 before the compilation is installed the mutator executes another store then we
 54 will be sure that it will be a complete sequence of M1+M2 since compilations get
 55 installed at safepoints and never "in the middle" of a put_to_scope. Hence that
 56 compilation installation will be invalidated. If the M1+M2 sequence happens after
 57 the code is installed, then the code will be invalidated by triggering a jettison.
 58
 59 Uninitialized->M1->C1->C2->M2: Compiler sees Uninitialized and will not fold. This is
 60 a sensible outcome since if the compiler read the variable's value, it would have
 61 seen Undefined.
 62
 63 Uninitialized->C1->C2->M1->M2: Compiler sees Uninitialized and will not fold.
 64 Uninitialized->C1->M1->C2->M2: Compiler sees Uninitialized and will not fold.
 65 Uninitialized->C1->M1->M2->C2: Compiler sees Uninitialized and will not fold.
 66 Uninitialized->M1->C1->M2->C2: Compiler sees Uninitialized and will not fold.
 67
 68 IsWatched->M1->M2->C1->C2: Compiler sees IsInvalidated and will not fold.
 69
 70 IsWatched->M1->C1->C2->M2: Compiler will fold, but will also register a desired
 71 watchpoint, and that watchpoint will get invalidated before the code is installed.
 72
 73 IsWatched->M1->C1->M2->C2: As above, will fold but the code will get invalidated.
 74 IsWatched->C1->C2->M1->M2: As above, will fold but the code will get invalidated.
 75 IsWatched->C1->M1->C2->M2: As above, will fold but the code will get invalidated.
 76 IsWatched->C1->M1->M2->C2: As above, will fold but the code will get invalidated.
 77
 78 Note that this kind of reasoning shows why having the mutator first bump the state and
 79 then store the new value would be wrong. If we had done that (M1 = bump state, M2 =
 80 execute put) then we could have the following deadly interleavings:
 81
 82 Uninitialized->M1->C1->C2->M2:
 83 Uninitialized->M1->C1->M2->C2: Mutator bumps the state to IsWatched and then the
 84 compiler folds Undefined, since M2 hasn't executed yet. Although C2 will set the
 85 watchpoint, M1 didn't notify it - it mearly initiated watching. M2 then stores a
 86 value other than Undefined, and you're toast.
 87
 88 You could fix this sort of thing by making the Desired Watchpoints machinery more
 89 sophisticated, for example having it track the value that was folded; if the global
 90 variable's value was later found to be different then we could invalidate the
 91 compilation. I decided to instead just use the right interleaving.
 92
 93 This is a 0.5% speed-up on SunSpider, mostly due to a 20% speed-up on math-cordic.
 94 It's a 0.6% slow-down on LongSpider, mostly due to a 25% slow-down on 3d-cube. This is
 95 because 3d-cube takes global variable assignment slow paths very often. Note that this
 96 3d-cube slow-down doesn't manifest as much in SunSpider (only 6% there). This patch is
 97 also a 1.5% speed-up on V8v7 and a 2.8% speed-up on Octane v1, mostly due to deltablue
 98 (3.7%), richards (4%), and mandreel (26%). This is a 2% speed-up on Kraken, mostly due
 99 to a 17.5% speed-up on imaging-gaussian-blur. Something that really illustrates the
 100 slam-dunk-itude of this patch is the wide range of speed-ups on JSRegress. Casual JS
 101 programming often leads to global-var-based idioms and those variables tend to be
 102 assigned once, leading to excellent constant folding opportunities in an optimizing
 103 JIT. This is very evident in the speed-ups on JSRegress.
 104
 105 This still needs some porting and build love, but otherwise it's ready to review.
 106
 107 * assembler/MacroAssemblerX86Common.h:
 108 (JSC::MacroAssemblerX86Common::memoryFence):
 109 * assembler/MacroAssemblerX86_64.h:
 110 (JSC::MacroAssemblerX86_64::load8):
 111 * assembler/X86Assembler.h:
 112 (JSC::X86Assembler::mfence):
 113 (JSC::X86Assembler::X86InstructionFormatter::threeByteOp):
 114 * bytecode/CodeBlock.cpp:
 115 (JSC::CodeBlock::CodeBlock):
 116 * bytecode/Watchpoint.cpp:
 117 (JSC::WatchpointSet::WatchpointSet):
 118 (JSC::WatchpointSet::add):
 119 (JSC::WatchpointSet::notifyWriteSlow):
 120 * bytecode/Watchpoint.h:
 121 (JSC::WatchpointSet::state):
 122 (JSC::WatchpointSet::isStillValid):
 123 (JSC::WatchpointSet::addressOfSetIsNotEmpty):
 124 * dfg/DFGAbstractInterpreterInlines.h:
 125 (JSC::DFG::::executeEffects):
 126 * dfg/DFGByteCodeParser.cpp:
 127 (JSC::DFG::ByteCodeParser::getJSConstantForValue):
 128 (JSC::DFG::ByteCodeParser::getJSConstant):
 129 (JSC::DFG::ByteCodeParser::parseBlock):
 130 * dfg/DFGClobberize.h:
 131 (JSC::DFG::clobberize):
 132 * dfg/DFGFixupPhase.cpp:
 133 (JSC::DFG::FixupPhase::fixupNode):
 134 * dfg/DFGNode.h:
 135 (JSC::DFG::Node::isStronglyProvedConstantIn):
 136 (JSC::DFG::Node::hasIdentifierNumberForCheck):
 137 (JSC::DFG::Node::hasRegisterPointer):
 138 * dfg/DFGNodeFlags.h:
 139 * dfg/DFGNodeType.h:
 140 * dfg/DFGOperations.cpp:
 141 * dfg/DFGOperations.h:
 142 * dfg/DFGPredictionPropagationPhase.cpp:
 143 (JSC::DFG::PredictionPropagationPhase::propagate):
 144 * dfg/DFGSafeToExecute.h:
 145 (JSC::DFG::safeToExecute):
 146 * dfg/DFGSpeculativeJIT.cpp:
 147 (JSC::DFG::SpeculativeJIT::compileNotifyPutGlobalVar):
 148 * dfg/DFGSpeculativeJIT.h:
 149 (JSC::DFG::SpeculativeJIT::callOperation):
 150 * dfg/DFGSpeculativeJIT64.cpp:
 151 (JSC::DFG::SpeculativeJIT::compile):
 152 * jit/JIT.h:
 153 * jit/JITOperations.h:
 154 * jit/JITPropertyAccess.cpp:
 155 (JSC::JIT::emitPutGlobalVar):
 156 (JSC::JIT::emit_op_put_to_scope):
 157 (JSC::JIT::emitSlow_op_put_to_scope):
 158 * llint/LowLevelInterpreter64.asm:
 159 * offlineasm/arm.rb:
 160 * offlineasm/arm64.rb:
 161 * offlineasm/cloop.rb:
 162 * offlineasm/instructions.rb:
 163 * offlineasm/x86.rb:
 164 * runtime/JSGlobalObject.cpp:
 165 (JSC::JSGlobalObject::addGlobalVar):
 166 * runtime/JSGlobalObject.h:
 167 (JSC::JSGlobalObject::addVar):
 168 (JSC::JSGlobalObject::addConst):
 169 (JSC::JSGlobalObject::addFunction):
 170 * runtime/JSScope.cpp:
 171 (JSC::abstractAccess):
 172 * runtime/SymbolTable.cpp:
 173 (JSC::SymbolTableEntry::couldBeWatched):
 174 (JSC::SymbolTableEntry::prepareToWatch):
 175 (JSC::SymbolTableEntry::notifyWriteSlow):
 176 * runtime/SymbolTable.h:
 177
11782013-11-18 Michael Saboff <msaboff@apple.com>
2179
3180 ARM64 CRASH: Debug builds crash in emitPointerValidation()
159479

Source/JavaScriptCore/assembler/MacroAssemblerX86Common.h

@@public:
624624#endif
625625 m_assembler.movb_rm(src, address.offset, address.base, address.index, address.scale);
626626 }
 627
 628 void store8(RegisterID src, Address address)
 629 {
 630#if CPU(X86)
 631 // On 32-bit x86 we can only store from the first 4 registers;
 632 // esp..edi are mapped to the 'h' registers!
 633 if (src >= 4) {
 634 // Pick a temporary register.
 635 RegisterID temp = getUnusedRegister(address);
 636
 637 // Swap to the temporary register to perform the store.
 638 swap(src, temp);
 639 m_assembler.movb_rm(temp, address.offset, address.base);
 640 swap(src, temp);
 641 return;
 642 }
 643#endif
 644 m_assembler.movb_rm(src, address.offset, address.base);
 645 }
627646
628647 void store16(RegisterID src, BaseIndex address)
629648 {

@@public:
14191438 {
14201439 m_assembler.nop();
14211440 }
 1441
 1442 void memoryFence()
 1443 {
 1444 m_assembler.mfence();
 1445 }
14221446
14231447 static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination)
14241448 {
159462

Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h

@@public:
4747 using MacroAssemblerX86Common::branchAdd32;
4848 using MacroAssemblerX86Common::or32;
4949 using MacroAssemblerX86Common::sub32;
 50 using MacroAssemblerX86Common::load8;
5051 using MacroAssemblerX86Common::load32;
5152 using MacroAssemblerX86Common::store32;
5253 using MacroAssemblerX86Common::store8;

@@public:
9192 move(TrustedImmPtr(address.m_ptr), scratchRegister);
9293 sub32(imm, Address(scratchRegister));
9394 }
 95
 96 void load8(const void* address, RegisterID dest)
 97 {
 98 move(TrustedImmPtr(address), dest);
 99 load8(dest, dest);
 100 }
94101
95102 void load32(const void* address, RegisterID dest)
96103 {

@@public:
126133 store8(imm, Address(scratchRegister));
127134 }
128135
 136 void store8(RegisterID reg, void* address)
 137 {
 138 move(TrustedImmPtr(address), scratchRegister);
 139 store8(reg, Address(scratchRegister));
 140 }
 141
129142 Call call()
130143 {
131144 DataLabelPtr label = moveWithPatch(TrustedImmPtr(0), scratchRegister);
159462

Source/JavaScriptCore/assembler/X86Assembler.h

@@private:
267267 OP2_MOVD_EdVd = 0x7E,
268268 OP2_JCC_rel32 = 0x80,
269269 OP_SETCC = 0x90,
 270 OP2_3BYTE_ESCAPE = 0xAE,
270271 OP2_IMUL_GvEv = 0xAF,
271272 OP2_MOVZX_GvEb = 0xB6,
272273 OP2_MOVSX_GvEb = 0xBE,

@@private:
277278 OP2_PSRLQ_UdqIb = 0x73,
278279 OP2_POR_VdqWdq = 0XEB,
279280 } TwoByteOpcodeID;
 281
 282 typedef enum {
 283 OP3_MFENCE = 0xF0,
 284 } ThreeByteOpcodeID;
280285
281286 TwoByteOpcodeID jccRel32(Condition cond)
282287 {

@@public:
13031308 m_formatter.immediate8(imm);
13041309 }
13051310
 1311 void movb_rm(RegisterID src, int offset, RegisterID base)
 1312 {
 1313 m_formatter.oneByteOp8(OP_MOV_EbGb, src, base, offset);
 1314 }
 1315
13061316 void movb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
13071317 {
13081318 m_formatter.oneByteOp8(OP_MOV_EbGb, src, base, index, scale, offset);

@@public:
18731883 m_formatter.prefix(PRE_SSE_F2);
18741884 m_formatter.twoByteOp(OP2_SQRTSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
18751885 }
1876 
 1886
18771887 // Misc instructions:
18781888
18791889 void int3()

@@public:
18901900 {
18911901 m_formatter.prefix(PRE_PREDICT_BRANCH_NOT_TAKEN);
18921902 }
 1903
 1904 void mfence()
 1905 {
 1906 m_formatter.threeByteOp(OP3_MFENCE);
 1907 }
18931908
18941909 // Assembler admin methods:
18951910

@@private:
23012316 }
23022317#endif
23032318
 2319 void threeByteOp(ThreeByteOpcodeID opcode)
 2320 {
 2321 m_buffer.ensureSpace(maxInstructionSize);
 2322 m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
 2323 m_buffer.putByteUnchecked(OP2_3BYTE_ESCAPE);
 2324 m_buffer.putByteUnchecked(opcode);
 2325 }
 2326
23042327#if CPU(X86_64)
23052328 // Quad-word-sized operands:
23062329 //

@@private:
24132436 registerModRM(reg, rm);
24142437 }
24152438
 2439 void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset)
 2440 {
 2441 m_buffer.ensureSpace(maxInstructionSize);
 2442 emitRexIf(byteRegRequiresRex(reg) || byteRegRequiresRex(base), reg, 0, base);
 2443 m_buffer.putByteUnchecked(opcode);
 2444 memoryModRM(reg, base, offset);
 2445 }
 2446
24162447 void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset)
24172448 {
24182449 m_buffer.ensureSpace(maxInstructionSize);
159462

Source/JavaScriptCore/bytecode/CodeBlock.cpp

@@CodeBlock::CodeBlock(ScriptExecutable* o
18371837 if (entry.isNull())
18381838 break;
18391839
1840  // It's likely that we'll write to this var, so notify now and avoid the overhead of doing so at runtime.
1841  entry.notifyWrite();
1842 
18431840 instructions[i + 0] = vm()->interpreter->getOpcode(op_init_global_const);
18441841 instructions[i + 1] = &m_globalObject->registerAt(entry.getIndex());
18451842 break;
159462

Source/JavaScriptCore/bytecode/Watchpoint.cpp

@@Watchpoint::~Watchpoint()
4040
4141WatchpointSet::WatchpointSet(WatchpointState state)
4242 : m_state(state)
 43 , m_setIsNotEmpty(false)
4344{
4445}
4546

@@void WatchpointSet::add(Watchpoint* watc
6061 if (!watchpoint)
6162 return;
6263 m_set.push(watchpoint);
 64 m_setIsNotEmpty = true;
6365 m_state = IsWatched;
6466}
6567

@@void WatchpointSet::notifyWriteSlow()
6769{
6870 ASSERT(state() == IsWatched);
6971
 72 WTF::storeStoreFence();
7073 fireAllWatchpoints();
7174 m_state = IsInvalidated;
7275 WTF::storeStoreFence();
159462

Source/JavaScriptCore/bytecode/Watchpoint.h

@@public:
5959 WatchpointSet(WatchpointState);
6060 ~WatchpointSet(); // Note that this will not fire any of the watchpoints; if you need to know when a WatchpointSet dies then you need a separate mechanism for this.
6161
62  WatchpointState state() const { return static_cast<WatchpointState>(m_state); }
 62 // It is safe to call this from another thread. It may return an old
 63 // state. Guarantees that if *first* read the state() of the thing being
 64 // watched and it returned IsWatched and *second* you actually read its
 65 // value then it's safe to assume that if the state being watched changes
 66 // then also the watchpoint state() will change to IsInvalidated.
 67 WatchpointState state() const
 68 {
 69 WTF::loadLoadFence();
 70 WatchpointState result = static_cast<WatchpointState>(m_state);
 71 WTF::loadLoadFence();
 72 return result;
 73 }
6374
6475 // It is safe to call this from another thread. It may return true
6576 // even if the set actually had been invalidated, but that ought to happen

@@public:
6980 // issuing a load-load fence prior to querying the state.
7081 bool isStillValid() const
7182 {
72  WTF::loadLoadFence();
7383 return state() != IsInvalidated;
7484 }
7585 // Like isStillValid(), may be called from another thread.

@@public:
99109 }
100110
101111 int8_t* addressOfState() { return &m_state; }
 112 int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; }
102113
103114 JS_EXPORT_PRIVATE void notifyWriteSlow(); // Call only if you've checked isWatched.
104115

@@private:
109120
110121 SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set;
111122 int8_t m_state;
 123 int8_t m_setIsNotEmpty;
112124};
113125
114126// InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which
159462

Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h

@@bool AbstractInterpreter<AbstractStateTy
15111511 break;
15121512
15131513 case PutGlobalVar:
 1514 case NotifyPutGlobalVar:
15141515 break;
15151516
15161517 case CheckHasInstance:
159462

Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

@@private:
516516 // constant folding. I.e. creating constants using this if we had constant
517517 // field inference would be a bad idea, since the bytecode parser's folding
518518 // doesn't handle liveness preservation.
519  Node* getJSConstantForValue(JSValue constantValue)
 519 Node* getJSConstantForValue(JSValue constantValue, NodeFlags flags = NodeIsStaticConstant)
520520 {
521521 unsigned constantIndex;
522522 if (!m_codeBlock->findConstant(constantValue, constantIndex)) {

@@private:
526526
527527 ASSERT(m_constants.size() == m_codeBlock->numberOfConstantRegisters());
528528
529  return getJSConstant(constantIndex);
 529 return getJSConstant(constantIndex, flags);
530530 }
531531
532  Node* getJSConstant(unsigned constant)
 532 Node* getJSConstant(unsigned constant, NodeFlags flags = NodeIsStaticConstant)
533533 {
534534 Node* node = m_constants[constant].asJSValue;
535535 if (node)
536536 return node;
537537
538538 Node* result = addToGraph(JSConstant, OpInfo(constant));
 539 result->mergeFlags(flags);
539540 m_constants[constant].asJSValue = result;
540541 return result;
541542 }

@@bool ByteCodeParser::parseBlock(unsigned
31003101
31013102 addToGraph(GlobalVarWatchpoint, OpInfo(operand), OpInfo(identifierNumber));
31023103 JSValue specificValue = globalObject->registerAt(entry.getIndex()).get();
3103  set(VirtualRegister(dst), cellConstant(specificValue.asCell()));
 3104 if (specificValue.isCell())
 3105 set(VirtualRegister(dst), cellConstant(specificValue.asCell()));
 3106 else
 3107 set(VirtualRegister(dst), getJSConstantForValue(specificValue, 0));
31043108 break;
31053109 }
31063110 case ClosureVar:

@@bool ByteCodeParser::parseBlock(unsigned
31233127 ResolveType resolveType = ResolveModeAndType(currentInstruction[4].u.operand).type();
31243128 StringImpl* uid = m_graph.identifiers()[identifierNumber];
31253129
3126  Structure* structure;
 3130 Structure* structure = 0;
 3131 WatchpointSet* watchpoints = 0;
31273132 uintptr_t operand;
31283133 {
31293134 ConcurrentJITLocker locker(m_inlineStackTop->m_profiledBlock->m_lock);
31303135 if (resolveType == GlobalVar || resolveType == GlobalVarWithVarInjectionChecks)
3131  structure = 0;
 3136 watchpoints = currentInstruction[5].u.watchpointSet;
31323137 else
31333138 structure = currentInstruction[5].u.structure.get();
31343139 operand = reinterpret_cast<uintptr_t>(currentInstruction[6].u.pointer);

@@bool ByteCodeParser::parseBlock(unsigned
31533158 }
31543159 case GlobalVar:
31553160 case GlobalVarWithVarInjectionChecks: {
3156  addToGraph(Phantom, get(VirtualRegister(scope)));
31573161 SymbolTableEntry entry = globalObject->symbolTable()->get(uid);
3158  ASSERT(!entry.couldBeWatched() || !m_graph.watchpoints().isStillValid(entry.watchpointSet()));
 3162 ASSERT(watchpoints == entry.watchpointSet());
31593163 addToGraph(PutGlobalVar, OpInfo(operand), get(VirtualRegister(value)));
 3164 if (watchpoints->state() != IsInvalidated)
 3165 addToGraph(NotifyPutGlobalVar, OpInfo(operand), OpInfo(identifierNumber));
31603166 // Keep scope alive until after put.
31613167 addToGraph(Phantom, get(VirtualRegister(scope)));
31623168 break;
159462

Source/JavaScriptCore/dfg/DFGClobberize.h

@@void clobberize(Graph& graph, Node* node
142142 case InvalidationPoint:
143143 write(SideState);
144144 return;
 145
 146 case NotifyPutGlobalVar:
 147 write(Watchpoint_fire);
 148 return;
145149
146150 case CreateActivation:
147151 case CreateArguments:
159462

Source/JavaScriptCore/dfg/DFGFixupPhase.cpp

@@private:
903903 case GetClosureVar:
904904 case GetGlobalVar:
905905 case PutGlobalVar:
 906 case NotifyPutGlobalVar:
906907 case GlobalVarWatchpoint:
907908 case VarInjectionWatchpoint:
908909 case AllocationProfileWatchpoint:
159462

Source/JavaScriptCore/dfg/DFGNode.h

@@struct Node {
359359
360360 bool isStronglyProvedConstantIn(InlineCallFrame* inlineCallFrame)
361361 {
362  return isConstant() && codeOrigin.inlineCallFrame == inlineCallFrame;
 362 return !!(flags() & NodeIsStaticConstant)
 363 && codeOrigin.inlineCallFrame == inlineCallFrame;
363364 }
364365
365366 bool isStronglyProvedConstantIn(const CodeOrigin& codeOrigin)

@@struct Node {
749750
750751 bool hasIdentifierNumberForCheck()
751752 {
752  return op() == GlobalVarWatchpoint;
 753 return op() == GlobalVarWatchpoint || op() == NotifyPutGlobalVar;
753754 }
754755
755756 unsigned identifierNumberForCheck()

@@struct Node {
760761
761762 bool hasRegisterPointer()
762763 {
763  return op() == GetGlobalVar || op() == PutGlobalVar || op() == GlobalVarWatchpoint;
 764 return op() == GetGlobalVar || op() == PutGlobalVar || op() == GlobalVarWatchpoint || op() == NotifyPutGlobalVar;
764765 }
765766
766767 WriteBarrier<Unknown>* registerPointer()
159462

Source/JavaScriptCore/dfg/DFGNodeFlags.h

@@namespace JSC { namespace DFG {
7070
7171#define NodeExitsForward 0x8000
7272
 73#define NodeIsStaticConstant 0x10000 // Used only by the parser, to determine if a constant arose statically and hence could be folded at parse-time.
 74
7375typedef uint32_t NodeFlags;
7476
7577static inline bool bytecodeUsesAsNumber(NodeFlags flags)
159462

Source/JavaScriptCore/dfg/DFGNodeType.h

@@namespace JSC { namespace DFG {
184184 macro(PutClosureVar, NodeMustGenerate) \
185185 macro(GetGlobalVar, NodeResultJS) \
186186 macro(PutGlobalVar, NodeMustGenerate) \
 187 macro(NotifyPutGlobalVar, NodeMustGenerate) \
187188 macro(GlobalVarWatchpoint, NodeMustGenerate) \
188189 macro(VarInjectionWatchpoint, NodeMustGenerate) \
189190 macro(CheckFunction, NodeMustGenerate) \
159462

Source/JavaScriptCore/dfg/DFGOperations.cpp

@@char* JIT_OPERATION operationSwitchStrin
998998 return static_cast<char*>(exec->codeBlock()->stringSwitchJumpTable(tableIndex).ctiForValue(string->value(exec).impl()).executableAddress());
999999}
10001000
 1001void JIT_OPERATION operationNotifyWrite(ExecState* exec, WatchpointSet* set)
 1002{
 1003 VM& vm = exec->vm();
 1004 NativeCallFrameTracer tracer(&vm, exec);
 1005
 1006 set->notifyWrite();
 1007}
 1008
10011009double JIT_OPERATION operationFModOnInts(int32_t a, int32_t b)
10021010{
10031011 return fmod(a, b);
159462

Source/JavaScriptCore/dfg/DFGOperations.h

@@JSCell* JIT_OPERATION operationMakeRope2
125125JSCell* JIT_OPERATION operationMakeRope3(ExecState*, JSString*, JSString*, JSString*);
126126char* JIT_OPERATION operationFindSwitchImmTargetForDouble(ExecState*, EncodedJSValue, size_t tableIndex);
127127char* JIT_OPERATION operationSwitchString(ExecState*, size_t tableIndex, JSString*);
 128void JIT_OPERATION operationNotifyWrite(ExecState*, WatchpointSet*);
128129
129130#if ENABLE(FTL_JIT)
130131// FIXME: Make calls work well. Currently they're a pure regression.
159462

Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp

@@private:
515515 case CheckTierUpInLoop:
516516 case CheckTierUpAtReturn:
517517 case CheckTierUpAndOSREnter:
518  case InvalidationPoint: {
 518 case InvalidationPoint:
 519 case Int52ToValue:
 520 case Int52ToDouble: {
519521 // This node should never be visible at this stage of compilation. It is
520522 // inserted by fixup(), which follows this phase.
521523 RELEASE_ASSERT_NOT_REACHED();

@@private:
580582 case CheckWatchdogTimer:
581583 case Unreachable:
582584 case LoopHint:
583  case Int52ToValue:
584  case Int52ToDouble:
 585 case NotifyPutGlobalVar:
585586 break;
586587
587588 // This gets ignored because it already has a prediction.
159462

Source/JavaScriptCore/dfg/DFGSafeToExecute.h

@@bool safeToExecute(AbstractStateType& st
242242 case Int52ToDouble:
243243 case Int52ToValue:
244244 case InvalidationPoint:
 245 case NotifyPutGlobalVar:
245246 return true;
246247
247248 case GetByVal:
159462

Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp

@@void SpeculativeJIT::compile(Node* node)
43684368 noResult(node);
43694369 break;
43704370 }
 4371
 4372 case NotifyPutGlobalVar: {
 4373 compileNotifyPutGlobalVar(node);
 4374 break;
 4375 }
43714376
43724377 case VarInjectionWatchpoint: {
43734378 noResult(node);
159462

Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp

@@void SpeculativeJIT::emitSwitch(Node* no
56015601 RELEASE_ASSERT_NOT_REACHED();
56025602}
56035603
 5604void SpeculativeJIT::compileNotifyPutGlobalVar(Node* node)
 5605{
 5606 WatchpointSet* set = m_jit.globalObjectFor(node->codeOrigin)->symbolTable()->get(
 5607 m_jit.graph().identifiers()[node->identifierNumberForCheck()]).watchpointSet();
 5608
 5609 GPRTemporary temp(this);
 5610 GPRReg tempGPR = temp.gpr();
 5611
 5612 m_jit.load8(set->addressOfState(), tempGPR);
 5613
 5614 JITCompiler::JumpList ready;
 5615
 5616 ready.append(m_jit.branch32(JITCompiler::Equal, tempGPR, TrustedImm32(IsInvalidated)));
 5617
 5618 if (set->state() == ClearWatchpoint) {
 5619 JITCompiler::Jump isWatched =
 5620 m_jit.branch32(JITCompiler::NotEqual, tempGPR, TrustedImm32(ClearWatchpoint));
 5621
 5622 m_jit.store8(TrustedImm32(IsWatched), set->addressOfState());
 5623 ready.append(m_jit.jump());
 5624
 5625 isWatched.link(&m_jit);
 5626 }
 5627
 5628 JITCompiler::Jump slowCase = m_jit.branchTest8(
 5629 JITCompiler::NonZero, JITCompiler::AbsoluteAddress(set->addressOfSetIsNotEmpty()));
 5630 m_jit.store8(TrustedImm32(IsInvalidated), set->addressOfState());
 5631
 5632 ready.link(&m_jit);
 5633
 5634 addSlowPathGenerator(
 5635 slowPathCall(slowCase, this, operationNotifyWrite, NoResult, set));
 5636
 5637 m_jit.memoryFence();
 5638
 5639 noResult(node);
 5640}
 5641
56045642void SpeculativeJIT::addBranch(const MacroAssembler::JumpList& jump, BasicBlock* destination)
56055643{
56065644 for (unsigned i = jump.jumps().size(); i--;)
159462

Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h

@@public:
10841084 return appendCallWithExceptionCheck(operation);
10851085 }
10861086
1087  JITCompiler::Call callOperation(V_JITOperation_W operation, WatchpointSet* watchpointSet)
 1087 JITCompiler::Call callOperation(V_JITOperation_EW operation, WatchpointSet* watchpointSet)
10881088 {
1089  m_jit.setupArguments(TrustedImmPtr(watchpointSet));
 1089 m_jit.setupArgumentsWithExecState(TrustedImmPtr(watchpointSet));
10901090 return appendCall(operation);
10911091 }
10921092

@@public:
20452045 void compileNewFunctionExpression(Node*);
20462046 bool compileRegExpExec(Node*);
20472047
 2048 void compileNotifyPutGlobalVar(Node*);
 2049
20482050 // size can be an immediate or a register, and must be in bytes. If size is a register,
20492051 // it must be a different register than resultGPR. Emits code that place a pointer to
20502052 // the end of the allocation. The returned jump is the jump to the slow path.
159462

Source/JavaScriptCore/jit/JIT.h

@@namespace JSC {
613613 void emitGetGlobalVar(uintptr_t operand);
614614 void emitGetClosureVar(int scope, uintptr_t operand);
615615 void emitPutGlobalProperty(uintptr_t* operandSlot, int value);
616  void emitPutGlobalVar(uintptr_t operand, int value);
 616 void emitPutGlobalVar(uintptr_t operand, int value, WatchpointSet*);
617617 void emitPutClosureVar(int scope, uintptr_t operand, int value);
618618
619619 void emitInitRegister(int dst);
159462

Source/JavaScriptCore/jit/JITOperations.h

@@typedef void JIT_OPERATION (*V_JITOperat
154154typedef void JIT_OPERATION (*V_JITOperation_EPc)(ExecState*, Instruction*);
155155typedef void JIT_OPERATION (*V_JITOperation_EPZJ)(ExecState*, void*, int32_t, EncodedJSValue);
156156typedef void JIT_OPERATION (*V_JITOperation_ESsiJJI)(ExecState*, StructureStubInfo*, EncodedJSValue, EncodedJSValue, StringImpl*);
157 typedef void JIT_OPERATION (*V_JITOperation_W)(WatchpointSet*);
 157typedef void JIT_OPERATION (*V_JITOperation_EW)(ExecState*, WatchpointSet*);
158158typedef void JIT_OPERATION (*V_JITOperation_EZ)(ExecState*, int32_t);
159159typedef void JIT_OPERATION (*V_JITOperation_EVm)(ExecState*, VM*);
160160typedef char* JIT_OPERATION (*P_JITOperation_E)(ExecState*);
159462

Source/JavaScriptCore/jit/JITPropertyAccess.cpp

@@void JIT::emitPutGlobalProperty(uintptr_
771771 storePtr(regT2, BaseIndex(regT0, regT1, TimesEight, (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue)));
772772}
773773
774 void JIT::emitPutGlobalVar(uintptr_t operand, int value)
 774void JIT::emitPutGlobalVar(uintptr_t operand, int value, WatchpointSet* set)
775775{
 776 if (set && set->state() != IsInvalidated) {
 777 load8(set->addressOfState(), regT1);
 778
 779 JumpList ready;
 780
 781 ready.append(branch32(Equal, regT1, TrustedImm32(IsInvalidated)));
 782
 783 if (set->state() == ClearWatchpoint) {
 784 Jump isWatched = branch32(NotEqual, regT1, TrustedImm32(ClearWatchpoint));
 785
 786 move(TrustedImm32(IsWatched), regT1);
 787 ready.append(jump());
 788
 789 isWatched.link(this);
 790 }
 791
 792 addSlowCase(branchTest8(NonZero, AbsoluteAddress(set->addressOfSetIsNotEmpty())));
 793 move(TrustedImm32(IsInvalidated), regT1);
 794 ready.link(this);
 795 }
 796
776797 emitGetVirtualRegister(value, regT0);
777798 storePtr(regT0, reinterpret_cast<void*>(operand));
 799
 800 if (set && set->state() != IsInvalidated) {
 801 memoryFence();
 802 store8(regT1, set->addressOfState());
 803 }
778804}
779805
780806void JIT::emitPutClosureVar(int scope, uintptr_t operand, int value)

@@void JIT::emit_op_put_to_scope(Instructi
802828 case GlobalVar:
803829 case GlobalVarWithVarInjectionChecks:
804830 emitVarInjectionCheck(needsVarInjectionChecks(resolveType));
805  emitPutGlobalVar(*operandSlot, value);
 831 emitPutGlobalVar(*operandSlot, value, currentInstruction[5].u.watchpointSet);
806832 break;
807833 case ClosureVar:
808834 case ClosureVarWithVarInjectionChecks:

@@void JIT::emit_op_put_to_scope(Instructi
818844void JIT::emitSlow_op_put_to_scope(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
819845{
820846 ResolveType resolveType = ResolveModeAndType(currentInstruction[4].u.operand).type();
821  if (resolveType == GlobalVar || resolveType == ClosureVar)
 847 unsigned linkCount = 0;
 848 if (resolveType != GlobalVar && resolveType != ClosureVar)
 849 linkCount++;
 850 if ((resolveType == GlobalVar || resolveType == GlobalVarWithVarInjectionChecks)
 851 && currentInstruction[5].u.watchpointSet->state() != IsInvalidated)
 852 linkCount++;
 853 if (!linkCount)
822854 return;
823 
824  linkSlowCase(iter);
 855 while (linkCount--)
 856 linkSlowCase(iter);
825857 callOperation(operationPutToScope, currentInstruction);
826858}
827859
159462

Source/JavaScriptCore/llint/LowLevelInterpreter64.asm

@@macro putProperty()
19551955end
19561956
19571957macro putGlobalVar()
 1958 loadpFromInstruction(5, t2)
 1959 loadb WatchpointSet::m_state[t2], t3
 1960 bieq t3, IsInvalidated, .ready
 1961 bineq t3, ClearWatchpoint, .needToInvalidate
 1962 move IsWatched, t3
 1963 jmp .ready
 1964.needToInvalidate:
 1965 btbnz WatchpointSet::m_setIsNotEmpty[t2], .pDynamic
 1966 move IsInvalidated, t3
 1967.ready:
19581968 loadisFromInstruction(3, t0)
19591969 loadConstantOrVariable(t0, t1)
19601970 loadpFromInstruction(6, t0)
19611971 storeq t1, [t0]
 1972 memfence
 1973 storeb t3, WatchpointSet::m_state[t2]
19621974end
19631975
19641976macro putClosureVar()
159462

Source/JavaScriptCore/offlineasm/arm64.rb

@@class Instruction
807807 operands[0].arm64EmitLea(operands[1], :ptr)
808808 when "smulli"
809809 $asm.puts "smaddl #{operands[2].arm64Operand(:ptr)}, #{operands[0].arm64Operand(:int)}, #{operands[1].arm64Operand(:int)}, xzr"
 810 when "memfence"
 811 $asm.puts "dmb sy"
810812 else
811813 lowerDefault
812814 end
159462

Source/JavaScriptCore/offlineasm/arm.rb

@@class Instruction
597597 when "smulli"
598598 raise "Wrong number of arguments to smull in #{self.inspect} at #{codeOriginString}" unless operands.length == 4
599599 $asm.puts "smull #{operands[2].armOperand}, #{operands[3].armOperand}, #{operands[0].armOperand}, #{operands[1].armOperand}"
 600 when "memfence"
 601 $asm.puts "dmb sy"
600602 else
601603 lowerDefault
602604 end
159462

Source/JavaScriptCore/offlineasm/cloop.rb

@@class Instruction
10911091 cloopEmitOpAndBranch(operands, "|", :int32, "== 0")
10921092 when "borrinz"
10931093 cloopEmitOpAndBranch(operands, "|", :int32, "!= 0")
1094 
 1094
 1095 when "memfence"
10951096 when "pushCalleeSaves"
10961097 when "popCalleeSaves"
10971098
159462

Source/JavaScriptCore/offlineasm/instructions.rb

@@MACRO_INSTRUCTIONS =
250250 "leai",
251251 "leap",
252252 "pushCalleeSaves",
253  "popCalleeSaves"
 253 "popCalleeSaves",
 254 "memfence"
254255 ]
255256
256257X86_INSTRUCTIONS =
159462

Source/JavaScriptCore/offlineasm/x86.rb

@@class Instruction
13631363 $asm.puts "leal #{operands[0].x86AddressOperand(:int)}, #{operands[1].x86Operand(:int)}"
13641364 when "leap"
13651365 $asm.puts "lea#{x86Suffix(:ptr)} #{operands[0].x86AddressOperand(:ptr)}, #{operands[1].x86Operand(:ptr)}"
 1366 when "memfence"
 1367 $asm.puts "mfence"
13661368 else
13671369 lowerDefault
13681370 end
159462

Source/JavaScriptCore/runtime/JSGlobalObject.cpp

@@bool JSGlobalObject::defineOwnProperty(J
210210 return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
211211}
212212
213 int JSGlobalObject::addGlobalVar(const Identifier& ident, ConstantMode constantMode, FunctionMode functionMode)
 213int JSGlobalObject::addGlobalVar(const Identifier& ident, ConstantMode constantMode, VarAddMode varAddMode)
214214{
215215 ConcurrentJITLocker locker(symbolTable()->m_lock);
216216 int index = symbolTable()->size(locker);
217217 SymbolTableEntry newEntry(index, (constantMode == IsConstant) ? ReadOnly : 0);
218  if (functionMode == IsFunctionToSpecialize)
219  newEntry.attemptToWatch();
 218 if (constantMode == IsVariable)
 219 newEntry.prepareToWatch(SymbolTableEntry::NotInitialized);
220220 SymbolTable::Map::AddResult result = symbolTable()->add(locker, ident.impl(), newEntry);
221221 if (result.isNewEntry)
222222 addRegisters(1);
223  else {
224  result.iterator->value.notifyWrite();
 223 else
225224 index = result.iterator->value.getIndex();
226  }
 225 if (constantMode == IsVariable && varAddMode == AddToInitialize)
 226 result.iterator->value.notifyWrite();
227227 return index;
228228}
229229
159462

Source/JavaScriptCore/runtime/JSGlobalObject.h

@@protected:
289289 }
290290
291291 enum ConstantMode { IsConstant, IsVariable };
292  enum FunctionMode { IsFunctionToSpecialize, NotFunctionOrNotSpecializable };
293  int addGlobalVar(const Identifier&, ConstantMode, FunctionMode);
 292 enum VarAddMode { AddUninitialized, AddToInitialize };
 293 int addGlobalVar(const Identifier&, ConstantMode, VarAddMode);
294294
295295public:
296296 JS_EXPORT_PRIVATE ~JSGlobalObject();

@@public:
315315 void addVar(ExecState* exec, const Identifier& propertyName)
316316 {
317317 if (!hasProperty(exec, propertyName))
318  addGlobalVar(propertyName, IsVariable, NotFunctionOrNotSpecializable);
 318 addGlobalVar(propertyName, IsVariable, AddUninitialized);
319319 }
320320 void addConst(ExecState* exec, const Identifier& propertyName)
321321 {
322322 if (!hasProperty(exec, propertyName))
323  addGlobalVar(propertyName, IsConstant, NotFunctionOrNotSpecializable);
 323 addGlobalVar(propertyName, IsConstant, AddUninitialized);
324324 }
325325 void addFunction(ExecState* exec, const Identifier& propertyName, JSValue value)
326326 {
327  bool propertyDidExist = removeDirect(exec->vm(), propertyName); // Newly declared functions overwrite existing properties.
328  int index = addGlobalVar(propertyName, IsVariable, !propertyDidExist ? IsFunctionToSpecialize : NotFunctionOrNotSpecializable);
 327 removeDirect(exec->vm(), propertyName); // Newly declared functions overwrite existing properties.
 328 int index = addGlobalVar(propertyName, IsVariable, AddToInitialize);
329329 registerAt(index).set(exec->vm(), this, value);
330330 }
331331
159462

Source/JavaScriptCore/runtime/JSScope.cpp

@@static inline bool abstractAccess(ExecSt
7777 if (JSGlobalObject* globalObject = jsDynamicCast<JSGlobalObject*>(scope)) {
7878 SymbolTableEntry entry = globalObject->symbolTable()->get(ident.impl());
7979 if (!entry.isNull()) {
80  if (getOrPut == Put) {
81  if (entry.isReadOnly()) {
82  // We know the property will be at global scope, but we don't know how to cache it.
83  op = ResolveOp(Dynamic, 0, 0, 0, 0);
84  return true;
85  }
86 
87  // It's likely that we'll write to this var, so notify now and avoid the overhead of doing so at runtime.
88  entry.notifyWrite();
 80 if (getOrPut == Put && entry.isReadOnly()) {
 81 // We know the property will be at global scope, but we don't know how to cache it.
 82 op = ResolveOp(Dynamic, 0, 0, 0, 0);
 83 return true;
8984 }
9085
9186 op = ResolveOp(
159462

Source/JavaScriptCore/runtime/JSSymbolTableObject.h

@@inline bool symbolTablePut(
123123 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object));
124124
125125 WriteBarrierBase<Unknown>* reg;
 126 WatchpointSet* set = 0;
126127 {
127128 SymbolTable& symbolTable = *object->symbolTable();
128129 GCSafeConcurrentJITLocker locker(symbolTable.m_lock, exec->vm().heap);

@@inline bool symbolTablePut(
137138 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
138139 return true;
139140 }
140  if (UNLIKELY(wasFat))
141  iter->value.notifyWrite();
 141 set = iter->value.watchpointSet();
142142 reg = &object->registerAt(fastEntry.getIndex());
143143 }
144144 // I'd prefer we not hold lock while executing barriers, since I prefer to reserve
145145 // the right for barriers to be able to trigger GC. And I don't want to hold VM
146146 // locks while GC'ing.
147147 reg->set(vm, object, value);
 148 if (set)
 149 set->notifyWrite();
148150 return true;
149151}
150152

@@inline bool symbolTablePutWithAttributes
156158 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(object));
157159
158160 WriteBarrierBase<Unknown>* reg;
 161 WatchpointSet* set = 0;
159162 {
160163 SymbolTable& symbolTable = *object->symbolTable();
161164 ConcurrentJITLocker locker(symbolTable.m_lock);

@@inline bool symbolTablePutWithAttributes
164167 return false;
165168 SymbolTableEntry& entry = iter->value;
166169 ASSERT(!entry.isNull());
167  entry.notifyWrite();
 170 set = entry.watchpointSet();
168171 entry.setAttributes(attributes);
169172 reg = &object->registerAt(entry.getIndex());
170173 }
171174 reg->set(vm, object, value);
 175 if (set)
 176 set->notifyWrite();
172177 return true;
173178}
174179
159462

Source/JavaScriptCore/runtime/SymbolTable.cpp

@@bool SymbolTableEntry::couldBeWatched()
6565 WatchpointSet* watchpoints = fatEntry()->m_watchpoints.get();
6666 if (!watchpoints)
6767 return false;
68  return watchpoints->isStillValid();
 68 return watchpoints->state() == IsWatched;
6969}
7070
71 void SymbolTableEntry::attemptToWatch()
 71void SymbolTableEntry::prepareToWatch(WatchState state)
7272{
7373 FatEntry* entry = inflate();
74  if (!entry->m_watchpoints)
75  entry->m_watchpoints = adoptRef(new WatchpointSet(IsWatched));
 74 ASSERT(!entry->m_watchpoints);
 75 entry->m_watchpoints = adoptRef(
 76 new WatchpointSet(state == AlreadyInitialized ? IsWatched : ClearWatchpoint));
7677}
7778
7879void SymbolTableEntry::addWatchpoint(Watchpoint* watchpoint)

@@void SymbolTableEntry::notifyWriteSlow()
8687 WatchpointSet* watchpoints = fatEntry()->m_watchpoints.get();
8788 if (!watchpoints)
8889 return;
 90
 91 if (watchpoints->state() == ClearWatchpoint) {
 92 watchpoints->startWatching();
 93 return;
 94 }
 95
8996 watchpoints->notifyWrite();
9097}
9198
159462

Source/JavaScriptCore/runtime/SymbolTable.h

@@struct SymbolTableEntry {
220220
221221 bool couldBeWatched();
222222
223  // Notify an opportunity to create a watchpoint for a variable. This is
224  // idempotent and fail-silent. It is idempotent in the sense that if
225  // a watchpoint set had already been created, then another one will not
226  // be created. Hence two calls to this method have the same effect as
227  // one call. It is also fail-silent, in the sense that if a watchpoint
228  // set had been created and had already been invalidated, then this will
229  // just return. This means that couldBeWatched() may return false even
230  // immediately after a call to attemptToWatch().
231  void attemptToWatch();
 223 enum WatchState { NotInitialized, AlreadyInitialized };
 224 void prepareToWatch(WatchState);
232225
233226 void addWatchpoint(Watchpoint*);
234227
159462

LayoutTests/ChangeLog

 12013-11-18 Filip Pizlo <fpizlo@apple.com>
 2
 3 Infer constant global variables
 4 https://bugs.webkit.org/show_bug.cgi?id=124464
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 * js/regress/global-var-const-infer-expected.txt: Added.
 9 * js/regress/global-var-const-infer.html: Added.
 10 * js/regress/script-tests/global-var-const-infer.js: Added.
 11 (foo):
 12 (check):
 13
1142013-11-18 Sun-woo Nam <sunny.nam@samsung.com>
215
316 [EFL] Layout tests need to be rebaselined.
159479

LayoutTests/js/regress/global-var-const-infer-expected.txt

 1JSRegress/global-var-const-infer
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6PASS no exception thrown
 7PASS successfullyParsed is true
 8
 9TEST COMPLETE
 10
0

LayoutTests/js/regress/global-var-const-infer.html

 1<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 2<html>
 3<head>
 4<script src="../resources/js-test-pre.js"></script>
 5</head>
 6<body>
 7<script src="resources/regress-pre.js"></script>
 8<script src="script-tests/global-var-const-infer.js"></script>
 9<script src="resources/regress-post.js"></script>
 10<script src="../resources/js-test-post.js"></script>
 11</body>
 12</html>
0

LayoutTests/js/regress/script-tests/global-var-const-infer.js

 1function foo() {
 2 return a + b;
 3}
 4
 5noInline(foo);
 6
 7var a = 4;
 8var b = 5;
 9
 10function check(actual, expected) {
 11 if (actual == expected)
 12 return;
 13 throw "Error: expected " + expected + " but got " + actual;
 14}
 15
 16for (var i = 0; i < 100; ++i)
 17 check(foo(), 9);
 18
 19a = 6;
 20
 21for (var i = 0; i < 1000; ++i)
 22 check(foo(), 11);
 23
 24b = 7;
 25
 26for (var i = 0; i < 10000; ++i)
 27 check(foo(), 13);
0