Skip to content

Commit ce6852e

Browse files
mkustermanncommit-bot@chromium.org
authored andcommitted
[VM] Optimize hand-written assembly for the implementation [AssertAssignableInstr]
Use a register calling-convention between "inlined code" and the stub. Also avoid unnecessary push/pop/load-from-stack instructions. Issue #31798 Change-Id: I0c07f0bb7ee524b9f5bd6d989b0203f3a53a3625 Reviewed-on: https://dart-review.googlesource.com/34641 Commit-Queue: Martin Kustermann <[email protected]> Reviewed-by: Erik Corry <[email protected]>
1 parent 73e35eb commit ce6852e

File tree

2 files changed

+99
-103
lines changed

2 files changed

+99
-103
lines changed

runtime/vm/compiler/backend/flow_graph_compiler_x64.cc

Lines changed: 49 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,15 @@ void FlowGraphCompiler::GenerateBoolToJump(Register bool_register,
188188
__ Bind(&fall_through);
189189
}
190190

191-
// Clobbers RCX.
191+
// Call stub to perform subtype test using a cache (see
192+
// stub_code_x64.cc:GenerateSubtypeNTestCacheStub)
193+
//
194+
// Inputs:
195+
// - RAX : instance to test against.
196+
// - RDX : instantiator type arguments (if necessary).
197+
// - RCX : function type arguments (if necessary).
198+
//
199+
// Preserves RAX/RCX/RDX.
192200
RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub(
193201
TypeTestStubKind test_kind,
194202
Register instance_reg,
@@ -197,37 +205,27 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub(
197205
Register temp_reg,
198206
Label* is_instance_lbl,
199207
Label* is_not_instance_lbl) {
208+
ASSERT(temp_reg == kNoRegister);
200209
const SubtypeTestCache& type_test_cache =
201210
SubtypeTestCache::ZoneHandle(zone(), SubtypeTestCache::New());
202-
__ LoadUniqueObject(temp_reg, type_test_cache);
203-
__ pushq(temp_reg); // Subtype test cache.
204-
__ pushq(instance_reg); // Instance.
211+
__ LoadUniqueObject(R9, type_test_cache);
205212
if (test_kind == kTestTypeOneArg) {
206213
ASSERT(instantiator_type_arguments_reg == kNoRegister);
207214
ASSERT(function_type_arguments_reg == kNoRegister);
208-
__ PushObject(Object::null_object()); // Unused instantiator type args.
209-
__ PushObject(Object::null_object()); // Unused function type args.
210215
__ Call(*StubCode::Subtype1TestCache_entry());
211216
} else if (test_kind == kTestTypeTwoArgs) {
212217
ASSERT(instantiator_type_arguments_reg == kNoRegister);
213218
ASSERT(function_type_arguments_reg == kNoRegister);
214-
__ PushObject(Object::null_object()); // Unused instantiator type args.
215-
__ PushObject(Object::null_object()); // Unused function type args.
216219
__ Call(*StubCode::Subtype2TestCache_entry());
217220
} else if (test_kind == kTestTypeFourArgs) {
218-
__ pushq(instantiator_type_arguments_reg);
219-
__ pushq(function_type_arguments_reg);
221+
ASSERT(RDX == instantiator_type_arguments_reg);
222+
ASSERT(RCX == function_type_arguments_reg);
220223
__ Call(*StubCode::Subtype4TestCache_entry());
221224
} else {
222225
UNREACHABLE();
223226
}
224-
// Result is in RCX: null -> not found, otherwise Bool::True or Bool::False.
225-
ASSERT(instance_reg != RCX);
226-
ASSERT(temp_reg != RCX);
227-
__ Drop(2);
228-
__ popq(instance_reg); // Restore receiver.
229-
__ popq(temp_reg); // Discard.
230-
GenerateBoolToJump(RCX, is_instance_lbl, is_not_instance_lbl);
227+
// Result is in R8: null -> not found, otherwise Bool::True or Bool::False.
228+
GenerateBoolToJump(R8, is_instance_lbl, is_not_instance_lbl);
231229
return type_test_cache.raw();
232230
}
233231

@@ -301,7 +299,7 @@ FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest(
301299
// Regular subtype test cache involving instance's type arguments.
302300
const Register kInstantiatorTypeArgumentsReg = kNoRegister;
303301
const Register kFunctionTypeArgumentsReg = kNoRegister;
304-
const Register kTempReg = R10;
302+
const Register kTempReg = kNoRegister;
305303
return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, kInstanceReg,
306304
kInstantiatorTypeArgumentsReg,
307305
kFunctionTypeArgumentsReg, kTempReg,
@@ -320,9 +318,13 @@ void FlowGraphCompiler::CheckClassIds(Register class_id_reg,
320318
}
321319

322320
// Testing against an instantiated type with no arguments, without
323-
// SubtypeTestCache.
324-
// RAX: instance to test against (preserved).
325-
// Clobbers R10, R13.
321+
// SubtypeTestCache
322+
//
323+
// Inputs:
324+
// - RAX : instance to test against
325+
//
326+
// Preserves RAX/RCX/RDX.
327+
//
326328
// Returns true if there is a fallthrough.
327329
bool FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest(
328330
TokenPosition token_pos,
@@ -387,9 +389,13 @@ bool FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest(
387389
}
388390

389391
// Uses SubtypeTestCache to store instance class and result.
390-
// RAX: instance to test.
391-
// Clobbers R10, R13.
392392
// Immediate class test already done.
393+
//
394+
// Inputs:
395+
// RAX : instance to test against.
396+
//
397+
// Preserves RAX/RCX/RDX.
398+
//
393399
// TODO(srdjan): Implement a quicker subtype check, as type test
394400
// arrays can grow too high, but they may be useful when optimizing
395401
// code (type-feedback).
@@ -410,28 +416,35 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup(
410416

411417
const Register kInstantiatorTypeArgumentsReg = kNoRegister;
412418
const Register kFunctionTypeArgumentsReg = kNoRegister;
413-
const Register kTempReg = R10;
419+
const Register kTempReg = kNoRegister;
414420
return GenerateCallSubtypeTestStub(kTestTypeOneArg, kInstanceReg,
415421
kInstantiatorTypeArgumentsReg,
416422
kFunctionTypeArgumentsReg, kTempReg,
417423
is_instance_lbl, is_not_instance_lbl);
418424
}
419425

420426
// Generates inlined check if 'type' is a type parameter or type itself
421-
// RAX: instance (preserved).
422-
// Clobbers RDI, RDX, R10.
427+
//
428+
// Inputs:
429+
// - RAX : instance to test against.
430+
// - RDX : instantiator type arguments (if necessary).
431+
// - RCX : function type arguments (if necessary).
432+
//
433+
// Preserves RAX/RCX/RDX.
423434
RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest(
424435
TokenPosition token_pos,
425436
const AbstractType& type,
426437
Label* is_instance_lbl,
427438
Label* is_not_instance_lbl) {
439+
const Register kInstanceReg = RAX;
440+
const Register kInstantiatorTypeArgumentsReg = RDX;
441+
const Register kFunctionTypeArgumentsReg = RCX;
442+
const Register kTempReg = kNoRegister;
428443
__ Comment("UninstantiatedTypeTest");
429444
ASSERT(!type.IsInstantiated());
430445
// Skip check if destination is a dynamic type.
431446
if (type.IsTypeParameter()) {
432447
const TypeParameter& type_param = TypeParameter::Cast(type);
433-
__ movq(RDX, Address(RSP, 1 * kWordSize)); // Get instantiator type args.
434-
__ movq(RCX, Address(RSP, 0 * kWordSize)); // Get function type args.
435448
// RDX: instantiator type arguments.
436449
// RCX: function type arguments.
437450
const Register kTypeArgumentsReg =
@@ -463,13 +476,6 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest(
463476
__ jmp(&fall_through);
464477

465478
__ Bind(&not_smi);
466-
// RAX: instance.
467-
// RDX: instantiator type arguments.
468-
// RCX: function type arguments.
469-
const Register kInstanceReg = RAX;
470-
const Register kInstantiatorTypeArgumentsReg = RDX;
471-
const Register kFunctionTypeArgumentsReg = RCX;
472-
const Register kTempReg = R10;
473479
const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle(
474480
zone(), GenerateCallSubtypeTestStub(
475481
kTestTypeFourArgs, kInstanceReg,
@@ -479,16 +485,10 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest(
479485
return type_test_cache.raw();
480486
}
481487
if (type.IsType()) {
482-
const Register kInstanceReg = RAX;
483-
const Register kInstantiatorTypeArgumentsReg = RDX;
484-
const Register kFunctionTypeArgumentsReg = RCX;
485488
__ testq(kInstanceReg, Immediate(kSmiTagMask)); // Is instance Smi?
486489
__ j(ZERO, is_not_instance_lbl);
487-
__ movq(kInstantiatorTypeArgumentsReg, Address(RSP, 1 * kWordSize));
488-
__ movq(kFunctionTypeArgumentsReg, Address(RSP, 0 * kWordSize));
489490
// Uninstantiated type class is known at compile time, but the type
490491
// arguments are determined at runtime by the instantiator(s).
491-
const Register kTempReg = R10;
492492
return GenerateCallSubtypeTestStub(kTestTypeFourArgs, kInstanceReg,
493493
kInstantiatorTypeArgumentsReg,
494494
kFunctionTypeArgumentsReg, kTempReg,
@@ -498,13 +498,12 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest(
498498
}
499499

500500
// Inputs:
501-
// - RAX: instance to test against (preserved).
502-
// - RDX: optional instantiator type arguments (preserved).
503-
// - RCX: optional function type arguments (preserved).
504-
// Clobbers R10, R13.
505-
// Returns:
506-
// - preserved instance in RAX, optional instantiator type arguments in RDX, and
507-
// optional function type arguments in RCX.
501+
// - RAX : instance to test against.
502+
// - RDX : instantiator type arguments.
503+
// - RCX : function type arguments.
504+
//
505+
// Preserves RAX/RCX/RDX.
506+
//
508507
// Note that this inlined code must be followed by the runtime_call code, as it
509508
// may fall through to it. Otherwise, this inline code will jump to the label
510509
// is_instance or to the label is_not_instance.
@@ -557,9 +556,6 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
557556
ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded());
558557
ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType());
559558

560-
__ pushq(RDX); // Store instantiator type arguments.
561-
__ pushq(RCX); // Store function type arguments.
562-
563559
Label is_instance, is_not_instance;
564560
// If type is instantiated and non-parameterized, we can inline code
565561
// checking whether the tested instance is a Smi.
@@ -577,15 +573,14 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
577573

578574
// Generate inline instanceof test.
579575
SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(zone());
576+
// The registers RAX, RCX, RDX are preserved across the call.
580577
test_cache =
581578
GenerateInlineInstanceof(token_pos, type, &is_instance, &is_not_instance);
582579

583580
// test_cache is null if there is no fall-through.
584581
Label done;
585582
if (!test_cache.IsNull()) {
586583
// Generate runtime call.
587-
__ movq(RDX, Address(RSP, 1 * kWordSize)); // Get instantiator type args.
588-
__ movq(RCX, Address(RSP, 0 * kWordSize)); // Get function type args.
589584
__ PushObject(Object::null_object()); // Make room for the result.
590585
__ pushq(RAX); // Push the instance.
591586
__ PushObject(type); // Push the type.
@@ -607,8 +602,6 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
607602
__ Bind(&is_instance);
608603
__ LoadObject(RAX, Bool::Get(true));
609604
__ Bind(&done);
610-
__ popq(RCX); // Remove pushed function type arguments.
611-
__ popq(RDX); // Remove pushed instantiator type arguments.
612605
}
613606

614607
// Optimize assignable type check by adding inlined tests for:
@@ -635,8 +628,6 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
635628
ASSERT(dst_type.IsMalformedOrMalbounded() ||
636629
(!dst_type.IsDynamicType() && !dst_type.IsObjectType() &&
637630
!dst_type.IsVoidType()));
638-
__ pushq(RDX); // Store instantiator type arguments.
639-
__ pushq(RCX); // Store function type arguments.
640631
// A null object is always assignable and is returned as result.
641632
Label is_assignable, runtime_call;
642633
__ CompareObject(RAX, Object::null_object());
@@ -654,19 +645,16 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
654645
__ int3();
655646

656647
__ Bind(&is_assignable); // For a null object.
657-
__ popq(RCX); // Remove pushed function type arguments.
658-
__ popq(RDX); // Remove pushed instantiator type arguments.
659648
return;
660649
}
661650

662651
// Generate inline type check, linking to runtime call if not assignable.
663652
SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(zone());
653+
// The registers RAX, RCX, RDX are preserved across the call.
664654
test_cache = GenerateInlineInstanceof(token_pos, dst_type, &is_assignable,
665655
&runtime_call);
666656

667657
__ Bind(&runtime_call);
668-
__ movq(RDX, Address(RSP, 1 * kWordSize)); // Get instantiator type args.
669-
__ movq(RCX, Address(RSP, 0 * kWordSize)); // Get function type args.
670658
__ PushObject(Object::null_object()); // Make room for the result.
671659
__ pushq(RAX); // Push the source object.
672660
__ PushObject(dst_type); // Push the type of the destination.
@@ -682,8 +670,6 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
682670
__ popq(RAX);
683671

684672
__ Bind(&is_assignable);
685-
__ popq(RCX); // Remove pushed function type arguments.
686-
__ popq(RDX); // Remove pushed instantiator type arguments.
687673
}
688674

689675
void FlowGraphCompiler::EmitInstructionEpilogue(Instruction* instr) {

0 commit comments

Comments
 (0)