blob: 03504035d715c0fa3466e94a13a2b3169beb98ad [file] [log] [blame]
// Copyright 2012 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.
#include "src/compiler/simplified-operator.h"
#include "src/base/lazy-instance.h"
#include "src/compiler/opcodes.h"
#include "src/compiler/operator.h"
#include "src/types.h"
namespace v8 {
namespace internal {
namespace compiler {
size_t hash_value(BaseTaggedness base_taggedness) {
return static_cast<uint8_t>(base_taggedness);
}
std::ostream& operator<<(std::ostream& os, BaseTaggedness base_taggedness) {
switch (base_taggedness) {
case kUntaggedBase:
return os << "untagged base";
case kTaggedBase:
return os << "tagged base";
}
UNREACHABLE();
return os;
}
MachineType BufferAccess::machine_type() const {
switch (external_array_type_) {
case kExternalUint8Array:
case kExternalUint8ClampedArray:
return MachineType::Uint8();
case kExternalInt8Array:
return MachineType::Int8();
case kExternalUint16Array:
return MachineType::Uint16();
case kExternalInt16Array:
return MachineType::Int16();
case kExternalUint32Array:
return MachineType::Uint32();
case kExternalInt32Array:
return MachineType::Int32();
case kExternalFloat32Array:
return MachineType::Float32();
case kExternalFloat64Array:
return MachineType::Float64();
}
UNREACHABLE();
return MachineType::None();
}
bool operator==(BufferAccess lhs, BufferAccess rhs) {
return lhs.external_array_type() == rhs.external_array_type();
}
bool operator!=(BufferAccess lhs, BufferAccess rhs) { return !(lhs == rhs); }
size_t hash_value(BufferAccess access) {
return base::hash<ExternalArrayType>()(access.external_array_type());
}
std::ostream& operator<<(std::ostream& os, BufferAccess access) {
switch (access.external_array_type()) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case kExternal##Type##Array: \
return os << #Type;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
}
UNREACHABLE();
return os;
}
BufferAccess const BufferAccessOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kLoadBuffer ||
op->opcode() == IrOpcode::kStoreBuffer);
return OpParameter<BufferAccess>(op);
}
bool operator==(FieldAccess const& lhs, FieldAccess const& rhs) {
// On purpose we don't include the write barrier kind here, as this method is
// really only relevant for eliminating loads and they don't care about the
// write barrier mode.
return lhs.base_is_tagged == rhs.base_is_tagged && lhs.offset == rhs.offset &&
lhs.machine_type == rhs.machine_type;
}
bool operator!=(FieldAccess const& lhs, FieldAccess const& rhs) {
return !(lhs == rhs);
}
size_t hash_value(FieldAccess const& access) {
// On purpose we don't include the write barrier kind here, as this method is
// really only relevant for eliminating loads and they don't care about the
// write barrier mode.
return base::hash_combine(access.base_is_tagged, access.offset,
access.machine_type);
}
std::ostream& operator<<(std::ostream& os, FieldAccess const& access) {
os << "[" << access.base_is_tagged << ", " << access.offset << ", ";
#ifdef OBJECT_PRINT
Handle<Name> name;
if (access.name.ToHandle(&name)) {
name->Print(os);
os << ", ";
}
#endif
access.type->PrintTo(os);
os << ", " << access.machine_type << ", " << access.write_barrier_kind << "]";
return os;
}
bool operator==(ElementAccess const& lhs, ElementAccess const& rhs) {
// On purpose we don't include the write barrier kind here, as this method is
// really only relevant for eliminating loads and they don't care about the
// write barrier mode.
return lhs.base_is_tagged == rhs.base_is_tagged &&
lhs.header_size == rhs.header_size &&
lhs.machine_type == rhs.machine_type;
}
bool operator!=(ElementAccess const& lhs, ElementAccess const& rhs) {
return !(lhs == rhs);
}
size_t hash_value(ElementAccess const& access) {
// On purpose we don't include the write barrier kind here, as this method is
// really only relevant for eliminating loads and they don't care about the
// write barrier mode.
return base::hash_combine(access.base_is_tagged, access.header_size,
access.machine_type);
}
std::ostream& operator<<(std::ostream& os, ElementAccess const& access) {
os << access.base_is_tagged << ", " << access.header_size << ", ";
access.type->PrintTo(os);
os << ", " << access.machine_type << ", " << access.write_barrier_kind;
return os;
}
const FieldAccess& FieldAccessOf(const Operator* op) {
DCHECK_NOT_NULL(op);
DCHECK(op->opcode() == IrOpcode::kLoadField ||
op->opcode() == IrOpcode::kStoreField);
return OpParameter<FieldAccess>(op);
}
const ElementAccess& ElementAccessOf(const Operator* op) {
DCHECK_NOT_NULL(op);
DCHECK(op->opcode() == IrOpcode::kLoadElement ||
op->opcode() == IrOpcode::kStoreElement);
return OpParameter<ElementAccess>(op);
}
Type* TypeOf(const Operator* op) {
DCHECK_EQ(IrOpcode::kTypeGuard, op->opcode());
return OpParameter<Type*>(op);
}
#define PURE_OP_LIST(V) \
V(BooleanNot, Operator::kNoProperties, 1) \
V(BooleanToNumber, Operator::kNoProperties, 1) \
V(NumberEqual, Operator::kCommutative, 2) \
V(NumberLessThan, Operator::kNoProperties, 2) \
V(NumberLessThanOrEqual, Operator::kNoProperties, 2) \
V(NumberAdd, Operator::kCommutative, 2) \
V(NumberSubtract, Operator::kNoProperties, 2) \
V(NumberMultiply, Operator::kCommutative, 2) \
V(NumberDivide, Operator::kNoProperties, 2) \
V(NumberModulus, Operator::kNoProperties, 2) \
V(NumberBitwiseOr, Operator::kCommutative, 2) \
V(NumberBitwiseXor, Operator::kCommutative, 2) \
V(NumberBitwiseAnd, Operator::kCommutative, 2) \
V(NumberShiftLeft, Operator::kNoProperties, 2) \
V(NumberShiftRight, Operator::kNoProperties, 2) \
V(NumberShiftRightLogical, Operator::kNoProperties, 2) \
V(NumberImul, Operator::kCommutative, 2) \
V(NumberClz32, Operator::kNoProperties, 1) \
V(NumberCeil, Operator::kNoProperties, 1) \
V(NumberFloor, Operator::kNoProperties, 1) \
V(NumberRound, Operator::kNoProperties, 1) \
V(NumberTrunc, Operator::kNoProperties, 1) \
V(NumberToInt32, Operator::kNoProperties, 1) \
V(NumberToUint32, Operator::kNoProperties, 1) \
V(NumberIsHoleNaN, Operator::kNoProperties, 1) \
V(StringToNumber, Operator::kNoProperties, 1) \
V(ChangeTaggedSignedToInt32, Operator::kNoProperties, 1) \
V(ChangeTaggedToInt32, Operator::kNoProperties, 1) \
V(ChangeTaggedToUint32, Operator::kNoProperties, 1) \
V(ChangeTaggedToFloat64, Operator::kNoProperties, 1) \
V(ChangeInt31ToTaggedSigned, Operator::kNoProperties, 1) \
V(ChangeInt32ToTagged, Operator::kNoProperties, 1) \
V(ChangeUint32ToTagged, Operator::kNoProperties, 1) \
V(ChangeFloat64ToTagged, Operator::kNoProperties, 1) \
V(ChangeTaggedToBit, Operator::kNoProperties, 1) \
V(ChangeBitToTagged, Operator::kNoProperties, 1) \
V(TruncateTaggedToWord32, Operator::kNoProperties, 1) \
V(ObjectIsCallable, Operator::kNoProperties, 1) \
V(ObjectIsNumber, Operator::kNoProperties, 1) \
V(ObjectIsReceiver, Operator::kNoProperties, 1) \
V(ObjectIsSmi, Operator::kNoProperties, 1) \
V(ObjectIsString, Operator::kNoProperties, 1) \
V(ObjectIsUndetectable, Operator::kNoProperties, 1) \
V(StringEqual, Operator::kCommutative, 2) \
V(StringLessThan, Operator::kNoProperties, 2) \
V(StringLessThanOrEqual, Operator::kNoProperties, 2)
struct SimplifiedOperatorGlobalCache final {
#define PURE(Name, properties, input_count) \
struct Name##Operator final : public Operator { \
Name##Operator() \
: Operator(IrOpcode::k##Name, Operator::kPure | properties, #Name, \
input_count, 0, 0, 1, 0, 0) {} \
}; \
Name##Operator k##Name;
PURE_OP_LIST(PURE)
#undef PURE
template <PretenureFlag kPretenure>
struct AllocateOperator final : public Operator1<PretenureFlag> {
AllocateOperator()
: Operator1<PretenureFlag>(IrOpcode::kAllocate, Operator::kNoThrow,
"Allocate", 1, 1, 1, 1, 1, 0, kPretenure) {}
};
AllocateOperator<NOT_TENURED> kAllocateNotTenuredOperator;
AllocateOperator<TENURED> kAllocateTenuredOperator;
#define BUFFER_ACCESS(Type, type, TYPE, ctype, size) \
struct LoadBuffer##Type##Operator final : public Operator1<BufferAccess> { \
LoadBuffer##Type##Operator() \
: Operator1<BufferAccess>(IrOpcode::kLoadBuffer, \
Operator::kNoThrow | Operator::kNoWrite, \
"LoadBuffer", 3, 1, 1, 1, 1, 0, \
BufferAccess(kExternal##Type##Array)) {} \
}; \
struct StoreBuffer##Type##Operator final : public Operator1<BufferAccess> { \
StoreBuffer##Type##Operator() \
: Operator1<BufferAccess>(IrOpcode::kStoreBuffer, \
Operator::kNoRead | Operator::kNoThrow, \
"StoreBuffer", 4, 1, 1, 0, 1, 0, \
BufferAccess(kExternal##Type##Array)) {} \
}; \
LoadBuffer##Type##Operator kLoadBuffer##Type; \
StoreBuffer##Type##Operator kStoreBuffer##Type;
TYPED_ARRAYS(BUFFER_ACCESS)
#undef BUFFER_ACCESS
};
static base::LazyInstance<SimplifiedOperatorGlobalCache>::type kCache =
LAZY_INSTANCE_INITIALIZER;
SimplifiedOperatorBuilder::SimplifiedOperatorBuilder(Zone* zone)
: cache_(kCache.Get()), zone_(zone) {}
#define GET_FROM_CACHE(Name, properties, input_count) \
const Operator* SimplifiedOperatorBuilder::Name() { return &cache_.k##Name; }
PURE_OP_LIST(GET_FROM_CACHE)
#undef GET_FROM_CACHE
const Operator* SimplifiedOperatorBuilder::ReferenceEqual(Type* type) {
return new (zone()) Operator(IrOpcode::kReferenceEqual,
Operator::kCommutative | Operator::kPure,
"ReferenceEqual", 2, 0, 0, 1, 0, 0);
}
const Operator* SimplifiedOperatorBuilder::TypeGuard(Type* type) {
class TypeGuardOperator final : public Operator1<Type*> {
public:
explicit TypeGuardOperator(Type* type)
: Operator1<Type*>( // --
IrOpcode::kTypeGuard, Operator::kPure, // opcode
"TypeGuard", // name
1, 0, 1, 1, 0, 0, // counts
type) {} // parameter
void PrintParameter(std::ostream& os) const final {
parameter()->PrintTo(os);
}
};
return new (zone()) TypeGuardOperator(type);
}
const Operator* SimplifiedOperatorBuilder::Allocate(PretenureFlag pretenure) {
switch (pretenure) {
case NOT_TENURED:
return &cache_.kAllocateNotTenuredOperator;
case TENURED:
return &cache_.kAllocateTenuredOperator;
}
UNREACHABLE();
return nullptr;
}
const Operator* SimplifiedOperatorBuilder::LoadBuffer(BufferAccess access) {
switch (access.external_array_type()) {
#define LOAD_BUFFER(Type, type, TYPE, ctype, size) \
case kExternal##Type##Array: \
return &cache_.kLoadBuffer##Type;
TYPED_ARRAYS(LOAD_BUFFER)
#undef LOAD_BUFFER
}
UNREACHABLE();
return nullptr;
}
const Operator* SimplifiedOperatorBuilder::StoreBuffer(BufferAccess access) {
switch (access.external_array_type()) {
#define STORE_BUFFER(Type, type, TYPE, ctype, size) \
case kExternal##Type##Array: \
return &cache_.kStoreBuffer##Type;
TYPED_ARRAYS(STORE_BUFFER)
#undef STORE_BUFFER
}
UNREACHABLE();
return nullptr;
}
#define ACCESS_OP_LIST(V) \
V(LoadField, FieldAccess, Operator::kNoWrite, 1, 1, 1) \
V(StoreField, FieldAccess, Operator::kNoRead, 2, 1, 0) \
V(LoadElement, ElementAccess, Operator::kNoWrite, 2, 1, 1) \
V(StoreElement, ElementAccess, Operator::kNoRead, 3, 1, 0)
#define ACCESS(Name, Type, properties, value_input_count, control_input_count, \
output_count) \
const Operator* SimplifiedOperatorBuilder::Name(const Type& access) { \
return new (zone()) \
Operator1<Type>(IrOpcode::k##Name, Operator::kNoThrow | properties, \
#Name, value_input_count, 1, control_input_count, \
output_count, 1, 0, access); \
}
ACCESS_OP_LIST(ACCESS)
#undef ACCESS
} // namespace compiler
} // namespace internal
} // namespace v8