1/*
2 * Copyright (C) 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
26#pragma once
27
28#include "StaticPropertyAnalysis.h"
29#include <wtf/HashMap.h>
30
31namespace JSC {
32
33// Used for flow-insensitive static analysis of the number of properties assigned to an object.
34// We use this analysis with other runtime data to produce an optimization guess. This analysis
35// is understood to be lossy, and it's OK if it turns out to be wrong sometimes.
36class StaticPropertyAnalyzer {
37public:
38 void createThis(RegisterID* dst, InstructionStream::MutableRef instructionRef);
39 void newObject(RegisterID* dst, InstructionStream::MutableRef instructionRef);
40 void putById(RegisterID* dst, unsigned propertyIndex); // propertyIndex is an index into a uniqued set of strings.
41 void mov(RegisterID* dst, RegisterID* src);
42
43 void kill();
44 void kill(RegisterID* dst);
45
46private:
47 void kill(StaticPropertyAnalysis*);
48
49 typedef HashMap<int, RefPtr<StaticPropertyAnalysis>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> AnalysisMap;
50 AnalysisMap m_analyses;
51};
52
53inline void StaticPropertyAnalyzer::createThis(RegisterID* dst, InstructionStream::MutableRef instructionRef)
54{
55 AnalysisMap::AddResult addResult = m_analyses.add(
56 dst->index(), StaticPropertyAnalysis::create(WTFMove(instructionRef)));
57 ASSERT_UNUSED(addResult, addResult.isNewEntry); // Can't have two 'this' in the same constructor.
58}
59
60inline void StaticPropertyAnalyzer::newObject(RegisterID* dst, InstructionStream::MutableRef instructionRef)
61{
62 auto analysis = StaticPropertyAnalysis::create(WTFMove(instructionRef));
63 AnalysisMap::AddResult addResult = m_analyses.add(dst->index(), analysis.copyRef());
64 if (!addResult.isNewEntry) {
65 kill(addResult.iterator->value.get());
66 addResult.iterator->value = WTFMove(analysis);
67 }
68}
69
70inline void StaticPropertyAnalyzer::putById(RegisterID* dst, unsigned propertyIndex)
71{
72 StaticPropertyAnalysis* analysis = m_analyses.get(dst->index());
73 if (!analysis)
74 return;
75 analysis->addPropertyIndex(propertyIndex);
76}
77
78inline void StaticPropertyAnalyzer::mov(RegisterID* dst, RegisterID* src)
79{
80 RefPtr<StaticPropertyAnalysis> analysis = m_analyses.get(src->index());
81 if (!analysis) {
82 kill(dst);
83 return;
84 }
85
86 AnalysisMap::AddResult addResult = m_analyses.add(dst->index(), analysis);
87 if (!addResult.isNewEntry) {
88 kill(addResult.iterator->value.get());
89 addResult.iterator->value = WTFMove(analysis);
90 }
91}
92
93inline void StaticPropertyAnalyzer::kill(StaticPropertyAnalysis* analysis)
94{
95 if (!analysis)
96 return;
97 if (!analysis->hasOneRef()) // Aliases for this object still exist, so it might acquire more properties.
98 return;
99 analysis->record();
100}
101
102inline void StaticPropertyAnalyzer::kill(RegisterID* dst)
103{
104 // We observe kills in order to avoid piling on properties to an object after
105 // its bytecode register has been recycled.
106
107 // Consider these cases:
108
109 // (1) Aliased temporary
110 // var o1 = { name: name };
111 // var o2 = { name: name };
112
113 // (2) Aliased local -- no control flow
114 // var local;
115 // local = new Object;
116 // local.name = name;
117 // ...
118
119 // local = lookup();
120 // local.didLookup = true;
121 // ...
122
123 // (3) Aliased local -- control flow
124 // var local;
125 // if (condition)
126 // local = { };
127 // else {
128 // local = new Object;
129 // }
130 // local.name = name;
131
132 // (Note: our default codegen for "new Object" looks like case (3).)
133
134 // Case (1) is easy because temporaries almost never survive across control flow.
135
136 // Cases (2) and (3) are hard. Case (2) should kill "local", while case (3) should
137 // not. There is no great way to solve these cases with simple static analysis.
138
139 // Since this is a simple static analysis, we just try to catch the simplest cases,
140 // so we accept kills to any registers except for registers that have no inferred
141 // properties yet.
142
143 AnalysisMap::iterator it = m_analyses.find(dst->index());
144 if (it == m_analyses.end())
145 return;
146 if (!it->value->propertyIndexCount())
147 return;
148
149 kill(it->value.get());
150 m_analyses.remove(it);
151}
152
153inline void StaticPropertyAnalyzer::kill()
154{
155 while (m_analyses.size())
156 kill(m_analyses.take(m_analyses.begin()->key).get());
157}
158
159} // namespace JSC
160