mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
[vm/compiler] Use unboxed index/length for GenericCheckBoundInstr on 64-bit architectures
flutter-release: * flutter_app_so_size: -0.47% * flutter_app_so_brotli_size: -0.48% * flutter_app_so_gzip_size: -0.57% flutter-release-sizeopt: * flutter_app_so_size: -0.45% * flutter_app_so_brotli_size: -0.64% * flutter_app_so_gzip_size: -0.65% Issue https://github.com/dart-lang/sdk/issues/32165 Change-Id: If63b71e5d52f6ddb55fe05668a6480eb00a13303 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150300 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
e18b937c1f
commit
142e15c2af
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2020, 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.
|
||||
|
||||
// VMOptions=--shared_slow_path_triggers_gc
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
Uint8List l = int.parse('1') == 1 ? Uint8List(10) : Uint8List(0);
|
||||
final int outOfRangeSmi = int.parse('-1');
|
||||
final int outOfRangeSmi2 = int.parse('10');
|
||||
final int outOfRangeMint = int.parse('${0x7fffffffffffffff}');
|
||||
final int outOfRangeMint2 = int.parse('${0x8000000000000000}');
|
||||
|
||||
buildErrorMatcher(int outOfRangeValue) {
|
||||
return (error) {
|
||||
return error is RangeError &&
|
||||
error.start == 0 &&
|
||||
error.end == 9 &&
|
||||
error.invalidValue == outOfRangeValue;
|
||||
};
|
||||
}
|
||||
|
||||
main() {
|
||||
for (int i = 0; i < 10; ++i) l[i] = i;
|
||||
|
||||
Expect.throws(() => l[outOfRangeSmi], buildErrorMatcher(outOfRangeSmi));
|
||||
Expect.throws(() => l[outOfRangeSmi2], buildErrorMatcher(outOfRangeSmi2));
|
||||
Expect.throws(() => l[outOfRangeMint], buildErrorMatcher(outOfRangeMint));
|
||||
Expect.throws(() => l[outOfRangeMint2], buildErrorMatcher(outOfRangeMint2));
|
||||
|
||||
if (int.parse('1') == 0) l = Uint8List(0);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Expect.equals(i, l[i]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2020, 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.
|
||||
|
||||
// VMOptions=--shared_slow_path_triggers_gc
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
Uint8List l = int.parse('1') == 1 ? Uint8List(10) : Uint8List(0);
|
||||
final int outOfRangeSmi = int.parse('-1');
|
||||
final int outOfRangeSmi2 = int.parse('10');
|
||||
final int outOfRangeMint = int.parse('${0x7fffffffffffffff}');
|
||||
final int outOfRangeMint2 = int.parse('${0x8000000000000000}');
|
||||
|
||||
buildErrorMatcher(int outOfRangeValue) {
|
||||
return (error) {
|
||||
return error is RangeError &&
|
||||
error.start == 0 &&
|
||||
error.end == 9 &&
|
||||
error.invalidValue == outOfRangeValue;
|
||||
};
|
||||
}
|
||||
|
||||
main() {
|
||||
for (int i = 0; i < 10; ++i) l[i] = i;
|
||||
|
||||
Expect.throws(() => l[outOfRangeSmi], buildErrorMatcher(outOfRangeSmi));
|
||||
Expect.throws(() => l[outOfRangeSmi2], buildErrorMatcher(outOfRangeSmi2));
|
||||
Expect.throws(() => l[outOfRangeMint], buildErrorMatcher(outOfRangeMint));
|
||||
Expect.throws(() => l[outOfRangeMint2], buildErrorMatcher(outOfRangeMint2));
|
||||
|
||||
if (int.parse('1') == 0) l = Uint8List(0);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Expect.equals(i, l[i]);
|
||||
}
|
||||
}
|
|
@ -1522,7 +1522,9 @@ bool Instruction::IsDominatedBy(Instruction* dom) {
|
|||
bool Instruction::HasUnmatchedInputRepresentations() const {
|
||||
for (intptr_t i = 0; i < InputCount(); i++) {
|
||||
Definition* input = InputAt(i)->definition();
|
||||
if (RequiredInputRepresentation(i) != input->representation()) {
|
||||
const Representation input_representation = RequiredInputRepresentation(i);
|
||||
if (input_representation != kNoRepresentation &&
|
||||
input_representation != input->representation()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -5150,6 +5152,9 @@ LocationSummary* GenericCheckBoundInstr::MakeLocationSummary(Zone* zone,
|
|||
}
|
||||
|
||||
void GenericCheckBoundInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
ASSERT(representation() == RequiredInputRepresentation(kIndexPos));
|
||||
ASSERT(representation() == RequiredInputRepresentation(kLengthPos));
|
||||
|
||||
RangeErrorSlowPath* slow_path =
|
||||
new RangeErrorSlowPath(this, compiler->CurrentTryIndex());
|
||||
compiler->AddSlowPathCode(slow_path);
|
||||
|
@ -5158,8 +5163,15 @@ void GenericCheckBoundInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
Register length = length_loc.reg();
|
||||
Register index = index_loc.reg();
|
||||
const intptr_t index_cid = this->index()->Type()->ToCid();
|
||||
if (index_cid != kSmiCid) {
|
||||
__ BranchIfNotSmi(index, slow_path->entry_label());
|
||||
|
||||
// The length comes from one of our variable-sized heap objects (e.g. typed
|
||||
// data array) and is therefore guaranteed to be in the positive Smi range.
|
||||
if (representation() == kTagged) {
|
||||
if (index_cid != kSmiCid) {
|
||||
__ BranchIfNotSmi(index, slow_path->entry_label());
|
||||
}
|
||||
} else {
|
||||
ASSERT(representation() == kUnboxedInt64);
|
||||
}
|
||||
__ CompareRegisters(index, length);
|
||||
__ BranchIf(UNSIGNED_GREATER_EQUAL, slow_path->entry_label());
|
||||
|
@ -5714,6 +5726,22 @@ LoadIndexedInstr::LoadIndexedInstr(Value* array,
|
|||
SetInputAt(1, index);
|
||||
}
|
||||
|
||||
Definition* LoadIndexedInstr::Canonicalize(FlowGraph* flow_graph) {
|
||||
auto Z = flow_graph->zone();
|
||||
if (auto box = index()->definition()->AsBoxInt64()) {
|
||||
// TODO(dartbug.com/39432): Make LoadIndexed fully suport unboxed indices.
|
||||
if (!box->ComputeCanDeoptimize() && compiler::target::kWordSize == 8) {
|
||||
auto load = new (Z) LoadIndexedInstr(
|
||||
array()->CopyWithType(Z), box->value()->CopyWithType(Z),
|
||||
/*index_unboxed=*/true, index_scale(), class_id(), alignment_,
|
||||
GetDeoptId(), token_pos(), result_type_);
|
||||
flow_graph->InsertBefore(this, load, env(), FlowGraph::kValue);
|
||||
return load;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
StoreIndexedInstr::StoreIndexedInstr(Value* array,
|
||||
Value* index,
|
||||
Value* value,
|
||||
|
@ -5738,6 +5766,23 @@ StoreIndexedInstr::StoreIndexedInstr(Value* array,
|
|||
SetInputAt(kValuePos, value);
|
||||
}
|
||||
|
||||
Instruction* StoreIndexedInstr::Canonicalize(FlowGraph* flow_graph) {
|
||||
auto Z = flow_graph->zone();
|
||||
if (auto box = index()->definition()->AsBoxInt64()) {
|
||||
// TODO(dartbug.com/39432): Make StoreIndexed fully suport unboxed indices.
|
||||
if (!box->ComputeCanDeoptimize() && compiler::target::kWordSize == 8) {
|
||||
auto store = new (Z) StoreIndexedInstr(
|
||||
array()->CopyWithType(Z), box->value()->CopyWithType(Z),
|
||||
value()->CopyWithType(Z), emit_store_barrier_,
|
||||
/*index_unboxed=*/true, index_scale(), class_id(), alignment_,
|
||||
GetDeoptId(), token_pos(), speculative_mode_);
|
||||
flow_graph->InsertBefore(this, store, env(), FlowGraph::kEffect);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
bool Utf8ScanInstr::IsScanFlagsUnboxed() const {
|
||||
return FlowGraphCompiler::IsUnboxedField(scan_flags_field_.field());
|
||||
}
|
||||
|
|
|
@ -5564,6 +5564,8 @@ class LoadIndexedInstr : public TemplateDefinition<2, NoThrow> {
|
|||
|
||||
virtual bool HasUnknownSideEffects() const { return false; }
|
||||
|
||||
virtual Definition* Canonicalize(FlowGraph* flow_graph);
|
||||
|
||||
ADD_EXTRA_INFO_TO_S_EXPRESSION_SUPPORT
|
||||
|
||||
private:
|
||||
|
@ -5864,6 +5866,8 @@ class StoreIndexedInstr : public TemplateInstruction<3, NoThrow> {
|
|||
|
||||
void PrintOperandsTo(BufferFormatter* f) const;
|
||||
|
||||
virtual Instruction* Canonicalize(FlowGraph* flow_graph);
|
||||
|
||||
ADD_EXTRA_INFO_TO_S_EXPRESSION_SUPPORT
|
||||
|
||||
private:
|
||||
|
@ -8697,6 +8701,12 @@ class CheckArrayBoundInstr : public CheckBoundBase {
|
|||
// or otherwise throws an out-of-bounds exception (viz. non-speculative).
|
||||
class GenericCheckBoundInstr : public CheckBoundBase {
|
||||
public:
|
||||
// We prefer to have unboxed inputs on 64-bit where values can fit into a
|
||||
// register.
|
||||
static bool UseUnboxedRepresentation() {
|
||||
return compiler::target::kWordSize == 8;
|
||||
}
|
||||
|
||||
GenericCheckBoundInstr(Value* length, Value* index, intptr_t deopt_id)
|
||||
: CheckBoundBase(length, index, deopt_id) {}
|
||||
|
||||
|
@ -8707,6 +8717,21 @@ class GenericCheckBoundInstr : public CheckBoundBase {
|
|||
virtual CompileType ComputeType() const;
|
||||
virtual bool RecomputeType();
|
||||
|
||||
virtual intptr_t DeoptimizationTarget() const { return DeoptId::kNone; }
|
||||
|
||||
virtual SpeculativeMode SpeculativeModeOfInput(intptr_t index) const {
|
||||
return kNotSpeculative;
|
||||
}
|
||||
|
||||
virtual Representation representation() const {
|
||||
return UseUnboxedRepresentation() ? kUnboxedInt64 : kTagged;
|
||||
}
|
||||
|
||||
virtual Representation RequiredInputRepresentation(intptr_t idx) const {
|
||||
ASSERT(idx == kIndexPos || idx == kLengthPos);
|
||||
return UseUnboxedRepresentation() ? kUnboxedInt64 : kTagged;
|
||||
}
|
||||
|
||||
// GenericCheckBound can implicitly call Dart code (RangeError or
|
||||
// ArgumentError constructor), so it can lazily deopt.
|
||||
virtual bool ComputeCanDeoptimize() const {
|
||||
|
|
|
@ -279,6 +279,9 @@ Instruction* ILMatcher::MatchInternal(std::vector<MatchCode> match_codes,
|
|||
if (branch == nullptr) return nullptr;
|
||||
return branch->false_successor();
|
||||
}
|
||||
if (opcode == kNop) {
|
||||
return cursor;
|
||||
}
|
||||
if (opcode == kMoveAny) {
|
||||
return cursor->next();
|
||||
}
|
||||
|
@ -347,6 +350,9 @@ const char* ILMatcher::MatchOpCodeToCString(MatchOpCode opcode) {
|
|||
if (opcode == kMatchAndMoveBranchFalse) {
|
||||
return "kMatchAndMoveBranchFalse";
|
||||
}
|
||||
if (opcode == kNop) {
|
||||
return "kNop";
|
||||
}
|
||||
if (opcode == kMoveAny) {
|
||||
return "kMoveAny";
|
||||
}
|
||||
|
|
|
@ -110,6 +110,9 @@ enum MatchOpCode {
|
|||
// Matches a branch and moves right.
|
||||
kMatchAndMoveBranchFalse,
|
||||
|
||||
// Is ignored.
|
||||
kNop,
|
||||
|
||||
// Moves forward across any instruction.
|
||||
kMoveAny,
|
||||
|
||||
|
|
|
@ -1617,19 +1617,17 @@ LocationSummary* LoadIndexedInstr::MakeLocationSummary(Zone* zone,
|
|||
LocationSummary* locs = new (zone)
|
||||
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
|
||||
locs->set_in(0, Location::RequiresRegister());
|
||||
// The smi index is either untagged (element size == 1), or it is left smi
|
||||
// tagged (for all element sizes > 1).
|
||||
if (index_scale() == 1) {
|
||||
locs->set_in(1,
|
||||
CanBeImmediateIndex(index(), class_id())
|
||||
? Location::Constant(index()->definition()->AsConstant())
|
||||
: Location::WritableRegister());
|
||||
} else {
|
||||
locs->set_in(1,
|
||||
CanBeImmediateIndex(index(), class_id())
|
||||
? Location::Constant(index()->definition()->AsConstant())
|
||||
: Location::RequiresRegister());
|
||||
}
|
||||
// For tagged index with index_scale=1 as well as untagged index with
|
||||
// index_scale=16 we need a writable register due to assdressing mode
|
||||
// restrictions on X64.
|
||||
const bool need_writable_index_register =
|
||||
(index_scale() == 1 && !index_unboxed_) ||
|
||||
(index_scale() == 16 && index_unboxed_);
|
||||
locs->set_in(
|
||||
1, CanBeImmediateIndex(index(), class_id())
|
||||
? Location::Constant(index()->definition()->AsConstant())
|
||||
: (need_writable_index_register ? Location::WritableRegister()
|
||||
: Location::RequiresRegister()));
|
||||
if ((representation() == kUnboxedDouble) ||
|
||||
(representation() == kUnboxedFloat32x4) ||
|
||||
(representation() == kUnboxedInt32x4) ||
|
||||
|
@ -1646,22 +1644,27 @@ void LoadIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
const Register array = locs()->in(0).reg();
|
||||
const Location index = locs()->in(1);
|
||||
|
||||
intptr_t index_scale = index_scale_;
|
||||
if (index.IsRegister()) {
|
||||
if (index_scale == 1 && !index_unboxed_) {
|
||||
__ SmiUntag(index.reg());
|
||||
} else if (index_scale == 16 && index_unboxed_) {
|
||||
// X64 does not support addressing mode using TIMES_16.
|
||||
__ SmiTag(index.reg());
|
||||
index_scale >>= 1;
|
||||
}
|
||||
} else {
|
||||
ASSERT(index.IsConstant());
|
||||
}
|
||||
|
||||
compiler::Address element_address =
|
||||
index.IsRegister() ? compiler::Assembler::ElementAddressForRegIndex(
|
||||
IsExternal(), class_id(), index_scale(),
|
||||
IsExternal(), class_id(), index_scale,
|
||||
index_unboxed_, array, index.reg())
|
||||
: compiler::Assembler::ElementAddressForIntIndex(
|
||||
IsExternal(), class_id(), index_scale(), array,
|
||||
IsExternal(), class_id(), index_scale, array,
|
||||
Smi::Cast(index.constant()).Value());
|
||||
|
||||
if (index_scale() == 1 && !index_unboxed_) {
|
||||
if (index.IsRegister()) {
|
||||
__ SmiUntag(index.reg());
|
||||
} else {
|
||||
ASSERT(index.IsConstant());
|
||||
}
|
||||
}
|
||||
|
||||
if (representation() == kUnboxedDouble ||
|
||||
representation() == kUnboxedFloat32x4 ||
|
||||
representation() == kUnboxedInt32x4 ||
|
||||
|
@ -1848,19 +1851,17 @@ LocationSummary* StoreIndexedInstr::MakeLocationSummary(Zone* zone,
|
|||
LocationSummary* locs = new (zone)
|
||||
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
|
||||
locs->set_in(0, Location::RequiresRegister());
|
||||
// The smi index is either untagged (element size == 1), or it is left smi
|
||||
// tagged (for all element sizes > 1).
|
||||
if (index_scale() == 1) {
|
||||
locs->set_in(1,
|
||||
CanBeImmediateIndex(index(), class_id())
|
||||
? Location::Constant(index()->definition()->AsConstant())
|
||||
: Location::WritableRegister());
|
||||
} else {
|
||||
locs->set_in(1,
|
||||
CanBeImmediateIndex(index(), class_id())
|
||||
? Location::Constant(index()->definition()->AsConstant())
|
||||
: Location::RequiresRegister());
|
||||
}
|
||||
// For tagged index with index_scale=1 as well as untagged index with
|
||||
// index_scale=16 we need a writable register due to assdressing mode
|
||||
// restrictions on X64.
|
||||
const bool need_writable_index_register =
|
||||
(index_scale() == 1 && !index_unboxed_) ||
|
||||
(index_scale() == 16 && index_unboxed_);
|
||||
locs->set_in(
|
||||
1, CanBeImmediateIndex(index(), class_id())
|
||||
? Location::Constant(index()->definition()->AsConstant())
|
||||
: (need_writable_index_register ? Location::WritableRegister()
|
||||
: Location::RequiresRegister()));
|
||||
switch (class_id()) {
|
||||
case kArrayCid:
|
||||
locs->set_in(2, ShouldEmitStoreBarrier()
|
||||
|
@ -1915,17 +1916,27 @@ void StoreIndexedInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
|||
const Register array = locs()->in(0).reg();
|
||||
const Location index = locs()->in(1);
|
||||
|
||||
intptr_t index_scale = index_scale_;
|
||||
if (index.IsRegister()) {
|
||||
if (index_scale == 1 && !index_unboxed_) {
|
||||
__ SmiUntag(index.reg());
|
||||
} else if (index_scale == 16 && index_unboxed_) {
|
||||
// X64 does not support addressing mode using TIMES_16.
|
||||
__ SmiTag(index.reg());
|
||||
index_scale >>= 1;
|
||||
}
|
||||
} else {
|
||||
ASSERT(index.IsConstant());
|
||||
}
|
||||
|
||||
compiler::Address element_address =
|
||||
index.IsRegister() ? compiler::Assembler::ElementAddressForRegIndex(
|
||||
IsExternal(), class_id(), index_scale(),
|
||||
IsExternal(), class_id(), index_scale,
|
||||
index_unboxed_, array, index.reg())
|
||||
: compiler::Assembler::ElementAddressForIntIndex(
|
||||
IsExternal(), class_id(), index_scale(), array,
|
||||
IsExternal(), class_id(), index_scale, array,
|
||||
Smi::Cast(index.constant()).Value());
|
||||
|
||||
if ((index_scale() == 1) && index.IsRegister() && !index_unboxed_) {
|
||||
__ SmiUntag(index.reg());
|
||||
}
|
||||
switch (class_id()) {
|
||||
case kArrayCid:
|
||||
if (ShouldEmitStoreBarrier()) {
|
||||
|
|
|
@ -220,6 +220,7 @@ ISOLATE_UNIT_TEST_CASE(Inliner_List_generate) {
|
|||
RELEASE_ASSERT(cursor.TryMatch({
|
||||
kMoveGlob,
|
||||
kMatchAndMoveCreateArray,
|
||||
kWordSize == 8 ? kMatchAndMoveUnboxInt64 : kNop,
|
||||
kMatchAndMoveGoto,
|
||||
|
||||
// Loop header
|
||||
|
@ -231,6 +232,7 @@ ISOLATE_UNIT_TEST_CASE(Inliner_List_generate) {
|
|||
|
||||
// Loop body
|
||||
kMatchAndMoveTargetEntry,
|
||||
kWordSize == 8 ? kMatchAndMoveUnboxInt64 : kNop,
|
||||
kMatchAndMoveGenericCheckBound,
|
||||
kMatchAndMoveStoreIndexed,
|
||||
kMatchAndMoveCheckedSmiOp,
|
||||
|
@ -247,7 +249,6 @@ ISOLATE_UNIT_TEST_CASE(Inliner_List_generate) {
|
|||
kMatchAndMoveTargetEntry,
|
||||
kMatchReturn,
|
||||
}));
|
||||
|
||||
} else {
|
||||
Instruction* unbox1 = nullptr;
|
||||
Instruction* unbox2 = nullptr;
|
||||
|
@ -267,7 +268,7 @@ ISOLATE_UNIT_TEST_CASE(Inliner_List_generate) {
|
|||
|
||||
// Loop body
|
||||
kMatchAndMoveTargetEntry,
|
||||
kMatchAndMoveBoxInt64,
|
||||
kWordSize == 4 ? kMatchAndMoveBoxInt64 : kNop,
|
||||
kMatchAndMoveBoxInt64,
|
||||
kMatchAndMoveStoreIndexed,
|
||||
kMatchAndMoveBinaryInt64Op,
|
||||
|
|
|
@ -77,7 +77,9 @@ ISOLATE_UNIT_TEST_CASE(IRTest_TypedDataAOT_Inlining) {
|
|||
}
|
||||
|
||||
EXPECT(load_field->InputAt(0)->definition()->IsParameter());
|
||||
EXPECT(bounds_check->InputAt(0)->definition() == load_field);
|
||||
EXPECT(bounds_check->length()
|
||||
->definition()
|
||||
->OriginalDefinitionIgnoreBoxingAndConstraints() == load_field);
|
||||
EXPECT(load_untagged->InputAt(0)->definition()->IsParameter());
|
||||
EXPECT(load_indexed->InputAt(0)->definition() == load_untagged);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,22 @@ namespace dart {
|
|||
|
||||
namespace compiler {
|
||||
|
||||
intptr_t StubCodeCompiler::WordOffsetFromFpToCpuRegister(
|
||||
Register cpu_register) {
|
||||
ASSERT(RegisterSet::Contains(kDartAvailableCpuRegs, cpu_register));
|
||||
|
||||
// Skip FP + saved PC.
|
||||
intptr_t slots_from_fp = 2;
|
||||
for (intptr_t i = 0; i < kNumberOfCpuRegisters; i++) {
|
||||
Register reg = static_cast<Register>(i);
|
||||
if (reg == cpu_register) break;
|
||||
if (RegisterSet::Contains(kDartAvailableCpuRegs, reg)) {
|
||||
slots_from_fp++;
|
||||
}
|
||||
}
|
||||
return slots_from_fp;
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateInitStaticFieldStub(Assembler* assembler) {
|
||||
__ EnterStubFrame();
|
||||
__ PushObject(NullObject()); // Make room for result.
|
||||
|
|
|
@ -125,6 +125,18 @@ class StubCodeCompiler : public AllStatic {
|
|||
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.
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "vm/globals.h"
|
||||
|
||||
// For `AllocateObjectInstr::WillAllocateNewOrRemembered`
|
||||
// For `GenericCheckBoundInstr::UseUnboxedRepresentation`
|
||||
#include "vm/compiler/backend/il.h"
|
||||
|
||||
#define SHOULD_NOT_INCLUDE_RUNTIME
|
||||
|
@ -164,14 +165,11 @@ void StubCodeCompiler::GenerateCallToRuntimeStub(Assembler* assembler) {
|
|||
__ Ret();
|
||||
}
|
||||
|
||||
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_r0 = false,
|
||||
std::initializer_list<Register> runtime_call_arguments = {}) {
|
||||
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) {
|
||||
// If the target CPU does not support VFP the caller should always use the
|
||||
// non-FPU stub.
|
||||
if (save_fpu_registers && !TargetCPUFeatures::vfp_supported()) {
|
||||
|
@ -187,44 +185,45 @@ void GenerateSharedStub(
|
|||
// To make the stack map calculation architecture independent we do the same
|
||||
// as on intel.
|
||||
__ Push(LR);
|
||||
|
||||
__ PushRegisters(all_registers);
|
||||
__ ldr(CODE_REG, Address(THR, self_code_stub_offset_from_thread));
|
||||
__ EnterStubFrame();
|
||||
|
||||
if (store_runtime_result_in_r0) {
|
||||
ASSERT(all_registers.ContainsRegister(R0));
|
||||
ASSERT(allow_return);
|
||||
|
||||
// Push an even value so it will not be seen as a pointer
|
||||
__ Push(LR);
|
||||
}
|
||||
|
||||
for (Register argument_reg : runtime_call_arguments) {
|
||||
__ PushRegister(argument_reg);
|
||||
}
|
||||
__ CallRuntime(*target, /*argument_count=*/runtime_call_arguments.size());
|
||||
perform_runtime_call();
|
||||
if (!allow_return) {
|
||||
__ Breakpoint();
|
||||
return;
|
||||
}
|
||||
|
||||
__ Drop(runtime_call_arguments.size());
|
||||
if (store_runtime_result_in_r0) {
|
||||
__ Pop(R0);
|
||||
}
|
||||
__ LeaveStubFrame();
|
||||
if (store_runtime_result_in_r0) {
|
||||
// Stores the runtime result in stack where R0 was pushed ( R0 is the very
|
||||
// last register to be pushed by __ PushRegisters(all_registers) )
|
||||
__ str(R0, Address(SP));
|
||||
}
|
||||
|
||||
__ PopRegisters(all_registers);
|
||||
__ Pop(LR);
|
||||
__ bx(LR);
|
||||
}
|
||||
|
||||
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_r0 = false) {
|
||||
ASSERT(!store_runtime_result_in_r0 || allow_return);
|
||||
auto perform_runtime_call = [&]() {
|
||||
if (store_runtime_result_in_r0) {
|
||||
__ PushRegister(LR); // Push an even register.
|
||||
}
|
||||
__ CallRuntime(*target, /*argument_count=*/0);
|
||||
if (store_runtime_result_in_r0) {
|
||||
__ PopRegister(R0);
|
||||
__ str(
|
||||
R0,
|
||||
Address(FP, target::kWordSize *
|
||||
StubCodeCompiler::WordOffsetFromFpToCpuRegister(R0)));
|
||||
}
|
||||
};
|
||||
GenerateSharedStubGeneric(assembler, save_fpu_registers,
|
||||
self_code_stub_offset_from_thread, allow_return,
|
||||
perform_runtime_call);
|
||||
}
|
||||
|
||||
// R1: The extracted method.
|
||||
// R4: The type_arguments_field_offset (or 0)
|
||||
// SP+0: The object from which we are tearing a method off.
|
||||
|
@ -497,7 +496,7 @@ void StubCodeCompiler::GenerateNullErrorSharedWithoutFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/false, &kNullErrorRuntimeEntry,
|
||||
target::Thread::null_error_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, {});
|
||||
/*allow_return=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateNullErrorSharedWithFPURegsStub(
|
||||
|
@ -505,7 +504,7 @@ void StubCodeCompiler::GenerateNullErrorSharedWithFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/true, &kNullErrorRuntimeEntry,
|
||||
target::Thread::null_error_shared_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, {});
|
||||
/*allow_return=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateNullArgErrorSharedWithoutFPURegsStub(
|
||||
|
@ -513,7 +512,7 @@ void StubCodeCompiler::GenerateNullArgErrorSharedWithoutFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/false, &kArgumentNullErrorRuntimeEntry,
|
||||
target::Thread::null_arg_error_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, {});
|
||||
/*allow_return=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateNullArgErrorSharedWithFPURegsStub(
|
||||
|
@ -521,27 +520,34 @@ void StubCodeCompiler::GenerateNullArgErrorSharedWithFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/true, &kArgumentNullErrorRuntimeEntry,
|
||||
target::Thread::null_arg_error_shared_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, {});
|
||||
/*allow_return=*/false);
|
||||
}
|
||||
|
||||
static void GenerateRangeError(Assembler* assembler, bool with_fpu_regs) {
|
||||
auto perform_runtime_call = [&]() {
|
||||
ASSERT(!GenericCheckBoundInstr::UseUnboxedRepresentation());
|
||||
__ PushRegister(RangeErrorABI::kLengthReg);
|
||||
__ PushRegister(RangeErrorABI::kIndexReg);
|
||||
__ CallRuntime(kRangeErrorRuntimeEntry, /*argument_count=*/2);
|
||||
__ Breakpoint();
|
||||
};
|
||||
|
||||
GenerateSharedStubGeneric(
|
||||
assembler, /*save_fpu_registers=*/with_fpu_regs,
|
||||
with_fpu_regs
|
||||
? target::Thread::range_error_shared_with_fpu_regs_stub_offset()
|
||||
: target::Thread::range_error_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, perform_runtime_call);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateRangeErrorSharedWithoutFPURegsStub(
|
||||
Assembler* assembler) {
|
||||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/false, &kRangeErrorRuntimeEntry,
|
||||
target::Thread::range_error_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false,
|
||||
/*store_runtime_result_in_r0=*/false,
|
||||
{RangeErrorABI::kLengthReg, RangeErrorABI::kIndexReg});
|
||||
GenerateRangeError(assembler, /*with_fpu_regs=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateRangeErrorSharedWithFPURegsStub(
|
||||
Assembler* assembler) {
|
||||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/true, &kRangeErrorRuntimeEntry,
|
||||
target::Thread::range_error_shared_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false,
|
||||
/*store_runtime_result_in_r0=*/false,
|
||||
{RangeErrorABI::kLengthReg, RangeErrorABI::kIndexReg});
|
||||
GenerateRangeError(assembler, /*with_fpu_regs=*/true);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateStackOverflowSharedWithoutFPURegsStub(
|
||||
|
@ -549,7 +555,7 @@ void StubCodeCompiler::GenerateStackOverflowSharedWithoutFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/false, &kStackOverflowRuntimeEntry,
|
||||
target::Thread::stack_overflow_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/true, {});
|
||||
/*allow_return=*/true);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateStackOverflowSharedWithFPURegsStub(
|
||||
|
@ -557,7 +563,7 @@ void StubCodeCompiler::GenerateStackOverflowSharedWithFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/true, &kStackOverflowRuntimeEntry,
|
||||
target::Thread::stack_overflow_shared_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/true, {});
|
||||
/*allow_return=*/true);
|
||||
}
|
||||
|
||||
// Input parameters:
|
||||
|
@ -1204,7 +1210,7 @@ void StubCodeCompiler::GenerateAllocateMintSharedWithFPURegsStub(
|
|||
&kAllocateMintRuntimeEntry,
|
||||
target::Thread::allocate_mint_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/true,
|
||||
/*store_runtime_result_in_r0=*/true, {});
|
||||
/*store_runtime_result_in_r0=*/true);
|
||||
}
|
||||
|
||||
// Called for allocation of Mint.
|
||||
|
@ -1223,7 +1229,7 @@ void StubCodeCompiler::GenerateAllocateMintSharedWithoutFPURegsStub(
|
|||
assembler, /*save_fpu_registers=*/false, &kAllocateMintRuntimeEntry,
|
||||
target::Thread::allocate_mint_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/true,
|
||||
/*store_runtime_result_in_r0=*/true, {});
|
||||
/*store_runtime_result_in_r0=*/true);
|
||||
}
|
||||
|
||||
// Called when invoking Dart code from C++ (VM code).
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "vm/globals.h"
|
||||
|
||||
// For `AllocateObjectInstr::WillAllocateNewOrRemembered`
|
||||
// For `GenericCheckBoundInstr::UseUnboxedRepresentation`
|
||||
#include "vm/compiler/backend/il.h"
|
||||
|
||||
#define SHOULD_NOT_INCLUDE_RUNTIME
|
||||
|
@ -184,14 +185,12 @@ void StubCodeCompiler::GenerateCallToRuntimeStub(Assembler* assembler) {
|
|||
__ ret();
|
||||
}
|
||||
|
||||
void GenerateSharedStub(
|
||||
static void GenerateSharedStubGeneric(
|
||||
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_r0 = false,
|
||||
std::initializer_list<Register> runtime_call_arguments = {}) {
|
||||
std::function<void()> perform_runtime_call) {
|
||||
// We want the saved registers to appear like part of the caller's frame, so
|
||||
// we push them before calling EnterStubFrame.
|
||||
RegisterSet all_registers;
|
||||
|
@ -203,37 +202,42 @@ void GenerateSharedStub(
|
|||
__ PushRegisters(all_registers);
|
||||
__ ldr(CODE_REG, Address(THR, self_code_stub_offset_from_thread));
|
||||
__ EnterStubFrame();
|
||||
if (store_runtime_result_in_r0) {
|
||||
ASSERT(all_registers.ContainsRegister(R0));
|
||||
ASSERT(allow_return);
|
||||
|
||||
// Push an even value so it will not be seen as a pointer
|
||||
__ Push(LR);
|
||||
}
|
||||
for (Register argument_reg : runtime_call_arguments) {
|
||||
__ PushRegister(argument_reg);
|
||||
}
|
||||
__ CallRuntime(*target, /*argument_count=*/runtime_call_arguments.size());
|
||||
perform_runtime_call();
|
||||
if (!allow_return) {
|
||||
__ Breakpoint();
|
||||
return;
|
||||
}
|
||||
|
||||
__ Drop(runtime_call_arguments.size());
|
||||
if (store_runtime_result_in_r0) {
|
||||
__ Pop(R0);
|
||||
}
|
||||
__ LeaveStubFrame();
|
||||
if (store_runtime_result_in_r0) {
|
||||
// Stores the runtime result in stack where R0 was pushed ( R0 is the very
|
||||
// last register to be pushed by __ PushRegisters(all_registers) )
|
||||
__ str(R0, Address(SP));
|
||||
}
|
||||
__ PopRegisters(all_registers);
|
||||
__ Pop(LR);
|
||||
__ ret(LR);
|
||||
}
|
||||
|
||||
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_r0 = false) {
|
||||
ASSERT(!store_runtime_result_in_r0 || allow_return);
|
||||
auto perform_runtime_call = [&]() {
|
||||
if (store_runtime_result_in_r0) {
|
||||
__ PushRegister(NULL_REG);
|
||||
}
|
||||
__ CallRuntime(*target, /*argument_count=*/0);
|
||||
if (store_runtime_result_in_r0) {
|
||||
__ PopRegister(R0);
|
||||
__ str(
|
||||
R0,
|
||||
Address(FP, target::kWordSize *
|
||||
StubCodeCompiler::WordOffsetFromFpToCpuRegister(R0)));
|
||||
}
|
||||
};
|
||||
GenerateSharedStubGeneric(assembler, save_fpu_registers,
|
||||
self_code_stub_offset_from_thread, allow_return,
|
||||
perform_runtime_call);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateEnterSafepointStub(Assembler* assembler) {
|
||||
RegisterSet all_registers;
|
||||
all_registers.AddAllGeneralRegisters();
|
||||
|
@ -549,7 +553,7 @@ void StubCodeCompiler::GenerateNullErrorSharedWithoutFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/false, &kNullErrorRuntimeEntry,
|
||||
target::Thread::null_error_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, {});
|
||||
/*allow_return=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateNullErrorSharedWithFPURegsStub(
|
||||
|
@ -557,7 +561,7 @@ void StubCodeCompiler::GenerateNullErrorSharedWithFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/true, &kNullErrorRuntimeEntry,
|
||||
target::Thread::null_error_shared_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, {});
|
||||
/*allow_return=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateNullArgErrorSharedWithoutFPURegsStub(
|
||||
|
@ -565,7 +569,7 @@ void StubCodeCompiler::GenerateNullArgErrorSharedWithoutFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/false, &kArgumentNullErrorRuntimeEntry,
|
||||
target::Thread::null_arg_error_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, {});
|
||||
/*allow_return=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateNullArgErrorSharedWithFPURegsStub(
|
||||
|
@ -573,27 +577,64 @@ void StubCodeCompiler::GenerateNullArgErrorSharedWithFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/true, &kArgumentNullErrorRuntimeEntry,
|
||||
target::Thread::null_arg_error_shared_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, {});
|
||||
/*allow_return=*/false);
|
||||
}
|
||||
|
||||
static void GenerateRangeError(Assembler* assembler, bool with_fpu_regs) {
|
||||
auto perform_runtime_call = [&]() {
|
||||
// If the generated code has unboxed index/length we need to box them before
|
||||
// calling the runtime entry.
|
||||
if (GenericCheckBoundInstr::UseUnboxedRepresentation()) {
|
||||
Label length, smi_case;
|
||||
|
||||
// The user-controlled index might not fit into a Smi.
|
||||
__ adds(RangeErrorABI::kIndexReg, RangeErrorABI::kIndexReg,
|
||||
compiler::Operand(RangeErrorABI::kIndexReg));
|
||||
__ BranchIf(NO_OVERFLOW, &length);
|
||||
{
|
||||
// Allocate a mint, reload the two registers and popualte the mint.
|
||||
__ PushRegister(NULL_REG);
|
||||
__ CallRuntime(kAllocateMintRuntimeEntry, /*argument_count=*/0);
|
||||
__ PopRegister(RangeErrorABI::kIndexReg);
|
||||
__ ldr(TMP,
|
||||
Address(FP, target::kWordSize *
|
||||
StubCodeCompiler::WordOffsetFromFpToCpuRegister(
|
||||
RangeErrorABI::kIndexReg)));
|
||||
__ str(TMP, FieldAddress(RangeErrorABI::kIndexReg,
|
||||
target::Mint::value_offset()));
|
||||
__ ldr(RangeErrorABI::kLengthReg,
|
||||
Address(FP, target::kWordSize *
|
||||
StubCodeCompiler::WordOffsetFromFpToCpuRegister(
|
||||
RangeErrorABI::kLengthReg)));
|
||||
}
|
||||
|
||||
// Length is guaranteed to be in positive Smi range (it comes from a load
|
||||
// of a vm recognized array).
|
||||
__ Bind(&length);
|
||||
__ SmiTag(RangeErrorABI::kLengthReg);
|
||||
}
|
||||
__ PushRegister(RangeErrorABI::kLengthReg);
|
||||
__ PushRegister(RangeErrorABI::kIndexReg);
|
||||
__ CallRuntime(kRangeErrorRuntimeEntry, /*argument_count=*/2);
|
||||
__ Breakpoint();
|
||||
};
|
||||
|
||||
GenerateSharedStubGeneric(
|
||||
assembler, /*save_fpu_registers=*/with_fpu_regs,
|
||||
with_fpu_regs
|
||||
? target::Thread::range_error_shared_with_fpu_regs_stub_offset()
|
||||
: target::Thread::range_error_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, perform_runtime_call);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateRangeErrorSharedWithoutFPURegsStub(
|
||||
Assembler* assembler) {
|
||||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/false, &kRangeErrorRuntimeEntry,
|
||||
target::Thread::range_error_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false,
|
||||
/*store_runtime_result_in_r0=*/false,
|
||||
{RangeErrorABI::kLengthReg, RangeErrorABI::kIndexReg});
|
||||
GenerateRangeError(assembler, /*with_fpu_regs=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateRangeErrorSharedWithFPURegsStub(
|
||||
Assembler* assembler) {
|
||||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/true, &kRangeErrorRuntimeEntry,
|
||||
target::Thread::range_error_shared_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false,
|
||||
/*store_runtime_result_in_r0=*/false,
|
||||
{RangeErrorABI::kLengthReg, RangeErrorABI::kIndexReg});
|
||||
GenerateRangeError(assembler, /*with_fpu_regs=*/true);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateStackOverflowSharedWithoutFPURegsStub(
|
||||
|
@ -601,7 +642,7 @@ void StubCodeCompiler::GenerateStackOverflowSharedWithoutFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/false, &kStackOverflowRuntimeEntry,
|
||||
target::Thread::stack_overflow_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/true, {});
|
||||
/*allow_return=*/true);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateStackOverflowSharedWithFPURegsStub(
|
||||
|
@ -609,7 +650,7 @@ void StubCodeCompiler::GenerateStackOverflowSharedWithFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/true, &kStackOverflowRuntimeEntry,
|
||||
target::Thread::stack_overflow_shared_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/true, {});
|
||||
/*allow_return=*/true);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GeneratePrintStopMessageStub(Assembler* assembler) {
|
||||
|
@ -1288,7 +1329,7 @@ void StubCodeCompiler::GenerateAllocateMintSharedWithFPURegsStub(
|
|||
&kAllocateMintRuntimeEntry,
|
||||
target::Thread::allocate_mint_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/true,
|
||||
/*store_runtime_result_in_r0=*/true, {});
|
||||
/*store_runtime_result_in_r0=*/true);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateAllocateMintSharedWithoutFPURegsStub(
|
||||
|
@ -1306,7 +1347,7 @@ void StubCodeCompiler::GenerateAllocateMintSharedWithoutFPURegsStub(
|
|||
assembler, /*save_fpu_registers=*/false, &kAllocateMintRuntimeEntry,
|
||||
target::Thread::allocate_mint_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/true,
|
||||
/*store_runtime_result_in_r0=*/true, {});
|
||||
/*store_runtime_result_in_r0=*/true);
|
||||
}
|
||||
|
||||
// Called when invoking Dart code from C++ (VM code).
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "vm/globals.h"
|
||||
|
||||
// For `AllocateObjectInstr::WillAllocateNewOrRemembered`
|
||||
// For `GenericCheckBoundInstr::UseUnboxedRepresentation`
|
||||
#include "vm/compiler/backend/il.h"
|
||||
|
||||
#define SHOULD_NOT_INCLUDE_RUNTIME
|
||||
|
@ -163,13 +164,12 @@ void StubCodeCompiler::GenerateCallToRuntimeStub(Assembler* assembler) {
|
|||
__ ret();
|
||||
}
|
||||
|
||||
void GenerateSharedStub(
|
||||
static void GenerateSharedStubGeneric(
|
||||
Assembler* assembler,
|
||||
bool save_fpu_registers,
|
||||
const RuntimeEntry* target,
|
||||
intptr_t self_code_stub_offset_from_thread,
|
||||
bool allow_return,
|
||||
std::initializer_list<Register> runtime_call_arguments) {
|
||||
std::function<void()> perform_runtime_call) {
|
||||
// We want the saved registers to appear like part of the caller's frame, so
|
||||
// we push them before calling EnterStubFrame.
|
||||
__ PushRegisters(kDartAvailableCpuRegs,
|
||||
|
@ -177,30 +177,22 @@ void GenerateSharedStub(
|
|||
|
||||
const intptr_t kSavedCpuRegisterSlots =
|
||||
Utils::CountOneBitsWord(kDartAvailableCpuRegs);
|
||||
|
||||
const intptr_t kSavedFpuRegisterSlots =
|
||||
save_fpu_registers
|
||||
? kNumberOfFpuRegisters * kFpuRegisterSize / target::kWordSize
|
||||
: 0;
|
||||
|
||||
const intptr_t kAllSavedRegistersSlots =
|
||||
kSavedCpuRegisterSlots + kSavedFpuRegisterSlots;
|
||||
|
||||
// Copy down the return address so the stack layout is correct.
|
||||
__ pushq(Address(RSP, kAllSavedRegistersSlots * target::kWordSize));
|
||||
|
||||
__ movq(CODE_REG, Address(THR, self_code_stub_offset_from_thread));
|
||||
|
||||
__ EnterStubFrame();
|
||||
for (Register argument_reg : runtime_call_arguments) {
|
||||
__ PushRegister(argument_reg);
|
||||
}
|
||||
__ CallRuntime(*target, /*argument_count=*/runtime_call_arguments.size());
|
||||
perform_runtime_call();
|
||||
if (!allow_return) {
|
||||
__ Breakpoint();
|
||||
return;
|
||||
}
|
||||
__ Drop(runtime_call_arguments.size());
|
||||
__ LeaveStubFrame();
|
||||
|
||||
// Drop "official" return address -- we can just use the one stored above the
|
||||
|
@ -213,6 +205,19 @@ void GenerateSharedStub(
|
|||
__ ret();
|
||||
}
|
||||
|
||||
static void GenerateSharedStub(Assembler* assembler,
|
||||
bool save_fpu_registers,
|
||||
const RuntimeEntry* target,
|
||||
intptr_t self_code_stub_offset_from_thread,
|
||||
bool allow_return) {
|
||||
auto perform_runtime_call = [&]() {
|
||||
__ CallRuntime(*target, /*argument_count=*/0);
|
||||
};
|
||||
GenerateSharedStubGeneric(assembler, save_fpu_registers,
|
||||
self_code_stub_offset_from_thread, allow_return,
|
||||
perform_runtime_call);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateEnterSafepointStub(Assembler* assembler) {
|
||||
RegisterSet all_registers;
|
||||
all_registers.AddAllGeneralRegisters();
|
||||
|
@ -484,7 +489,7 @@ void StubCodeCompiler::GenerateNullErrorSharedWithoutFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/false, &kNullErrorRuntimeEntry,
|
||||
target::Thread::null_error_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, {});
|
||||
/*allow_return=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateNullErrorSharedWithFPURegsStub(
|
||||
|
@ -492,7 +497,7 @@ void StubCodeCompiler::GenerateNullErrorSharedWithFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/true, &kNullErrorRuntimeEntry,
|
||||
target::Thread::null_error_shared_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, {});
|
||||
/*allow_return=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateNullArgErrorSharedWithoutFPURegsStub(
|
||||
|
@ -500,7 +505,7 @@ void StubCodeCompiler::GenerateNullArgErrorSharedWithoutFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/false, &kArgumentNullErrorRuntimeEntry,
|
||||
target::Thread::null_arg_error_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, {});
|
||||
/*allow_return=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateNullArgErrorSharedWithFPURegsStub(
|
||||
|
@ -508,25 +513,66 @@ void StubCodeCompiler::GenerateNullArgErrorSharedWithFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/true, &kArgumentNullErrorRuntimeEntry,
|
||||
target::Thread::null_arg_error_shared_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, {});
|
||||
/*allow_return=*/false);
|
||||
}
|
||||
|
||||
static void GenerateRangeError(Assembler* assembler, bool with_fpu_regs) {
|
||||
auto perform_runtime_call = [&]() {
|
||||
// If the generated code has unboxed index/length we need to box them before
|
||||
// calling the runtime entry.
|
||||
if (GenericCheckBoundInstr::UseUnboxedRepresentation()) {
|
||||
Label length, smi_case;
|
||||
|
||||
// The user-controlled index might not fit into a Smi.
|
||||
__ addq(RangeErrorABI::kIndexReg, RangeErrorABI::kIndexReg);
|
||||
__ BranchIf(NO_OVERFLOW, &length);
|
||||
{
|
||||
// Allocate a mint, reload the two registers and popualte the mint.
|
||||
__ PushImmediate(Immediate(0));
|
||||
__ CallRuntime(kAllocateMintRuntimeEntry, /*argument_count=*/0);
|
||||
__ PopRegister(RangeErrorABI::kIndexReg);
|
||||
__ movq(
|
||||
TMP,
|
||||
Address(RBP, target::kWordSize *
|
||||
StubCodeCompiler::WordOffsetFromFpToCpuRegister(
|
||||
RangeErrorABI::kIndexReg)));
|
||||
__ movq(FieldAddress(RangeErrorABI::kIndexReg,
|
||||
target::Mint::value_offset()),
|
||||
TMP);
|
||||
__ movq(
|
||||
RangeErrorABI::kLengthReg,
|
||||
Address(RBP, target::kWordSize *
|
||||
StubCodeCompiler::WordOffsetFromFpToCpuRegister(
|
||||
RangeErrorABI::kLengthReg)));
|
||||
}
|
||||
|
||||
// Length is guaranteed to be in positive Smi range (it comes from a load
|
||||
// of a vm recognized array).
|
||||
__ Bind(&length);
|
||||
__ SmiTag(RangeErrorABI::kLengthReg);
|
||||
}
|
||||
__ PushRegister(RangeErrorABI::kLengthReg);
|
||||
__ PushRegister(RangeErrorABI::kIndexReg);
|
||||
__ CallRuntime(kRangeErrorRuntimeEntry, /*argument_count=*/2);
|
||||
__ Breakpoint();
|
||||
};
|
||||
|
||||
GenerateSharedStubGeneric(
|
||||
assembler, /*save_fpu_registers=*/with_fpu_regs,
|
||||
with_fpu_regs
|
||||
? target::Thread::range_error_shared_with_fpu_regs_stub_offset()
|
||||
: target::Thread::range_error_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false, perform_runtime_call);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateRangeErrorSharedWithoutFPURegsStub(
|
||||
Assembler* assembler) {
|
||||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/false, &kRangeErrorRuntimeEntry,
|
||||
target::Thread::range_error_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false,
|
||||
{RangeErrorABI::kLengthReg, RangeErrorABI::kIndexReg});
|
||||
GenerateRangeError(assembler, /*with_fpu_regs=*/false);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateRangeErrorSharedWithFPURegsStub(
|
||||
Assembler* assembler) {
|
||||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/true, &kRangeErrorRuntimeEntry,
|
||||
target::Thread::range_error_shared_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/false,
|
||||
{RangeErrorABI::kLengthReg, RangeErrorABI::kIndexReg});
|
||||
GenerateRangeError(assembler, /*with_fpu_regs=*/true);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateStackOverflowSharedWithoutFPURegsStub(
|
||||
|
@ -534,7 +580,7 @@ void StubCodeCompiler::GenerateStackOverflowSharedWithoutFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/false, &kStackOverflowRuntimeEntry,
|
||||
target::Thread::stack_overflow_shared_without_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/true, {});
|
||||
/*allow_return=*/true);
|
||||
}
|
||||
|
||||
void StubCodeCompiler::GenerateStackOverflowSharedWithFPURegsStub(
|
||||
|
@ -542,7 +588,7 @@ void StubCodeCompiler::GenerateStackOverflowSharedWithFPURegsStub(
|
|||
GenerateSharedStub(
|
||||
assembler, /*save_fpu_registers=*/true, &kStackOverflowRuntimeEntry,
|
||||
target::Thread::stack_overflow_shared_with_fpu_regs_stub_offset(),
|
||||
/*allow_return=*/true, {});
|
||||
/*allow_return=*/true);
|
||||
}
|
||||
|
||||
// Input parameters:
|
||||
|
|
Loading…
Reference in a new issue