deps/v8/src/compiler/representation-change.h

Summary

Maintainability
Test Coverage
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_COMPILER_REPRESENTATION_CHANGE_H_
#define V8_COMPILER_REPRESENTATION_CHANGE_H_

#include "src/compiler/js-graph.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties-inl.h"
#include "src/compiler/simplified-operator.h"

namespace v8 {
namespace internal {
namespace compiler {

// The types and representations tracked during representation inference
// and change insertion.
// TODO(titzer): First, merge MachineType and RepType.
// TODO(titzer): Second, Use the real type system instead of RepType.
enum RepType {
  // Representations.
  rBit = 1 << 0,
  rWord32 = 1 << 1,
  rWord64 = 1 << 2,
  rFloat64 = 1 << 3,
  rTagged = 1 << 4,

  // Types.
  tBool = 1 << 5,
  tInt32 = 1 << 6,
  tUint32 = 1 << 7,
  tInt64 = 1 << 8,
  tUint64 = 1 << 9,
  tNumber = 1 << 10,
  tAny = 1 << 11
};

#define REP_TYPE_STRLEN 24

typedef uint16_t RepTypeUnion;


inline void RenderRepTypeUnion(char* buf, RepTypeUnion info) {
  base::OS::SNPrintF(buf, REP_TYPE_STRLEN, "{%s%s%s%s%s %s%s%s%s%s%s%s}",
                     (info & rBit) ? "k" : " ", (info & rWord32) ? "w" : " ",
                     (info & rWord64) ? "q" : " ",
                     (info & rFloat64) ? "f" : " ",
                     (info & rTagged) ? "t" : " ", (info & tBool) ? "Z" : " ",
                     (info & tInt32) ? "I" : " ", (info & tUint32) ? "U" : " ",
                     (info & tInt64) ? "L" : " ", (info & tUint64) ? "J" : " ",
                     (info & tNumber) ? "N" : " ", (info & tAny) ? "*" : " ");
}


const RepTypeUnion rMask = rBit | rWord32 | rWord64 | rFloat64 | rTagged;
const RepTypeUnion tMask =
    tBool | tInt32 | tUint32 | tInt64 | tUint64 | tNumber | tAny;
const RepType rPtr = kPointerSize == 4 ? rWord32 : rWord64;

// Contains logic related to changing the representation of values for constants
// and other nodes, as well as lowering Simplified->Machine operators.
// Eagerly folds any representation changes for constants.
class RepresentationChanger {
 public:
  RepresentationChanger(JSGraph* jsgraph, SimplifiedOperatorBuilder* simplified,
                        MachineOperatorBuilder* machine, Isolate* isolate)
      : jsgraph_(jsgraph),
        simplified_(simplified),
        machine_(machine),
        isolate_(isolate),
        testing_type_errors_(false),
        type_error_(false) {}


  Node* GetRepresentationFor(Node* node, RepTypeUnion output_type,
                             RepTypeUnion use_type) {
    if (!IsPowerOf2(output_type & rMask)) {
      // There should be only one output representation.
      return TypeError(node, output_type, use_type);
    }
    if ((use_type & rMask) == (output_type & rMask)) {
      // Representations are the same. That's a no-op.
      return node;
    }
    if (use_type & rTagged) {
      return GetTaggedRepresentationFor(node, output_type);
    } else if (use_type & rFloat64) {
      return GetFloat64RepresentationFor(node, output_type);
    } else if (use_type & rWord32) {
      return GetWord32RepresentationFor(node, output_type);
    } else if (use_type & rBit) {
      return GetBitRepresentationFor(node, output_type);
    } else if (use_type & rWord64) {
      return GetWord64RepresentationFor(node, output_type);
    } else {
      return node;
    }
  }

  Node* GetTaggedRepresentationFor(Node* node, RepTypeUnion output_type) {
    // Eagerly fold representation changes for constants.
    switch (node->opcode()) {
      case IrOpcode::kNumberConstant:
      case IrOpcode::kHeapConstant:
        return node;  // No change necessary.
      case IrOpcode::kInt32Constant:
        if (output_type & tUint32) {
          uint32_t value = ValueOf<uint32_t>(node->op());
          return jsgraph()->Constant(static_cast<double>(value));
        } else if (output_type & tInt32) {
          int32_t value = ValueOf<int32_t>(node->op());
          return jsgraph()->Constant(value);
        } else if (output_type & rBit) {
          return ValueOf<int32_t>(node->op()) == 0 ? jsgraph()->FalseConstant()
                                                   : jsgraph()->TrueConstant();
        } else {
          return TypeError(node, output_type, rTagged);
        }
      case IrOpcode::kFloat64Constant:
        return jsgraph()->Constant(ValueOf<double>(node->op()));
      default:
        break;
    }
    // Select the correct X -> Tagged operator.
    Operator* op;
    if (output_type & rBit) {
      op = simplified()->ChangeBitToBool();
    } else if (output_type & rWord32) {
      if (output_type & tUint32) {
        op = simplified()->ChangeUint32ToTagged();
      } else if (output_type & tInt32) {
        op = simplified()->ChangeInt32ToTagged();
      } else {
        return TypeError(node, output_type, rTagged);
      }
    } else if (output_type & rFloat64) {
      op = simplified()->ChangeFloat64ToTagged();
    } else {
      return TypeError(node, output_type, rTagged);
    }
    return jsgraph()->graph()->NewNode(op, node);
  }

  Node* GetFloat64RepresentationFor(Node* node, RepTypeUnion output_type) {
    // Eagerly fold representation changes for constants.
    switch (node->opcode()) {
      case IrOpcode::kNumberConstant:
        return jsgraph()->Float64Constant(ValueOf<double>(node->op()));
      case IrOpcode::kInt32Constant:
        if (output_type & tUint32) {
          uint32_t value = ValueOf<uint32_t>(node->op());
          return jsgraph()->Float64Constant(static_cast<double>(value));
        } else {
          int32_t value = ValueOf<int32_t>(node->op());
          return jsgraph()->Float64Constant(value);
        }
      case IrOpcode::kFloat64Constant:
        return node;  // No change necessary.
      default:
        break;
    }
    // Select the correct X -> Float64 operator.
    Operator* op;
    if (output_type & rWord32) {
      if (output_type & tUint32) {
        op = machine()->ChangeUint32ToFloat64();
      } else if (output_type & tInt32) {
        op = machine()->ChangeInt32ToFloat64();
      } else {
        return TypeError(node, output_type, rFloat64);
      }
    } else if (output_type & rTagged) {
      op = simplified()->ChangeTaggedToFloat64();
    } else {
      return TypeError(node, output_type, rFloat64);
    }
    return jsgraph()->graph()->NewNode(op, node);
  }

  Node* GetWord32RepresentationFor(Node* node, RepTypeUnion output_type) {
    // Eagerly fold representation changes for constants.
    switch (node->opcode()) {
      case IrOpcode::kInt32Constant:
        return node;  // No change necessary.
      case IrOpcode::kNumberConstant:
      case IrOpcode::kFloat64Constant: {
        if (output_type & tUint32) {
          int32_t value = static_cast<int32_t>(
              static_cast<uint32_t>(ValueOf<double>(node->op())));
          return jsgraph()->Int32Constant(value);
        } else if (output_type & tInt32) {
          int32_t value = FastD2I(ValueOf<double>(node->op()));
          return jsgraph()->Int32Constant(value);
        } else {
          return TypeError(node, output_type, rWord32);
        }
      }
      default:
        break;
    }
    // Select the correct X -> Word32 operator.
    Operator* op = NULL;
    if (output_type & rFloat64) {
      if (output_type & tUint32) {
        op = machine()->ChangeFloat64ToUint32();
      } else if (output_type & tInt32) {
        op = machine()->ChangeFloat64ToInt32();
      } else {
        return TypeError(node, output_type, rWord32);
      }
    } else if (output_type & rTagged) {
      if (output_type & tUint32) {
        op = simplified()->ChangeTaggedToUint32();
      } else if (output_type & tInt32) {
        op = simplified()->ChangeTaggedToInt32();
      } else {
        return TypeError(node, output_type, rWord32);
      }
    } else if (output_type & rBit) {
      return node;  // Sloppy comparison -> word32.
    } else {
      return TypeError(node, output_type, rWord32);
    }
    return jsgraph()->graph()->NewNode(op, node);
  }

  Node* GetBitRepresentationFor(Node* node, RepTypeUnion output_type) {
    // Eagerly fold representation changes for constants.
    switch (node->opcode()) {
      case IrOpcode::kInt32Constant: {
        int32_t value = ValueOf<int32_t>(node->op());
        if (value == 0 || value == 1) return node;
        return jsgraph()->OneConstant();  // value != 0
      }
      case IrOpcode::kHeapConstant: {
        Handle<Object> handle = ValueOf<Handle<Object> >(node->op());
        DCHECK(*handle == isolate()->heap()->true_value() ||
               *handle == isolate()->heap()->false_value());
        return jsgraph()->Int32Constant(
            *handle == isolate()->heap()->true_value() ? 1 : 0);
      }
      default:
        break;
    }
    // Select the correct X -> Bit operator.
    Operator* op;
    if (output_type & rWord32) {
      return node;  // No change necessary.
    } else if (output_type & rWord64) {
      return node;  // TODO(titzer): No change necessary, on 64-bit.
    } else if (output_type & rTagged) {
      op = simplified()->ChangeBoolToBit();
    } else {
      return TypeError(node, output_type, rBit);
    }
    return jsgraph()->graph()->NewNode(op, node);
  }

  Node* GetWord64RepresentationFor(Node* node, RepTypeUnion output_type) {
    if (output_type & rBit) {
      return node;  // Sloppy comparison -> word64
    }
    // Can't really convert Word64 to anything else. Purported to be internal.
    return TypeError(node, output_type, rWord64);
  }

  static RepType TypeForMachineType(MachineType rep) {
    // TODO(titzer): merge MachineType and RepType.
    switch (rep) {
      case kMachineWord8:
        return rWord32;
      case kMachineWord16:
        return rWord32;
      case kMachineWord32:
        return rWord32;
      case kMachineWord64:
        return rWord64;
      case kMachineFloat64:
        return rFloat64;
      case kMachineTagged:
        return rTagged;
      default:
        UNREACHABLE();
        return static_cast<RepType>(0);
    }
  }

  Operator* Int32OperatorFor(IrOpcode::Value opcode) {
    switch (opcode) {
      case IrOpcode::kNumberAdd:
        return machine()->Int32Add();
      case IrOpcode::kNumberSubtract:
        return machine()->Int32Sub();
      case IrOpcode::kNumberEqual:
        return machine()->Word32Equal();
      case IrOpcode::kNumberLessThan:
        return machine()->Int32LessThan();
      case IrOpcode::kNumberLessThanOrEqual:
        return machine()->Int32LessThanOrEqual();
      default:
        UNREACHABLE();
        return NULL;
    }
  }

  Operator* Uint32OperatorFor(IrOpcode::Value opcode) {
    switch (opcode) {
      case IrOpcode::kNumberAdd:
        return machine()->Int32Add();
      case IrOpcode::kNumberSubtract:
        return machine()->Int32Sub();
      case IrOpcode::kNumberEqual:
        return machine()->Word32Equal();
      case IrOpcode::kNumberLessThan:
        return machine()->Uint32LessThan();
      case IrOpcode::kNumberLessThanOrEqual:
        return machine()->Uint32LessThanOrEqual();
      default:
        UNREACHABLE();
        return NULL;
    }
  }

  Operator* Float64OperatorFor(IrOpcode::Value opcode) {
    switch (opcode) {
      case IrOpcode::kNumberAdd:
        return machine()->Float64Add();
      case IrOpcode::kNumberSubtract:
        return machine()->Float64Sub();
      case IrOpcode::kNumberMultiply:
        return machine()->Float64Mul();
      case IrOpcode::kNumberDivide:
        return machine()->Float64Div();
      case IrOpcode::kNumberModulus:
        return machine()->Float64Mod();
      case IrOpcode::kNumberEqual:
        return machine()->Float64Equal();
      case IrOpcode::kNumberLessThan:
        return machine()->Float64LessThan();
      case IrOpcode::kNumberLessThanOrEqual:
        return machine()->Float64LessThanOrEqual();
      default:
        UNREACHABLE();
        return NULL;
    }
  }

  RepType TypeForField(const FieldAccess& access) {
    RepType tElement = static_cast<RepType>(0);  // TODO(titzer)
    RepType rElement = TypeForMachineType(access.representation);
    return static_cast<RepType>(tElement | rElement);
  }

  RepType TypeForElement(const ElementAccess& access) {
    RepType tElement = static_cast<RepType>(0);  // TODO(titzer)
    RepType rElement = TypeForMachineType(access.representation);
    return static_cast<RepType>(tElement | rElement);
  }

  RepType TypeForBasePointer(const FieldAccess& access) {
    if (access.tag() != 0) return static_cast<RepType>(tAny | rTagged);
    return kPointerSize == 8 ? rWord64 : rWord32;
  }

  RepType TypeForBasePointer(const ElementAccess& access) {
    if (access.tag() != 0) return static_cast<RepType>(tAny | rTagged);
    return kPointerSize == 8 ? rWord64 : rWord32;
  }

  RepType TypeFromUpperBound(Type* type) {
    if (type->Is(Type::None()))
      return tAny;  // TODO(titzer): should be an error
    if (type->Is(Type::Signed32())) return tInt32;
    if (type->Is(Type::Unsigned32())) return tUint32;
    if (type->Is(Type::Number())) return tNumber;
    if (type->Is(Type::Boolean())) return tBool;
    return tAny;
  }

 private:
  JSGraph* jsgraph_;
  SimplifiedOperatorBuilder* simplified_;
  MachineOperatorBuilder* machine_;
  Isolate* isolate_;

  friend class RepresentationChangerTester;  // accesses the below fields.

  bool testing_type_errors_;  // If {true}, don't abort on a type error.
  bool type_error_;           // Set when a type error is detected.

  Node* TypeError(Node* node, RepTypeUnion output_type, RepTypeUnion use) {
    type_error_ = true;
    if (!testing_type_errors_) {
      char buf1[REP_TYPE_STRLEN];
      char buf2[REP_TYPE_STRLEN];
      RenderRepTypeUnion(buf1, output_type);
      RenderRepTypeUnion(buf2, use);
      V8_Fatal(__FILE__, __LINE__,
               "RepresentationChangerError: node #%d:%s of rep"
               "%s cannot be changed to rep%s",
               node->id(), node->op()->mnemonic(), buf1, buf2);
    }
    return node;
  }

  JSGraph* jsgraph() { return jsgraph_; }
  Isolate* isolate() { return isolate_; }
  SimplifiedOperatorBuilder* simplified() { return simplified_; }
  MachineOperatorBuilder* machine() { return machine_; }
};
}
}
}  // namespace v8::internal::compiler

#endif  // V8_COMPILER_REPRESENTATION_CHANGE_H_