c0ebcb9f1b
When loading a late field without initializer, we check if it was not initialized and throw an exception. This change adds slow path (shared if supported) for throwing LateInitializationError instead of calling an initializer which would throw an exception. This improves register allocation around accesses to such late fields. In addition, this CL removes duplicated code for generating shared stubs. Size of Flutter gallery snapshot on armv8 -0.45% (gzip -0.34%, brotli -0.41%) Null-safe version of ParticleSystemUpdate benchmark on x64 JIT +9-17%, AOT +3-4%. Change-Id: Ia12c1efec0871a2fd7acc6db75f255853af52d0f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/168993 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Tess Strickland <sstrickl@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
190 lines
6.4 KiB
C++
190 lines
6.4 KiB
C++
// Copyright (c) 2019, 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.
|
|
|
|
#ifndef RUNTIME_VM_COMPILER_STUB_CODE_COMPILER_H_
|
|
#define RUNTIME_VM_COMPILER_STUB_CODE_COMPILER_H_
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
#error "AOT runtime should not use compiler sources (including header files)"
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#include <functional>
|
|
|
|
#include "vm/allocation.h"
|
|
#include "vm/compiler/runtime_api.h"
|
|
#include "vm/constants.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/stub_code_list.h"
|
|
#include "vm/tagged_pointer.h"
|
|
|
|
namespace dart {
|
|
|
|
// Forward declarations.
|
|
class Code;
|
|
|
|
namespace compiler {
|
|
|
|
// Forward declarations.
|
|
class Assembler;
|
|
|
|
// Represents an unresolved PC-relative Call/TailCall.
|
|
class UnresolvedPcRelativeCall : public ZoneAllocated {
|
|
public:
|
|
UnresolvedPcRelativeCall(intptr_t offset,
|
|
const dart::Code& target,
|
|
bool is_tail_call)
|
|
: offset_(offset), target_(target), is_tail_call_(is_tail_call) {}
|
|
|
|
intptr_t offset() const { return offset_; }
|
|
const dart::Code& target() const { return target_; }
|
|
bool is_tail_call() const { return is_tail_call_; }
|
|
|
|
private:
|
|
const intptr_t offset_;
|
|
const dart::Code& target_;
|
|
const bool is_tail_call_;
|
|
};
|
|
|
|
using UnresolvedPcRelativeCalls = GrowableArray<UnresolvedPcRelativeCall*>;
|
|
|
|
class StubCodeCompiler : public AllStatic {
|
|
public:
|
|
#if !defined(TARGET_ARCH_IA32)
|
|
static void GenerateBuildMethodExtractorStub(
|
|
Assembler* assembler,
|
|
const Object& closure_allocation_stub,
|
|
const Object& context_allocation_stub);
|
|
#endif
|
|
|
|
static ArrayPtr BuildStaticCallsTable(
|
|
Zone* zone,
|
|
compiler::UnresolvedPcRelativeCalls* unresolved_calls);
|
|
|
|
#define STUB_CODE_GENERATE(name) \
|
|
static void Generate##name##Stub(Assembler* assembler);
|
|
VM_STUB_CODE_LIST(STUB_CODE_GENERATE)
|
|
#undef STUB_CODE_GENERATE
|
|
|
|
static void GenerateAllocationStubForClass(
|
|
Assembler* assembler,
|
|
UnresolvedPcRelativeCalls* unresolved_calls,
|
|
const Class& cls,
|
|
const dart::Code& allocate_object,
|
|
const dart::Code& allocat_object_parametrized);
|
|
|
|
enum Optimized {
|
|
kUnoptimized,
|
|
kOptimized,
|
|
};
|
|
enum CallType {
|
|
kInstanceCall,
|
|
kStaticCall,
|
|
};
|
|
enum Exactness {
|
|
kCheckExactness,
|
|
kIgnoreExactness,
|
|
};
|
|
static void GenerateNArgsCheckInlineCacheStub(
|
|
Assembler* assembler,
|
|
intptr_t num_args,
|
|
const RuntimeEntry& handle_ic_miss,
|
|
Token::Kind kind,
|
|
Optimized optimized,
|
|
CallType type,
|
|
Exactness exactness);
|
|
static void GenerateNArgsCheckInlineCacheStubForEntryKind(
|
|
Assembler* assembler,
|
|
intptr_t num_args,
|
|
const RuntimeEntry& handle_ic_miss,
|
|
Token::Kind kind,
|
|
Optimized optimized,
|
|
CallType type,
|
|
Exactness exactness,
|
|
CodeEntryKind entry_kind);
|
|
static void GenerateUsageCounterIncrement(Assembler* assembler,
|
|
Register temp_reg);
|
|
static void GenerateOptimizedUsageCounterIncrement(Assembler* assembler);
|
|
|
|
#if defined(TARGET_ARCH_X64)
|
|
static constexpr intptr_t kNativeCallbackTrampolineSize = 10;
|
|
static constexpr intptr_t kNativeCallbackSharedStubSize = 217;
|
|
static constexpr intptr_t kNativeCallbackTrampolineStackDelta = 2;
|
|
#elif defined(TARGET_ARCH_IA32)
|
|
static constexpr intptr_t kNativeCallbackTrampolineSize = 10;
|
|
static constexpr intptr_t kNativeCallbackSharedStubSize = 90;
|
|
static constexpr intptr_t kNativeCallbackTrampolineStackDelta = 2;
|
|
#elif defined(TARGET_ARCH_ARM)
|
|
static constexpr intptr_t kNativeCallbackTrampolineSize = 12;
|
|
static constexpr intptr_t kNativeCallbackSharedStubSize = 140;
|
|
static constexpr intptr_t kNativeCallbackTrampolineStackDelta = 4;
|
|
#elif defined(TARGET_ARCH_ARM64)
|
|
static constexpr intptr_t kNativeCallbackTrampolineSize = 12;
|
|
static constexpr intptr_t kNativeCallbackSharedStubSize = 268;
|
|
static constexpr intptr_t kNativeCallbackTrampolineStackDelta = 2;
|
|
#endif
|
|
|
|
static void GenerateJITCallbackTrampolines(Assembler* assembler,
|
|
intptr_t next_callback_id);
|
|
|
|
// Calculates the offset (in words) from FP to the provided [cpu_register].
|
|
//
|
|
// Assumes
|
|
// * all [kDartAvailableCpuRegs] followed by saved-PC, saved-FP were
|
|
// pushed on the stack
|
|
// * [cpu_register] is in [kDartAvailableCpuRegs]
|
|
//
|
|
// The intended use of this function is to find registers on the stack which
|
|
// were spilled in the
|
|
// `StubCode::*<stub-name>Shared{With,Without}FpuRegsStub()`
|
|
static intptr_t WordOffsetFromFpToCpuRegister(Register cpu_register);
|
|
|
|
private:
|
|
// Common function for generating InitLateInstanceField and
|
|
// InitLateFinalInstanceField stubs.
|
|
static void GenerateInitLateInstanceFieldStub(Assembler* assembler,
|
|
bool is_final);
|
|
|
|
// Common function for generating Allocate<TypedData>Array stubs.
|
|
static void GenerateAllocateTypedDataArrayStub(Assembler* assembler,
|
|
intptr_t cid);
|
|
|
|
static void GenerateSharedStubGeneric(
|
|
Assembler* assembler,
|
|
bool save_fpu_registers,
|
|
intptr_t self_code_stub_offset_from_thread,
|
|
bool allow_return,
|
|
std::function<void()> perform_runtime_call);
|
|
|
|
// Generates shared slow path stub which saves registers and calls
|
|
// [target] runtime entry.
|
|
// If [store_runtime_result_in_result_register], then stub puts result into
|
|
// SharedSlowPathStubABI::kResultReg.
|
|
static void GenerateSharedStub(
|
|
Assembler* assembler,
|
|
bool save_fpu_registers,
|
|
const RuntimeEntry* target,
|
|
intptr_t self_code_stub_offset_from_thread,
|
|
bool allow_return,
|
|
bool store_runtime_result_in_result_register = false);
|
|
|
|
static void GenerateLateInitializationError(Assembler* assembler,
|
|
bool with_fpu_regs);
|
|
|
|
static void GenerateRangeError(Assembler* assembler, bool with_fpu_regs);
|
|
};
|
|
|
|
} // namespace compiler
|
|
|
|
enum DeoptStubKind { kLazyDeoptFromReturn, kLazyDeoptFromThrow, kEagerDeopt };
|
|
|
|
// Zap value used to indicate unused CODE_REG in deopt.
|
|
static const uword kZapCodeReg = 0xf1f1f1f1;
|
|
|
|
// Zap value used to indicate unused return address in deopt.
|
|
static const uword kZapReturnAddress = 0xe1e1e1e1;
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_COMPILER_STUB_CODE_COMPILER_H_
|