Home | History | Annotate | Download | only in compiler
      1 // Copyright 2016 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/typed-optimization.h"
      6 
      7 #include "src/compilation-dependencies.h"
      8 #include "src/compiler/js-graph.h"
      9 #include "src/compiler/node-properties.h"
     10 #include "src/compiler/simplified-operator.h"
     11 #include "src/compiler/type-cache.h"
     12 #include "src/isolate-inl.h"
     13 
     14 namespace v8 {
     15 namespace internal {
     16 namespace compiler {
     17 
     18 TypedOptimization::TypedOptimization(Editor* editor,
     19                                      CompilationDependencies* dependencies,
     20                                      Flags flags, JSGraph* jsgraph)
     21     : AdvancedReducer(editor),
     22       dependencies_(dependencies),
     23       flags_(flags),
     24       jsgraph_(jsgraph),
     25       true_type_(Type::HeapConstant(factory()->true_value(), graph()->zone())),
     26       false_type_(
     27           Type::HeapConstant(factory()->false_value(), graph()->zone())),
     28       type_cache_(TypeCache::Get()) {}
     29 
     30 TypedOptimization::~TypedOptimization() {}
     31 
     32 Reduction TypedOptimization::Reduce(Node* node) {
     33   // Check if the output type is a singleton.  In that case we already know the
     34   // result value and can simply replace the node if it's eliminable.
     35   if (!NodeProperties::IsConstant(node) && NodeProperties::IsTyped(node) &&
     36       node->op()->HasProperty(Operator::kEliminatable)) {
     37     // TODO(v8:5303): We must not eliminate FinishRegion here. This special
     38     // case can be removed once we have separate operators for value and
     39     // effect regions.
     40     if (node->opcode() == IrOpcode::kFinishRegion) return NoChange();
     41     // We can only constant-fold nodes here, that are known to not cause any
     42     // side-effect, may it be a JavaScript observable side-effect or a possible
     43     // eager deoptimization exit (i.e. {node} has an operator that doesn't have
     44     // the Operator::kNoDeopt property).
     45     Type* upper = NodeProperties::GetType(node);
     46     if (upper->IsInhabited()) {
     47       if (upper->IsHeapConstant()) {
     48         Node* replacement =
     49             jsgraph()->Constant(upper->AsHeapConstant()->Value());
     50         ReplaceWithValue(node, replacement);
     51         return Changed(replacement);
     52       } else if (upper->Is(Type::MinusZero())) {
     53         Node* replacement = jsgraph()->Constant(factory()->minus_zero_value());
     54         ReplaceWithValue(node, replacement);
     55         return Changed(replacement);
     56       } else if (upper->Is(Type::NaN())) {
     57         Node* replacement = jsgraph()->NaNConstant();
     58         ReplaceWithValue(node, replacement);
     59         return Changed(replacement);
     60       } else if (upper->Is(Type::Null())) {
     61         Node* replacement = jsgraph()->NullConstant();
     62         ReplaceWithValue(node, replacement);
     63         return Changed(replacement);
     64       } else if (upper->Is(Type::PlainNumber()) &&
     65                  upper->Min() == upper->Max()) {
     66         Node* replacement = jsgraph()->Constant(upper->Min());
     67         ReplaceWithValue(node, replacement);
     68         return Changed(replacement);
     69       } else if (upper->Is(Type::Undefined())) {
     70         Node* replacement = jsgraph()->UndefinedConstant();
     71         ReplaceWithValue(node, replacement);
     72         return Changed(replacement);
     73       }
     74     }
     75   }
     76   switch (node->opcode()) {
     77     case IrOpcode::kCheckHeapObject:
     78       return ReduceCheckHeapObject(node);
     79     case IrOpcode::kCheckMaps:
     80       return ReduceCheckMaps(node);
     81     case IrOpcode::kCheckString:
     82       return ReduceCheckString(node);
     83     case IrOpcode::kLoadField:
     84       return ReduceLoadField(node);
     85     case IrOpcode::kNumberCeil:
     86     case IrOpcode::kNumberRound:
     87     case IrOpcode::kNumberTrunc:
     88       return ReduceNumberRoundop(node);
     89     case IrOpcode::kNumberFloor:
     90       return ReduceNumberFloor(node);
     91     case IrOpcode::kNumberToUint8Clamped:
     92       return ReduceNumberToUint8Clamped(node);
     93     case IrOpcode::kPhi:
     94       return ReducePhi(node);
     95     case IrOpcode::kReferenceEqual:
     96       return ReduceReferenceEqual(node);
     97     case IrOpcode::kSelect:
     98       return ReduceSelect(node);
     99     default:
    100       break;
    101   }
    102   return NoChange();
    103 }
    104 
    105 namespace {
    106 
    107 MaybeHandle<Map> GetStableMapFromObjectType(Type* object_type) {
    108   if (object_type->IsHeapConstant()) {
    109     Handle<Map> object_map(object_type->AsHeapConstant()->Value()->map());
    110     if (object_map->is_stable()) return object_map;
    111   }
    112   return MaybeHandle<Map>();
    113 }
    114 
    115 }  // namespace
    116 
    117 Reduction TypedOptimization::ReduceCheckHeapObject(Node* node) {
    118   Node* const input = NodeProperties::GetValueInput(node, 0);
    119   Type* const input_type = NodeProperties::GetType(input);
    120   if (!input_type->Maybe(Type::SignedSmall())) {
    121     ReplaceWithValue(node, input);
    122     return Replace(input);
    123   }
    124   return NoChange();
    125 }
    126 
    127 Reduction TypedOptimization::ReduceCheckMaps(Node* node) {
    128   // The CheckMaps(o, ...map...) can be eliminated if map is stable,
    129   // o has type Constant(object) and map == object->map, and either
    130   //  (1) map cannot transition further, or
    131   //  (2) we can add a code dependency on the stability of map
    132   //      (to guard the Constant type information).
    133   Node* const object = NodeProperties::GetValueInput(node, 0);
    134   Type* const object_type = NodeProperties::GetType(object);
    135   Node* const effect = NodeProperties::GetEffectInput(node);
    136   Handle<Map> object_map;
    137   if (GetStableMapFromObjectType(object_type).ToHandle(&object_map)) {
    138     for (int i = 1; i < node->op()->ValueInputCount(); ++i) {
    139       Node* const map = NodeProperties::GetValueInput(node, i);
    140       Type* const map_type = NodeProperties::GetType(map);
    141       if (map_type->IsHeapConstant() &&
    142           map_type->AsHeapConstant()->Value().is_identical_to(object_map)) {
    143         if (object_map->CanTransition()) {
    144           dependencies()->AssumeMapStable(object_map);
    145         }
    146         return Replace(effect);
    147       }
    148     }
    149   }
    150   return NoChange();
    151 }
    152 
    153 Reduction TypedOptimization::ReduceCheckString(Node* node) {
    154   Node* const input = NodeProperties::GetValueInput(node, 0);
    155   Type* const input_type = NodeProperties::GetType(input);
    156   if (input_type->Is(Type::String())) {
    157     ReplaceWithValue(node, input);
    158     return Replace(input);
    159   }
    160   return NoChange();
    161 }
    162 
    163 Reduction TypedOptimization::ReduceLoadField(Node* node) {
    164   Node* const object = NodeProperties::GetValueInput(node, 0);
    165   Type* const object_type = NodeProperties::GetType(object);
    166   FieldAccess const& access = FieldAccessOf(node->op());
    167   if (access.base_is_tagged == kTaggedBase &&
    168       access.offset == HeapObject::kMapOffset) {
    169     // We can replace LoadField[Map](o) with map if is stable, and
    170     // o has type Constant(object) and map == object->map, and either
    171     //  (1) map cannot transition further, or
    172     //  (2) deoptimization is enabled and we can add a code dependency on the
    173     //      stability of map (to guard the Constant type information).
    174     Handle<Map> object_map;
    175     if (GetStableMapFromObjectType(object_type).ToHandle(&object_map)) {
    176       if (object_map->CanTransition()) {
    177         if (flags() & kDeoptimizationEnabled) {
    178           dependencies()->AssumeMapStable(object_map);
    179         } else {
    180           return NoChange();
    181         }
    182       }
    183       Node* const value = jsgraph()->HeapConstant(object_map);
    184       ReplaceWithValue(node, value);
    185       return Replace(value);
    186     }
    187   }
    188   return NoChange();
    189 }
    190 
    191 Reduction TypedOptimization::ReduceNumberFloor(Node* node) {
    192   Node* const input = NodeProperties::GetValueInput(node, 0);
    193   Type* const input_type = NodeProperties::GetType(input);
    194   if (input_type->Is(type_cache_.kIntegerOrMinusZeroOrNaN)) {
    195     return Replace(input);
    196   }
    197   if (input_type->Is(Type::PlainNumber()) &&
    198       input->opcode() == IrOpcode::kNumberDivide) {
    199     Node* const lhs = NodeProperties::GetValueInput(input, 0);
    200     Type* const lhs_type = NodeProperties::GetType(lhs);
    201     Node* const rhs = NodeProperties::GetValueInput(input, 1);
    202     Type* const rhs_type = NodeProperties::GetType(rhs);
    203     if (lhs_type->Is(Type::Unsigned32()) && rhs_type->Is(Type::Unsigned32())) {
    204       // We can replace
    205       //
    206       //   NumberFloor(NumberDivide(lhs: unsigned32,
    207       //                            rhs: unsigned32)): plain-number
    208       //
    209       // with
    210       //
    211       //   NumberToUint32(NumberDivide(lhs, rhs))
    212       //
    213       // and just smash the type of the {lhs} on the {node},
    214       // as the truncated result must be in the same range as
    215       // {lhs} since {rhs} cannot be less than 1 (due to the
    216       // plain-number type constraint on the {node}).
    217       NodeProperties::ChangeOp(node, simplified()->NumberToUint32());
    218       NodeProperties::SetType(node, lhs_type);
    219       return Changed(node);
    220     }
    221   }
    222   return NoChange();
    223 }
    224 
    225 Reduction TypedOptimization::ReduceNumberRoundop(Node* node) {
    226   Node* const input = NodeProperties::GetValueInput(node, 0);
    227   Type* const input_type = NodeProperties::GetType(input);
    228   if (input_type->Is(type_cache_.kIntegerOrMinusZeroOrNaN)) {
    229     return Replace(input);
    230   }
    231   return NoChange();
    232 }
    233 
    234 Reduction TypedOptimization::ReduceNumberToUint8Clamped(Node* node) {
    235   Node* const input = NodeProperties::GetValueInput(node, 0);
    236   Type* const input_type = NodeProperties::GetType(input);
    237   if (input_type->Is(type_cache_.kUint8)) {
    238     return Replace(input);
    239   }
    240   return NoChange();
    241 }
    242 
    243 Reduction TypedOptimization::ReducePhi(Node* node) {
    244   // Try to narrow the type of the Phi {node}, which might be more precise now
    245   // after lowering based on types, i.e. a SpeculativeNumberAdd has a more
    246   // precise type than the JSAdd that was in the graph when the Typer was run.
    247   DCHECK_EQ(IrOpcode::kPhi, node->opcode());
    248   int arity = node->op()->ValueInputCount();
    249   Type* type = NodeProperties::GetType(node->InputAt(0));
    250   for (int i = 1; i < arity; ++i) {
    251     type = Type::Union(type, NodeProperties::GetType(node->InputAt(i)),
    252                        graph()->zone());
    253   }
    254   Type* const node_type = NodeProperties::GetType(node);
    255   if (!node_type->Is(type)) {
    256     type = Type::Intersect(node_type, type, graph()->zone());
    257     NodeProperties::SetType(node, type);
    258     return Changed(node);
    259   }
    260   return NoChange();
    261 }
    262 
    263 Reduction TypedOptimization::ReduceReferenceEqual(Node* node) {
    264   DCHECK_EQ(IrOpcode::kReferenceEqual, node->opcode());
    265   Node* const lhs = NodeProperties::GetValueInput(node, 0);
    266   Node* const rhs = NodeProperties::GetValueInput(node, 1);
    267   Type* const lhs_type = NodeProperties::GetType(lhs);
    268   Type* const rhs_type = NodeProperties::GetType(rhs);
    269   if (!lhs_type->Maybe(rhs_type)) {
    270     return Replace(jsgraph()->FalseConstant());
    271   }
    272   return NoChange();
    273 }
    274 
    275 Reduction TypedOptimization::ReduceSelect(Node* node) {
    276   DCHECK_EQ(IrOpcode::kSelect, node->opcode());
    277   Node* const condition = NodeProperties::GetValueInput(node, 0);
    278   Type* const condition_type = NodeProperties::GetType(condition);
    279   Node* const vtrue = NodeProperties::GetValueInput(node, 1);
    280   Type* const vtrue_type = NodeProperties::GetType(vtrue);
    281   Node* const vfalse = NodeProperties::GetValueInput(node, 2);
    282   Type* const vfalse_type = NodeProperties::GetType(vfalse);
    283   if (condition_type->Is(true_type_)) {
    284     // Select(condition:true, vtrue, vfalse) => vtrue
    285     return Replace(vtrue);
    286   }
    287   if (condition_type->Is(false_type_)) {
    288     // Select(condition:false, vtrue, vfalse) => vfalse
    289     return Replace(vfalse);
    290   }
    291   if (vtrue_type->Is(true_type_) && vfalse_type->Is(false_type_)) {
    292     // Select(condition, vtrue:true, vfalse:false) => condition
    293     return Replace(condition);
    294   }
    295   if (vtrue_type->Is(false_type_) && vfalse_type->Is(true_type_)) {
    296     // Select(condition, vtrue:false, vfalse:true) => BooleanNot(condition)
    297     node->TrimInputCount(1);
    298     NodeProperties::ChangeOp(node, simplified()->BooleanNot());
    299     return Changed(node);
    300   }
    301   // Try to narrow the type of the Select {node}, which might be more precise
    302   // now after lowering based on types.
    303   Type* type = Type::Union(vtrue_type, vfalse_type, graph()->zone());
    304   Type* const node_type = NodeProperties::GetType(node);
    305   if (!node_type->Is(type)) {
    306     type = Type::Intersect(node_type, type, graph()->zone());
    307     NodeProperties::SetType(node, type);
    308     return Changed(node);
    309   }
    310   return NoChange();
    311 }
    312 
    313 Factory* TypedOptimization::factory() const { return isolate()->factory(); }
    314 
    315 Graph* TypedOptimization::graph() const { return jsgraph()->graph(); }
    316 
    317 Isolate* TypedOptimization::isolate() const { return jsgraph()->isolate(); }
    318 
    319 SimplifiedOperatorBuilder* TypedOptimization::simplified() const {
    320   return jsgraph()->simplified();
    321 }
    322 
    323 }  // namespace compiler
    324 }  // namespace internal
    325 }  // namespace v8
    326