OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "src/compiler/js-for-in-lowering.h" |
| 6 |
| 7 #include "src/code-factory.h" |
| 8 #include "src/compiler/access-builder.h" |
| 9 #include "src/compiler/common-operator.h" |
| 10 #include "src/compiler/graph.h" |
| 11 #include "src/compiler/js-graph.h" |
| 12 #include "src/compiler/linkage.h" |
| 13 #include "src/compiler/node-properties.h" |
| 14 #include "src/compiler/simplified-operator.h" |
| 15 |
| 16 namespace v8 { |
| 17 namespace internal { |
| 18 namespace compiler { |
| 19 |
| 20 Reduction JSForInLowering::Reduce(Node* node) { |
| 21 switch (node->opcode()) { |
| 22 case IrOpcode::kJSForInHasOwnProperty: |
| 23 return ReduceJSForInHasOwnProperty(node); |
| 24 case IrOpcode::kJSForInLoadProperty: |
| 25 return ReduceJSForInLoadProperty(node); |
| 26 case IrOpcode::kJSForInNext: |
| 27 return ReduceJSForInNext(node); |
| 28 case IrOpcode::kJSForInPrepare: |
| 29 return ReduceJSForInPrepare(node); |
| 30 default: |
| 31 break; |
| 32 } |
| 33 return NoChange(); |
| 34 } |
| 35 |
| 36 Reduction JSForInLowering::ReduceJSForInHasOwnProperty(Node* node) { |
| 37 DCHECK_EQ(IrOpcode::kJSForInHasOwnProperty, node->opcode()); |
| 38 Node* target = NodeProperties::GetValueInput(node, 0); |
| 39 Node* receiver = NodeProperties::GetValueInput(node, 1); |
| 40 Node* name = NodeProperties::GetValueInput(node, 2); |
| 41 Node* enumerator = NodeProperties::GetValueInput(node, 3); |
| 42 Node* frame_state = NodeProperties::GetFrameStateInput(node); |
| 43 Node* effect = NodeProperties::GetEffectInput(node); |
| 44 Node* control = NodeProperties::GetControlInput(node); |
| 45 |
| 46 // Load the map of the {receiver}. |
| 47 Node* receiver_map = effect = |
| 48 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
| 49 receiver, effect, control); |
| 50 |
| 51 // Check if the expected map still matches that of the {receiver}. |
| 52 Node* check0 = graph()->NewNode(simplified()->ReferenceEqual(), receiver_map, |
| 53 enumerator); |
| 54 Node* branch0 = |
| 55 graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control); |
| 56 |
| 57 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
| 58 Node* etrue0 = effect; |
| 59 Node* vtrue0; |
| 60 { |
| 61 // Don't need filtering since {enumerator} still matches the {receiver} map. |
| 62 vtrue0 = jsgraph()->TrueConstant(); |
| 63 } |
| 64 |
| 65 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); |
| 66 Node* efalse0 = effect; |
| 67 Node* vfalse0; |
| 68 { |
| 69 // We need to call Object.prototype.hasOwnProperty here. |
| 70 CallDescriptor const* const desc = Linkage::GetJSCallDescriptor( |
| 71 graph()->zone(), false, 2, CallDescriptor::kNeedsFrameState); |
| 72 Node* context = efalse0 = graph()->NewNode( |
| 73 simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target, |
| 74 efalse0, if_false0); |
| 75 vfalse0 = efalse0 = graph()->NewNode(common()->Call(desc), target, receiver, |
| 76 name, jsgraph()->UndefinedConstant(), |
| 77 jsgraph()->OneConstant(), context, |
| 78 frame_state, efalse0, if_false0); |
| 79 NodeProperties::SetType(vfalse0, Type::Boolean()); |
| 80 |
| 81 // Update potential {IfException} uses of {node} to point to the above |
| 82 // Object.prototype.hasOwnProperty call node instead. |
| 83 Node* if_exception = nullptr; |
| 84 if (NodeProperties::IsExceptionalCall(node, &if_exception)) { |
| 85 if_false0 = graph()->NewNode(common()->IfSuccess(), vfalse0); |
| 86 NodeProperties::ReplaceControlInput(if_exception, vfalse0); |
| 87 NodeProperties::ReplaceEffectInput(if_exception, efalse0); |
| 88 Revisit(if_exception); |
| 89 } |
| 90 } |
| 91 |
| 92 control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); |
| 93 effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control); |
| 94 Node* value = |
| 95 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), vtrue0, |
| 96 vfalse0, control); |
| 97 ReplaceWithValue(node, value, effect, control); |
| 98 return Replace(value); |
| 99 } |
| 100 |
| 101 Reduction JSForInLowering::ReduceJSForInLoadProperty(Node* node) { |
| 102 DCHECK_EQ(IrOpcode::kJSForInLoadProperty, node->opcode()); |
| 103 Node* receiver = NodeProperties::GetValueInput(node, 0); |
| 104 Node* name = NodeProperties::GetValueInput(node, 1); |
| 105 Node* enumerator = NodeProperties::GetValueInput(node, 2); |
| 106 Node* index = NodeProperties::GetValueInput(node, 3); |
| 107 Node* context = NodeProperties::GetContextInput(node); |
| 108 Node* frame_state = NodeProperties::GetFrameStateInput(node); |
| 109 Node* effect = NodeProperties::GetEffectInput(node); |
| 110 Node* control = NodeProperties::GetControlInput(node); |
| 111 |
| 112 // We know that the {index} is in Unsigned32 range here, otherwise executing |
| 113 // the JSForInNext wouldn't be valid. Unfortunately due to OSR and generators |
| 114 // this is not always reflected in the types, hence we might need to rename |
| 115 // the {index} here. |
| 116 if (!NodeProperties::GetType(index)->Is(Type::Unsigned32())) { |
| 117 index = graph()->NewNode(common()->TypeGuard(Type::Unsigned32()), index, |
| 118 control); |
| 119 } |
| 120 |
| 121 // Load the map of the {receiver}. |
| 122 Node* receiver_map = effect = |
| 123 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
| 124 receiver, effect, control); |
| 125 |
| 126 // Check if the expected map still matches that of the {receiver}. |
| 127 Node* check0 = graph()->NewNode(simplified()->ReferenceEqual(), receiver_map, |
| 128 enumerator); |
| 129 Node* branch0 = |
| 130 graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control); |
| 131 |
| 132 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
| 133 Node* etrue0 = effect; |
| 134 Node* vtrue0; |
| 135 { |
| 136 // We are using the enumeration cache, so we can just load the value |
| 137 // for the property {name} on {receiver} by loading the FieldIndex |
| 138 // from the indices part of the enum cache. |
| 139 Node* receiver_descriptors = etrue0 = graph()->NewNode( |
| 140 simplified()->LoadField(AccessBuilder::ForMapDescriptors()), |
| 141 receiver_map, etrue0, if_true0); |
| 142 Node* receiver_enum_cache = etrue0 = graph()->NewNode( |
| 143 simplified()->LoadField(AccessBuilder::ForDescriptorArrayEnumCache()), |
| 144 receiver_descriptors, etrue0, if_true0); |
| 145 Node* receiver_enum_indices = etrue0 = graph()->NewNode( |
| 146 simplified()->LoadField( |
| 147 AccessBuilder::ForDescriptorArrayEnumCacheBridgeIndicesCache()), |
| 148 receiver_enum_cache, etrue0, if_true0); |
| 149 // TODO(bmeurer): Weird hack! |
| 150 receiver_enum_indices = etrue0 = |
| 151 graph()->NewNode(simplified()->CheckHeapObject(), receiver_enum_indices, |
| 152 etrue0, if_true0); |
| 153 index = etrue0 = graph()->NewNode( |
| 154 simplified()->LoadElement( |
| 155 AccessBuilder::ForFixedArrayElement(FAST_SMI_ELEMENTS)), |
| 156 receiver_enum_indices, index, etrue0, if_true0); |
| 157 vtrue0 = etrue0 = graph()->NewNode(simplified()->LoadFieldByIndex(), |
| 158 receiver, index, etrue0, if_true0); |
| 159 } |
| 160 |
| 161 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); |
| 162 Node* efalse0 = effect; |
| 163 Node* vfalse0; |
| 164 { |
| 165 // Load the property with the given {name} from the {receiver} using |
| 166 // the GetProperty stub. |
| 167 Callable const callable = CodeFactory::GetProperty(isolate()); |
| 168 CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( |
| 169 isolate(), graph()->zone(), callable.descriptor(), 0, |
| 170 CallDescriptor::kNeedsFrameState); |
| 171 vfalse0 = efalse0 = graph()->NewNode( |
| 172 common()->Call(desc), jsgraph()->HeapConstant(callable.code()), |
| 173 receiver, name, context, frame_state, efalse0, if_false0); |
| 174 NodeProperties::SetType(vfalse0, Type::NonInternal()); |
| 175 |
| 176 // Update potential {IfException} uses of {node} to point to the ahove |
| 177 // GetProperty stub call node instead. |
| 178 Node* if_exception = nullptr; |
| 179 if (NodeProperties::IsExceptionalCall(node, &if_exception)) { |
| 180 if_false0 = graph()->NewNode(common()->IfSuccess(), vfalse0); |
| 181 NodeProperties::ReplaceControlInput(if_exception, vfalse0); |
| 182 NodeProperties::ReplaceEffectInput(if_exception, efalse0); |
| 183 Revisit(if_exception); |
| 184 } |
| 185 } |
| 186 |
| 187 control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); |
| 188 effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control); |
| 189 Node* value = |
| 190 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), vtrue0, |
| 191 vfalse0, control); |
| 192 ReplaceWithValue(node, value, effect, control); |
| 193 return Replace(value); |
| 194 } |
| 195 |
| 196 Reduction JSForInLowering::ReduceJSForInNext(Node* node) { |
| 197 DCHECK_EQ(IrOpcode::kJSForInNext, node->opcode()); |
| 198 Node* receiver = NodeProperties::GetValueInput(node, 0); |
| 199 Node* cache_array = NodeProperties::GetValueInput(node, 1); |
| 200 Node* cache_type = NodeProperties::GetValueInput(node, 2); |
| 201 Node* index = NodeProperties::GetValueInput(node, 3); |
| 202 Node* context = NodeProperties::GetContextInput(node); |
| 203 Node* frame_state = NodeProperties::GetFrameStateInput(node); |
| 204 Node* effect = NodeProperties::GetEffectInput(node); |
| 205 Node* control = NodeProperties::GetControlInput(node); |
| 206 |
| 207 // We know that the {index} is in Unsigned32 range here, otherwise executing |
| 208 // the JSForInNext wouldn't be valid. Unfortunately due to OSR and generators |
| 209 // this is not always reflected in the types, hence we might need to rename |
| 210 // the {index} here. |
| 211 if (!NodeProperties::GetType(index)->Is(Type::Unsigned32())) { |
| 212 index = graph()->NewNode(common()->TypeGuard(Type::Unsigned32()), index, |
| 213 control); |
| 214 } |
| 215 |
| 216 // Load the next {key} from the {cache_array}. |
| 217 Node* key = effect = graph()->NewNode( |
| 218 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()), |
| 219 cache_array, index, effect, control); |
| 220 |
| 221 // Load the map of the {receiver}. |
| 222 Node* receiver_map = effect = |
| 223 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
| 224 receiver, effect, control); |
| 225 |
| 226 // Check if the expected map still matches that of the {receiver}. |
| 227 Node* check0 = graph()->NewNode(simplified()->ReferenceEqual(), receiver_map, |
| 228 cache_type); |
| 229 Node* branch0 = |
| 230 graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control); |
| 231 |
| 232 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
| 233 Node* etrue0; |
| 234 Node* vtrue0; |
| 235 { |
| 236 // Don't need filtering since expected map still matches that of the |
| 237 // {receiver}; in this case the {key} is definitely a String. |
| 238 etrue0 = effect; |
| 239 vtrue0 = |
| 240 graph()->NewNode(common()->TypeGuard(Type::String()), key, if_true0); |
| 241 } |
| 242 |
| 243 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); |
| 244 Node* efalse0; |
| 245 Node* vfalse0; |
| 246 { |
| 247 // Filter the {key} to check if it's still a valid property of the |
| 248 // {receiver} (does the ToName conversion implicitly). |
| 249 Callable const callable = CodeFactory::ForInFilter(isolate()); |
| 250 CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( |
| 251 isolate(), graph()->zone(), callable.descriptor(), 0, |
| 252 CallDescriptor::kNeedsFrameState); |
| 253 vfalse0 = efalse0 = graph()->NewNode( |
| 254 common()->Call(desc), jsgraph()->HeapConstant(callable.code()), key, |
| 255 receiver, context, frame_state, effect, if_false0); |
| 256 NodeProperties::SetType(vfalse0, Type::StringOrUndefined()); |
| 257 |
| 258 // Update potential {IfException} uses of {node} to point to the ahove |
| 259 // ForInFilter stub call node instead. |
| 260 Node* if_exception = nullptr; |
| 261 if (NodeProperties::IsExceptionalCall(node, &if_exception)) { |
| 262 if_false0 = graph()->NewNode(common()->IfSuccess(), vfalse0); |
| 263 NodeProperties::ReplaceControlInput(if_exception, vfalse0); |
| 264 NodeProperties::ReplaceEffectInput(if_exception, efalse0); |
| 265 Revisit(if_exception); |
| 266 } |
| 267 } |
| 268 |
| 269 control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); |
| 270 effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control); |
| 271 Node* value = |
| 272 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), vtrue0, |
| 273 vfalse0, control); |
| 274 ReplaceWithValue(node, value, effect, control); |
| 275 return Replace(value); |
| 276 } |
| 277 |
| 278 Reduction JSForInLowering::ReduceJSForInPrepare(Node* node) { |
| 279 DCHECK_EQ(IrOpcode::kJSForInPrepare, node->opcode()); |
| 280 Callable const callable = CodeFactory::ForInPrepare(isolate()); |
| 281 CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( |
| 282 isolate(), graph()->zone(), callable.descriptor(), 0, |
| 283 CallDescriptor::kNeedsFrameState, node->op()->properties(), |
| 284 MachineType::AnyTagged(), 3); |
| 285 Node* stub = jsgraph()->HeapConstant(callable.code()); |
| 286 node->InsertInput(graph()->zone(), 0, stub); |
| 287 NodeProperties::ChangeOp(node, common()->Call(desc)); |
| 288 return Changed(node); |
| 289 } |
| 290 |
| 291 CommonOperatorBuilder* JSForInLowering::common() const { |
| 292 return jsgraph()->common(); |
| 293 } |
| 294 |
| 295 Graph* JSForInLowering::graph() const { return jsgraph()->graph(); } |
| 296 |
| 297 Isolate* JSForInLowering::isolate() const { return jsgraph()->isolate(); } |
| 298 |
| 299 SimplifiedOperatorBuilder* JSForInLowering::simplified() const { |
| 300 return jsgraph()->simplified(); |
| 301 } |
| 302 |
| 303 } // namespace compiler |
| 304 } // namespace internal |
| 305 } // namespace v8 |
OLD | NEW |