Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(217)

Side by Side Diff: runtime/vm/flow_graph_compiler_x64.cc

Issue 10447133: FlowGraphCompiler is not a visitor any longer. Start consolidating shared code between the x64 and … (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « runtime/vm/flow_graph_compiler_x64.h ('k') | runtime/vm/intermediate_language.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_X64. 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_X64.
6 #if defined(TARGET_ARCH_X64) 6 #if defined(TARGET_ARCH_X64)
7 7
8 #include "vm/flow_graph_compiler.h" 8 #include "vm/flow_graph_compiler.h"
9 9
10 #include "lib/error.h" 10 #include "lib/error.h"
(...skipping 14 matching lines...) Expand all
25 25
26 DEFINE_FLAG(bool, print_scopes, false, "Print scopes of local variables."); 26 DEFINE_FLAG(bool, print_scopes, false, "Print scopes of local variables.");
27 DEFINE_FLAG(bool, trace_functions, false, "Trace entry of each function."); 27 DEFINE_FLAG(bool, trace_functions, false, "Trace entry of each function.");
28 DECLARE_FLAG(bool, enable_type_checks); 28 DECLARE_FLAG(bool, enable_type_checks);
29 DECLARE_FLAG(bool, intrinsify); 29 DECLARE_FLAG(bool, intrinsify);
30 DECLARE_FLAG(int, optimization_counter_threshold); 30 DECLARE_FLAG(int, optimization_counter_threshold);
31 DECLARE_FLAG(bool, print_ast); 31 DECLARE_FLAG(bool, print_ast);
32 DECLARE_FLAG(bool, report_usage_count); 32 DECLARE_FLAG(bool, report_usage_count);
33 DECLARE_FLAG(bool, code_comments); 33 DECLARE_FLAG(bool, code_comments);
34 34
35 class DeoptimizationStub : public ZoneAllocated {
36 public:
37 DeoptimizationStub(intptr_t deopt_id,
38 intptr_t deopt_token_index,
39 intptr_t try_index,
40 DeoptReasonId reason)
41 : deopt_id_(deopt_id),
42 deopt_token_index_(deopt_token_index),
43 try_index_(try_index),
44 reason_(reason),
45 registers_(2),
46 entry_label_() {}
47 35
48 void Push(Register reg) { registers_.Add(reg); } 36 void DeoptimizationStub::GenerateCode(FlowGraphCompilerShared* compiler) {
49 Label* entry_label() { return &entry_label_; }
50
51 void GenerateCode(FlowGraphCompiler* compiler);
52
53 private:
54 const intptr_t deopt_id_;
55 const intptr_t deopt_token_index_;
56 const intptr_t try_index_;
57 const DeoptReasonId reason_;
58 GrowableArray<Register> registers_;
59 Label entry_label_;
60
61 DISALLOW_COPY_AND_ASSIGN(DeoptimizationStub);
62 };
63
64
65 void DeoptimizationStub::GenerateCode(FlowGraphCompiler* compiler) {
66 Assembler* assem = compiler->assembler(); 37 Assembler* assem = compiler->assembler();
67 #define __ assem-> 38 #define __ assem->
68 __ Comment("Deopt stub for id %d", deopt_id_); 39 __ Comment("Deopt stub for id %d", deopt_id_);
69 __ Bind(entry_label()); 40 __ Bind(entry_label());
70 for (intptr_t i = 0; i < registers_.length(); i++) { 41 for (intptr_t i = 0; i < registers_.length(); i++) {
71 if (registers_[i] != kNoRegister) { 42 if (registers_[i] != kNoRegister) {
72 __ pushq(registers_[i]); 43 __ pushq(registers_[i]);
73 } 44 }
74 } 45 }
75 __ movq(RAX, Immediate(Smi::RawValue(reason_))); 46 __ movq(RAX, Immediate(Smi::RawValue(reason_)));
76 __ call(&StubCode::DeoptimizeLabel()); 47 __ call(&StubCode::DeoptimizeLabel());
77 compiler->AddCurrentDescriptor(PcDescriptors::kOther, 48 compiler->AddCurrentDescriptor(PcDescriptors::kOther,
78 deopt_id_, 49 deopt_id_,
79 deopt_token_index_, 50 deopt_token_index_,
80 try_index_); 51 try_index_);
81 #undef __ 52 #undef __
82 } 53 }
83 54
84 55
85 FlowGraphCompiler::FlowGraphCompiler( 56 FlowGraphCompiler::FlowGraphCompiler(
86 Assembler* assembler, 57 Assembler* assembler,
87 const ParsedFunction& parsed_function, 58 const ParsedFunction& parsed_function,
88 const GrowableArray<BlockEntryInstr*>& block_order, 59 const GrowableArray<BlockEntryInstr*>& block_order,
89 bool is_optimizing) 60 bool is_optimizing)
90 : FlowGraphVisitor(block_order), 61 : FlowGraphCompilerShared(assembler,
91 assembler_(assembler), 62 parsed_function,
92 parsed_function_(parsed_function), 63 block_order,
93 block_info_(block_order.length()), 64 is_optimizing) {}
94 current_block_(NULL),
95 pc_descriptors_list_(NULL),
96 stackmap_builder_(NULL),
97 exception_handlers_list_(NULL),
98 deopt_stubs_(),
99 is_optimizing_(is_optimizing) {
100 }
101
102
103 void FlowGraphCompiler::InitCompiler() {
104 pc_descriptors_list_ = new DescriptorList();
105 exception_handlers_list_ = new ExceptionHandlerList();
106 block_info_.Clear();
107 for (int i = 0; i < block_order_.length(); ++i) {
108 block_info_.Add(new BlockInfo());
109 }
110 }
111
112
113 FlowGraphCompiler::~FlowGraphCompiler() {
114 // BlockInfos are zone-allocated, so their destructors are not called.
115 // Verify the labels explicitly here.
116 for (int i = 0; i < block_info_.length(); ++i) {
117 ASSERT(!block_info_[i]->label.IsLinked());
118 ASSERT(!block_info_[i]->label.HasNear());
119 }
120 }
121
122
123 intptr_t FlowGraphCompiler::StackSize() const {
124 return parsed_function_.stack_local_count() +
125 parsed_function_.copied_parameter_count();
126 }
127 65
128 66
129 void FlowGraphCompiler::Bailout(const char* reason) { 67 void FlowGraphCompiler::Bailout(const char* reason) {
130 const char* kFormat = "FlowGraphCompiler Bailout: %s %s."; 68 const char* kFormat = "FlowGraphCompiler Bailout: %s %s.";
131 const char* function_name = parsed_function_.function().ToCString(); 69 const char* function_name = parsed_function().function().ToCString();
132 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1; 70 intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name, reason) + 1;
133 char* chars = reinterpret_cast<char*>( 71 char* chars = reinterpret_cast<char*>(
134 Isolate::Current()->current_zone()->Allocate(len)); 72 Isolate::Current()->current_zone()->Allocate(len));
135 OS::SNPrint(chars, len, kFormat, function_name, reason); 73 OS::SNPrint(chars, len, kFormat, function_name, reason);
136 const Error& error = Error::Handle( 74 const Error& error = Error::Handle(
137 LanguageError::New(String::Handle(String::New(chars)))); 75 LanguageError::New(String::Handle(String::New(chars))));
138 Isolate::Current()->long_jump_base()->Jump(1, error); 76 Isolate::Current()->long_jump_base()->Jump(1, error);
139 } 77 }
140 78
141 79
142 #define __ assembler_-> 80 #define __ assembler()->
143 81
144 82
145 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if 83 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if
146 // type test is conclusive, otherwise fallthrough if a type test could not 84 // type test is conclusive, otherwise fallthrough if a type test could not
147 // be completed. 85 // be completed.
148 // RAX: instance (must survive), 86 // RAX: instance (must survive),
149 RawSubtypeTestCache* 87 RawSubtypeTestCache*
150 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( 88 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest(
151 intptr_t cid, 89 intptr_t cid,
152 intptr_t token_index, 90 intptr_t token_index,
(...skipping 462 matching lines...) Expand 10 before | Expand all | Expand 10 after
615 } else { 553 } else {
616 __ LoadObject(dst, value->AsConstant()->value()); 554 __ LoadObject(dst, value->AsConstant()->value());
617 } 555 }
618 } else { 556 } else {
619 ASSERT(value->IsUse()); 557 ASSERT(value->IsUse());
620 __ popq(dst); 558 __ popq(dst);
621 } 559 }
622 } 560 }
623 561
624 562
625 void FlowGraphCompiler::VisitUse(UseVal* val) {
626 // UseVal is never visited during code generation.
627 UNREACHABLE();
628 }
629
630
631 void FlowGraphCompiler::VisitConstant(ConstantVal* val) {
632 // Moved to intermediate_language_x64.cc.
633 UNREACHABLE();
634 }
635
636
637 void FlowGraphCompiler::VisitAssertAssignable(AssertAssignableComp* comp) {
638 // Moved to intermediate_language_x64.cc.
639 UNREACHABLE();
640 }
641
642
643 void FlowGraphCompiler::VisitAssertBoolean(AssertBooleanComp* comp) {
644 // Moved to intermediate_language_x64.cc.
645 UNREACHABLE();
646 }
647
648
649 void FlowGraphCompiler::EmitInstanceCall(intptr_t cid, 563 void FlowGraphCompiler::EmitInstanceCall(intptr_t cid,
650 intptr_t token_index, 564 intptr_t token_index,
651 intptr_t try_index, 565 intptr_t try_index,
652 const String& function_name, 566 const String& function_name,
653 intptr_t argument_count, 567 intptr_t argument_count,
654 const Array& argument_names, 568 const Array& argument_names,
655 intptr_t checked_argument_count) { 569 intptr_t checked_argument_count) {
656 ICData& ic_data = 570 ICData& ic_data =
657 ICData::ZoneHandle(ICData::New(parsed_function_.function(), 571 ICData::ZoneHandle(ICData::New(parsed_function().function(),
658 function_name, 572 function_name,
659 cid, 573 cid,
660 checked_argument_count)); 574 checked_argument_count));
661 const Array& arguments_descriptor = 575 const Array& arguments_descriptor =
662 CodeGenerator::ArgumentsDescriptor(argument_count, argument_names); 576 CodeGenerator::ArgumentsDescriptor(argument_count, argument_names);
663 __ LoadObject(RBX, ic_data); 577 __ LoadObject(RBX, ic_data);
664 __ LoadObject(R10, arguments_descriptor); 578 __ LoadObject(R10, arguments_descriptor);
665 579
666 uword label_address = 0; 580 uword label_address = 0;
667 switch (checked_argument_count) { 581 switch (checked_argument_count) {
(...skipping 24 matching lines...) Expand all
692 __ LoadObject(R10, arguments_descriptor); 606 __ LoadObject(R10, arguments_descriptor);
693 607
694 GenerateCall(token_index, 608 GenerateCall(token_index,
695 try_index, 609 try_index,
696 &StubCode::CallStaticFunctionLabel(), 610 &StubCode::CallStaticFunctionLabel(),
697 PcDescriptors::kFuncCall); 611 PcDescriptors::kFuncCall);
698 __ Drop(argument_count); 612 __ Drop(argument_count);
699 } 613 }
700 614
701 615
702 void FlowGraphCompiler::VisitCurrentContext(CurrentContextComp* comp) {
703 // Moved to intermediate_language_x64.cc.
704 UNREACHABLE();
705 }
706
707
708 void FlowGraphCompiler::VisitStoreContext(StoreContextComp* comp) {
709 // Moved to intermediate_language_x64.cc.
710 UNREACHABLE();
711 }
712
713
714 void FlowGraphCompiler::VisitClosureCall(ClosureCallComp* comp) {
715 // Moved to intermediate_language_x64.cc.
716 UNREACHABLE();
717 }
718
719
720 void FlowGraphCompiler::VisitInstanceCall(InstanceCallComp* comp) {
721 // Moved to intermediate_language_x64.cc.
722 UNREACHABLE();
723 }
724
725
726 void FlowGraphCompiler::VisitStrictCompare(StrictCompareComp* comp) {
727 // Moved to intermediate_language_x64.cc.
728 UNREACHABLE();
729 }
730
731
732 void FlowGraphCompiler::VisitEqualityCompare(EqualityCompareComp* comp) {
733 // Moved to intermediate_language_x64.cc.
734 UNREACHABLE();
735 }
736
737
738 void FlowGraphCompiler::VisitStaticCall(StaticCallComp* comp) {
739 // Moved to intermediate_language_x64.cc.
740 UNREACHABLE();
741 }
742
743
744 void FlowGraphCompiler::VisitLoadLocal(LoadLocalComp* comp) {
745 // Moved to intermediate_language_x64.cc.
746 UNREACHABLE();
747 }
748
749
750 void FlowGraphCompiler::VisitStoreLocal(StoreLocalComp* comp) {
751 // Moved to intermediate_language_x64.cc.
752 UNREACHABLE();
753 }
754
755
756 void FlowGraphCompiler::VisitNativeCall(NativeCallComp* comp) {
757 // Moved to intermediate_language_x64.cc.
758 UNREACHABLE();
759 }
760
761
762 void FlowGraphCompiler::VisitLoadInstanceField(LoadInstanceFieldComp* comp) {
763 // Moved to intermediate_language_x64.cc.
764 UNREACHABLE();
765 }
766
767
768 void FlowGraphCompiler::VisitStoreInstanceField(StoreInstanceFieldComp* comp) {
769 // Moved to intermediate_language_x64.cc.
770 UNREACHABLE();
771 }
772
773
774
775 void FlowGraphCompiler::VisitLoadStaticField(LoadStaticFieldComp* comp) {
776 // Moved to intermediate_language_x64.cc.
777 UNREACHABLE();
778 }
779
780
781 void FlowGraphCompiler::VisitStoreStaticField(StoreStaticFieldComp* comp) {
782 // Moved to intermediate_language_x64.cc.
783 UNREACHABLE();
784 }
785
786
787 void FlowGraphCompiler::VisitStoreIndexed(StoreIndexedComp* comp) {
788 // Moved to intermediate_language_x64.cc.
789 UNREACHABLE();
790 }
791
792
793 void FlowGraphCompiler::VisitInstanceSetter(InstanceSetterComp* comp) {
794 // Moved to intermediate_language_x64.cc.
795 UNREACHABLE();
796 }
797
798
799 void FlowGraphCompiler::VisitStaticSetter(StaticSetterComp* comp) {
800 // Moved to intermediate_language_x64.cc.
801 UNREACHABLE();
802 }
803
804
805 void FlowGraphCompiler::VisitBooleanNegate(BooleanNegateComp* comp) {
806 // Moved to intermediate_language_x64.cc.
807 UNREACHABLE();
808 }
809
810
811 // Optimize instanceof type test by adding inlined tests for: 616 // Optimize instanceof type test by adding inlined tests for:
812 // - NULL -> return false. 617 // - NULL -> return false.
813 // - Smi -> compile time subtype check (only if dst class is not parameterized). 618 // - Smi -> compile time subtype check (only if dst class is not parameterized).
814 // - Class equality (only if class is not parameterized). 619 // - Class equality (only if class is not parameterized).
815 // Inputs: 620 // Inputs:
816 // - RAX: object. 621 // - RAX: object.
817 // - RDX: instantiator type arguments or raw_null. 622 // - RDX: instantiator type arguments or raw_null.
818 // - RCX: instantiator or raw_null. 623 // - RCX: instantiator or raw_null.
819 // Destroys RCX and RDX. 624 // Destroys RCX and RDX.
820 // Returns: 625 // Returns:
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
885 __ jmp(&done, Assembler::kNearJump); 690 __ jmp(&done, Assembler::kNearJump);
886 691
887 __ Bind(&is_instance); 692 __ Bind(&is_instance);
888 __ LoadObject(RAX, negate_result ? bool_false : bool_true); 693 __ LoadObject(RAX, negate_result ? bool_false : bool_true);
889 __ Bind(&done); 694 __ Bind(&done);
890 __ popq(RDX); // Remove pushed instantiator type arguments.. 695 __ popq(RDX); // Remove pushed instantiator type arguments..
891 __ popq(RCX); // Remove pushed instantiator. 696 __ popq(RCX); // Remove pushed instantiator.
892 } 697 }
893 698
894 699
895 void FlowGraphCompiler::VisitInstanceOf(InstanceOfComp* comp) {
896 // Moved to intermediate_language_x64.cc.
897 UNREACHABLE();
898 }
899
900
901 void FlowGraphCompiler::VisitAllocateObject(AllocateObjectComp* comp) {
902 // Moved to intermediate_language_x64.cc.
903 UNREACHABLE();
904 }
905
906
907 void FlowGraphCompiler::VisitAllocateObjectWithBoundsCheck(
908 AllocateObjectWithBoundsCheckComp* comp) {
909 // Moved to intermediate_language_x64.cc.
910 UNREACHABLE();
911 }
912
913
914 void FlowGraphCompiler::VisitCreateArray(CreateArrayComp* comp) {
915 // Moved to intermediate_language_x64.cc.
916 UNREACHABLE();
917 }
918
919
920 void FlowGraphCompiler::VisitCreateClosure(CreateClosureComp* comp) {
921 // Moved to intermediate_language_x64.cc.
922 UNREACHABLE();
923 }
924
925
926 void FlowGraphCompiler::VisitLoadVMField(LoadVMFieldComp* comp) {
927 // Moved to intermediate_language_x64.cc.
928 UNREACHABLE();
929 }
930
931
932 void FlowGraphCompiler::VisitStoreVMField(StoreVMFieldComp* comp) {
933 // Moved to intermediate_language_x64.cc.
934 UNREACHABLE();
935 }
936
937
938 void FlowGraphCompiler::VisitInstantiateTypeArguments(
939 InstantiateTypeArgumentsComp* comp) {
940 // Moved to intermediate_language_x64.cc.
941 UNREACHABLE();
942 }
943
944
945 void FlowGraphCompiler::VisitExtractConstructorTypeArguments(
946 ExtractConstructorTypeArgumentsComp* comp) {
947 // Moved to intermediate_language_x64.cc.
948 UNREACHABLE();
949 }
950
951
952 void FlowGraphCompiler::VisitExtractConstructorInstantiator(
953 ExtractConstructorInstantiatorComp* comp) {
954 // Moved to intermediate_language_x64.cc.
955 UNREACHABLE();
956 }
957
958
959 void FlowGraphCompiler::VisitAllocateContext(AllocateContextComp* comp) {
960 // Moved to intermediate_language_x64.cc.
961 UNREACHABLE();
962 }
963
964
965 void FlowGraphCompiler::VisitChainContext(ChainContextComp* comp) {
966 // Moved to intermediate_language_x64.cc.
967 UNREACHABLE();
968 }
969
970
971 void FlowGraphCompiler::VisitCloneContext(CloneContextComp* comp) {
972 // Moved to intermediate_language_x64.cc.
973 UNREACHABLE();
974 }
975
976
977 void FlowGraphCompiler::VisitCatchEntry(CatchEntryComp* comp) {
978 // Moved to intermediate_language_x64.cc.
979 UNREACHABLE();
980 }
981
982
983 void FlowGraphCompiler::VisitBinaryOp(BinaryOpComp* comp) {
984 UNIMPLEMENTED();
985 }
986
987
988 void FlowGraphCompiler::VisitUnarySmiOp(UnarySmiOpComp* comp) {
989 UNIMPLEMENTED();
990 }
991
992
993 void FlowGraphCompiler::VisitNumberNegate(NumberNegateComp* comp) {
994 UNIMPLEMENTED();
995 }
996
997
998 void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { 700 void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) {
999 LocationSummary* locs = instr->locs(); 701 LocationSummary* locs = instr->locs();
1000 ASSERT(locs != NULL); 702 ASSERT(locs != NULL);
1001 703
1002 locs->AllocateRegisters(); 704 locs->AllocateRegisters();
1003 705
1004 // Load instruction inputs into allocated registers. 706 // Load instruction inputs into allocated registers.
1005 for (intptr_t i = locs->input_count() - 1; i >= 0; i--) { 707 for (intptr_t i = locs->input_count() - 1; i >= 0; i--) {
1006 Location loc = locs->in(i); 708 Location loc = locs->in(i);
1007 ASSERT(loc.kind() == Location::kRegister); 709 ASSERT(loc.kind() == Location::kRegister);
1008 __ popq(loc.reg()); 710 __ popq(loc.reg());
1009 } 711 }
1010 } 712 }
1011 713
1012 714
1013 void FlowGraphCompiler::VisitBlocks() { 715 void FlowGraphCompiler::VisitBlocks() {
1014 for (intptr_t i = 0; i < block_order_.length(); ++i) { 716 for (intptr_t i = 0; i < block_order().length(); ++i) {
1015 __ Comment("B%d", i); 717 __ Comment("B%d", i);
1016 // Compile the block entry. 718 // Compile the block entry.
1017 current_block_ = block_order_[i]; 719 set_current_block(block_order()[i]);
1018 Instruction* instr = current_block()->Accept(this); 720 current_block()->PrepareEntry(this);
721 Instruction* instr = current_block()->StraightLineSuccessor();
1019 // Compile all successors until an exit, branch, or a block entry. 722 // Compile all successors until an exit, branch, or a block entry.
1020 while ((instr != NULL) && !instr->IsBlockEntry()) { 723 while ((instr != NULL) && !instr->IsBlockEntry()) {
1021 if (FLAG_code_comments) EmitComment(instr); 724 if (FLAG_code_comments) EmitComment(instr);
1022 if (instr->locs() != NULL) { 725 ASSERT(instr->locs() != NULL);
1023 EmitInstructionPrologue(instr); 726 EmitInstructionPrologue(instr);
1024 instr->EmitNativeCode(this); 727 instr->EmitNativeCode(this);
1025 instr = instr->StraightLineSuccessor(); 728 instr = instr->StraightLineSuccessor();
1026 } else {
1027 instr = instr->Accept(this);
1028 }
1029 } 729 }
1030 730
1031 BlockEntryInstr* successor = 731 BlockEntryInstr* successor =
1032 (instr == NULL) ? NULL : instr->AsBlockEntry(); 732 (instr == NULL) ? NULL : instr->AsBlockEntry();
1033 if (successor != NULL) { 733 if (successor != NULL) {
1034 // Block ended with a "goto". We can fall through if it is the 734 // Block ended with a "goto". We can fall through if it is the
1035 // next block in the list. Otherwise, we need a jump. 735 // next block in the list. Otherwise, we need a jump.
1036 if ((i == block_order_.length() - 1) || 736 if ((i == block_order().length() - 1) ||
1037 (block_order_[i + 1] != successor)) { 737 (block_order()[i + 1] != successor)) {
1038 __ jmp(&block_info_[successor->postorder_number()]->label); 738 __ jmp(GetBlockLabel(successor));
1039 } 739 }
1040 } 740 }
1041 } 741 }
1042 } 742 }
1043 743
1044 744
1045 void FlowGraphCompiler::EmitComment(Instruction* instr) { 745 void FlowGraphCompiler::EmitComment(Instruction* instr) {
1046 char buffer[80]; 746 char buffer[80];
1047 BufferFormatter f(buffer, sizeof(buffer)); 747 BufferFormatter f(buffer, sizeof(buffer));
1048 instr->PrintTo(&f); 748 instr->PrintTo(&f);
1049 __ Comment("@%d: %s", instr->cid(), buffer); 749 __ Comment("@%d: %s", instr->cid(), buffer);
1050 } 750 }
1051 751
1052 752
1053 void FlowGraphCompiler::VisitGraphEntry(GraphEntryInstr* instr) {
1054 // Nothing to do.
1055 }
1056
1057
1058 void FlowGraphCompiler::VisitJoinEntry(JoinEntryInstr* instr) {
1059 __ Bind(&block_info_[instr->postorder_number()]->label);
1060 }
1061
1062
1063 void FlowGraphCompiler::VisitTargetEntry(TargetEntryInstr* instr) {
1064 __ Bind(&block_info_[instr->postorder_number()]->label);
1065 if (instr->HasTryIndex()) {
1066 exception_handlers_list_->AddHandler(instr->try_index(),
1067 assembler_->CodeSize());
1068 }
1069 }
1070
1071
1072 void FlowGraphCompiler::VisitDo(DoInstr* instr) {
1073 instr->computation()->Accept(this);
1074 }
1075
1076
1077 void FlowGraphCompiler::VisitBind(BindInstr* instr) {
1078 // Moved to intermediate_language_x64.cc.
1079 UNREACHABLE();
1080 }
1081
1082
1083 void FlowGraphCompiler::VisitReturn(ReturnInstr* instr) {
1084 // Moved to intermediate_language_x64.cc.
1085 UNREACHABLE();
1086 }
1087
1088
1089 void FlowGraphCompiler::VisitThrow(ThrowInstr* instr) {
1090 // Moved to intermediate_language_x64.cc.
1091 UNREACHABLE();
1092 }
1093
1094
1095 void FlowGraphCompiler::VisitReThrow(ReThrowInstr* instr) {
1096 // Moved to intermediate_language_x64.cc.
1097 UNREACHABLE();
1098 }
1099
1100
1101
1102 void FlowGraphCompiler::VisitBranch(BranchInstr* instr) {
1103 // Moved to intermediate_language_x64.cc.
1104 UNREACHABLE();
1105 }
1106
1107
1108 // Copied from CodeGenerator::CopyParameters (CodeGenerator will be deprecated). 753 // Copied from CodeGenerator::CopyParameters (CodeGenerator will be deprecated).
1109 void FlowGraphCompiler::CopyParameters() { 754 void FlowGraphCompiler::CopyParameters() {
1110 const Function& function = parsed_function_.function(); 755 const Function& function = parsed_function().function();
1111 LocalScope* scope = parsed_function_.node_sequence()->scope(); 756 LocalScope* scope = parsed_function().node_sequence()->scope();
1112 const int num_fixed_params = function.num_fixed_parameters(); 757 const int num_fixed_params = function.num_fixed_parameters();
1113 const int num_opt_params = function.num_optional_parameters(); 758 const int num_opt_params = function.num_optional_parameters();
1114 ASSERT(parsed_function_.first_parameter_index() == 759 ASSERT(parsed_function().first_parameter_index() ==
1115 ParsedFunction::kFirstLocalSlotIndex); 760 ParsedFunction::kFirstLocalSlotIndex);
1116 // Copy positional arguments. 761 // Copy positional arguments.
1117 // Check that no fewer than num_fixed_params positional arguments are passed 762 // Check that no fewer than num_fixed_params positional arguments are passed
1118 // in and that no more than num_params arguments are passed in. 763 // in and that no more than num_params arguments are passed in.
1119 // Passed argument i at fp[1 + argc - i] 764 // Passed argument i at fp[1 + argc - i]
1120 // copied to fp[ParsedFunction::kFirstLocalSlotIndex - i]. 765 // copied to fp[ParsedFunction::kFirstLocalSlotIndex - i].
1121 const int num_params = num_fixed_params + num_opt_params; 766 const int num_params = num_fixed_params + num_opt_params;
1122 767
1123 // Total number of args is the first Smi in args descriptor array (R10). 768 // Total number of args is the first Smi in args descriptor array (R10).
1124 __ movq(RBX, FieldAddress(R10, Array::data_offset())); 769 __ movq(RBX, FieldAddress(R10, Array::data_offset()));
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
1205 // fp[1 + argc - arg_pos]. 850 // fp[1 + argc - arg_pos].
1206 __ movq(RAX, Address(RDI, kWordSize)); // RAX is arg_pos as Smi. 851 __ movq(RAX, Address(RDI, kWordSize)); // RAX is arg_pos as Smi.
1207 __ addq(RDI, Immediate(2 * kWordSize)); // Point to next name/pos pair. 852 __ addq(RDI, Immediate(2 * kWordSize)); // Point to next name/pos pair.
1208 __ negq(RAX); 853 __ negq(RAX);
1209 Address argument_addr(RBX, RAX, TIMES_4, 0); // RAX is a negative Smi. 854 Address argument_addr(RBX, RAX, TIMES_4, 0); // RAX is a negative Smi.
1210 __ movq(RAX, argument_addr); 855 __ movq(RAX, argument_addr);
1211 __ jmp(&assign_optional_parameter, Assembler::kNearJump); 856 __ jmp(&assign_optional_parameter, Assembler::kNearJump);
1212 __ Bind(&load_default_value); 857 __ Bind(&load_default_value);
1213 // Load RAX with default argument at pos. 858 // Load RAX with default argument at pos.
1214 const Object& value = Object::ZoneHandle( 859 const Object& value = Object::ZoneHandle(
1215 parsed_function_.default_parameter_values().At( 860 parsed_function().default_parameter_values().At(
1216 param_pos - num_fixed_params)); 861 param_pos - num_fixed_params));
1217 __ LoadObject(RAX, value); 862 __ LoadObject(RAX, value);
1218 __ Bind(&assign_optional_parameter); 863 __ Bind(&assign_optional_parameter);
1219 // Assign RAX to fp[ParsedFunction::kFirstLocalSlotIndex - param_pos]. 864 // Assign RAX to fp[ParsedFunction::kFirstLocalSlotIndex - param_pos].
1220 // We do not use the final allocation index of the variable here, i.e. 865 // We do not use the final allocation index of the variable here, i.e.
1221 // scope->VariableAt(i)->index(), because captured variables still need 866 // scope->VariableAt(i)->index(), because captured variables still need
1222 // to be copied to the context that is not yet allocated. 867 // to be copied to the context that is not yet allocated.
1223 const Address param_addr( 868 const Address param_addr(
1224 RBP, (ParsedFunction::kFirstLocalSlotIndex - param_pos) * kWordSize); 869 RBP, (ParsedFunction::kFirstLocalSlotIndex - param_pos) * kWordSize);
1225 __ movq(param_addr, RAX); 870 __ movq(param_addr, RAX);
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
1305 !FLAG_report_usage_count && 950 !FLAG_report_usage_count &&
1306 (FLAG_optimization_counter_threshold >= 0) && 951 (FLAG_optimization_counter_threshold >= 0) &&
1307 !Isolate::Current()->debugger()->IsActive(); 952 !Isolate::Current()->debugger()->IsActive();
1308 } 953 }
1309 954
1310 955
1311 void FlowGraphCompiler::IntrinsifyGetter() { 956 void FlowGraphCompiler::IntrinsifyGetter() {
1312 // TOS: return address. 957 // TOS: return address.
1313 // +1 : receiver. 958 // +1 : receiver.
1314 // Sequence node has one return node, its input is load field node. 959 // Sequence node has one return node, its input is load field node.
1315 const SequenceNode& sequence_node = *parsed_function_.node_sequence(); 960 const SequenceNode& sequence_node = *parsed_function().node_sequence();
1316 ASSERT(sequence_node.length() == 1); 961 ASSERT(sequence_node.length() == 1);
1317 ASSERT(sequence_node.NodeAt(0)->IsReturnNode()); 962 ASSERT(sequence_node.NodeAt(0)->IsReturnNode());
1318 const ReturnNode& return_node = *sequence_node.NodeAt(0)->AsReturnNode(); 963 const ReturnNode& return_node = *sequence_node.NodeAt(0)->AsReturnNode();
1319 ASSERT(return_node.value()->IsLoadInstanceFieldNode()); 964 ASSERT(return_node.value()->IsLoadInstanceFieldNode());
1320 const LoadInstanceFieldNode& load_node = 965 const LoadInstanceFieldNode& load_node =
1321 *return_node.value()->AsLoadInstanceFieldNode(); 966 *return_node.value()->AsLoadInstanceFieldNode();
1322 __ movq(RAX, Address(RSP, 1 * kWordSize)); 967 __ movq(RAX, Address(RSP, 1 * kWordSize));
1323 __ movq(RAX, FieldAddress(RAX, load_node.field().Offset())); 968 __ movq(RAX, FieldAddress(RAX, load_node.field().Offset()));
1324 __ ret(); 969 __ ret();
1325 } 970 }
1326 971
1327 972
1328 void FlowGraphCompiler::IntrinsifySetter() { 973 void FlowGraphCompiler::IntrinsifySetter() {
1329 // TOS: return address. 974 // TOS: return address.
1330 // +1 : value 975 // +1 : value
1331 // +2 : receiver. 976 // +2 : receiver.
1332 // Sequence node has one store node and one return NULL node. 977 // Sequence node has one store node and one return NULL node.
1333 const SequenceNode& sequence_node = *parsed_function_.node_sequence(); 978 const SequenceNode& sequence_node = *parsed_function().node_sequence();
1334 ASSERT(sequence_node.length() == 2); 979 ASSERT(sequence_node.length() == 2);
1335 ASSERT(sequence_node.NodeAt(0)->IsStoreInstanceFieldNode()); 980 ASSERT(sequence_node.NodeAt(0)->IsStoreInstanceFieldNode());
1336 ASSERT(sequence_node.NodeAt(1)->IsReturnNode()); 981 ASSERT(sequence_node.NodeAt(1)->IsReturnNode());
1337 const StoreInstanceFieldNode& store_node = 982 const StoreInstanceFieldNode& store_node =
1338 *sequence_node.NodeAt(0)->AsStoreInstanceFieldNode(); 983 *sequence_node.NodeAt(0)->AsStoreInstanceFieldNode();
1339 __ movq(RAX, Address(RSP, 2 * kWordSize)); // Receiver. 984 __ movq(RAX, Address(RSP, 2 * kWordSize)); // Receiver.
1340 __ movq(RBX, Address(RSP, 1 * kWordSize)); // Value. 985 __ movq(RBX, Address(RSP, 1 * kWordSize)); // Value.
1341 __ StoreIntoObject(RAX, FieldAddress(RAX, store_node.field().Offset()), RBX); 986 __ StoreIntoObject(RAX, FieldAddress(RAX, store_node.field().Offset()), RBX);
1342 const Immediate raw_null = 987 const Immediate raw_null =
1343 Immediate(reinterpret_cast<intptr_t>(Object::null())); 988 Immediate(reinterpret_cast<intptr_t>(Object::null()));
1344 __ movq(RAX, raw_null); 989 __ movq(RAX, raw_null);
1345 __ ret(); 990 __ ret();
1346 } 991 }
1347 992
1348 993
1349 // Returns 'true' if code generation for this function is complete, i.e., 994 // Returns 'true' if code generation for this function is complete, i.e.,
1350 // no fall-through to regular code is needed. 995 // no fall-through to regular code is needed.
1351 bool FlowGraphCompiler::TryIntrinsify() { 996 bool FlowGraphCompiler::TryIntrinsify() {
1352 if (!CanOptimize()) return false; 997 if (!CanOptimize()) return false;
1353 // Intrinsification skips arguments checks, therefore disable if in checked 998 // Intrinsification skips arguments checks, therefore disable if in checked
1354 // mode. 999 // mode.
1355 if (FLAG_intrinsify && !FLAG_trace_functions && !FLAG_enable_type_checks) { 1000 if (FLAG_intrinsify && !FLAG_trace_functions && !FLAG_enable_type_checks) {
1356 if ((parsed_function_.function().kind() == RawFunction::kImplicitGetter)) { 1001 if ((parsed_function().function().kind() == RawFunction::kImplicitGetter)) {
1357 IntrinsifyGetter(); 1002 IntrinsifyGetter();
1358 return true; 1003 return true;
1359 } 1004 }
1360 if ((parsed_function_.function().kind() == RawFunction::kImplicitSetter)) { 1005 if ((parsed_function().function().kind() == RawFunction::kImplicitSetter)) {
1361 IntrinsifySetter(); 1006 IntrinsifySetter();
1362 return true; 1007 return true;
1363 } 1008 }
1364 } 1009 }
1365 // Even if an intrinsified version of the function was successfully 1010 // Even if an intrinsified version of the function was successfully
1366 // generated, it may fall through to the non-intrinsified method body. 1011 // generated, it may fall through to the non-intrinsified method body.
1367 if (!FLAG_trace_functions) { 1012 if (!FLAG_trace_functions) {
1368 return Intrinsifier::Intrinsify(parsed_function_.function(), assembler_); 1013 return Intrinsifier::Intrinsify(parsed_function().function(), assembler());
1369 } 1014 }
1370 return false; 1015 return false;
1371 } 1016 }
1372 1017
1373 1018
1374 void FlowGraphCompiler::CompileGraph() { 1019 void FlowGraphCompiler::CompileGraph() {
1375 InitCompiler(); 1020 InitCompiler();
1376 if (TryIntrinsify()) { 1021 if (TryIntrinsify()) {
1377 // Make it patchable: code must have a minimum code size, nop(2) increases 1022 // Make it patchable: code must have a minimum code size, nop(2) increases
1378 // the minimum code size appropriately. 1023 // the minimum code size appropriately.
1379 __ nop(2); 1024 __ nop(2);
1380 __ int3(); 1025 __ int3();
1381 __ jmp(&StubCode::FixCallersTargetLabel()); 1026 __ jmp(&StubCode::FixCallersTargetLabel());
1382 return; 1027 return;
1383 } 1028 }
1384 // Specialized version of entry code from CodeGenerator::GenerateEntryCode. 1029 // Specialized version of entry code from CodeGenerator::GenerateEntryCode.
1385 const Function& function = parsed_function_.function(); 1030 const Function& function = parsed_function().function();
1386 1031
1387 const int parameter_count = function.num_fixed_parameters(); 1032 const int parameter_count = function.num_fixed_parameters();
1388 const int num_copied_params = parsed_function_.copied_parameter_count(); 1033 const int num_copied_params = parsed_function().copied_parameter_count();
1389 const int local_count = parsed_function_.stack_local_count(); 1034 const int local_count = parsed_function().stack_local_count();
1390 AssemblerMacros::EnterDartFrame(assembler_, (StackSize() * kWordSize)); 1035 AssemblerMacros::EnterDartFrame(assembler(), (StackSize() * kWordSize));
1391 1036
1392 // We check the number of passed arguments when we have to copy them due to 1037 // We check the number of passed arguments when we have to copy them due to
1393 // the presence of optional named parameters. 1038 // the presence of optional named parameters.
1394 // No such checking code is generated if only fixed parameters are declared, 1039 // No such checking code is generated if only fixed parameters are declared,
1395 // unless we are debug mode or unless we are compiling a closure. 1040 // unless we are debug mode or unless we are compiling a closure.
1396 if (num_copied_params == 0) { 1041 if (num_copied_params == 0) {
1397 #ifdef DEBUG 1042 #ifdef DEBUG
1398 const bool check_arguments = true; 1043 const bool check_arguments = true;
1399 #else 1044 #else
1400 const bool check_arguments = function.IsClosureFunction(); 1045 const bool check_arguments = function.IsClosureFunction();
(...skipping 15 matching lines...) Expand all
1416 } 1061 }
1417 __ Bind(&argc_in_range); 1062 __ Bind(&argc_in_range);
1418 } 1063 }
1419 } else { 1064 } else {
1420 CopyParameters(); 1065 CopyParameters();
1421 } 1066 }
1422 1067
1423 // Initialize locals to null. 1068 // Initialize locals to null.
1424 if (local_count > 0) { 1069 if (local_count > 0) {
1425 __ movq(RAX, Immediate(reinterpret_cast<intptr_t>(Object::null()))); 1070 __ movq(RAX, Immediate(reinterpret_cast<intptr_t>(Object::null())));
1426 const int base = parsed_function_.first_stack_local_index(); 1071 const int base = parsed_function().first_stack_local_index();
1427 for (int i = 0; i < local_count; ++i) { 1072 for (int i = 0; i < local_count; ++i) {
1428 // Subtract index i (locals lie at lower addresses than RBP). 1073 // Subtract index i (locals lie at lower addresses than RBP).
1429 __ movq(Address(RBP, (base - i) * kWordSize), RAX); 1074 __ movq(Address(RBP, (base - i) * kWordSize), RAX);
1430 } 1075 }
1431 } 1076 }
1432 1077
1433 // Generate stack overflow check. 1078 // Generate stack overflow check.
1434 __ movq(TMP, Immediate(Isolate::Current()->stack_limit_address())); 1079 __ movq(TMP, Immediate(Isolate::Current()->stack_limit_address()));
1435 __ cmpq(RSP, Address(TMP, 0)); 1080 __ cmpq(RSP, Address(TMP, 0));
1436 Label no_stack_overflow; 1081 Label no_stack_overflow;
1437 __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump); 1082 __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump);
1438 GenerateCallRuntime(AstNode::kNoId, 1083 GenerateCallRuntime(AstNode::kNoId,
1439 function.token_index(), 1084 function.token_index(),
1440 CatchClauseNode::kInvalidTryIndex, 1085 CatchClauseNode::kInvalidTryIndex,
1441 kStackOverflowRuntimeEntry); 1086 kStackOverflowRuntimeEntry);
1442 __ Bind(&no_stack_overflow); 1087 __ Bind(&no_stack_overflow);
1443 1088
1444 if (FLAG_print_scopes) { 1089 if (FLAG_print_scopes) {
1445 // Print the function scope (again) after generating the prologue in order 1090 // Print the function scope (again) after generating the prologue in order
1446 // to see annotations such as allocation indices of locals. 1091 // to see annotations such as allocation indices of locals.
1447 if (FLAG_print_ast) { 1092 if (FLAG_print_ast) {
1448 // Second printing. 1093 // Second printing.
1449 OS::Print("Annotated "); 1094 OS::Print("Annotated ");
1450 } 1095 }
1451 AstPrinter::PrintFunctionScope(parsed_function_); 1096 AstPrinter::PrintFunctionScope(parsed_function());
1452 } 1097 }
1453 1098
1454 VisitBlocks(); 1099 VisitBlocks();
1455 1100
1456 __ int3(); 1101 __ int3();
1457 GenerateDeferredCode(); 1102 GenerateDeferredCode();
1458 // Emit function patching code. This will be swapped with the first 13 bytes 1103 // Emit function patching code. This will be swapped with the first 13 bytes
1459 // at entry point. 1104 // at entry point.
1460 pc_descriptors_list_->AddDescriptor(PcDescriptors::kPatchCode, 1105 pc_descriptors_list()->AddDescriptor(PcDescriptors::kPatchCode,
1461 assembler_->CodeSize(), 1106 assembler()->CodeSize(),
1462 AstNode::kNoId, 1107 AstNode::kNoId,
1463 0, 1108 0,
1464 -1); 1109 -1);
1465 __ jmp(&StubCode::FixCallersTargetLabel()); 1110 __ jmp(&StubCode::FixCallersTargetLabel());
1466 } 1111 }
1467 1112
1468 1113
1469 void FlowGraphCompiler::GenerateDeferredCode() {
1470 for (intptr_t i = 0; i < deopt_stubs_.length(); i++) {
1471 deopt_stubs_[i]->GenerateCode(this);
1472 }
1473 }
1474
1475
1476 // Infrastructure copied from class CodeGenerator. 1114 // Infrastructure copied from class CodeGenerator.
1477 void FlowGraphCompiler::GenerateCall(intptr_t token_index, 1115 void FlowGraphCompiler::GenerateCall(intptr_t token_index,
1478 intptr_t try_index, 1116 intptr_t try_index,
1479 const ExternalLabel* label, 1117 const ExternalLabel* label,
1480 PcDescriptors::Kind kind) { 1118 PcDescriptors::Kind kind) {
1481 __ call(label); 1119 __ call(label);
1482 AddCurrentDescriptor(kind, AstNode::kNoId, token_index, try_index); 1120 AddCurrentDescriptor(kind, AstNode::kNoId, token_index, try_index);
1483 } 1121 }
1484 1122
1485 1123
1486 void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid, 1124 void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid,
1487 intptr_t token_index, 1125 intptr_t token_index,
1488 intptr_t try_index, 1126 intptr_t try_index,
1489 const RuntimeEntry& entry) { 1127 const RuntimeEntry& entry) {
1490 __ CallRuntime(entry); 1128 __ CallRuntime(entry);
1491 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_index, try_index); 1129 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_index, try_index);
1492 } 1130 }
1493 1131
1494 1132
1495 // Uses current pc position and try-index.
1496 void FlowGraphCompiler::AddCurrentDescriptor(PcDescriptors::Kind kind,
1497 intptr_t cid,
1498 intptr_t token_index,
1499 intptr_t try_index) {
1500 pc_descriptors_list_->AddDescriptor(kind,
1501 assembler_->CodeSize(),
1502 cid,
1503 token_index,
1504 try_index);
1505 }
1506
1507
1508 Label* FlowGraphCompiler::AddDeoptStub(intptr_t deopt_id,
1509 intptr_t deopt_token_index,
1510 intptr_t try_index,
1511 DeoptReasonId reason,
1512 Register reg1,
1513 Register reg2) {
1514 DeoptimizationStub* stub =
1515 new DeoptimizationStub(deopt_id, deopt_token_index, try_index, reason);
1516 stub->Push(reg1);
1517 stub->Push(reg2);
1518 deopt_stubs_.Add(stub);
1519 return stub->entry_label();
1520 }
1521
1522
1523 void FlowGraphCompiler::FinalizePcDescriptors(const Code& code) {
1524 ASSERT(pc_descriptors_list_ != NULL);
1525 const PcDescriptors& descriptors = PcDescriptors::Handle(
1526 pc_descriptors_list_->FinalizePcDescriptors(code.EntryPoint()));
1527 descriptors.Verify(parsed_function_.function().is_optimizable());
1528 code.set_pc_descriptors(descriptors);
1529 }
1530
1531
1532 void FlowGraphCompiler::FinalizeStackmaps(const Code& code) {
1533 if (stackmap_builder_ == NULL) {
1534 // The unoptimizing compiler has no stack maps.
1535 code.set_stackmaps(Array::Handle());
1536 } else {
1537 // Finalize the stack map array and add it to the code object.
1538 code.set_stackmaps(
1539 Array::Handle(stackmap_builder_->FinalizeStackmaps(code)));
1540 }
1541 }
1542
1543
1544 void FlowGraphCompiler::FinalizeVarDescriptors(const Code& code) {
1545 const LocalVarDescriptors& var_descs = LocalVarDescriptors::Handle(
1546 parsed_function_.node_sequence()->scope()->GetVarDescriptors());
1547 code.set_var_descriptors(var_descs);
1548 }
1549
1550
1551 void FlowGraphCompiler::FinalizeExceptionHandlers(const Code& code) {
1552 ASSERT(exception_handlers_list_ != NULL);
1553 const ExceptionHandlers& handlers = ExceptionHandlers::Handle(
1554 exception_handlers_list_->FinalizeExceptionHandlers(code.EntryPoint()));
1555 code.set_exception_handlers(handlers);
1556 }
1557
1558
1559 void FlowGraphCompiler::FinalizeComments(const Code& code) { 1133 void FlowGraphCompiler::FinalizeComments(const Code& code) {
1560 code.set_comments(assembler_->GetCodeComments()); 1134 code.set_comments(assembler()->GetCodeComments());
1561 } 1135 }
1562 1136
1563 #undef __ 1137 #undef __
1564 1138
1565 } // namespace dart 1139 } // namespace dart
1566 1140
1567 #endif // defined TARGET_ARCH_X64 1141 #endif // defined TARGET_ARCH_X64
OLDNEW
« no previous file with comments | « runtime/vm/flow_graph_compiler_x64.h ('k') | runtime/vm/intermediate_language.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698