mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 23:41:50 +00:00
a11d9f17fb
We might interrupt Object._objectHashCode(obj) helper to serve a vm-service request between checking for unset hash code and setting a new hash code. While serving this request we might set the hash code of [obj] (e.g. when generating heap snapshot or in Instance::PrintJSONImpl). Later Object._objectHashCode will call Object_setHash, which (on 64-bit platforms) assumes that hash field in the object header is set to 0 and uses bitwise-or to initialize it. This leads to a mismatch between hash code that the first invocation of _objectHashCode will return and the value stored in the header (because we OR non-zero value set by vm-service with a value which _objectHashCode intended to use as a hash). This CL changes Object_setHash to avoid overwriting or mangling the hash value if it was already set. We also fix hash code generation in vm-service to ensure that we only generate values which are valid Smis because the rest of the code expects that. TEST=pkg/front_end/test/incremental_compiler_leak_test.dart Cq-Include-Trybots: luci.dart.try:vm-kernel-linux-release-simarm64-try,vm-kernel-precomp-linux-release-simarm64-try Change-Id: Ica913af8bc1cfef0ad60a9e7504531ee4de53015 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/197400 Commit-Queue: Vyacheslav Egorov <vegorov@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com>
326 lines
11 KiB
C++
326 lines
11 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
// Class for intrinsifying functions.
|
|
|
|
#include "vm/compiler/intrinsifier.h"
|
|
|
|
#include "vm/compiler/assembler/assembler.h"
|
|
#include "vm/compiler/backend/flow_graph.h"
|
|
#include "vm/compiler/backend/flow_graph_compiler.h"
|
|
#include "vm/compiler/backend/il_printer.h"
|
|
#include "vm/compiler/backend/linearscan.h"
|
|
#include "vm/compiler/frontend/flow_graph_builder.h"
|
|
#include "vm/compiler/jit/compiler.h"
|
|
#include "vm/cpu.h"
|
|
#include "vm/flags.h"
|
|
#include "vm/object.h"
|
|
#include "vm/parser.h"
|
|
#include "vm/symbols.h"
|
|
|
|
namespace dart {
|
|
|
|
DEFINE_FLAG(bool, intrinsify, true, "Instrinsify when possible");
|
|
DEFINE_FLAG(bool, trace_intrinsifier, false, "Trace intrinsifier");
|
|
|
|
namespace compiler {
|
|
|
|
bool Intrinsifier::CanIntrinsify(const Function& function) {
|
|
if (FLAG_trace_intrinsifier) {
|
|
THR_Print("CanIntrinsify %s ->", function.ToQualifiedCString());
|
|
}
|
|
if (!FLAG_intrinsify) return false;
|
|
// TODO(regis): We do not need to explicitly filter generic functions here,
|
|
// unless there are errors we don't detect at link time. Revisit if necessary.
|
|
if (function.IsClosureFunction()) {
|
|
if (FLAG_trace_intrinsifier) {
|
|
THR_Print("No, closure function.\n");
|
|
}
|
|
return false;
|
|
}
|
|
// Can occur because of compile-all flag.
|
|
if (function.is_external()) {
|
|
if (FLAG_trace_intrinsifier) {
|
|
THR_Print("No, external function.\n");
|
|
}
|
|
return false;
|
|
}
|
|
if (!function.is_intrinsic() && !CanIntrinsifyFieldAccessor(function)) {
|
|
if (FLAG_trace_intrinsifier) {
|
|
THR_Print("No, not intrinsic function.\n");
|
|
}
|
|
return false;
|
|
}
|
|
switch (function.recognized_kind()) {
|
|
case MethodRecognizer::kInt64ArrayGetIndexed:
|
|
case MethodRecognizer::kInt64ArraySetIndexed:
|
|
case MethodRecognizer::kUint64ArrayGetIndexed:
|
|
case MethodRecognizer::kUint64ArraySetIndexed:
|
|
// TODO(ajcbik): consider 32-bit as well.
|
|
if (target::kBitsPerWord == 64) {
|
|
break;
|
|
}
|
|
if (FLAG_trace_intrinsifier) {
|
|
THR_Print("No, 64-bit int intrinsic on 32-bit platform.\n");
|
|
}
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
if (FLAG_trace_intrinsifier) {
|
|
THR_Print("Yes.\n");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Intrinsifier::CanIntrinsifyFieldAccessor(const Function& function) {
|
|
const bool is_getter = function.IsImplicitGetterFunction();
|
|
const bool is_setter = function.IsImplicitSetterFunction();
|
|
if (!is_getter && !is_setter) return false;
|
|
|
|
Field& field = Field::Handle(function.accessor_field());
|
|
ASSERT(!field.IsNull());
|
|
|
|
// The checks further down examine the field and its guard.
|
|
//
|
|
// In JIT mode we only intrinsify the field accessor if there is no active
|
|
// guard, meaning the state transition has reached its final `kDynamicCid`
|
|
// state (where it stays).
|
|
//
|
|
// If we intrinsify, the intrinsified code therefore does not depend on the
|
|
// field guard and we do not add it to the guarded fields via
|
|
// [ParsedFunction::AddToGuardedFields].
|
|
if (CompilerState::Current().should_clone_fields()) {
|
|
field = field.CloneFromOriginal();
|
|
}
|
|
|
|
// We only graph intrinsify implicit instance getters/setter for now.
|
|
if (!field.is_instance()) return false;
|
|
|
|
if (is_getter) {
|
|
// We don't support complex getter cases.
|
|
if (field.is_late() || field.needs_load_guard()) return false;
|
|
|
|
if (FlowGraphCompiler::IsPotentialUnboxedField(field)) {
|
|
if (function.HasUnboxedReturnValue()) {
|
|
// In AOT mode: Unboxed fields contain the unboxed value and can be
|
|
// returned in unboxed form.
|
|
ASSERT(FLAG_precompiled_mode);
|
|
} else {
|
|
// In JIT mode: Unboxed fields contain a mutable box which we cannot
|
|
// return.
|
|
return false;
|
|
}
|
|
} else {
|
|
// If the field is boxed, then we can either return the box directly or
|
|
// unbox it and return unboxed representation.
|
|
return true;
|
|
}
|
|
} else {
|
|
ASSERT(is_setter);
|
|
|
|
// We don't support complex setter cases.
|
|
if (field.is_final()) {
|
|
RELEASE_ASSERT(field.is_late());
|
|
return false;
|
|
}
|
|
|
|
// We only support cases where there is no need to check for argument types.
|
|
//
|
|
// Normally we have to check the parameter type.
|
|
ASSERT(function.NeedsArgumentTypeChecks());
|
|
// Covariant parameter types have to be checked, which we don't support.
|
|
if (field.is_covariant() || field.is_generic_covariant_impl()) return false;
|
|
|
|
// If the incoming value is unboxed we only support real unboxed fields to
|
|
// avoid the need for boxing (which we cannot do in the intrinsic).
|
|
if (function.HasUnboxedParameters()) {
|
|
ASSERT(FLAG_precompiled_mode);
|
|
if (!FlowGraphCompiler::IsUnboxedField(field)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// We don't support field guards in graph intrinsic stores.
|
|
if (!FLAG_precompiled_mode && field.guarded_cid() != kDynamicCid) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
struct IntrinsicDesc {
|
|
const char* class_name;
|
|
const char* function_name;
|
|
};
|
|
|
|
struct LibraryInstrinsicsDesc {
|
|
Library& library;
|
|
IntrinsicDesc* intrinsics;
|
|
};
|
|
|
|
#define DEFINE_INTRINSIC(class_name, function_name, destination, fp) \
|
|
{#class_name, #function_name},
|
|
|
|
// clang-format off
|
|
static IntrinsicDesc core_intrinsics[] = {
|
|
CORE_LIB_INTRINSIC_LIST(DEFINE_INTRINSIC)
|
|
CORE_INTEGER_LIB_INTRINSIC_LIST(DEFINE_INTRINSIC)
|
|
GRAPH_CORE_INTRINSICS_LIST(DEFINE_INTRINSIC)
|
|
{nullptr, nullptr},
|
|
};
|
|
|
|
static IntrinsicDesc math_intrinsics[] = {
|
|
MATH_LIB_INTRINSIC_LIST(DEFINE_INTRINSIC)
|
|
GRAPH_MATH_LIB_INTRINSIC_LIST(DEFINE_INTRINSIC)
|
|
{nullptr, nullptr},
|
|
};
|
|
|
|
static IntrinsicDesc typed_data_intrinsics[] = {
|
|
GRAPH_TYPED_DATA_INTRINSICS_LIST(DEFINE_INTRINSIC)
|
|
{nullptr, nullptr},
|
|
};
|
|
|
|
static IntrinsicDesc developer_intrinsics[] = {
|
|
DEVELOPER_LIB_INTRINSIC_LIST(DEFINE_INTRINSIC)
|
|
{nullptr, nullptr},
|
|
};
|
|
|
|
static IntrinsicDesc internal_intrinsics[] = {
|
|
INTERNAL_LIB_INTRINSIC_LIST(DEFINE_INTRINSIC)
|
|
{nullptr, nullptr},
|
|
};
|
|
// clang-format on
|
|
|
|
void Intrinsifier::InitializeState() {
|
|
Thread* thread = Thread::Current();
|
|
Zone* zone = thread->zone();
|
|
Library& lib = Library::Handle(zone);
|
|
Class& cls = Class::Handle(zone);
|
|
Function& func = Function::Handle(zone);
|
|
String& str = String::Handle(zone);
|
|
String& str2 = String::Handle(zone);
|
|
Error& error = Error::Handle(zone);
|
|
|
|
static const intptr_t kNumLibs = 5;
|
|
LibraryInstrinsicsDesc intrinsics[kNumLibs] = {
|
|
{Library::Handle(zone, Library::CoreLibrary()), core_intrinsics},
|
|
{Library::Handle(zone, Library::MathLibrary()), math_intrinsics},
|
|
{Library::Handle(zone, Library::TypedDataLibrary()),
|
|
typed_data_intrinsics},
|
|
{Library::Handle(zone, Library::DeveloperLibrary()),
|
|
developer_intrinsics},
|
|
{Library::Handle(zone, Library::InternalLibrary()), internal_intrinsics},
|
|
};
|
|
|
|
for (intptr_t i = 0; i < kNumLibs; i++) {
|
|
lib = intrinsics[i].library.ptr();
|
|
for (IntrinsicDesc* intrinsic = intrinsics[i].intrinsics;
|
|
intrinsic->class_name != nullptr; intrinsic++) {
|
|
func = Function::null();
|
|
if (strcmp(intrinsic->class_name, "::") == 0) {
|
|
str = String::New(intrinsic->function_name);
|
|
func = lib.LookupFunctionAllowPrivate(str);
|
|
} else {
|
|
str = String::New(intrinsic->class_name);
|
|
cls = lib.LookupClassAllowPrivate(str);
|
|
ASSERT(FLAG_precompiled_mode || !cls.IsNull());
|
|
if (!cls.IsNull()) {
|
|
error = cls.EnsureIsFinalized(thread);
|
|
if (!error.IsNull()) {
|
|
OS::PrintErr("%s\n", error.ToErrorCString());
|
|
}
|
|
ASSERT(error.IsNull());
|
|
str = String::New(intrinsic->function_name);
|
|
if (intrinsic->function_name[0] == '.') {
|
|
str2 = String::New(intrinsic->class_name);
|
|
str = String::Concat(str2, str);
|
|
}
|
|
func = cls.LookupFunctionAllowPrivate(str);
|
|
}
|
|
}
|
|
if (!func.IsNull()) {
|
|
func.set_is_intrinsic(true);
|
|
} else if (!FLAG_precompiled_mode) {
|
|
FATAL2("Intrinsifier failed to find method %s in class %s\n",
|
|
intrinsic->function_name, intrinsic->class_name);
|
|
}
|
|
}
|
|
}
|
|
#undef SETUP_FUNCTION
|
|
}
|
|
|
|
// Returns true if fall-through code can be omitted.
|
|
bool Intrinsifier::Intrinsify(const ParsedFunction& parsed_function,
|
|
FlowGraphCompiler* compiler) {
|
|
const Function& function = parsed_function.function();
|
|
if (!CanIntrinsify(function)) {
|
|
return false;
|
|
}
|
|
|
|
if (GraphIntrinsifier::GraphIntrinsify(parsed_function, compiler)) {
|
|
return compiler->intrinsic_slow_path_label()->IsUnused();
|
|
}
|
|
|
|
#if !defined(HASH_IN_OBJECT_HEADER)
|
|
// These two are more complicated on 32 bit platforms, where the
|
|
// identity hash is not stored in the header of the object. We
|
|
// therefore don't intrinsify them, falling back on the native C++
|
|
// implementations.
|
|
if (function.recognized_kind() == MethodRecognizer::kObject_getHash ||
|
|
function.recognized_kind() ==
|
|
MethodRecognizer::kObject_setHashIfNotSetYet) {
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#if !defined(PRODUCT)
|
|
#define EMIT_BREAKPOINT() compiler->assembler()->Breakpoint()
|
|
#else
|
|
#define EMIT_BREAKPOINT()
|
|
#endif
|
|
|
|
#define EMIT_CASE(class_name, function_name, enum_name, fp) \
|
|
case MethodRecognizer::k##enum_name: { \
|
|
compiler->assembler()->Comment("Intrinsic"); \
|
|
Label normal_ir_body; \
|
|
const auto size_before = compiler->assembler()->CodeSize(); \
|
|
AsmIntrinsifier::enum_name(compiler->assembler(), &normal_ir_body); \
|
|
const auto size_after = compiler->assembler()->CodeSize(); \
|
|
if (size_before == size_after) return false; \
|
|
if (function.HasUnboxedParameters()) { \
|
|
FATAL1("Unsupported unboxed parameters in asm intrinsic %s", \
|
|
function.ToFullyQualifiedCString()); \
|
|
} \
|
|
if (function.HasUnboxedReturnValue()) { \
|
|
FATAL1("Unsupported unboxed return value in asm intrinsic %s", \
|
|
function.ToFullyQualifiedCString()); \
|
|
} \
|
|
if (!normal_ir_body.IsBound()) { \
|
|
EMIT_BREAKPOINT(); \
|
|
return true; \
|
|
} \
|
|
return false; \
|
|
}
|
|
|
|
switch (function.recognized_kind()) {
|
|
ALL_INTRINSICS_NO_INTEGER_LIB_LIST(EMIT_CASE);
|
|
default:
|
|
break;
|
|
}
|
|
switch (function.recognized_kind()) {
|
|
CORE_INTEGER_LIB_INTRINSIC_LIST(EMIT_CASE)
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#undef EMIT_BREAKPOINT
|
|
|
|
#undef EMIT_INTRINSIC
|
|
return false;
|
|
}
|
|
|
|
} // namespace compiler
|
|
} // namespace dart
|