diff --git a/runtime/tests/vm/dart/generic_check_bound_slow_path_test.dart b/runtime/tests/vm/dart/generic_check_bound_slow_path_test.dart new file mode 100644 index 00000000000..7290b2f700b --- /dev/null +++ b/runtime/tests/vm/dart/generic_check_bound_slow_path_test.dart @@ -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]); + } +} diff --git a/runtime/tests/vm/dart_2/generic_check_bound_slow_path_test.dart b/runtime/tests/vm/dart_2/generic_check_bound_slow_path_test.dart new file mode 100644 index 00000000000..7290b2f700b --- /dev/null +++ b/runtime/tests/vm/dart_2/generic_check_bound_slow_path_test.dart @@ -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]); + } +} diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc index dc254d49f77..511d125794d 100644 --- a/runtime/vm/compiler/backend/il.cc +++ b/runtime/vm/compiler/backend/il.cc @@ -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()); } diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h index 833b3443de7..1d28f31f532 100644 --- a/runtime/vm/compiler/backend/il.h +++ b/runtime/vm/compiler/backend/il.h @@ -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 { diff --git a/runtime/vm/compiler/backend/il_test_helper.cc b/runtime/vm/compiler/backend/il_test_helper.cc index acc6aac8fff..9c5788adad2 100644 --- a/runtime/vm/compiler/backend/il_test_helper.cc +++ b/runtime/vm/compiler/backend/il_test_helper.cc @@ -279,6 +279,9 @@ Instruction* ILMatcher::MatchInternal(std::vector 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"; } diff --git a/runtime/vm/compiler/backend/il_test_helper.h b/runtime/vm/compiler/backend/il_test_helper.h index a0ee7da77a8..ec00bb767a8 100644 --- a/runtime/vm/compiler/backend/il_test_helper.h +++ b/runtime/vm/compiler/backend/il_test_helper.h @@ -110,6 +110,9 @@ enum MatchOpCode { // Matches a branch and moves right. kMatchAndMoveBranchFalse, + // Is ignored. + kNop, + // Moves forward across any instruction. kMoveAny, diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc index 449cfbdf966..88915ae2743 100644 --- a/runtime/vm/compiler/backend/il_x64.cc +++ b/runtime/vm/compiler/backend/il_x64.cc @@ -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()) { diff --git a/runtime/vm/compiler/backend/inliner_test.cc b/runtime/vm/compiler/backend/inliner_test.cc index 4ded3e48cb6..2db12290caf 100644 --- a/runtime/vm/compiler/backend/inliner_test.cc +++ b/runtime/vm/compiler/backend/inliner_test.cc @@ -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, diff --git a/runtime/vm/compiler/backend/typed_data_aot_test.cc b/runtime/vm/compiler/backend/typed_data_aot_test.cc index b1dd7c03e7b..d978e86ea4f 100644 --- a/runtime/vm/compiler/backend/typed_data_aot_test.cc +++ b/runtime/vm/compiler/backend/typed_data_aot_test.cc @@ -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); } diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc index c02a24686f1..87f0a605f0d 100644 --- a/runtime/vm/compiler/stub_code_compiler.cc +++ b/runtime/vm/compiler/stub_code_compiler.cc @@ -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(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. diff --git a/runtime/vm/compiler/stub_code_compiler.h b/runtime/vm/compiler/stub_code_compiler.h index cf4e58d3030..875ea17e7ff 100644 --- a/runtime/vm/compiler/stub_code_compiler.h +++ b/runtime/vm/compiler/stub_code_compiler.h @@ -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::*Shared{With,Without}FpuRegsStub()` + static intptr_t WordOffsetFromFpToCpuRegister(Register cpu_register); + private: // Common function for generating InitLateInstanceField and // InitLateFinalInstanceField stubs. diff --git a/runtime/vm/compiler/stub_code_compiler_arm.cc b/runtime/vm/compiler/stub_code_compiler_arm.cc index 24a50e7abce..5d9365d42ac 100644 --- a/runtime/vm/compiler/stub_code_compiler_arm.cc +++ b/runtime/vm/compiler/stub_code_compiler_arm.cc @@ -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 runtime_call_arguments = {}) { +void GenerateSharedStubGeneric(Assembler* assembler, + bool save_fpu_registers, + intptr_t self_code_stub_offset_from_thread, + bool allow_return, + std::function 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). diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc index f8c009cc4bc..08d59312b4f 100644 --- a/runtime/vm/compiler/stub_code_compiler_arm64.cc +++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc @@ -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 runtime_call_arguments = {}) { + std::function 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). diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc index 67261a1ae4d..4d4637ad52c 100644 --- a/runtime/vm/compiler/stub_code_compiler_x64.cc +++ b/runtime/vm/compiler/stub_code_compiler_x64.cc @@ -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 runtime_call_arguments) { + std::function 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: