[turbofan] Introduce an ExternalPointer type.

This adds a new ExternalPointer type, which is an Internal type that is
used for ExternalReferences and other pointer values, like the pointers
into the asm.js heap. It also adds a PointerConstant operator, which we
use to represents these raw constants (we can probably remove that
particular operator again once WebAssembly ships with the validator).

R=mvstanton@chromium.org
BUG=v8:5267,v8:5270

Review-Url: https://codereview.chromium.org/2494753003
Cr-Commit-Position: refs/heads/master@{#40923}
This commit is contained in:
bmeurer 2016-11-11 05:04:08 -08:00 committed by Commit bot
parent e3c7324a2e
commit 7d24f1aefa
21 changed files with 120 additions and 110 deletions

View File

@ -357,7 +357,7 @@ FieldAccess AccessBuilder::ForFixedTypedArrayBaseExternalPointer() {
FieldAccess access = {kTaggedBase,
FixedTypedArrayBase::kExternalPointerOffset,
MaybeHandle<Name>(),
Type::OtherInternal(),
Type::ExternalPointer(),
MachineType::Pointer(),
kNoWriteBarrier};
return access;
@ -512,7 +512,7 @@ FieldAccess AccessBuilder::ForExternalStringResourceData() {
FieldAccess access = {kTaggedBase,
ExternalString::kResourceDataOffset,
Handle<Name>(),
Type::OtherInternal(),
Type::ExternalPointer(),
MachineType::Pointer(),
kNoWriteBarrier};
return access;

View File

@ -3780,7 +3780,7 @@ Node* AstGraphBuilder::BuildReturn(Node* return_value) {
return_value =
NewNode(javascript()->CallRuntime(Runtime::kTraceExit), return_value);
}
Node* pop_node = jsgraph()->Int32Constant(0);
Node* pop_node = jsgraph()->ZeroConstant();
Node* control = NewNode(common()->Return(), pop_node, return_value);
UpdateControlDependencyToLeaveFunction(control);
return control;

View File

@ -1734,7 +1734,7 @@ void BytecodeGraphBuilder::VisitStackCheck() {
void BytecodeGraphBuilder::VisitReturn() {
BuildLoopExitsForFunctionExit();
Node* pop_node = jsgraph()->Int32Constant(0);
Node* pop_node = jsgraph()->ZeroConstant();
Node* control =
NewNode(common()->Return(), pop_node, environment()->LookupAccumulator());
MergeControlToLeaveFunction(control);

View File

@ -859,9 +859,12 @@ void CodeGenerator::AddTranslationForOperand(Translation* translation,
handle(reinterpret_cast<Smi*>(constant.ToInt32()), isolate());
DCHECK(constant_object->IsSmi());
} else {
// TODO(jarin,bmeurer): We currently pass in raw pointers to the
// JSFunction::entry here. We should really consider fixing this.
DCHECK(type == MachineType::Int32() ||
type == MachineType::Uint32() ||
type.representation() == MachineRepresentation::kBit ||
type.representation() == MachineRepresentation::kWord32 ||
type.representation() == MachineRepresentation::kNone);
DCHECK(type.representation() != MachineRepresentation::kNone ||
constant.ToInt32() == FrameStateDescriptor::kImpossibleValue);
@ -873,7 +876,10 @@ void CodeGenerator::AddTranslationForOperand(Translation* translation,
case Constant::kInt64:
// When pointers are 8 bytes, we can use int64 constants to represent
// Smis.
DCHECK(type.representation() == MachineRepresentation::kTagged ||
// TODO(jarin,bmeurer): We currently pass in raw pointers to the
// JSFunction::entry here. We should really consider fixing this.
DCHECK(type.representation() == MachineRepresentation::kWord64 ||
type.representation() == MachineRepresentation::kTagged ||
type.representation() == MachineRepresentation::kTaggedSigned);
DCHECK_EQ(8, kPointerSize);
constant_object =

View File

@ -45,6 +45,10 @@ class CommonNodeCache final {
Node** FindExternalConstant(ExternalReference value);
Node** FindPointerConstant(intptr_t value) {
return pointer_constants_.Find(zone(), value);
}
Node** FindNumberConstant(double value) {
// We canonicalize double constants at the bit representation level.
return number_constants_.Find(zone(), bit_cast<int64_t>(value));
@ -73,6 +77,7 @@ class CommonNodeCache final {
Int32NodeCache float32_constants_;
Int64NodeCache float64_constants_;
IntPtrNodeCache external_constants_;
IntPtrNodeCache pointer_constants_;
Int64NodeCache number_constants_;
IntPtrNodeCache heap_constants_;
RelocInt32NodeCache relocatable_int32_constants_;

View File

@ -880,6 +880,13 @@ const Operator* CommonOperatorBuilder::NumberConstant(volatile double value) {
value); // parameter
}
const Operator* CommonOperatorBuilder::PointerConstant(intptr_t value) {
return new (zone()) Operator1<intptr_t>( // --
IrOpcode::kPointerConstant, Operator::kPure, // opcode
"PointerConstant", // name
0, 0, 0, 1, 0, 0, // counts
value); // parameter
}
const Operator* CommonOperatorBuilder::HeapConstant(
const Handle<HeapObject>& value) {

View File

@ -224,6 +224,7 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
const Operator* Float64Constant(volatile double);
const Operator* ExternalConstant(const ExternalReference&);
const Operator* NumberConstant(volatile double);
const Operator* PointerConstant(intptr_t);
const Operator* HeapConstant(const Handle<HeapObject>&);
const Operator* RelocatableInt32Constant(int32_t value,

View File

@ -674,8 +674,8 @@ Reduction JSCreateLowering::ReduceJSCreateClosure(Node* node) {
handle(Map::cast(native_context()->get(function_map_index)), isolate()));
// Note that it is only safe to embed the raw entry point of the compile
// lazy stub into the code, because that stub is immortal and immovable.
Node* compile_entry = jsgraph()->IntPtrConstant(reinterpret_cast<intptr_t>(
jsgraph()->isolate()->builtins()->CompileLazy()->entry()));
Node* compile_entry = jsgraph()->PointerConstant(
jsgraph()->isolate()->builtins()->CompileLazy()->entry());
Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant();
Node* empty_literals_array = jsgraph()->EmptyLiteralsArrayConstant();
Node* the_hole = jsgraph()->TheHoleConstant();

View File

@ -242,6 +242,13 @@ Node* JSGraph::Float64Constant(double value) {
return *loc;
}
Node* JSGraph::PointerConstant(intptr_t value) {
Node** loc = cache_.FindPointerConstant(value);
if (*loc == nullptr) {
*loc = graph()->NewNode(common()->PointerConstant(value));
}
return *loc;
}
Node* JSGraph::ExternalConstant(ExternalReference reference) {
Node** loc = cache_.FindExternalConstant(reference);

View File

@ -108,10 +108,6 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) {
return machine()->Is32() ? Int32Constant(static_cast<int32_t>(value))
: Int64Constant(static_cast<int64_t>(value));
}
template <typename T>
Node* PointerConstant(T* value) {
return IntPtrConstant(bit_cast<intptr_t>(value));
}
Node* RelocatableInt32Constant(int32_t value, RelocInfo::Mode rmode);
Node* RelocatableInt64Constant(int64_t value, RelocInfo::Mode rmode);
@ -123,6 +119,13 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) {
// Creates a Float64Constant node, usually canonicalized.
Node* Float64Constant(double value);
// Creates a PointerConstant node (asm.js only).
Node* PointerConstant(intptr_t value);
template <typename T>
Node* PointerConstant(T* value) {
return PointerConstant(bit_cast<intptr_t>(value));
}
// Creates an ExternalConstant node, usually canonicalized.
Node* ExternalConstant(ExternalReference ref);
Node* ExternalConstant(Runtime::FunctionId function_id);

View File

@ -41,6 +41,7 @@
V(Float64Constant) \
V(ExternalConstant) \
V(NumberConstant) \
V(PointerConstant) \
V(HeapConstant) \
V(RelocatableInt32Constant) \
V(RelocatableInt64Constant)

View File

@ -858,26 +858,8 @@ class RepresentationSelector {
return MachineRepresentation::kTagged;
} else if (type->Is(Type::Number())) {
return MachineRepresentation::kFloat64;
} else if (type->Is(Type::Internal())) {
// We mark (u)int64 as Type::Internal.
// TODO(jarin) This is a workaround for our lack of (u)int64
// types. This can be removed once we can represent (u)int64
// unambiguously. (At the moment internal objects, such as the hole,
// are also Type::Internal()).
bool is_word64 = GetInfo(node->InputAt(0))->representation() ==
MachineRepresentation::kWord64;
#ifdef DEBUG
if (node->opcode() != IrOpcode::kTypeGuard) {
// Check that all the inputs agree on being Word64.
DCHECK_EQ(IrOpcode::kPhi, node->opcode()); // This only works for phis.
for (int i = 1; i < node->op()->ValueInputCount(); i++) {
DCHECK_EQ(is_word64, GetInfo(node->InputAt(i))->representation() ==
MachineRepresentation::kWord64);
}
}
#endif
return is_word64 ? MachineRepresentation::kWord64
: MachineRepresentation::kTagged;
} else if (type->Is(Type::ExternalPointer())) {
return MachineType::PointerRepresentation();
}
return MachineRepresentation::kTagged;
}
@ -1015,7 +997,15 @@ class RepresentationSelector {
void VisitObjectState(Node* node) {
if (propagate()) {
for (int i = 0; i < node->InputCount(); i++) {
EnqueueInput(node, i, UseInfo::Any());
Node* input = node->InputAt(i);
Type* input_type = TypeOf(input);
// TODO(turbofan): Special treatment for ExternalPointer here,
// to avoid incompatible truncations. We really need a story
// for the JSFunction::entry field.
UseInfo use_info = input_type->Is(Type::ExternalPointer())
? UseInfo::PointerInt()
: UseInfo::Any();
EnqueueInput(node, i, use_info);
}
} else if (lower()) {
Zone* zone = jsgraph_->zone();
@ -1026,15 +1016,22 @@ class RepresentationSelector {
Node* input = node->InputAt(i);
NodeInfo* input_info = GetInfo(input);
Type* input_type = TypeOf(input);
MachineRepresentation rep = input_type->IsInhabited()
? input_info->representation()
: MachineRepresentation::kNone;
MachineType machine_type(rep, DeoptValueSemanticOf(input_type));
DCHECK(machine_type.representation() !=
MachineRepresentation::kWord32 ||
machine_type.semantic() == MachineSemantic::kInt32 ||
machine_type.semantic() == MachineSemantic::kUint32);
(*types)[i] = machine_type;
// TODO(turbofan): Special treatment for ExternalPointer here,
// to avoid incompatible truncations. We really need a story
// for the JSFunction::entry field.
if (input_type->Is(Type::ExternalPointer())) {
(*types)[i] = MachineType::Pointer();
} else {
MachineRepresentation rep = input_type->IsInhabited()
? input_info->representation()
: MachineRepresentation::kNone;
MachineType machine_type(rep, DeoptValueSemanticOf(input_type));
DCHECK(machine_type.representation() !=
MachineRepresentation::kWord32 ||
machine_type.semantic() == MachineSemantic::kInt32 ||
machine_type.semantic() == MachineSemantic::kUint32);
(*types)[i] = machine_type;
}
}
NodeProperties::ChangeOp(node,
jsgraph_->common()->TypedObjectState(types));
@ -1384,6 +1381,14 @@ class RepresentationSelector {
return VisitLeaf(node, MachineRepresentation::kTagged);
case IrOpcode::kHeapConstant:
return VisitLeaf(node, MachineRepresentation::kTaggedPointer);
case IrOpcode::kPointerConstant: {
VisitLeaf(node, MachineType::PointerRepresentation());
if (lower()) {
intptr_t const value = OpParameter<intptr_t>(node);
DeferReplacement(node, lowering->jsgraph()->IntPtrConstant(value));
}
return;
}
case IrOpcode::kBranch:
ProcessInput(node, 0, UseInfo::Bool());

View File

@ -598,15 +598,13 @@ Type* Typer::Visitor::TypeRetain(Node* node) {
}
Type* Typer::Visitor::TypeInt32Constant(Node* node) {
double number = OpParameter<int32_t>(node);
return Type::Intersect(Type::Range(number, number, zone()),
Type::Integral32(), zone());
UNREACHABLE();
return nullptr;
}
Type* Typer::Visitor::TypeInt64Constant(Node* node) {
// TODO(rossberg): This actually seems to be a PointerConstant so far...
return Type::Internal(); // TODO(rossberg): Add int64 bitset type?
UNREACHABLE();
return nullptr;
}
Type* Typer::Visitor::TypeRelocatableInt32Constant(Node* node) {
@ -624,13 +622,11 @@ Type* Typer::Visitor::TypeFloat32Constant(Node* node) {
return nullptr;
}
Type* Typer::Visitor::TypeFloat64Constant(Node* node) {
UNREACHABLE();
return nullptr;
}
Type* Typer::Visitor::TypeNumberConstant(Node* node) {
double number = OpParameter<double>(node);
return Type::NewConstant(number, zone());
@ -640,11 +636,13 @@ Type* Typer::Visitor::TypeHeapConstant(Node* node) {
return TypeConstant(OpParameter<Handle<HeapObject>>(node));
}
Type* Typer::Visitor::TypeExternalConstant(Node* node) {
return Type::Internal();
return Type::ExternalPointer();
}
Type* Typer::Visitor::TypePointerConstant(Node* node) {
return Type::ExternalPointer();
}
Type* Typer::Visitor::TypeSelect(Node* node) {
return Type::Union(Operand(node, 1), Operand(node, 2), zone());

View File

@ -123,6 +123,7 @@ namespace compiler {
V(Function, 1u << 19) \
V(Hole, 1u << 20) \
V(OtherInternal, 1u << 21) \
V(ExternalPointer, 1u << 22) \
\
V(Signed31, kUnsigned30 | kNegative31) \
V(Signed32, kSigned31 | kOtherUnsigned31 | kOtherSigned32) \
@ -160,7 +161,7 @@ namespace compiler {
V(StringOrReceiver, kString | kReceiver) \
V(Unique, kBoolean | kUniqueName | kNull | kUndefined | \
kReceiver) \
V(Internal, kHole | kOtherInternal) \
V(Internal, kHole | kExternalPointer | kOtherInternal) \
V(NonInternal, kPrimitive | kReceiver) \
V(NonNumber, kUnique | kString | kInternal) \
V(Any, 0xfffffffeu)

View File

@ -333,40 +333,35 @@ void Verifier::Visitor::Check(Node* node) {
CheckTypeIs(node, Type::Any());
break;
}
case IrOpcode::kInt32Constant: // TODO(rossberg): rename Word32Constant?
// Constants have no inputs.
CHECK_EQ(0, input_count);
// Type is a 32 bit integer, signed or unsigned.
CheckTypeIs(node, Type::Integral32());
break;
case IrOpcode::kInt64Constant:
// Constants have no inputs.
CHECK_EQ(0, input_count);
// Type is internal.
// TODO(rossberg): Introduce proper Int64 type.
CheckTypeIs(node, Type::Internal());
break;
case IrOpcode::kInt32Constant: // TODO(turbofan): rename Word32Constant?
case IrOpcode::kInt64Constant: // TODO(turbofan): rename Word64Constant?
case IrOpcode::kFloat32Constant:
case IrOpcode::kFloat64Constant:
case IrOpcode::kRelocatableInt32Constant:
case IrOpcode::kRelocatableInt64Constant:
// Constants have no inputs.
CHECK_EQ(0, input_count);
// Type is empty.
CheckNotTyped(node);
break;
case IrOpcode::kNumberConstant:
// Constants have no inputs.
CHECK_EQ(0, input_count);
// Type is a number.
CheckTypeIs(node, Type::Number());
break;
case IrOpcode::kRelocatableInt32Constant:
case IrOpcode::kRelocatableInt64Constant:
CHECK_EQ(0, input_count);
break;
case IrOpcode::kHeapConstant:
// Constants have no inputs.
CHECK_EQ(0, input_count);
// Type is anything.
CheckTypeIs(node, Type::Any());
break;
case IrOpcode::kExternalConstant:
case IrOpcode::kPointerConstant:
// Constants have no inputs.
CHECK_EQ(0, input_count);
// Type is considered internal.
CheckTypeIs(node, Type::Internal());
// Type is an external pointer.
CheckTypeIs(node, Type::ExternalPointer());
break;
case IrOpcode::kOsrValue:
// OSR values have a value and a control input.

View File

@ -169,11 +169,6 @@ class JSTypedLoweringTester : public HandleAndZoneScope {
CHECK_EQ(effect, NodeProperties::GetEffectInput(use));
}
void CheckInt32Constant(int32_t expected, Node* result) {
CHECK_EQ(IrOpcode::kInt32Constant, result->opcode());
CHECK_EQ(expected, OpParameter<int32_t>(result));
}
void CheckNumberConstant(double expected, Node* result) {
CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
CHECK_EQ(expected, OpParameter<double>(result));
@ -694,7 +689,7 @@ TEST(RemoveToNumberEffects) {
JSTypedLoweringTester R;
Node* effect_use = NULL;
Node* zero = R.graph.NewNode(R.common.Int32Constant(0));
Node* zero = R.graph.NewNode(R.common.NumberConstant(0));
for (int i = 0; i < 10; i++) {
Node* p0 = R.Parameter(Type::Number());
Node* ton = R.Unop(R.javascript.ToNumber(), p0);

View File

@ -119,7 +119,7 @@ class EscapeAnalysisTest : public TypedGraphTest {
if (!control) {
control = control_;
}
Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* zero = graph()->NewNode(common()->NumberConstant(0));
return control_ = graph()->NewNode(common()->Return(), zero, value, effect,
control);
}

View File

@ -70,11 +70,6 @@ class JSTypedLoweringTest : public TypedGraphTest {
return buffer;
}
Matcher<Node*> IsIntPtrConstant(intptr_t value) {
return sizeof(value) == 4 ? IsInt32Constant(static_cast<int32_t>(value))
: IsInt64Constant(static_cast<int64_t>(value));
}
JSOperatorBuilder* javascript() { return &javascript_; }
private:
@ -569,7 +564,7 @@ TEST_F(JSTypedLoweringTest, JSLoadPropertyFromExternalTypedArray) {
EXPECT_THAT(
r.replacement(),
IsLoadBuffer(BufferAccess(type),
IsIntPtrConstant(bit_cast<intptr_t>(&backing_store[0])),
IsPointerConstant(bit_cast<intptr_t>(&backing_store[0])),
offset_matcher,
IsNumberConstant(array->byte_length()->Number()), effect,
control));
@ -605,7 +600,7 @@ TEST_F(JSTypedLoweringTest, JSLoadPropertyFromExternalTypedArrayWithSafeKey) {
EXPECT_THAT(
r.replacement(),
IsLoadElement(access,
IsIntPtrConstant(bit_cast<intptr_t>(&backing_store[0])),
IsPointerConstant(bit_cast<intptr_t>(&backing_store[0])),
key, effect, control));
}
}
@ -650,11 +645,11 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArray) {
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsStoreBuffer(BufferAccess(type),
IsIntPtrConstant(bit_cast<intptr_t>(&backing_store[0])),
offset_matcher,
IsNumberConstant(array->byte_length()->Number()), value,
effect, control));
IsStoreBuffer(
BufferAccess(type),
IsPointerConstant(bit_cast<intptr_t>(&backing_store[0])),
offset_matcher, IsNumberConstant(array->byte_length()->Number()),
value, effect, control));
}
}
}
@ -703,11 +698,11 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArrayWithConversion) {
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsStoreBuffer(BufferAccess(type),
IsIntPtrConstant(bit_cast<intptr_t>(&backing_store[0])),
offset_matcher,
IsNumberConstant(array->byte_length()->Number()),
value_matcher, effect_matcher, control_matcher));
IsStoreBuffer(
BufferAccess(type),
IsPointerConstant(bit_cast<intptr_t>(&backing_store[0])),
offset_matcher, IsNumberConstant(array->byte_length()->Number()),
value_matcher, effect_matcher, control_matcher));
}
}
}
@ -744,7 +739,7 @@ TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArrayWithSafeKey) {
EXPECT_THAT(
r.replacement(),
IsStoreElement(
access, IsIntPtrConstant(bit_cast<intptr_t>(&backing_store[0])),
access, IsPointerConstant(bit_cast<intptr_t>(&backing_store[0])),
key, value, effect, control));
}
}

View File

@ -1790,6 +1790,10 @@ Matcher<Node*> IsNumberConstant(const Matcher<double>& value_matcher) {
new IsConstantMatcher<double>(IrOpcode::kNumberConstant, value_matcher));
}
Matcher<Node*> IsPointerConstant(const Matcher<intptr_t>& value_matcher) {
return MakeMatcher(new IsConstantMatcher<intptr_t>(IrOpcode::kPointerConstant,
value_matcher));
}
Matcher<Node*> IsSelect(const Matcher<MachineRepresentation>& type_matcher,
const Matcher<Node*>& value0_matcher,

View File

@ -93,6 +93,7 @@ Matcher<Node*> IsFloat64Constant(const Matcher<double>& value_matcher);
Matcher<Node*> IsInt32Constant(const Matcher<int32_t>& value_matcher);
Matcher<Node*> IsInt64Constant(const Matcher<int64_t>& value_matcher);
Matcher<Node*> IsNumberConstant(const Matcher<double>& value_matcher);
Matcher<Node*> IsPointerConstant(const Matcher<intptr_t>& value_matcher);
Matcher<Node*> IsSelect(const Matcher<MachineRepresentation>& type_matcher,
const Matcher<Node*>& value0_matcher,
const Matcher<Node*>& value1_matcher,

View File

@ -373,20 +373,6 @@ TEST_BINARY_MONOTONICITY(Divide)
TEST_BINARY_MONOTONICITY(Modulus)
#undef TEST_BINARY_MONOTONICITY
//------------------------------------------------------------------------------
// Regression tests
TEST_F(TyperTest, TypeRegressInt32Constant) {
int values[] = {-5, 10};
for (auto i : values) {
Node* c = graph()->NewNode(common()->Int32Constant(i));
Type* type = NodeProperties::GetType(c);
EXPECT_TRUE(type->Is(NewRange(i, i)));
}
}
} // namespace compiler
} // namespace internal
} // namespace v8