Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (192124 => 192125)
--- trunk/Source/_javascript_Core/ChangeLog 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-11-07 03:18:32 UTC (rev 192125)
@@ -1,3 +1,70 @@
+2015-11-06 Saam barati <sbar...@apple.com>
+
+ Control Flow Profiler should keep execution counts of basic blocks
+ https://bugs.webkit.org/show_bug.cgi?id=146099
+
+ Reviewed by Mark Lam.
+
+ This patch changes the control flow profiler to now
+ keep track of execution counts for each basic block
+ instead of a boolean indicating if the basic block has
+ executed at all. This has the consequence of us having to
+ always compile all op_profile_control_flows in the baseline and DFG.
+
+ This patch adds a new "executionCount" field to the inspector protocol
+ corresponding to the execution of a basic block. This patch, for now,
+ still maintains the previous field of "hasExecuted" even though this is
+ redundant with "executionCount".
+
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * inspector/agents/InspectorRuntimeAgent.cpp:
+ (Inspector::InspectorRuntimeAgent::getBasicBlocks):
+ * inspector/protocol/Runtime.json:
+ * jit/JITOpcodes.cpp:
+ (JSC::JIT::emit_op_profile_control_flow):
+ (JSC::JIT::emit_op_create_direct_arguments):
+ * jsc.cpp:
+ (GlobalObject::finishCreation):
+ (functionHasBasicBlockExecuted):
+ (functionBasicBlockExecutionCount):
+ (functionEnableExceptionFuzz):
+ (functionDrainMicrotasks):
+ (functionIs32BitPlatform):
+ (functionLoadWebAssembly):
+ * llint/LowLevelInterpreter.asm:
+ * llint/LowLevelInterpreter32_64.asm:
+ * llint/LowLevelInterpreter64.asm:
+ * runtime/BasicBlockLocation.cpp:
+ (JSC::BasicBlockLocation::BasicBlockLocation):
+ (JSC::BasicBlockLocation::dumpData):
+ (JSC::BasicBlockLocation::emitExecuteCode):
+ * runtime/BasicBlockLocation.h:
+ (JSC::BasicBlockLocation::endOffset):
+ (JSC::BasicBlockLocation::setStartOffset):
+ (JSC::BasicBlockLocation::setEndOffset):
+ (JSC::BasicBlockLocation::hasExecuted):
+ (JSC::BasicBlockLocation::executionCount):
+ * runtime/ControlFlowProfiler.cpp:
+ (JSC::ControlFlowProfiler::getBasicBlocksForSourceID):
+ (JSC::findBasicBlockAtTextOffset):
+ (JSC::ControlFlowProfiler::hasBasicBlockAtTextOffsetBeenExecuted):
+ (JSC::ControlFlowProfiler::basicBlockExecutionCountAtTextOffset):
+ * runtime/ControlFlowProfiler.h:
+ (JSC::ControlFlowProfiler::dummyBasicBlock):
+ * tests/controlFlowProfiler/execution-count.js: Added.
+ (noop):
+ (foo):
+ (a):
+ (b):
+ (baz):
+ (jaz):
+ (testWhile):
+ (is32BitPlatform.testMax):
+ (is32BitPlatform):
+
2015-11-06 Filip Pizlo <fpi...@apple.com>
B3 and Air should simplify CFGs
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (192124 => 192125)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2015-11-07 03:18:32 UTC (rev 192125)
@@ -4782,11 +4782,9 @@
break;
}
case ProfileControlFlow: {
+ GPRTemporary scratch1(this);
BasicBlockLocation* basicBlockLocation = node->basicBlockLocation();
- if (!basicBlockLocation->hasExecuted()) {
- GPRTemporary scratch1(this);
- basicBlockLocation->emitExecuteCode(m_jit, scratch1.gpr());
- }
+ basicBlockLocation->emitExecuteCode(m_jit, scratch1.gpr());
noResult(node);
break;
}
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (192124 => 192125)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2015-11-07 03:18:32 UTC (rev 192125)
@@ -4813,10 +4813,7 @@
}
case ProfileControlFlow: {
BasicBlockLocation* basicBlockLocation = node->basicBlockLocation();
- if (!basicBlockLocation->hasExecuted()) {
- GPRTemporary scratch1(this);
- basicBlockLocation->emitExecuteCode(m_jit, scratch1.gpr());
- }
+ basicBlockLocation->emitExecuteCode(m_jit);
noResult(node);
break;
}
Modified: trunk/Source/_javascript_Core/inspector/agents/InspectorRuntimeAgent.cpp (192124 => 192125)
--- trunk/Source/_javascript_Core/inspector/agents/InspectorRuntimeAgent.cpp 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/inspector/agents/InspectorRuntimeAgent.cpp 2015-11-07 03:18:32 UTC (rev 192125)
@@ -356,6 +356,7 @@
.setStartOffset(block.m_startOffset)
.setEndOffset(block.m_endOffset)
.setHasExecuted(block.m_hasExecuted)
+ .setExecutionCount(block.m_executionCount)
.release();
basicBlocks->addItem(WTF::move(location));
}
Modified: trunk/Source/_javascript_Core/inspector/protocol/Runtime.json (192124 => 192125)
--- trunk/Source/_javascript_Core/inspector/protocol/Runtime.json 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/inspector/protocol/Runtime.json 2015-11-07 03:18:32 UTC (rev 192125)
@@ -188,7 +188,8 @@
"properties": [
{ "name": "startOffset", "type": "integer", "description": "Start offset of the basic block." },
{ "name": "endOffset", "type": "integer", "description": "End offset of the basic block." },
- { "name": "hasExecuted", "type": "boolean", "description": "Indicates if the basic block has executed before." }
+ { "name": "hasExecuted", "type": "boolean", "description": "Indicates if the basic block has executed before." },
+ { "name": "executionCount", "type": "integer", "description": "Indicates how many times the basic block has executed." }
]
}
],
Modified: trunk/Source/_javascript_Core/jit/JITOpcodes.cpp (192124 => 192125)
--- trunk/Source/_javascript_Core/jit/JITOpcodes.cpp 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/jit/JITOpcodes.cpp 2015-11-07 03:18:32 UTC (rev 192125)
@@ -1368,8 +1368,11 @@
void JIT::emit_op_profile_control_flow(Instruction* currentInstruction)
{
BasicBlockLocation* basicBlockLocation = currentInstruction[1].u.basicBlockLocation;
- if (!basicBlockLocation->hasExecuted())
- basicBlockLocation->emitExecuteCode(*this, regT1);
+#if USE(JSVALUE64)
+ basicBlockLocation->emitExecuteCode(*this);
+#else
+ basicBlockLocation->emitExecuteCode(*this, regT0);
+#endif
}
void JIT::emit_op_create_direct_arguments(Instruction* currentInstruction)
Modified: trunk/Source/_javascript_Core/jsc.cpp (192124 => 192125)
--- trunk/Source/_javascript_Core/jsc.cpp 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/jsc.cpp 2015-11-07 03:18:32 UTC (rev 192125)
@@ -544,8 +544,10 @@
static EncodedJSValue JSC_HOST_CALL functionReturnTypeFor(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionDumpBasicBlockExecutionRanges(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionHasBasicBlockExecuted(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionBasicBlockExecutionCount(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionEnableExceptionFuzz(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionDrainMicrotasks(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionIs32BitPlatform(ExecState*);
#if ENABLE(WEBASSEMBLY)
static EncodedJSValue JSC_HOST_CALL functionLoadWebAssembly(ExecState*);
#endif
@@ -724,11 +726,14 @@
addFunction(vm, "dumpBasicBlockExecutionRanges", functionDumpBasicBlockExecutionRanges , 0);
addFunction(vm, "hasBasicBlockExecuted", functionHasBasicBlockExecuted, 2);
+ addFunction(vm, "basicBlockExecutionCount", functionBasicBlockExecutionCount, 2);
addFunction(vm, "enableExceptionFuzz", functionEnableExceptionFuzz, 0);
addFunction(vm, "drainMicrotasks", functionDrainMicrotasks, 0);
+ addFunction(vm, "is32BitPlatform", functionIs32BitPlatform, 0);
+
#if ENABLE(WEBASSEMBLY)
addFunction(vm, "loadWebAssembly", functionLoadWebAssembly, 3);
#endif
@@ -1505,6 +1510,24 @@
return JSValue::encode(jsBoolean(hasExecuted));
}
+EncodedJSValue JSC_HOST_CALL functionBasicBlockExecutionCount(ExecState* exec)
+{
+ RELEASE_ASSERT(exec->vm().controlFlowProfiler());
+
+ JSValue functionValue = exec->argument(0);
+ RELEASE_ASSERT(functionValue.isFunction());
+ FunctionExecutable* executable = (jsDynamicCast<JSFunction*>(functionValue.asCell()->getObject()))->jsExecutable();
+
+ RELEASE_ASSERT(exec->argument(1).isString());
+ String substring = exec->argument(1).getString(exec);
+ String sourceCodeText = executable->source().toString();
+ RELEASE_ASSERT(sourceCodeText.contains(substring));
+ int offset = sourceCodeText.find(substring) + executable->source().startOffset();
+
+ size_t executionCount = exec->vm().controlFlowProfiler()->basicBlockExecutionCountAtTextOffset(offset, executable->sourceID(), exec->vm());
+ return JSValue::encode(JSValue(executionCount));
+}
+
EncodedJSValue JSC_HOST_CALL functionEnableExceptionFuzz(ExecState*)
{
Options::useExceptionFuzz() = true;
@@ -1517,6 +1540,15 @@
return JSValue::encode(jsUndefined());
}
+EncodedJSValue JSC_HOST_CALL functionIs32BitPlatform(ExecState*)
+{
+#if USE(JSVALUE64)
+ return JSValue::encode(JSValue(JSC::JSValue::JSFalse));
+#else
+ return JSValue::encode(JSValue(JSC::JSValue::JSTrue));
+#endif
+}
+
#if ENABLE(WEBASSEMBLY)
EncodedJSValue JSC_HOST_CALL functionLoadWebAssembly(ExecState* exec)
{
Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm (192124 => 192125)
--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter.asm 2015-11-07 03:18:32 UTC (rev 192125)
@@ -1669,12 +1669,6 @@
callSlowPath(_slow_path_to_index_string)
dispatch(3)
-_llint_op_profile_control_flow:
- traceExecution()
- loadpFromInstruction(1, t0)
- storeb 1, BasicBlockLocation::m_hasExecuted[t0]
- dispatch(2)
-
# Lastly, make sure that we can link even though we don't support all opcodes.
# These opcodes should never arise when using LLInt or either JIT. We assert
# as much.
Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm (192124 => 192125)
--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm 2015-11-07 03:18:32 UTC (rev 192125)
@@ -2410,6 +2410,18 @@
.opProfileTypeDone:
dispatch(6)
+
+_llint_op_profile_control_flow:
+ traceExecution()
+ loadpFromInstruction(1, t0)
+ loadi BasicBlockLocation::m_executionCount[t0], t1
+ addi 1, t1
+ bieq t1, 0, .done # We overflowed.
+ storei t1, BasicBlockLocation::m_executionCount[t0]
+.done:
+ dispatch(2)
+
+
_llint_op_load_arrowfunction_this:
traceExecution()
loadi Callee + PayloadOffset[cfr], t0
Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm (192124 => 192125)
--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm 2015-11-07 03:18:32 UTC (rev 192125)
@@ -2284,6 +2284,13 @@
.opProfileTypeDone:
dispatch(6)
+_llint_op_profile_control_flow:
+ traceExecution()
+ loadpFromInstruction(1, t0)
+ addq 1, BasicBlockLocation::m_executionCount[t0]
+ dispatch(2)
+
+
_llint_op_load_arrowfunction_this:
traceExecution()
loadp Callee[cfr], t0
Modified: trunk/Source/_javascript_Core/runtime/BasicBlockLocation.cpp (192124 => 192125)
--- trunk/Source/_javascript_Core/runtime/BasicBlockLocation.cpp 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/runtime/BasicBlockLocation.cpp 2015-11-07 03:18:32 UTC (rev 192125)
@@ -36,7 +36,7 @@
BasicBlockLocation::BasicBlockLocation(int startOffset, int endOffset)
: m_startOffset(startOffset)
, m_endOffset(endOffset)
- , m_hasExecuted(false)
+ , m_executionCount(0)
{
}
@@ -75,15 +75,26 @@
{
Vector<Gap> executedRanges = getExecutedRanges();
for (Gap gap : executedRanges)
- dataLogF("\tBasicBlock: [%d, %d] hasExecuted: %s\n", gap.first, gap.second, hasExecuted() ? "true" : "false");
+ dataLogF("\tBasicBlock: [%d, %d] hasExecuted: %s, executionCount:%zu\n", gap.first, gap.second, hasExecuted() ? "true" : "false", m_executionCount);
}
#if ENABLE(JIT)
-void BasicBlockLocation::emitExecuteCode(CCallHelpers& jit, MacroAssembler::RegisterID ptrReg) const
+#if USE(JSVALUE64)
+void BasicBlockLocation::emitExecuteCode(CCallHelpers& jit) const
{
- jit.move(CCallHelpers::TrustedImmPtr(&m_hasExecuted), ptrReg);
- jit.store8(CCallHelpers::TrustedImm32(true), CCallHelpers::Address(ptrReg, 0));
+ static_assert(sizeof(size_t) == 8, "Assuming size_t is 64 bits on 64 bit platforms.");
+ jit.add64(CCallHelpers::TrustedImm32(1), CCallHelpers::AbsoluteAddress(&m_executionCount));
}
+#else
+void BasicBlockLocation::emitExecuteCode(CCallHelpers& jit, MacroAssembler::RegisterID scratch) const
+{
+ static_assert(sizeof(size_t) == 4, "Assuming size_t is 32 bits on 32 bit platforms.");
+ jit.load32(&m_executionCount, scratch);
+ CCallHelpers::Jump done = jit.branchAdd32(CCallHelpers::Zero, scratch, CCallHelpers::TrustedImm32(1), scratch);
+ jit.store32(scratch, bitwise_cast<void*>(&m_executionCount));
+ done.link(&jit);
+}
+#endif // USE(JSVALUE64)
#endif // ENABLE(JIT)
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/BasicBlockLocation.h (192124 => 192125)
--- trunk/Source/_javascript_Core/runtime/BasicBlockLocation.h 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/runtime/BasicBlockLocation.h 2015-11-07 03:18:32 UTC (rev 192125)
@@ -47,20 +47,25 @@
int endOffset() const { return m_endOffset; }
void setStartOffset(int startOffset) { m_startOffset = startOffset; }
void setEndOffset(int endOffset) { m_endOffset = endOffset; }
- bool hasExecuted() const { return m_hasExecuted; }
+ bool hasExecuted() const { return m_executionCount > 0; }
+ size_t executionCount() const { return m_executionCount; }
void insertGap(int, int);
Vector<Gap> getExecutedRanges() const;
JS_EXPORT_PRIVATE void dumpData() const;
#if ENABLE(JIT)
- void emitExecuteCode(CCallHelpers&, MacroAssembler::RegisterID) const;
+#if USE(JSVALUE64)
+ void emitExecuteCode(CCallHelpers&) const;
+#else
+ void emitExecuteCode(CCallHelpers&, MacroAssembler::RegisterID scratch) const;
#endif
+#endif
private:
friend class LLIntOffsetsExtractor;
int m_startOffset;
int m_endOffset;
- bool m_hasExecuted;
+ size_t m_executionCount;
Vector<Gap> m_gaps;
};
Modified: trunk/Source/_javascript_Core/runtime/ControlFlowProfiler.cpp (192124 => 192125)
--- trunk/Source/_javascript_Core/runtime/ControlFlowProfiler.cpp 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/runtime/ControlFlowProfiler.cpp 2015-11-07 03:18:32 UTC (rev 192125)
@@ -76,31 +76,33 @@
const BlockLocationCache& cache = bucketFindResult->value;
for (const BasicBlockLocation* block : cache.values()) {
bool hasExecuted = block->hasExecuted();
+ size_t executionCount = block->executionCount();
const Vector<BasicBlockLocation::Gap>& blockRanges = block->getExecutedRanges();
for (BasicBlockLocation::Gap gap : blockRanges) {
BasicBlockRange range;
range.m_hasExecuted = hasExecuted;
+ range.m_executionCount = executionCount;
range.m_startOffset = gap.first;
range.m_endOffset = gap.second;
result.append(range);
}
}
- const Vector<std::tuple<bool, unsigned, unsigned>>& unexecutedFunctionRanges = vm.functionHasExecutedCache()->getFunctionRanges(sourceID);
- for (const auto& functionRange : unexecutedFunctionRanges) {
+ const Vector<std::tuple<bool, unsigned, unsigned>>& functionRanges = vm.functionHasExecutedCache()->getFunctionRanges(sourceID);
+ for (const auto& functionRange : functionRanges) {
BasicBlockRange range;
range.m_hasExecuted = std::get<0>(functionRange);
range.m_startOffset = static_cast<int>(std::get<1>(functionRange));
range.m_endOffset = static_cast<int>(std::get<2>(functionRange));
+ range.m_executionCount = range.m_hasExecuted ? 1 : 0; // This is a hack. We don't actually count this.
result.append(range);
}
return result;
}
-bool ControlFlowProfiler::hasBasicBlockAtTextOffsetBeenExecuted(int offset, intptr_t sourceID, VM& vm)
+static BasicBlockRange findBasicBlockAtTextOffset(int offset, const Vector<BasicBlockRange>& blocks)
{
- const Vector<BasicBlockRange>& blocks = getBasicBlocksForSourceID(sourceID, vm);
int bestDistance = INT_MAX;
BasicBlockRange bestRange;
bestRange.m_startOffset = bestRange.m_endOffset = -1;
@@ -115,7 +117,21 @@
}
RELEASE_ASSERT(bestRange.m_startOffset != -1 && bestRange.m_endOffset != -1);
- return bestRange.m_hasExecuted;
+ return bestRange;
}
+bool ControlFlowProfiler::hasBasicBlockAtTextOffsetBeenExecuted(int offset, intptr_t sourceID, VM& vm)
+{
+ const Vector<BasicBlockRange>& blocks = getBasicBlocksForSourceID(sourceID, vm);
+ BasicBlockRange range = findBasicBlockAtTextOffset(offset, blocks);
+ return range.m_hasExecuted;
+}
+
+size_t ControlFlowProfiler::basicBlockExecutionCountAtTextOffset(int offset, intptr_t sourceID, VM& vm)
+{
+ const Vector<BasicBlockRange>& blocks = getBasicBlocksForSourceID(sourceID, vm);
+ BasicBlockRange range = findBasicBlockAtTextOffset(offset, blocks);
+ return range.m_executionCount;
+}
+
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/ControlFlowProfiler.h (192124 => 192125)
--- trunk/Source/_javascript_Core/runtime/ControlFlowProfiler.h 2015-11-07 02:32:13 UTC (rev 192124)
+++ trunk/Source/_javascript_Core/runtime/ControlFlowProfiler.h 2015-11-07 03:18:32 UTC (rev 192125)
@@ -87,6 +87,7 @@
int m_startOffset;
int m_endOffset;
bool m_hasExecuted;
+ size_t m_executionCount;
};
class ControlFlowProfiler {
@@ -98,7 +99,8 @@
JS_EXPORT_PRIVATE void dumpData() const;
Vector<BasicBlockRange> getBasicBlocksForSourceID(intptr_t sourceID, VM&) const;
BasicBlockLocation* dummyBasicBlock() { return &m_dummyBasicBlock; }
- JS_EXPORT_PRIVATE bool hasBasicBlockAtTextOffsetBeenExecuted(int, intptr_t, VM&); // This function exists for testing.
+ JS_EXPORT_PRIVATE bool hasBasicBlockAtTextOffsetBeenExecuted(int, intptr_t, VM&); // This function exists for testing.
+ JS_EXPORT_PRIVATE size_t basicBlockExecutionCountAtTextOffset(int, intptr_t, VM&); // This function exists for testing.
private:
typedef HashMap<BasicBlockKey, BasicBlockLocation*> BlockLocationCache;
Added: trunk/Source/_javascript_Core/tests/controlFlowProfiler/execution-count.js (0 => 192125)
--- trunk/Source/_javascript_Core/tests/controlFlowProfiler/execution-count.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/controlFlowProfiler/execution-count.js 2015-11-07 03:18:32 UTC (rev 192125)
@@ -0,0 +1,70 @@
+load("./driver/driver.js");
+
+function noop() { ; }
+function foo(num) {
+ for (let i = 0; i < num; i++) {
+ noop();
+ }
+}
+
+function a() { ; }
+function b() { ; }
+
+function baz(num) {
+ for (let i = 0; i < num; i++) {
+ i % 2 ? a() : b();
+ }
+}
+
+function jaz(num) {
+ for (let i = 0; i < num; i++) {
+ if (i % 2)
+ a();
+ else
+ b();
+ }
+}
+
+function testWhile(num) {
+ let i = num;
+ while (i--) {
+ noop();
+ if (i % 2)
+ a();
+ else
+ b();
+ }
+}
+
+foo(120);
+assert(basicBlockExecutionCount(foo, "noop()") === 120);
+noop();
+assert(basicBlockExecutionCount(noop, ";") === 121);
+
+baz(140);
+assert(basicBlockExecutionCount(baz, "a()") === 140/2);
+assert(basicBlockExecutionCount(baz, "b()") === 140/2);
+assert(basicBlockExecutionCount(a, ";") === 140/2);
+assert(basicBlockExecutionCount(b, ";") === 140/2);
+
+jaz(140);
+assert(basicBlockExecutionCount(jaz, "a()") === 140/2);
+assert(basicBlockExecutionCount(jaz, "b()") === 140/2);
+
+testWhile(140);
+assert(basicBlockExecutionCount(testWhile, "noop()") === 140);
+assert(basicBlockExecutionCount(testWhile, "a()") === 140/2);
+assert(basicBlockExecutionCount(testWhile, "b()") === 140/2);
+
+if (is32BitPlatform()) {
+ function testMax() {
+ let j = 0;
+ let numIters = Math.pow(2, 32) + 10;
+ for (let i = 0; i < numIters; i++) {
+ j++;
+ }
+ }
+
+ testMax();
+ assert(basicBlockExecutionCount(testMax, "j++") === Math.pow(2, 32) - 1);
+}