[turbofan] Add optional runtime checks for range types

This CL adds the --assert-types flag to d8, which is intended to
insert additional runtime checks after typed nodes, verifying the
validity of our typing rules. So far, only range types are checked.

Thanks to Neil Patil for suggesting something similar.

R=neis@chromium.org, tebbi@chromium.org

Change-Id: I5eb2c482235ec8cd07ee802ca7c12c86c2d3dc40
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1678372
Commit-Queue: Georg Schmid <gsps@google.com>
Reviewed-by: Georg Neis <neis@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62664}
This commit is contained in:
Georg Schmid 2019-07-12 09:47:16 +02:00 committed by Commit Bot
parent d7479d79c7
commit 2e82ead865
16 changed files with 211 additions and 1 deletions

View File

@ -1877,6 +1877,8 @@ v8_compiler_sources = [
"src/compiler/state-values-utils.h",
"src/compiler/store-store-elimination.cc",
"src/compiler/store-store-elimination.h",
"src/compiler/add-type-assertions-reducer.cc",
"src/compiler/add-type-assertions-reducer.h",
"src/compiler/type-cache.cc",
"src/compiler/type-cache.h",
"src/compiler/type-narrowing-reducer.cc",

View File

@ -1682,6 +1682,7 @@ extern operator '==' macro Word32Equal(bool, bool): bool;
extern operator '!=' macro Word32NotEqual(bool, bool): bool;
extern operator '+' macro Float64Add(float64, float64): float64;
extern operator '-' macro Float64Sub(float64, float64): float64;
extern operator '+' macro NumberAdd(Number, Number): Number;
extern operator '-' macro NumberSub(Number, Number): Number;
@ -1734,6 +1735,8 @@ extern macro TaggedIsNotSmi(Object): bool;
extern macro TaggedIsPositiveSmi(Object): bool;
extern macro IsValidPositiveSmi(intptr): bool;
extern macro IsInteger(HeapNumber): bool;
extern macro HeapObjectToJSDataView(HeapObject): JSDataView
labels CastError;
extern macro HeapObjectToJSProxy(HeapObject): JSProxy
@ -3070,3 +3073,41 @@ macro VerifiedUnreachable(): never {
StaticAssert(false);
unreachable;
}
macro Float64IsSomeInfinity(value: float64): bool {
if (value == V8_INFINITY) {
return true;
}
return value == (Convert<float64>(0) - V8_INFINITY);
}
@export
macro IsIntegerOrSomeInfinity(o: Object): bool {
typeswitch (o) {
case (Smi): {
return true;
}
case (hn: HeapNumber): {
if (Float64IsSomeInfinity(Convert<float64>(hn))) {
return true;
}
return IsInteger(hn);
}
case (Object): {
return false;
}
}
}
builtin CheckNumberInRange(implicit context: Context)(
value: Number, min: Number, max: Number): Undefined {
if (IsIntegerOrSomeInfinity(value) && min <= value && value <= max) {
return Undefined;
} else {
Print('Range type assertion failed! (value/min/max)');
Print(value);
Print(min);
Print(max);
unreachable;
}
}

View File

@ -0,0 +1,51 @@
// Copyright 2019 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/add-type-assertions-reducer.h"
#include "src/compiler/node-properties.h"
namespace v8 {
namespace internal {
namespace compiler {
AddTypeAssertionsReducer::AddTypeAssertionsReducer(Editor* editor,
JSGraph* jsgraph, Zone* zone)
: AdvancedReducer(editor),
jsgraph_(jsgraph),
visited_(jsgraph->graph()->NodeCount(), zone) {}
AddTypeAssertionsReducer::~AddTypeAssertionsReducer() = default;
Reduction AddTypeAssertionsReducer::Reduce(Node* node) {
if (node->opcode() == IrOpcode::kAssertType ||
node->opcode() == IrOpcode::kPhi || !NodeProperties::IsTyped(node) ||
visited_.Get(node)) {
return NoChange();
}
visited_.Set(node, true);
Type type = NodeProperties::GetType(node);
if (!type.IsRange()) {
return NoChange();
}
Node* assertion = graph()->NewNode(simplified()->AssertType(type), node);
NodeProperties::SetType(assertion, type);
for (Edge edge : node->use_edges()) {
Node* const user = edge.from();
DCHECK(!user->IsDead());
if (NodeProperties::IsValueEdge(edge) && user != assertion) {
edge.UpdateTo(assertion);
Revisit(user);
}
}
return NoChange();
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,45 @@
// Copyright 2019 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_ADD_TYPE_ASSERTIONS_REDUCER_H_
#define V8_COMPILER_ADD_TYPE_ASSERTIONS_REDUCER_H_
#include "src/common/globals.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-aux-data.h"
#include "src/compiler/simplified-operator.h"
namespace v8 {
namespace internal {
namespace compiler {
class V8_EXPORT_PRIVATE AddTypeAssertionsReducer final
: public NON_EXPORTED_BASE(AdvancedReducer) {
public:
AddTypeAssertionsReducer(Editor* editor, JSGraph* jsgraph, Zone* zone);
~AddTypeAssertionsReducer() final;
const char* reducer_name() const override {
return "AddTypeAssertionsReducer";
}
Reduction Reduce(Node* node) final;
private:
JSGraph* const jsgraph_;
NodeAuxData<bool> visited_;
Graph* graph() { return jsgraph_->graph(); }
SimplifiedOperatorBuilder* simplified() { return jsgraph_->simplified(); }
DISALLOW_COPY_AND_ASSIGN(AddTypeAssertionsReducer);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_ADD_TYPE_ASSERTIONS_REDUCER_H_

View File

@ -194,6 +194,7 @@ class EffectControlLinearizer {
void LowerTransitionAndStoreNumberElement(Node* node);
void LowerTransitionAndStoreNonNumberElement(Node* node);
void LowerRuntimeAbort(Node* node);
Node* LowerAssertType(Node* node);
Node* LowerConvertReceiver(Node* node);
Node* LowerDateNow(Node* node);
@ -1267,6 +1268,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kRuntimeAbort:
LowerRuntimeAbort(node);
break;
case IrOpcode::kAssertType:
result = LowerAssertType(node);
break;
case IrOpcode::kConvertReceiver:
result = LowerConvertReceiver(node);
break;
@ -5347,6 +5351,30 @@ void EffectControlLinearizer::LowerRuntimeAbort(Node* node) {
__ Int32Constant(1), __ NoContextConstant());
}
Node* EffectControlLinearizer::LowerAssertType(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kAssertType);
Type type = OpParameter<Type>(node->op());
DCHECK(type.IsRange());
auto range = type.AsRange();
Node* const input = node->InputAt(0);
Node* const min = __ NumberConstant(range->Min());
Node* const max = __ NumberConstant(range->Max());
{
Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kCheckNumberInRange);
Operator::Properties const properties = node->op()->properties();
CallDescriptor::Flags const flags = CallDescriptor::kNoFlags;
auto call_descriptor = Linkage::GetStubCallDescriptor(
graph()->zone(), callable.descriptor(),
callable.descriptor().GetStackParameterCount(), flags, properties);
__ Call(call_descriptor, __ HeapConstant(callable.code()), input, min, max,
__ NoContextConstant());
return input;
}
}
Node* EffectControlLinearizer::LowerConvertReceiver(Node* node) {
ConvertReceiverMode const mode = ConvertReceiverModeOf(node->op());
Node* value = node->InputAt(0);

View File

@ -52,6 +52,9 @@ Node* GraphAssembler::HeapConstant(Handle<HeapObject> object) {
return jsgraph()->HeapConstant(object);
}
Node* GraphAssembler::NumberConstant(double value) {
return jsgraph()->Constant(value);
}
Node* GraphAssembler::ExternalConstant(ExternalReference ref) {
return jsgraph()->ExternalConstant(ref);

View File

@ -200,6 +200,7 @@ class GraphAssembler {
Node* Float64Constant(double value);
Node* Projection(int index, Node* value);
Node* HeapConstant(Handle<HeapObject> object);
Node* NumberConstant(double value);
Node* CEntryStubConstant(int result_size);
Node* ExternalConstant(ExternalReference ref);

View File

@ -471,6 +471,7 @@
V(FindOrderedHashMapEntryForInt32Key) \
V(PoisonIndex) \
V(RuntimeAbort) \
V(AssertType) \
V(DateNow)
#define SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(V) V(SpeculativeBigIntAdd)

View File

@ -16,6 +16,7 @@
#include "src/codegen/compiler.h"
#include "src/codegen/optimized-compilation-info.h"
#include "src/codegen/register-configuration.h"
#include "src/compiler/add-type-assertions-reducer.h"
#include "src/compiler/backend/code-generator.h"
#include "src/compiler/backend/frame-elider.h"
#include "src/compiler/backend/instruction-selector.h"
@ -1421,6 +1422,19 @@ struct EscapeAnalysisPhase {
}
};
struct TypeAssertionsPhase {
static const char* phase_name() { return "V8.TFTypeAssertions"; }
void Run(PipelineData* data, Zone* temp_zone) {
GraphReducer graph_reducer(temp_zone, data->graph(),
data->jsgraph()->Dead());
AddTypeAssertionsReducer type_assertions(&graph_reducer, data->jsgraph(),
temp_zone);
AddReducer(data, &graph_reducer, &type_assertions);
graph_reducer.ReduceGraph();
}
};
struct SimplifiedLoweringPhase {
static const char* phase_name() { return "V8.TFSimplifiedLowering"; }
@ -2230,6 +2244,11 @@ bool PipelineImpl::OptimizeGraph(Linkage* linkage) {
RunPrintAndVerify(EscapeAnalysisPhase::phase_name());
}
if (FLAG_assert_types) {
Run<TypeAssertionsPhase>();
RunPrintAndVerify(TypeAssertionsPhase::phase_name());
}
// Perform simplified lowering. This has to run w/o the Typer decorator,
// because we cannot compute meaningful types anyways, and the computed types
// might even conflict with the representation/truncation logic.

View File

@ -3465,6 +3465,9 @@ class RepresentationSelector {
return SetOutput(node, MachineRepresentation::kNone);
case IrOpcode::kStaticAssert:
return VisitUnop(node, UseInfo::Any(), MachineRepresentation::kTagged);
case IrOpcode::kAssertType:
return VisitUnop(node, UseInfo::AnyTagged(),
MachineRepresentation::kTagged);
default:
FATAL(
"Representation inference: unsupported opcode %i (%s), node #%i\n.",

View File

@ -1231,6 +1231,13 @@ const Operator* SimplifiedOperatorBuilder::BigIntAsUintN(int bits) {
"BigIntAsUintN", 1, 0, 0, 1, 0, 0, bits);
}
const Operator* SimplifiedOperatorBuilder::AssertType(Type type) {
DCHECK(type.IsRange());
return new (zone()) Operator1<Type>(IrOpcode::kAssertType,
Operator::kNoThrow | Operator::kNoDeopt,
"AssertType", 1, 0, 0, 1, 0, 0, type);
}
const Operator* SimplifiedOperatorBuilder::CheckIf(
DeoptimizeReason reason, const VectorSlotPair& feedback) {
if (!feedback.IsValid()) {

View File

@ -888,6 +888,9 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
// Abort (for terminating execution on internal error).
const Operator* RuntimeAbort(AbortReason reason);
// Abort if the value input does not inhabit the given type
const Operator* AssertType(Type type);
const Operator* DateNow();
private:

View File

@ -2348,6 +2348,8 @@ Type Typer::Visitor::TypeFindOrderedHashMapEntryForInt32Key(Node* node) {
Type Typer::Visitor::TypeRuntimeAbort(Node* node) { UNREACHABLE(); }
Type Typer::Visitor::TypeAssertType(Node* node) { UNREACHABLE(); }
// Heap constants.
Type Typer::Visitor::TypeConstant(Handle<Object> value) {

View File

@ -1544,6 +1544,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
case IrOpcode::kCheckedTaggedToCompressedSigned:
case IrOpcode::kCheckedTaggedToCompressedPointer:
case IrOpcode::kCheckedTruncateTaggedToWord32:
case IrOpcode::kAssertType:
break;
case IrOpcode::kCheckFloat64Hole:

View File

@ -318,6 +318,9 @@ DEFINE_BOOL(future, FUTURE_BOOL,
DEFINE_IMPLICATION(future, write_protect_code_memory)
DEFINE_BOOL(assert_types, false,
"generate runtime type assertions to test the typer")
// Flags for experimental implementation features.
DEFINE_BOOL(allocation_site_pretenuring, true,
"pretenure with allocation sites")

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
// Flags: --allow-natives-syntax --no-assert-types
// Check that constant-folding of arithmetic results in identical nodes.
(function() {