blob: 94225bb2ef2d0c09e83f12acef2ca0aa4ccd8ca4 [file] [log] [blame]
// 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_