Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Namespaces | Macros | Enumerations | Functions
il_test.cc File Reference
#include "vm/compiler/backend/il.h"
#include <vector>
#include "platform/text_buffer.h"
#include "platform/utils.h"
#include "vm/class_id.h"
#include "vm/compiler/backend/block_builder.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/backend/il_test_helper.h"
#include "vm/compiler/backend/range_analysis.h"
#include "vm/compiler/backend/type_propagator.h"
#include "vm/unit_test.h"

Go to the source code of this file.

Namespaces

namespace  dart
 

Macros

#define RANGES_CONTAIN_EXPECTED_CIDS(ranges, cids)    RangesContainExpectedCids(dart::Expect(__FILE__, __LINE__), ranges, cids)
 

Enumerations

enum  dart::TypeDataField { dart::TypedDataBase_length , dart::TypedDataView_offset_in_bytes , dart::TypedDataView_typed_data }
 

Functions

 dart::ISOLATE_UNIT_TEST_CASE (InstructionTests)
 
 dart::ISOLATE_UNIT_TEST_CASE (OptimizationTests)
 
 dart::ISOLATE_UNIT_TEST_CASE (IRTest_EliminateWriteBarrier)
 
static void dart::ExpectStores (FlowGraph *flow_graph, const std::vector< const char * > &expected_stores)
 
static void dart::RunInitializingStoresTest (const Library &root_library, const char *function_name, CompilerPass::PipelineMode mode, const std::vector< const char * > &expected_stores)
 
 dart::ISOLATE_UNIT_TEST_CASE (IRTest_InitializingStores)
 
bool dart::TestIntConverterCanonicalizationRule (Thread *thread, int64_t min_value, int64_t max_value, Representation initial, Representation intermediate, Representation final)
 
 dart::ISOLATE_UNIT_TEST_CASE (IL_IntConverterCanonicalization)
 
 dart::ISOLATE_UNIT_TEST_CASE (IL_PhiCanonicalization)
 
 dart::ISOLATE_UNIT_TEST_CASE (IL_UnboxIntegerCanonicalization)
 
static void dart::WriteCidTo (intptr_t cid, BaseTextBuffer *buffer)
 
static void dart::TestNullAwareEqualityCompareCanonicalization (Thread *thread, bool allow_representation_change)
 
 dart::ISOLATE_UNIT_TEST_CASE (IL_Canonicalize_EqualityCompare)
 
static void dart::WriteCidRangeVectorTo (const CidRangeVector &ranges, BaseTextBuffer *buffer)
 
static bool dart::ExpectRangesContainCid (const Expect &expect, const CidRangeVector &ranges, intptr_t expected)
 
static void dart::RangesContainExpectedCids (const Expect &expect, const CidRangeVector &ranges, const GrowableArray< intptr_t > &expected)
 
 dart::ISOLATE_UNIT_TEST_CASE (HierarchyInfo_Object_Subtype)
 
 dart::ISOLATE_UNIT_TEST_CASE (HierarchyInfo_Function_Subtype)
 
 dart::ISOLATE_UNIT_TEST_CASE (HierarchyInfo_Num_Subtype)
 
 dart::ISOLATE_UNIT_TEST_CASE (HierarchyInfo_Int_Subtype)
 
 dart::ISOLATE_UNIT_TEST_CASE (HierarchyInfo_String_Subtype)
 
 dart::ISOLATE_UNIT_TEST_CASE (IRTest_DoubleEqualsSmi)
 
 dart::ISOLATE_UNIT_TEST_CASE (IRTest_LoadThread)
 
 dart::ISOLATE_UNIT_TEST_CASE (IRTest_CachableIdempotentCall)
 
FlowGraphdart::SetupFfiFlowgraph (TestPipeline *pipeline, const compiler::ffi::CallMarshaller &marshaller, uword native_entry, bool is_leaf)
 
 dart::ISOLATE_UNIT_TEST_CASE (IRTest_FfiCallInstrLeafDoesntSpill)
 
static void dart::TestConstantFoldToSmi (const Library &root_library, const char *function_name, CompilerPass::PipelineMode mode, intptr_t expected_value)
 
 dart::ISOLATE_UNIT_TEST_CASE (ConstantFold_bitLength)
 
static void dart::TestRepresentationChangeDuringCanonicalization (Thread *thread, bool allow_representation_change)
 
 dart::ISOLATE_UNIT_TEST_CASE (IL_Canonicalize_RepresentationChange)
 
static void dart::TestCanonicalizationOfTypedDataViewFieldLoads (Thread *thread, TypeDataField field_kind)
 
 dart::ISOLATE_UNIT_TEST_CASE (IL_Canonicalize_TypedDataViewFactory)
 
 dart::ISOLATE_UNIT_TEST_CASE (IL_Canonicalize_InstanceCallWithNoICDataInAOT)
 
static void dart::TestTestRangeCanonicalize (const AbstractType &type, uword lower, uword upper, bool result)
 
 dart::ISOLATE_UNIT_TEST_CASE (IL_Canonicalize_TestRange)
 
void dart::TestStaticFieldForwarding (Thread *thread, const Class &test_cls, const Field &field, intptr_t num_stores, bool expected_to_forward)
 
 dart::ISOLATE_UNIT_TEST_CASE (IL_Canonicalize_FinalFieldForwarding)
 

Macro Definition Documentation

◆ RANGES_CONTAIN_EXPECTED_CIDS

#define RANGES_CONTAIN_EXPECTED_CIDS (   ranges,
  cids 
)     RangesContainExpectedCids(dart::Expect(__FILE__, __LINE__), ranges, cids)

Definition at line 498 of file il_test.cc.

500 {
501 HierarchyInfo hi(thread);
502 const auto& type =
503 Type::Handle(IsolateGroup::Current()->object_store()->object_type());
504 const bool is_nullable = Instance::NullIsAssignableTo(type);
505 EXPECT(hi.CanUseSubtypeRangeCheckFor(type));
506 const auto& cls = Class::Handle(type.type_class());
507
508 ClassTable* const class_table = thread->isolate_group()->class_table();
509 const intptr_t num_cids = class_table->NumCids();
510 auto& to_check = Class::Handle(thread->zone());
511 auto& rare_type = AbstractType::Handle(thread->zone());
512
513 GrowableArray<intptr_t> expected_concrete_cids;
514 GrowableArray<intptr_t> expected_abstract_cids;
515 for (intptr_t cid = kInstanceCid; cid < num_cids; cid++) {
516 if (!class_table->HasValidClassAt(cid)) continue;
517 if (cid == kNullCid) continue;
518 if (cid == kNeverCid) continue;
519 if (cid == kDynamicCid && !is_nullable) continue;
520 if (cid == kVoidCid && !is_nullable) continue;
521 to_check = class_table->At(cid);
522 // Only add concrete classes.
523 if (to_check.is_abstract()) {
524 expected_abstract_cids.Add(cid);
525 } else {
526 expected_concrete_cids.Add(cid);
527 }
528 if (cid != kTypeArgumentsCid) { // Cannot call RareType() on this.
529 rare_type = to_check.RareType();
530 EXPECT(rare_type.IsSubtypeOf(type, Heap::kNew));
531 }
532 }
533
534 const CidRangeVector& concrete_range = hi.SubtypeRangesForClass(
535 cls, /*include_abstract=*/false, /*exclude_null=*/!is_nullable);
536 RANGES_CONTAIN_EXPECTED_CIDS(concrete_range, expected_concrete_cids);
537
538 GrowableArray<intptr_t> expected_cids;
539 expected_cids.AddArray(expected_concrete_cids);
540 expected_cids.AddArray(expected_abstract_cids);
541 const CidRangeVector& abstract_range = hi.SubtypeRangesForClass(
542 cls, /*include_abstract=*/true, /*exclude_null=*/!is_nullable);
543 RANGES_CONTAIN_EXPECTED_CIDS(abstract_range, expected_cids);
544}
545
546ISOLATE_UNIT_TEST_CASE(HierarchyInfo_Function_Subtype) {
547 HierarchyInfo hi(thread);
548 const auto& type =
549 Type::Handle(IsolateGroup::Current()->object_store()->function_type());
550 EXPECT(hi.CanUseSubtypeRangeCheckFor(type));
551 const auto& cls = Class::Handle(type.type_class());
552
553 GrowableArray<intptr_t> expected_concrete_cids;
554 expected_concrete_cids.Add(kClosureCid);
555
556 GrowableArray<intptr_t> expected_abstract_cids;
557 expected_abstract_cids.Add(type.type_class_id());
558
559 const CidRangeVector& concrete_range = hi.SubtypeRangesForClass(
560 cls, /*include_abstract=*/false, /*exclude_null=*/true);
561 RANGES_CONTAIN_EXPECTED_CIDS(concrete_range, expected_concrete_cids);
562
563 GrowableArray<intptr_t> expected_cids;
564 expected_cids.AddArray(expected_concrete_cids);
565 expected_cids.AddArray(expected_abstract_cids);
566 const CidRangeVector& abstract_range = hi.SubtypeRangesForClass(
567 cls, /*include_abstract=*/true, /*exclude_null=*/true);
568 RANGES_CONTAIN_EXPECTED_CIDS(abstract_range, expected_cids);
569}
570
571ISOLATE_UNIT_TEST_CASE(HierarchyInfo_Num_Subtype) {
572 HierarchyInfo hi(thread);
573 const auto& num_type = Type::Handle(Type::Number());
574 const auto& int_type = Type::Handle(Type::IntType());
575 const auto& double_type = Type::Handle(Type::Double());
576 EXPECT(hi.CanUseSubtypeRangeCheckFor(num_type));
577 const auto& cls = Class::Handle(num_type.type_class());
578
579 GrowableArray<intptr_t> expected_concrete_cids;
580 expected_concrete_cids.Add(kSmiCid);
581 expected_concrete_cids.Add(kMintCid);
582 expected_concrete_cids.Add(kDoubleCid);
583
584 GrowableArray<intptr_t> expected_abstract_cids;
585 expected_abstract_cids.Add(num_type.type_class_id());
586 expected_abstract_cids.Add(int_type.type_class_id());
587 expected_abstract_cids.Add(double_type.type_class_id());
588
589 const CidRangeVector& concrete_range = hi.SubtypeRangesForClass(
590 cls, /*include_abstract=*/false, /*exclude_null=*/true);
591 RANGES_CONTAIN_EXPECTED_CIDS(concrete_range, expected_concrete_cids);
592
593 GrowableArray<intptr_t> expected_cids;
594 expected_cids.AddArray(expected_concrete_cids);
595 expected_cids.AddArray(expected_abstract_cids);
596 const CidRangeVector& abstract_range = hi.SubtypeRangesForClass(
597 cls, /*include_abstract=*/true, /*exclude_null=*/true);
598 RANGES_CONTAIN_EXPECTED_CIDS(abstract_range, expected_cids);
599}
600
601ISOLATE_UNIT_TEST_CASE(HierarchyInfo_Int_Subtype) {
602 HierarchyInfo hi(thread);
603 const auto& type = Type::Handle(Type::IntType());
604 EXPECT(hi.CanUseSubtypeRangeCheckFor(type));
605 const auto& cls = Class::Handle(type.type_class());
606
607 GrowableArray<intptr_t> expected_concrete_cids;
608 expected_concrete_cids.Add(kSmiCid);
609 expected_concrete_cids.Add(kMintCid);
610
611 GrowableArray<intptr_t> expected_abstract_cids;
612 expected_abstract_cids.Add(type.type_class_id());
613
614 const CidRangeVector& concrete_range = hi.SubtypeRangesForClass(
615 cls, /*include_abstract=*/false, /*exclude_null=*/true);
616 RANGES_CONTAIN_EXPECTED_CIDS(concrete_range, expected_concrete_cids);
617
618 GrowableArray<intptr_t> expected_cids;
619 expected_cids.AddArray(expected_concrete_cids);
620 expected_cids.AddArray(expected_abstract_cids);
621 const CidRangeVector& abstract_range = hi.SubtypeRangesForClass(
622 cls, /*include_abstract=*/true, /*exclude_null=*/true);
623 RANGES_CONTAIN_EXPECTED_CIDS(abstract_range, expected_cids);
624}
625
626ISOLATE_UNIT_TEST_CASE(HierarchyInfo_String_Subtype) {
627 HierarchyInfo hi(thread);
628 const auto& type = Type::Handle(Type::StringType());
629 EXPECT(hi.CanUseSubtypeRangeCheckFor(type));
630 const auto& cls = Class::Handle(type.type_class());
631
632 GrowableArray<intptr_t> expected_concrete_cids;
633 expected_concrete_cids.Add(kOneByteStringCid);
634 expected_concrete_cids.Add(kTwoByteStringCid);
635
636 GrowableArray<intptr_t> expected_abstract_cids;
637 expected_abstract_cids.Add(type.type_class_id());
638
639 const CidRangeVector& concrete_range = hi.SubtypeRangesForClass(
640 cls, /*include_abstract=*/false, /*exclude_null=*/true);
641 THR_Print("Checking concrete subtype ranges for String\n");
642 RANGES_CONTAIN_EXPECTED_CIDS(concrete_range, expected_concrete_cids);
643
644 GrowableArray<intptr_t> expected_cids;
645 expected_cids.AddArray(expected_concrete_cids);
646 expected_cids.AddArray(expected_abstract_cids);
647 const CidRangeVector& abstract_range = hi.SubtypeRangesForClass(
648 cls, /*include_abstract=*/true, /*exclude_null=*/true);
649 THR_Print("Checking concrete and abstract subtype ranges for String\n");
650 RANGES_CONTAIN_EXPECTED_CIDS(abstract_range, expected_cids);
651}
652
653// This test verifies that double == Smi is recognized and
654// implemented using EqualityCompare.
655// Regression test for https://github.com/dart-lang/sdk/issues/47031.
656ISOLATE_UNIT_TEST_CASE(IRTest_DoubleEqualsSmi) {
657 const char* kScript = R"(
658 bool foo(double x) => (x + 0.5) == 0;
659 main() {
660 foo(-0.5);
661 }
662 )";
663
664 const auto& root_library = Library::Handle(LoadTestScript(kScript));
665 const auto& function = Function::Handle(GetFunction(root_library, "foo"));
666
667 TestPipeline pipeline(function, CompilerPass::kAOT);
668 FlowGraph* flow_graph = pipeline.RunPasses({});
669
670 auto entry = flow_graph->graph_entry()->normal_entry();
671 ILMatcher cursor(flow_graph, entry, /*trace=*/true,
672 ParallelMovesHandling::kSkip);
673
674 RELEASE_ASSERT(cursor.TryMatch({
675 kMoveGlob,
676 kMatchAndMoveBinaryDoubleOp,
677 kMatchAndMoveEqualityCompare,
678 kMatchDartReturn,
679 }));
680}
681
682ISOLATE_UNIT_TEST_CASE(IRTest_LoadThread) {
683 // clang-format off
684 auto kScript = R"(
685 import 'dart:ffi';
686
687 int myFunction() {
688 return 100;
689 }
690
691 void anotherFunction() {}
692 )";
693 // clang-format on
694
695 const auto& root_library = Library::Handle(LoadTestScript(kScript));
696 Zone* const zone = Thread::Current()->zone();
697 auto& invoke_result = Instance::Handle(zone);
698 invoke_result ^= Invoke(root_library, "myFunction");
699 EXPECT_EQ(Smi::New(100), invoke_result.ptr());
700
701 const auto& my_function =
702 Function::Handle(GetFunction(root_library, "myFunction"));
703
704 TestPipeline pipeline(my_function, CompilerPass::kJIT);
705 FlowGraph* flow_graph = pipeline.RunPasses({
706 CompilerPass::kComputeSSA,
707 });
708
709 DartReturnInstr* return_instr = nullptr;
710 {
711 ILMatcher cursor(flow_graph, flow_graph->graph_entry()->normal_entry());
712
713 EXPECT(cursor.TryMatch({
714 kMoveGlob,
715 {kMatchDartReturn, &return_instr},
716 }));
717 }
718
719 auto* const load_thread_instr = new (zone) LoadThreadInstr();
720 flow_graph->InsertBefore(return_instr, load_thread_instr, nullptr,
721 FlowGraph::kValue);
722 auto load_thread_value = Value(load_thread_instr);
723
724 auto* const convert_instr = new (zone) IntConverterInstr(
725 kUntagged, kUnboxedAddress, &load_thread_value, DeoptId::kNone);
726 flow_graph->InsertBefore(return_instr, convert_instr, nullptr,
727 FlowGraph::kValue);
728 auto convert_value = Value(convert_instr);
729
730 auto* const box_instr = BoxInstr::Create(kUnboxedAddress, &convert_value);
731 flow_graph->InsertBefore(return_instr, box_instr, nullptr, FlowGraph::kValue);
732
733 return_instr->InputAt(0)->definition()->ReplaceUsesWith(box_instr);
734
735 {
736 // Check we constructed the right graph.
737 ILMatcher cursor(flow_graph, flow_graph->graph_entry()->normal_entry());
738 EXPECT(cursor.TryMatch({
739 kMoveGlob,
740 kMatchAndMoveLoadThread,
741 kMatchAndMoveIntConverter,
742 kMatchAndMoveBox,
743 kMatchDartReturn,
744 }));
745 }
746
747 pipeline.RunForcedOptimizedAfterSSAPasses();
748
749 {
750#if !defined(PRODUCT) && !defined(USING_THREAD_SANITIZER)
751 SetFlagScope<bool> sfs(&FLAG_disassemble_optimized, true);
752#endif
753 pipeline.CompileGraphAndAttachFunction();
754 }
755
756 // Ensure we can successfully invoke the function.
757 invoke_result ^= Invoke(root_library, "myFunction");
758 intptr_t result_int = Integer::Cast(invoke_result).AsInt64Value();
759 EXPECT_EQ(reinterpret_cast<intptr_t>(thread), result_int);
760}
761
762#if !defined(TARGET_ARCH_IA32)
763ISOLATE_UNIT_TEST_CASE(IRTest_CachableIdempotentCall) {
764 // clang-format off
765 auto kScript = Utils::CStringUniquePtr(OS::SCreate(nullptr, R"(
766 int globalCounter = 0;
767
768 int increment() => ++globalCounter;
769
770 int cachedIncrement() {
771 // We will replace this call with a cacheable call,
772 // which will lead to the counter no longer being incremented.
773 // Make sure to return the value, so we can see that the boxing and
774 // unboxing works as expected.
775 return increment();
776 }
777
778 int multipleIncrement() {
779 int returnValue = 0;
780 for(int i = 0; i < 10; i++) {
781 // Save the last returned value.
782 returnValue = cachedIncrement();
783 }
784 return returnValue;
785 }
786 )"), std::free);
787 // clang-format on
788
789 const auto& root_library = Library::Handle(LoadTestScript(kScript.get()));
790 const auto& first_result =
791 Object::Handle(Invoke(root_library, "multipleIncrement"));
792 EXPECT(first_result.IsSmi());
793 if (first_result.IsSmi()) {
794 const intptr_t int_value = Smi::Cast(first_result).Value();
795 EXPECT_EQ(10, int_value);
796 }
797
798 const auto& cached_increment_function =
799 Function::Handle(GetFunction(root_library, "cachedIncrement"));
800
801 const auto& increment_function =
802 Function::ZoneHandle(GetFunction(root_library, "increment"));
803
804 TestPipeline pipeline(cached_increment_function, CompilerPass::kJIT);
805 FlowGraph* flow_graph = pipeline.RunPasses({
806 CompilerPass::kComputeSSA,
807 });
808
809 StaticCallInstr* static_call = nullptr;
810 {
811 ILMatcher cursor(flow_graph, flow_graph->graph_entry()->normal_entry());
812
813 EXPECT(cursor.TryMatch({
814 kMoveGlob,
815 {kMatchAndMoveStaticCall, &static_call},
816 kMoveGlob,
817 kMatchDartReturn,
818 }));
819 }
820
822 CachableIdempotentCallInstr* call = new CachableIdempotentCallInstr(
823 InstructionSource(), kUnboxedAddress, increment_function,
824 static_call->type_args_len(), Array::empty_array(), std::move(args),
825 DeoptId::kNone);
826 static_call->ReplaceWith(call, nullptr);
827
828 pipeline.RunForcedOptimizedAfterSSAPasses();
829
830 {
831 ILMatcher cursor(flow_graph, flow_graph->graph_entry()->normal_entry());
832
833 EXPECT(cursor.TryMatch({
834 kMoveGlob,
835 kMatchAndMoveCachableIdempotentCall,
836 kMoveGlob,
837 // The cacheable call returns unboxed, so select representations
838 // adds boxing.
839 kMatchBox,
840 kMoveGlob,
841 kMatchDartReturn,
842 }));
843 }
844
845 {
846#if !defined(PRODUCT)
847 SetFlagScope<bool> sfs(&FLAG_disassemble_optimized, true);
848#endif
849 pipeline.CompileGraphAndAttachFunction();
850 }
851
852 const auto& second_result =
853 Object::Handle(Invoke(root_library, "multipleIncrement"));
854 EXPECT(second_result.IsSmi());
855 if (second_result.IsSmi()) {
856 const intptr_t int_value = Smi::Cast(second_result).Value();
857 EXPECT_EQ(11, int_value);
858 }
859}
860#endif
861
862// Helper to set up an inlined FfiCall by replacing a StaticCall.
863FlowGraph* SetupFfiFlowgraph(TestPipeline* pipeline,
864 const compiler::ffi::CallMarshaller& marshaller,
865 uword native_entry,
866 bool is_leaf) {
867 FlowGraph* flow_graph = pipeline->RunPasses({CompilerPass::kComputeSSA});
868
869 {
870 // Locate the placeholder call.
871 StaticCallInstr* static_call = nullptr;
872 {
873 ILMatcher cursor(flow_graph, flow_graph->graph_entry()->normal_entry(),
874 /*trace=*/false);
875 cursor.TryMatch({kMoveGlob, {kMatchStaticCall, &static_call}});
876 }
877 RELEASE_ASSERT(static_call != nullptr);
878
879 // Store the native entry as an unboxed constant and convert it to an
880 // untagged pointer for the FfiCall.
881 Zone* const Z = flow_graph->zone();
882 auto* const load_entry_point = new (Z) IntConverterInstr(
883 kUnboxedIntPtr, kUntagged,
884 new (Z) Value(flow_graph->GetConstant(
885 Integer::Handle(Z, Integer::NewCanonical(native_entry)),
886 kUnboxedIntPtr)),
887 DeoptId::kNone);
888 flow_graph->InsertBefore(static_call, load_entry_point, /*env=*/nullptr,
889 FlowGraph::kValue);
890
891 // Make an FfiCall based on ffi_trampoline that calls our native function.
892 const intptr_t num_arguments =
893 FfiCallInstr::InputCountForMarshaller(marshaller);
894 RELEASE_ASSERT(num_arguments == 1);
895 InputsArray arguments(num_arguments);
896 arguments.Add(new (Z) Value(load_entry_point));
897 auto* const ffi_call = new (Z)
898 FfiCallInstr(DeoptId::kNone, marshaller, is_leaf, std::move(arguments));
900 ffi_call->InputAt(ffi_call->TargetAddressIndex())->definition() ==
901 load_entry_point);
902 flow_graph->InsertBefore(static_call, ffi_call, /*env=*/nullptr,
903 FlowGraph::kEffect);
904
905 // Remove the placeholder call.
906 static_call->RemoveFromGraph(/*return_previous=*/false);
907 }
908
909 // Run remaining relevant compiler passes.
910 pipeline->RunAdditionalPasses({
911 CompilerPass::kApplyICData,
912 CompilerPass::kTryOptimizePatterns,
913 CompilerPass::kSetOuterInliningId,
914 CompilerPass::kTypePropagation,
915 // Skipping passes that don't seem to do anything for this test.
916 CompilerPass::kWidenSmiToInt32,
917 CompilerPass::kSelectRepresentations,
918 // Skipping passes that don't seem to do anything for this test.
919 CompilerPass::kTypePropagation,
920 CompilerPass::kRangeAnalysis,
921 // Skipping passes that don't seem to do anything for this test.
922 CompilerPass::kFinalizeGraph,
923 CompilerPass::kCanonicalize,
924 CompilerPass::kAllocateRegisters,
925 CompilerPass::kReorderBlocks,
926 });
927
928 return flow_graph;
929}
930
931// Test that FFI calls spill all live values to the stack, and that FFI leaf
932// calls are free to use available ABI callee-save registers to avoid spilling.
933// Additionally test that register allocation is done correctly by clobbering
934// all volatile registers in the native function being called.
935ISOLATE_UNIT_TEST_CASE(IRTest_FfiCallInstrLeafDoesntSpill) {
936 const char* kScript = R"(
937 import 'dart:ffi';
938
939 // This is purely a placeholder and is never called.
940 void placeholder() {}
941
942 // Will call the "doFfiCall" and exercise its code.
943 bool invokeDoFfiCall() {
944 final double result = doFfiCall(1, 2, 3, 1.0, 2.0, 3.0);
945 if (result != (2 + 3 + 4 + 2.0 + 3.0 + 4.0)) {
946 throw 'Failed. Result was $result.';
947 }
948 return true;
949 }
950
951 // Will perform a "C" call while having live values in registers
952 // across the FfiCall.
953 double doFfiCall(int a, int b, int c, double x, double y, double z) {
954 // Ensure there is at least one live value in a register.
955 a += 1;
956 b += 1;
957 c += 1;
958 x += 1.0;
959 y += 1.0;
960 z += 1.0;
961 // We'll replace this StaticCall with an FfiCall.
962 placeholder();
963 // Use the live value.
964 return (a + b + c + x + y + z);
965 }
966
967 // FFI trampoline function.
968 typedef NT = Void Function();
969 typedef DT = void Function();
970 Pointer<NativeFunction<NT>> ptr = Pointer.fromAddress(0);
971 DT getFfiTrampolineClosure() => ptr.asFunction(isLeaf:true);
972 )";
973
974 const auto& root_library = Library::Handle(LoadTestScript(kScript));
975
976 // Build a "C" function that we can actually invoke.
977 auto& c_function = Instructions::Handle(
978 BuildInstructions([](compiler::Assembler* assembler) {
979 // Clobber all volatile registers to make sure caller doesn't rely on
980 // any non-callee-save register.
981 for (intptr_t reg = 0; reg < kNumberOfFpuRegisters; reg++) {
982 if ((kAbiVolatileFpuRegs & (1 << reg)) != 0) {
983#if defined(TARGET_ARCH_ARM)
984 // On ARM we need an extra scratch register for LoadDImmediate.
985 assembler->LoadDImmediate(static_cast<DRegister>(reg), 0.0, R3);
986#else
987 assembler->LoadDImmediate(static_cast<FpuRegister>(reg), 0.0);
988#endif
989 }
990 }
991 for (intptr_t reg = 0; reg < kNumberOfCpuRegisters; reg++) {
992 if ((kDartVolatileCpuRegs & (1 << reg)) != 0) {
993 assembler->LoadImmediate(static_cast<Register>(reg), 0xDEADBEEF);
994 }
995 }
996 assembler->Ret();
997 }));
998 uword native_entry = c_function.EntryPoint();
999
1000 // Get initial compilation done.
1001 Invoke(root_library, "invokeDoFfiCall");
1002
1003 const Function& do_ffi_call =
1004 Function::Handle(GetFunction(root_library, "doFfiCall"));
1005 RELEASE_ASSERT(!do_ffi_call.IsNull());
1006
1007 const auto& value = Closure::Handle(
1008 Closure::RawCast(Invoke(root_library, "getFfiTrampolineClosure")));
1009 RELEASE_ASSERT(value.IsClosure());
1010 const auto& ffi_trampoline =
1011 Function::ZoneHandle(Closure::Cast(value).function());
1012 RELEASE_ASSERT(!ffi_trampoline.IsNull());
1013
1014 // Construct the FFICallInstr from the trampoline matching our native
1015 // function.
1016 const char* error = nullptr;
1017 auto* const zone = thread->zone();
1018 const auto& c_signature =
1019 FunctionType::ZoneHandle(zone, ffi_trampoline.FfiCSignature());
1020 const auto marshaller_ptr = compiler::ffi::CallMarshaller::FromFunction(
1021 zone, ffi_trampoline, /*function_params_start_at=*/1, c_signature,
1022 &error);
1023 RELEASE_ASSERT(error == nullptr);
1024 RELEASE_ASSERT(marshaller_ptr != nullptr);
1025 const auto& marshaller = *marshaller_ptr;
1026
1027 const auto& compile_and_run =
1028 [&](bool is_leaf, std::function<void(ParallelMoveInstr*)> verify) {
1029 // Build the SSA graph for "doFfiCall"
1030 TestPipeline pipeline(do_ffi_call, CompilerPass::kJIT);
1031 FlowGraph* flow_graph =
1032 SetupFfiFlowgraph(&pipeline, marshaller, native_entry, is_leaf);
1033
1034 {
1035 ParallelMoveInstr* parallel_move = nullptr;
1036 ILMatcher cursor(flow_graph,
1037 flow_graph->graph_entry()->normal_entry(),
1038 /*trace=*/false);
1039 while (cursor.TryMatch(
1040 {kMoveGlob, {kMatchAndMoveParallelMove, &parallel_move}})) {
1041 verify(parallel_move);
1042 }
1043 }
1044
1045 // Finish the compilation and attach code so we can run it.
1046 pipeline.CompileGraphAndAttachFunction();
1047
1048 // Ensure we can successfully invoke the FFI call.
1049 auto& result = Object::Handle(Invoke(root_library, "invokeDoFfiCall"));
1050 RELEASE_ASSERT(result.IsBool());
1051 EXPECT(Bool::Cast(result).value());
1052 };
1053
1054 intptr_t num_cpu_reg_to_stack_nonleaf = 0;
1055 intptr_t num_cpu_reg_to_stack_leaf = 0;
1056 intptr_t num_fpu_reg_to_stack_nonleaf = 0;
1057 intptr_t num_fpu_reg_to_stack_leaf = 0;
1058
1059 // Test non-leaf spills live values.
1060 compile_and_run(/*is_leaf=*/false, [&](ParallelMoveInstr* parallel_move) {
1061 // TargetAddress is passed in register, live values are all spilled.
1062 for (int i = 0; i < parallel_move->NumMoves(); i++) {
1063 auto move = parallel_move->moves()[i];
1064 if (move->src_slot()->IsRegister() && move->dest_slot()->IsStackSlot()) {
1065 num_cpu_reg_to_stack_nonleaf++;
1066 } else if (move->src_slot()->IsFpuRegister() &&
1067 move->dest_slot()->IsDoubleStackSlot()) {
1068 num_fpu_reg_to_stack_nonleaf++;
1069 }
1070 }
1071 });
1072
1073 // Test leaf calls do not cause spills of live values.
1074 compile_and_run(/*is_leaf=*/true, [&](ParallelMoveInstr* parallel_move) {
1075 // TargetAddress is passed in registers, live values are not spilled and
1076 // remains in callee-save registers.
1077 for (int i = 0; i < parallel_move->NumMoves(); i++) {
1078 auto move = parallel_move->moves()[i];
1079 if (move->src_slot()->IsRegister() && move->dest_slot()->IsStackSlot()) {
1080 num_cpu_reg_to_stack_leaf++;
1081 } else if (move->src_slot()->IsFpuRegister() &&
1082 move->dest_slot()->IsDoubleStackSlot()) {
1083 num_fpu_reg_to_stack_leaf++;
1084 }
1085 }
1086 });
1087
1088 // We should have less moves to the stack (i.e. spilling) in leaf calls.
1089 EXPECT_LT(num_cpu_reg_to_stack_leaf, num_cpu_reg_to_stack_nonleaf);
1090 // We don't have volatile FPU registers on all platforms.
1091 const bool has_callee_save_fpu_regs =
1092 Utils::CountOneBitsWord(kAbiVolatileFpuRegs) <
1093 Utils::CountOneBitsWord(kAllFpuRegistersList);
1094 EXPECT(!has_callee_save_fpu_regs ||
1095 num_fpu_reg_to_stack_leaf < num_fpu_reg_to_stack_nonleaf);
1096}
1097
1098static void TestConstantFoldToSmi(const Library& root_library,
1099 const char* function_name,
1100 CompilerPass::PipelineMode mode,
1101 intptr_t expected_value) {
1102 const auto& function =
1103 Function::Handle(GetFunction(root_library, function_name));
1104
1105 TestPipeline pipeline(function, mode);
1106 FlowGraph* flow_graph = pipeline.RunPasses({});
1107
1108 auto entry = flow_graph->graph_entry()->normal_entry();
1109 EXPECT(entry != nullptr);
1110
1111 DartReturnInstr* ret = nullptr;
1112
1113 ILMatcher cursor(flow_graph, entry, true, ParallelMovesHandling::kSkip);
1114 RELEASE_ASSERT(cursor.TryMatch({
1115 kMoveGlob,
1116 {kMatchDartReturn, &ret},
1117 }));
1118
1119 ConstantInstr* constant = ret->value()->definition()->AsConstant();
1120 EXPECT(constant != nullptr);
1121 if (constant != nullptr) {
1122 const Object& value = constant->value();
1123 EXPECT(value.IsSmi());
1124 if (value.IsSmi()) {
1125 const intptr_t int_value = Smi::Cast(value).Value();
1126 EXPECT_EQ(expected_value, int_value);
1127 }
1128 }
1129}
1130
1131ISOLATE_UNIT_TEST_CASE(ConstantFold_bitLength) {
1132 // clang-format off
1133 auto kScript = R"(
1134 b0() => 0. bitLength; // 0...00000
1135 b1() => 1. bitLength; // 0...00001
1136 b100() => 100. bitLength;
1137 b200() => 200. bitLength;
1138 bffff() => 0xffff. bitLength;
1139 m1() => (-1).bitLength; // 1...11111
1140 m2() => (-2).bitLength; // 1...11110
1141
1142 main() {
1143 b0();
1144 b1();
1145 b100();
1146 b200();
1147 bffff();
1148 m1();
1149 m2();
1150 }
1151 )";
1152 // clang-format on
1153
1154 const auto& root_library = Library::Handle(LoadTestScript(kScript));
1155 Invoke(root_library, "main");
1156
1157 auto test = [&](const char* function, intptr_t expected) {
1158 TestConstantFoldToSmi(root_library, function, CompilerPass::kJIT, expected);
1159 TestConstantFoldToSmi(root_library, function, CompilerPass::kAOT, expected);
1160 };
1161
1162 test("b0", 0);
1163 test("b1", 1);
1164 test("b100", 7);
1165 test("b200", 8);
1166 test("bffff", 16);
1167 test("m1", 0);
1168 test("m2", 1);
1169}
1170
1172 Thread* thread,
1173 bool allow_representation_change) {
1174 using compiler::BlockBuilder;
1175
1176 const auto& lib = Library::Handle(Library::CoreLibrary());
1177 const Class& list_class =
1178 Class::Handle(lib.LookupClassAllowPrivate(Symbols::_List()));
1179 EXPECT(!list_class.IsNull());
1180 const Error& err = Error::Handle(list_class.EnsureIsFinalized(thread));
1181 EXPECT(err.IsNull());
1182 const Function& list_filled = Function::ZoneHandle(
1183 list_class.LookupFactoryAllowPrivate(Symbols::_ListFilledFactory()));
1184 EXPECT(!list_filled.IsNull());
1185
1186 CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
1187
1188 FlowGraphBuilderHelper H(/*num_parameters=*/1);
1189 H.AddVariable("param", AbstractType::ZoneHandle(Type::IntType()));
1190
1191 auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
1192
1193 Definition* param = nullptr;
1194 LoadFieldInstr* load = nullptr;
1195 UnboxInstr* unbox = nullptr;
1196 Definition* add = nullptr;
1197 {
1198 BlockBuilder builder(H.flow_graph(), normal_entry);
1199 param = builder.AddParameter(0, kUnboxedInt64);
1200
1202 args.Add(new Value(H.flow_graph()->constant_null()));
1203 args.Add(new Value(param));
1204 args.Add(new Value(H.IntConstant(0)));
1205 StaticCallInstr* array = builder.AddDefinition(new StaticCallInstr(
1206 InstructionSource(), list_filled, 1, Array::empty_array(),
1207 std::move(args), DeoptId::kNone, 0, ICData::kNoRebind));
1208 array->UpdateType(CompileType::FromCid(kArrayCid));
1209 array->SetResultType(thread->zone(), CompileType::FromCid(kArrayCid));
1210 array->set_is_known_list_constructor(true);
1211
1212 load = builder.AddDefinition(new LoadFieldInstr(
1213 new Value(array), Slot::Array_length(), InstructionSource()));
1214
1215 unbox = builder.AddDefinition(new UnboxInt64Instr(
1216 new Value(load), DeoptId::kNone, Instruction::kNotSpeculative));
1217
1218 add = builder.AddDefinition(new BinaryInt64OpInstr(
1219 Token::kADD, new Value(unbox), new Value(H.IntConstant(1)),
1220 S.GetNextDeoptId(), Instruction::kNotSpeculative));
1221
1222 Definition* box = builder.AddDefinition(new BoxInt64Instr(new Value(add)));
1223
1224 builder.AddReturn(new Value(box));
1225 }
1226
1227 H.FinishGraph();
1228
1229 if (!allow_representation_change) {
1230 H.flow_graph()->disallow_unmatched_representations();
1231 }
1232
1233 H.flow_graph()->Canonicalize();
1234
1235 if (allow_representation_change) {
1236 EXPECT(add->InputAt(0)->definition() == param);
1237 } else {
1238 EXPECT(add->InputAt(0)->definition() == unbox);
1239 EXPECT(unbox->value()->definition() == load);
1240 }
1241}
1242
1243ISOLATE_UNIT_TEST_CASE(IL_Canonicalize_RepresentationChange) {
1246}
1247
1248enum TypeDataField {
1252};
1253
1255 Thread* thread,
1256 TypeDataField field_kind) {
1257 const auto& typed_data_lib = Library::Handle(Library::TypedDataLibrary());
1258 const auto& view_cls = Class::Handle(
1259 typed_data_lib.LookupClassAllowPrivate(Symbols::_Float32ArrayView()));
1260 const Error& err = Error::Handle(view_cls.EnsureIsFinalized(thread));
1261 EXPECT(err.IsNull());
1262 const auto& factory =
1263 Function::ZoneHandle(view_cls.LookupFactoryAllowPrivate(String::Handle(
1264 String::Concat(Symbols::_Float32ArrayView(), Symbols::DotUnder()))));
1265 EXPECT(!factory.IsNull());
1266
1267 using compiler::BlockBuilder;
1268 CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true);
1269 FlowGraphBuilderHelper H;
1270
1271 const Slot* field = nullptr;
1272 switch (field_kind) {
1274 field = &Slot::TypedDataBase_length();
1275 break;
1277 field = &Slot::TypedDataView_offset_in_bytes();
1278 break;
1280 field = &Slot::TypedDataView_typed_data();
1281 break;
1282 }
1283
1284 auto b1 = H.flow_graph()->graph_entry()->normal_entry();
1285
1286 const auto constant_4 = H.IntConstant(4);
1287 const auto constant_1 = H.IntConstant(1);
1288
1289 Definition* array;
1290 Definition* load;
1291 DartReturnInstr* ret;
1292
1293 {
1294 BlockBuilder builder(H.flow_graph(), b1);
1295 // array <- AllocateTypedData(1)
1296 array = builder.AddDefinition(new AllocateTypedDataInstr(
1297 InstructionSource(), kTypedDataFloat64ArrayCid, new Value(constant_1),
1298 DeoptId::kNone));
1299 // view <- StaticCall(_Float32ArrayView._, null, array, 4, 1)
1300 const auto view = builder.AddDefinition(new StaticCallInstr(
1301 InstructionSource(), factory, 1, Array::empty_array(),
1302 {new Value(H.flow_graph()->constant_null()), new Value(array),
1303 new Value(constant_4), new Value(constant_1)},
1304 DeoptId::kNone, 1, ICData::RebindRule::kStatic));
1305 // array_alias <- LoadField(view.length)
1306 load = builder.AddDefinition(
1307 new LoadFieldInstr(new Value(view), *field, InstructionSource()));
1308 // Return(load)
1309 ret = builder.AddReturn(new Value(load));
1310 }
1311 H.FinishGraph();
1312 H.flow_graph()->Canonicalize();
1313
1314 switch (field_kind) {
1316 EXPECT_PROPERTY(ret->value()->definition(), &it == constant_1);
1317 break;
1319 EXPECT_PROPERTY(ret->value()->definition(), &it == constant_4);
1320 break;
1322 EXPECT_PROPERTY(ret->value()->definition(), &it == array);
1323 break;
1324 }
1325}
1326
1327ISOLATE_UNIT_TEST_CASE(IL_Canonicalize_TypedDataViewFactory) {
1328 TestCanonicalizationOfTypedDataViewFieldLoads(thread, TypedDataBase_length);
1330 TypedDataView_offset_in_bytes);
1332 TypedDataView_typed_data);
1333}
1334
1335// Check that canonicalize can devirtualize InstanceCall based on type
1336// information in AOT mode.
1337ISOLATE_UNIT_TEST_CASE(IL_Canonicalize_InstanceCallWithNoICDataInAOT) {
1338 const auto& typed_data_lib = Library::Handle(Library::TypedDataLibrary());
1339 const auto& view_cls = Class::Handle(typed_data_lib.LookupClassAllowPrivate(
1340 String::Handle(Symbols::New(thread, "_TypedListBase"))));
1341 const Error& err = Error::Handle(view_cls.EnsureIsFinalized(thread));
1342 EXPECT(err.IsNull());
1343 const auto& getter = Function::Handle(
1344 view_cls.LookupFunctionAllowPrivate(Symbols::GetLength()));
1345 EXPECT(!getter.IsNull());
1346
1347 using compiler::BlockBuilder;
1348 CompilerState S(thread, /*is_aot=*/true, /*is_optimizing=*/true);
1349 FlowGraphBuilderHelper H;
1350
1351 auto b1 = H.flow_graph()->graph_entry()->normal_entry();
1352
1353 InstanceCallInstr* length_call;
1354 DartReturnInstr* ret;
1355
1356 {
1357 BlockBuilder builder(H.flow_graph(), b1);
1358 // array <- AllocateTypedData(1)
1359 const auto array = builder.AddDefinition(new AllocateTypedDataInstr(
1360 InstructionSource(), kTypedDataFloat64ArrayCid,
1361 new Value(H.IntConstant(1)), DeoptId::kNone));
1362 // length_call <- InstanceCall('get:length', array, ICData[])
1363 length_call = builder.AddDefinition(new InstanceCallInstr(
1364 InstructionSource(), Symbols::GetLength(), Token::kGET,
1365 /*args=*/{new Value(array)}, 0, Array::empty_array(), 1,
1366 /*deopt_id=*/42));
1367 length_call->EnsureICData(H.flow_graph());
1368 // Return(load)
1369 ret = builder.AddReturn(new Value(length_call));
1370 }
1371 H.FinishGraph();
1372 H.flow_graph()->Canonicalize();
1373
1374 EXPECT_PROPERTY(length_call, it.previous() == nullptr);
1375 EXPECT_PROPERTY(ret->value()->definition(), it.IsStaticCall());
1376 EXPECT_PROPERTY(ret->value()->definition()->AsStaticCall(),
1377 it.function().ptr() == getter.ptr());
1378}
1379
1380static void TestTestRangeCanonicalize(const AbstractType& type,
1381 uword lower,
1382 uword upper,
1383 bool result) {
1384 using compiler::BlockBuilder;
1385 CompilerState S(Thread::Current(), /*is_aot=*/true, /*is_optimizing=*/true);
1386 FlowGraphBuilderHelper H(/*num_parameters=*/1);
1387 H.AddVariable("v0", type);
1388
1389 auto normal_entry = H.flow_graph()->graph_entry()->normal_entry();
1390
1391 DartReturnInstr* ret;
1392 {
1393 BlockBuilder builder(H.flow_graph(), normal_entry);
1394 Definition* param = builder.AddParameter(0, kTagged);
1395 Definition* load_cid =
1396 builder.AddDefinition(new LoadClassIdInstr(new Value(param)));
1397 Definition* test_range = builder.AddDefinition(new TestRangeInstr(
1398 InstructionSource(), new Value(load_cid), lower, upper, kTagged));
1399 ret = builder.AddReturn(new Value(test_range));
1400 }
1401 H.FinishGraph();
1402 H.flow_graph()->Canonicalize();
1403
1404 EXPECT_PROPERTY(ret, it.value()->BindsToConstant());
1405 EXPECT_PROPERTY(ret,
1406 it.value()->BoundConstant().ptr() == Bool::Get(result).ptr());
1407}
1408
1409ISOLATE_UNIT_TEST_CASE(IL_Canonicalize_TestRange) {
1410 HierarchyInfo hierarchy_info(thread);
1411 TestTestRangeCanonicalize(AbstractType::ZoneHandle(Type::IntType()),
1412 kOneByteStringCid, kTwoByteStringCid, false);
1413 TestTestRangeCanonicalize(AbstractType::ZoneHandle(Type::IntType()), kSmiCid,
1414 kMintCid, true);
1415 TestTestRangeCanonicalize(AbstractType::ZoneHandle(Type::NullType()), kSmiCid,
1416 kMintCid, false);
1417 TestTestRangeCanonicalize(AbstractType::ZoneHandle(Type::Double()), kSmiCid,
1418 kMintCid, false);
1419 TestTestRangeCanonicalize(AbstractType::ZoneHandle(Type::ObjectType()), 1,
1420 kClassIdTagMax, true);
1421}
1422
1423void TestStaticFieldForwarding(Thread* thread,
1424 const Class& test_cls,
1425 const Field& field,
1426 intptr_t num_stores,
1427 bool expected_to_forward) {
1428 EXPECT(num_stores <= 2);
1429
1430 using compiler::BlockBuilder;
1431 CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true);
1432 FlowGraphBuilderHelper H;
1433
1434 auto b1 = H.flow_graph()->graph_entry()->normal_entry();
1435
1436 const auto constant_42 = H.IntConstant(42);
1437 const auto constant_24 = H.IntConstant(24);
1438 Definition* load;
1439 DartReturnInstr* ret;
1440
1441 {
1442 BlockBuilder builder(H.flow_graph(), b1);
1443 // obj <- AllocateObject(TestClass)
1444 const auto obj = builder.AddDefinition(
1445 new AllocateObjectInstr(InstructionSource(), test_cls, DeoptId::kNone));
1446
1447 if (num_stores >= 1) {
1448 // StoreField(o.field = 42)
1449 builder.AddInstruction(new StoreFieldInstr(
1450 field, new Value(obj), new Value(constant_42),
1451 StoreBarrierType::kNoStoreBarrier, InstructionSource(),
1452 &H.flow_graph()->parsed_function(),
1453 StoreFieldInstr::Kind::kInitializing));
1454 }
1455
1456 if (num_stores >= 2) {
1457 // StoreField(o.field = 24)
1458 builder.AddInstruction(new StoreFieldInstr(
1459 field, new Value(obj), new Value(constant_24),
1460 StoreBarrierType::kNoStoreBarrier, InstructionSource(),
1461 &H.flow_graph()->parsed_function()));
1462 }
1463
1464 // load <- LoadField(view.field)
1465 load = builder.AddDefinition(new LoadFieldInstr(
1466 new Value(obj), Slot::Get(field, &H.flow_graph()->parsed_function()),
1467 InstructionSource()));
1468
1469 // Return(load)
1470 ret = builder.AddReturn(new Value(load));
1471 }
1472 H.FinishGraph();
1473 H.flow_graph()->Canonicalize();
1474
1475 if (expected_to_forward) {
1476 EXPECT_PROPERTY(ret->value()->definition(), &it == constant_42);
1477 } else {
1478 EXPECT_PROPERTY(ret->value()->definition(), &it == load);
1479 }
1480}
1481
1482ISOLATE_UNIT_TEST_CASE(IL_Canonicalize_FinalFieldForwarding) {
1483 const char* script_chars = R"(
1484 import 'dart:typed_data';
1485
1486 class TestClass {
1487 final dynamic finalField;
1488 late final dynamic lateFinalField;
1489 dynamic normalField;
1490
1491 TestClass(this.finalField, this.lateFinalField, this.normalField);
1492 }
1493 )";
1494 const auto& lib = Library::Handle(LoadTestScript(script_chars));
1495
1496 const auto& test_cls = Class::ZoneHandle(
1497 lib.LookupClass(String::Handle(Symbols::New(thread, "TestClass"))));
1498 const auto& err = Error::Handle(test_cls.EnsureIsFinalized(thread));
1499 EXPECT(err.IsNull());
1500
1501 const auto lookup_field = [&](const char* name) -> const Field& {
1502 const auto& original_field = Field::Handle(
1503 test_cls.LookupField(String::Handle(Symbols::New(thread, name))));
1504 EXPECT(!original_field.IsNull());
1505 return Field::Handle(original_field.CloneFromOriginal());
1506 };
1507
1508 const auto& final_field = lookup_field("finalField");
1509 const auto& late_final_field = lookup_field("lateFinalField");
1510 const auto& normal_field = lookup_field("normalField");
1511
1512 TestStaticFieldForwarding(thread, test_cls, final_field, /*num_stores=*/0,
1513 /*expected_to_forward=*/false);
1514 TestStaticFieldForwarding(thread, test_cls, final_field, /*num_stores=*/1,
1515 /*expected_to_forward=*/true);
1516 TestStaticFieldForwarding(thread, test_cls, final_field, /*num_stores=*/2,
1517 /*expected_to_forward=*/false);
1518
1519 TestStaticFieldForwarding(thread, test_cls, late_final_field,
1520 /*num_stores=*/0, /*expected_to_forward=*/false);
1521 TestStaticFieldForwarding(thread, test_cls, late_final_field,
1522 /*num_stores=*/1, /*expected_to_forward=*/false);
1523 TestStaticFieldForwarding(thread, test_cls, late_final_field,
1524 /*num_stores=*/2, /*expected_to_forward=*/false);
1525
1526 TestStaticFieldForwarding(thread, test_cls, normal_field, /*num_stores=*/0,
1527 /*expected_to_forward=*/false);
1528 TestStaticFieldForwarding(thread, test_cls, normal_field, /*num_stores=*/1,
1529 /*expected_to_forward=*/false);
1530 TestStaticFieldForwarding(thread, test_cls, normal_field, /*num_stores=*/2,
1531 /*expected_to_forward=*/false);
1532}
1533
1534} // namespace dart
#define test(name)
static void test_range(skiatest::Reporter *reporter)
SI T load(const P *ptr)
#define EXPECT(type, expectedAlignment, expectedSize)
#define RELEASE_ASSERT(cond)
Definition assert.h:327
#define Z
void Add(const T &value)
#define H
#define THR_Print(format,...)
Definition log.h:20
if(end==-1)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static const char * expected_value
const uint8_t uint32_t uint32_t GError ** error
uint8_t value
GAsyncResult * result
const char * name
Definition fuchsia.cc:50
#define RANGES_CONTAIN_EXPECTED_CIDS(ranges, cids)
Definition il_test.cc:498
#define EXPECT_PROPERTY(entity, property)
LibraryPtr LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri)
TypeDataField
Definition il_test.cc:1249
@ TypedDataView_offset_in_bytes
Definition il_test.cc:1251
@ TypedDataBase_length
Definition il_test.cc:1250
@ TypedDataView_typed_data
Definition il_test.cc:1252
static void TestTestRangeCanonicalize(const AbstractType &type, uword lower, uword upper, bool result)
Definition il_test.cc:1381
static void TestRepresentationChangeDuringCanonicalization(Thread *thread, bool allow_representation_change)
Definition il_test.cc:1172
GrowableArray< Value * > InputsArray
Definition il.h:895
static void TestCanonicalizationOfTypedDataViewFieldLoads(Thread *thread, TypeDataField field_kind)
Definition il_test.cc:1255
ObjectPtr Invoke(const Library &lib, const char *name)
FunctionPtr GetFunction(const Library &lib, const char *name)
MallocGrowableArray< CidRangeValue > CidRangeVector
Definition il.h:253
uintptr_t uword
Definition globals.h:501
@ kNumberOfCpuRegisters
const int kNumberOfFpuRegisters
static void TestConstantFoldToSmi(const Library &root_library, const char *function_name, CompilerPass::PipelineMode mode, intptr_t expected_value)
Definition il_test.cc:1099
const intptr_t cid
QRegister FpuRegister
void TestStaticFieldForwarding(Thread *thread, const Class &test_cls, const Field &field, intptr_t num_stores, bool expected_to_forward)
Definition il_test.cc:1424
InstructionsPtr BuildInstructions(std::function< void(compiler::Assembler *assembler)> fun)
FlowGraph * SetupFfiFlowgraph(TestPipeline *pipeline, const compiler::ffi::CallMarshaller &marshaller, uword native_entry, bool is_leaf)
Definition il_test.cc:864
call(args)
Definition dom.py:159
Definition SkMD5.cpp:130
#define ISOLATE_UNIT_TEST_CASE(name)
Definition unit_test.h:64