[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:
Martin Kustermann 2020-06-22 19:55:36 +00:00 committed by commit-bot@chromium.org
parent e18b937c1f
commit 142e15c2af
14 changed files with 463 additions and 173 deletions

View file

@ -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]);
}
}

View file

@ -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]);
}
}

View file

@ -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());
}

View file

@ -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 {

View file

@ -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";
}

View file

@ -110,6 +110,9 @@ enum MatchOpCode {
// Matches a branch and moves right.
kMatchAndMoveBranchFalse,
// Is ignored.
kNop,
// Moves forward across any instruction.
kMoveAny,

View file

@ -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()) {

View file

@ -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,

View file

@ -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);
}

View file

@ -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.

View file

@ -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.

View file

@ -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).

View file

@ -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).

View file

@ -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: