DFGConstantFoldingPhase.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. /*
  2. * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. * 1. Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * 2. Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
  14. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  16. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
  17. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  20. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  21. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  23. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. #include "config.h"
  26. #include "DFGConstantFoldingPhase.h"
  27. #if ENABLE(DFG_JIT)
  28. #include "DFGAbstractState.h"
  29. #include "DFGBasicBlock.h"
  30. #include "DFGGraph.h"
  31. #include "DFGInsertionSet.h"
  32. #include "DFGPhase.h"
  33. #include "GetByIdStatus.h"
  34. #include "Operations.h"
  35. #include "PutByIdStatus.h"
  36. namespace JSC { namespace DFG {
  37. class ConstantFoldingPhase : public Phase {
  38. public:
  39. ConstantFoldingPhase(Graph& graph)
  40. : Phase(graph, "constant folding")
  41. , m_state(graph)
  42. , m_insertionSet(graph)
  43. {
  44. }
  45. bool run()
  46. {
  47. bool changed = false;
  48. for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
  49. BasicBlock* block = m_graph.m_blocks[blockIndex].get();
  50. if (!block)
  51. continue;
  52. if (!block->cfaDidFinish)
  53. changed |= paintUnreachableCode(blockIndex);
  54. if (block->cfaFoundConstants)
  55. changed |= foldConstants(blockIndex);
  56. }
  57. return changed;
  58. }
  59. private:
  60. bool foldConstants(BlockIndex blockIndex)
  61. {
  62. #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
  63. dataLogF("Constant folding considering Block #%u.\n", blockIndex);
  64. #endif
  65. BasicBlock* block = m_graph.m_blocks[blockIndex].get();
  66. bool changed = false;
  67. m_state.beginBasicBlock(block);
  68. for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
  69. if (!m_state.isValid())
  70. break;
  71. Node* node = block->at(indexInBlock);
  72. bool eliminated = false;
  73. switch (node->op()) {
  74. case CheckArgumentsNotCreated: {
  75. if (!isEmptySpeculation(
  76. m_state.variables().operand(
  77. m_graph.argumentsRegisterFor(node->codeOrigin)).m_type))
  78. break;
  79. node->convertToPhantom();
  80. eliminated = true;
  81. break;
  82. }
  83. case CheckStructure:
  84. case ForwardCheckStructure:
  85. case ArrayifyToStructure: {
  86. AbstractValue& value = m_state.forNode(node->child1());
  87. StructureSet set;
  88. if (node->op() == ArrayifyToStructure)
  89. set = node->structure();
  90. else
  91. set = node->structureSet();
  92. if (value.m_currentKnownStructure.isSubsetOf(set)) {
  93. m_state.execute(indexInBlock); // Catch the fact that we may filter on cell.
  94. node->convertToPhantom();
  95. eliminated = true;
  96. break;
  97. }
  98. StructureAbstractValue& structureValue = value.m_futurePossibleStructure;
  99. if (structureValue.isSubsetOf(set)
  100. && structureValue.hasSingleton()) {
  101. Structure* structure = structureValue.singleton();
  102. m_state.execute(indexInBlock); // Catch the fact that we may filter on cell.
  103. node->convertToStructureTransitionWatchpoint(structure);
  104. eliminated = true;
  105. break;
  106. }
  107. break;
  108. }
  109. case CheckArray:
  110. case Arrayify: {
  111. if (!node->arrayMode().alreadyChecked(m_graph, node, m_state.forNode(node->child1())))
  112. break;
  113. node->convertToPhantom();
  114. eliminated = true;
  115. break;
  116. }
  117. case CheckFunction: {
  118. if (m_state.forNode(node->child1()).value() != node->function())
  119. break;
  120. node->convertToPhantom();
  121. eliminated = true;
  122. break;
  123. }
  124. case GetById:
  125. case GetByIdFlush: {
  126. CodeOrigin codeOrigin = node->codeOrigin;
  127. Edge childEdge = node->child1();
  128. Node* child = childEdge.node();
  129. unsigned identifierNumber = node->identifierNumber();
  130. if (childEdge.useKind() != CellUse)
  131. break;
  132. Structure* structure = m_state.forNode(child).bestProvenStructure();
  133. if (!structure)
  134. break;
  135. bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
  136. bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
  137. GetByIdStatus status = GetByIdStatus::computeFor(
  138. vm(), structure, codeBlock()->identifier(identifierNumber));
  139. if (!status.isSimple()) {
  140. // FIXME: We could handle prototype cases.
  141. // https://bugs.webkit.org/show_bug.cgi?id=110386
  142. break;
  143. }
  144. ASSERT(status.structureSet().size() == 1);
  145. ASSERT(status.chain().isEmpty());
  146. ASSERT(status.structureSet().singletonStructure() == structure);
  147. // Now before we do anything else, push the CFA forward over the GetById
  148. // and make sure we signal to the loop that it should continue and not
  149. // do any eliminations.
  150. m_state.execute(indexInBlock);
  151. eliminated = true;
  152. if (needsWatchpoint) {
  153. ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure)));
  154. m_insertionSet.insertNode(
  155. indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
  156. OpInfo(structure), childEdge);
  157. } else if (needsCellCheck) {
  158. m_insertionSet.insertNode(
  159. indexInBlock, SpecNone, Phantom, codeOrigin, childEdge);
  160. }
  161. childEdge.setUseKind(KnownCellUse);
  162. Edge propertyStorage;
  163. if (isInlineOffset(status.offset()))
  164. propertyStorage = childEdge;
  165. else {
  166. propertyStorage = Edge(m_insertionSet.insertNode(
  167. indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge));
  168. }
  169. node->convertToGetByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
  170. StorageAccessData storageAccessData;
  171. storageAccessData.offset = indexRelativeToBase(status.offset());
  172. storageAccessData.identifierNumber = identifierNumber;
  173. m_graph.m_storageAccessData.append(storageAccessData);
  174. break;
  175. }
  176. case PutById:
  177. case PutByIdDirect: {
  178. CodeOrigin codeOrigin = node->codeOrigin;
  179. Edge childEdge = node->child1();
  180. Node* child = childEdge.node();
  181. unsigned identifierNumber = node->identifierNumber();
  182. ASSERT(childEdge.useKind() == CellUse);
  183. Structure* structure = m_state.forNode(child).bestProvenStructure();
  184. if (!structure)
  185. break;
  186. bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
  187. bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
  188. PutByIdStatus status = PutByIdStatus::computeFor(
  189. vm(),
  190. m_graph.globalObjectFor(codeOrigin),
  191. structure,
  192. codeBlock()->identifier(identifierNumber),
  193. node->op() == PutByIdDirect);
  194. if (!status.isSimpleReplace() && !status.isSimpleTransition())
  195. break;
  196. ASSERT(status.oldStructure() == structure);
  197. // Now before we do anything else, push the CFA forward over the PutById
  198. // and make sure we signal to the loop that it should continue and not
  199. // do any eliminations.
  200. m_state.execute(indexInBlock);
  201. eliminated = true;
  202. if (needsWatchpoint) {
  203. ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure)));
  204. m_insertionSet.insertNode(
  205. indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
  206. OpInfo(structure), childEdge);
  207. } else if (needsCellCheck) {
  208. m_insertionSet.insertNode(
  209. indexInBlock, SpecNone, Phantom, codeOrigin, childEdge);
  210. }
  211. childEdge.setUseKind(KnownCellUse);
  212. StructureTransitionData* transitionData = 0;
  213. if (status.isSimpleTransition()) {
  214. transitionData = m_graph.addStructureTransitionData(
  215. StructureTransitionData(structure, status.newStructure()));
  216. if (node->op() == PutById) {
  217. if (!structure->storedPrototype().isNull()) {
  218. addStructureTransitionCheck(
  219. codeOrigin, indexInBlock,
  220. structure->storedPrototype().asCell());
  221. }
  222. for (WriteBarrier<Structure>* it = status.structureChain()->head(); *it; ++it) {
  223. JSValue prototype = (*it)->storedPrototype();
  224. if (prototype.isNull())
  225. continue;
  226. ASSERT(prototype.isCell());
  227. addStructureTransitionCheck(
  228. codeOrigin, indexInBlock, prototype.asCell());
  229. }
  230. }
  231. }
  232. Edge propertyStorage;
  233. if (isInlineOffset(status.offset()))
  234. propertyStorage = childEdge;
  235. else if (status.isSimpleReplace() || structure->outOfLineCapacity() == status.newStructure()->outOfLineCapacity()) {
  236. propertyStorage = Edge(m_insertionSet.insertNode(
  237. indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge));
  238. } else if (!structure->outOfLineCapacity()) {
  239. ASSERT(status.newStructure()->outOfLineCapacity());
  240. ASSERT(!isInlineOffset(status.offset()));
  241. propertyStorage = Edge(m_insertionSet.insertNode(
  242. indexInBlock, SpecNone, AllocatePropertyStorage,
  243. codeOrigin, OpInfo(transitionData), childEdge));
  244. } else {
  245. ASSERT(structure->outOfLineCapacity());
  246. ASSERT(status.newStructure()->outOfLineCapacity() > structure->outOfLineCapacity());
  247. ASSERT(!isInlineOffset(status.offset()));
  248. propertyStorage = Edge(m_insertionSet.insertNode(
  249. indexInBlock, SpecNone, ReallocatePropertyStorage, codeOrigin,
  250. OpInfo(transitionData), childEdge,
  251. Edge(m_insertionSet.insertNode(
  252. indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge))));
  253. }
  254. if (status.isSimpleTransition()) {
  255. m_insertionSet.insertNode(
  256. indexInBlock, SpecNone, PutStructure, codeOrigin,
  257. OpInfo(transitionData), childEdge);
  258. }
  259. node->convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
  260. StorageAccessData storageAccessData;
  261. storageAccessData.offset = indexRelativeToBase(status.offset());
  262. storageAccessData.identifierNumber = identifierNumber;
  263. m_graph.m_storageAccessData.append(storageAccessData);
  264. break;
  265. }
  266. default:
  267. break;
  268. }
  269. if (eliminated) {
  270. changed = true;
  271. continue;
  272. }
  273. m_state.execute(indexInBlock);
  274. if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant())
  275. continue;
  276. JSValue value = m_state.forNode(node).value();
  277. if (!value)
  278. continue;
  279. CodeOrigin codeOrigin = node->codeOrigin;
  280. AdjacencyList children = node->children;
  281. if (node->op() == GetLocal) {
  282. // GetLocals without a Phi child are guaranteed dead. We don't have to
  283. // do anything about them.
  284. if (!node->child1())
  285. continue;
  286. if (m_graph.m_form != LoadStore) {
  287. VariableAccessData* variable = node->variableAccessData();
  288. Node* phi = node->child1().node();
  289. if (phi->op() == Phi
  290. && block->variablesAtHead.operand(variable->local()) == phi
  291. && block->variablesAtTail.operand(variable->local()) == node) {
  292. // Keep the graph threaded for easy cases. This is improves compile
  293. // times. It would be correct to just dethread here.
  294. m_graph.convertToConstant(node, value);
  295. Node* phantom = m_insertionSet.insertNode(
  296. indexInBlock, SpecNone, PhantomLocal, codeOrigin,
  297. OpInfo(variable), Edge(phi));
  298. block->variablesAtHead.operand(variable->local()) = phantom;
  299. block->variablesAtTail.operand(variable->local()) = phantom;
  300. changed = true;
  301. continue;
  302. }
  303. m_graph.dethread();
  304. }
  305. } else
  306. ASSERT(!node->hasVariableAccessData());
  307. m_graph.convertToConstant(node, value);
  308. m_insertionSet.insertNode(
  309. indexInBlock, SpecNone, Phantom, codeOrigin, children);
  310. changed = true;
  311. }
  312. m_state.reset();
  313. m_insertionSet.execute(block);
  314. return changed;
  315. }
  316. #if !ASSERT_DISABLED
  317. bool isCapturedAtOrAfter(BasicBlock* block, unsigned indexInBlock, int operand)
  318. {
  319. for (; indexInBlock < block->size(); ++indexInBlock) {
  320. Node* node = block->at(indexInBlock);
  321. if (!node->hasLocal())
  322. continue;
  323. if (node->local() != operand)
  324. continue;
  325. if (node->variableAccessData()->isCaptured())
  326. return true;
  327. }
  328. return false;
  329. }
  330. #endif // !ASSERT_DISABLED
  331. void addStructureTransitionCheck(CodeOrigin codeOrigin, unsigned indexInBlock, JSCell* cell)
  332. {
  333. Node* weakConstant = m_insertionSet.insertNode(
  334. indexInBlock, speculationFromValue(cell), WeakJSConstant, codeOrigin, OpInfo(cell));
  335. if (cell->structure()->transitionWatchpointSetIsStillValid()) {
  336. m_insertionSet.insertNode(
  337. indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
  338. OpInfo(cell->structure()), Edge(weakConstant, CellUse));
  339. return;
  340. }
  341. m_insertionSet.insertNode(
  342. indexInBlock, SpecNone, CheckStructure, codeOrigin,
  343. OpInfo(m_graph.addStructureSet(cell->structure())), Edge(weakConstant, CellUse));
  344. }
  345. // This is necessary because the CFA may reach conclusions about constants based on its
  346. // assumption that certain code must exit, but then those constants may lead future
  347. // reexecutions of the CFA to believe that the same code will now no longer exit. Thus
  348. // to ensure soundness, we must paint unreachable code as such, by inserting an
  349. // unconditional ForceOSRExit wherever we find that a node would have always exited.
  350. // This will only happen in cases where we are making static speculations, or we're
  351. // making totally wrong speculations due to imprecision on the prediction propagator.
  352. bool paintUnreachableCode(BlockIndex blockIndex)
  353. {
  354. bool changed = false;
  355. #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
  356. dataLogF("Painting unreachable code in Block #%u.\n", blockIndex);
  357. #endif
  358. BasicBlock* block = m_graph.m_blocks[blockIndex].get();
  359. m_state.beginBasicBlock(block);
  360. for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
  361. m_state.execute(indexInBlock);
  362. if (m_state.isValid())
  363. continue;
  364. Node* node = block->at(indexInBlock);
  365. switch (node->op()) {
  366. case Return:
  367. case Throw:
  368. case ThrowReferenceError:
  369. case ForceOSRExit:
  370. // Do nothing. These nodes will already do the right thing.
  371. break;
  372. default:
  373. m_insertionSet.insertNode(
  374. indexInBlock, SpecNone, ForceOSRExit, node->codeOrigin);
  375. changed = true;
  376. break;
  377. }
  378. break;
  379. }
  380. m_state.reset();
  381. m_insertionSet.execute(block);
  382. return changed;
  383. }
  384. AbstractState m_state;
  385. InsertionSet m_insertionSet;
  386. };
  387. bool performConstantFolding(Graph& graph)
  388. {
  389. SamplingRegion samplingRegion("DFG Constant Folding Phase");
  390. return runPhase<ConstantFoldingPhase>(graph);
  391. }
  392. } } // namespace JSC::DFG
  393. #endif // ENABLE(DFG_JIT)