Chromium Code Reviews| OLD | NEW |
|---|---|
| 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_IA32. | 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. |
| 6 #if defined(TARGET_ARCH_IA32) | 6 #if defined(TARGET_ARCH_IA32) |
| 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" |
| 11 #include "vm/ast_printer.h" | 11 #include "vm/ast_printer.h" |
| 12 #include "vm/compiler_stats.h" | |
| 13 #include "vm/il_printer.h" | 12 #include "vm/il_printer.h" |
| 14 #include "vm/locations.h" | 13 #include "vm/locations.h" |
| 15 #include "vm/object_store.h" | 14 #include "vm/object_store.h" |
| 15 #include "vm/parser.h" | |
| 16 #include "vm/stub_code.h" | 16 #include "vm/stub_code.h" |
| 17 | 17 |
| 18 namespace dart { | 18 namespace dart { |
| 19 | 19 |
| 20 DECLARE_FLAG(bool, compiler_stats); | 20 DEFINE_FLAG(bool, print_scopes, false, "Print scopes of local variables."); |
| 21 DEFINE_FLAG(bool, trace_functions, false, "Trace entry of each function."); | |
|
srdjan
2012/06/25 22:18:17
How about moving them into flow_graph_compiler.cc
regis
2012/06/25 22:22:59
Done.
| |
| 21 DECLARE_FLAG(bool, enable_type_checks); | 22 DECLARE_FLAG(bool, enable_type_checks); |
| 22 DECLARE_FLAG(bool, print_ast); | 23 DECLARE_FLAG(bool, print_ast); |
| 23 DECLARE_FLAG(bool, print_scopes); | |
| 24 DECLARE_FLAG(bool, trace_functions); | |
| 25 | 24 |
| 26 | 25 |
| 27 void DeoptimizationStub::GenerateCode(FlowGraphCompiler* compiler) { | 26 void DeoptimizationStub::GenerateCode(FlowGraphCompiler* compiler) { |
| 28 Assembler* assem = compiler->assembler(); | 27 Assembler* assem = compiler->assembler(); |
| 29 #define __ assem-> | 28 #define __ assem-> |
| 30 __ Comment("Deopt stub for id %d", deopt_id_); | 29 __ Comment("Deopt stub for id %d", deopt_id_); |
| 31 __ Bind(entry_label()); | 30 __ Bind(entry_label()); |
| 32 for (intptr_t i = 0; i < registers_.length(); i++) { | 31 for (intptr_t i = 0; i < registers_.length(); i++) { |
| 33 if (registers_[i] != kNoRegister) { | 32 if (registers_[i] != kNoRegister) { |
| 34 __ pushl(registers_[i]); | 33 __ pushl(registers_[i]); |
| 35 } | 34 } |
| 36 } | 35 } |
| 37 __ movl(EAX, Immediate(Smi::RawValue(reason_))); | 36 __ movl(EAX, Immediate(Smi::RawValue(reason_))); |
| 38 __ call(&StubCode::DeoptimizeLabel()); | 37 __ call(&StubCode::DeoptimizeLabel()); |
| 39 compiler->AddCurrentDescriptor(PcDescriptors::kOther, | 38 compiler->AddCurrentDescriptor(PcDescriptors::kOther, |
| 40 deopt_id_, | 39 deopt_id_, |
| 41 deopt_token_pos_, | 40 deopt_token_pos_, |
| 42 try_index_); | 41 try_index_); |
| 43 #undef __ | 42 #undef __ |
| 44 } | 43 } |
| 45 | 44 |
| 46 | 45 |
| 47 | |
| 48 #define __ assembler()-> | 46 #define __ assembler()-> |
| 49 | 47 |
| 50 void FlowGraphCompiler::GenerateInlinedGetter(intptr_t offset) { | |
| 51 // TOS: return address. | |
| 52 // +1 : receiver. | |
| 53 // Sequence node has one return node, its input is load field node. | |
| 54 __ movl(EAX, Address(ESP, 1 * kWordSize)); | |
| 55 __ movl(EAX, FieldAddress(EAX, offset)); | |
| 56 __ ret(); | |
| 57 } | |
| 58 | |
| 59 | |
| 60 void FlowGraphCompiler::GenerateInlinedSetter(intptr_t offset) { | |
| 61 // TOS: return address. | |
| 62 // +1 : value | |
| 63 // +2 : receiver. | |
| 64 __ movl(EAX, Address(ESP, 2 * kWordSize)); // Receiver. | |
| 65 __ movl(EBX, Address(ESP, 1 * kWordSize)); // Value. | |
| 66 __ StoreIntoObject(EAX, FieldAddress(EAX, offset), EBX); | |
| 67 const Immediate raw_null = | |
| 68 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 69 __ movl(EAX, raw_null); | |
| 70 __ ret(); | |
| 71 } | |
| 72 | |
| 73 | |
| 74 void FlowGraphCompiler::GenerateInlinedMathSqrt(Label* done) { | |
| 75 Label smi_to_double, double_op, call_method; | |
| 76 __ movl(EAX, Address(ESP, 0)); | |
| 77 __ testl(EAX, Immediate(kSmiTagMask)); | |
| 78 __ j(ZERO, &smi_to_double); | |
| 79 __ CompareClassId(EAX, kDouble, EBX); | |
| 80 __ j(NOT_EQUAL, &call_method); | |
| 81 __ movsd(XMM1, FieldAddress(EAX, Double::value_offset())); | |
| 82 __ Bind(&double_op); | |
| 83 __ sqrtsd(XMM0, XMM1); | |
| 84 AssemblerMacros::TryAllocate(assembler_, | |
| 85 double_class_, | |
| 86 &call_method, | |
| 87 EAX); // Result register. | |
| 88 __ movsd(FieldAddress(EAX, Double::value_offset()), XMM0); | |
| 89 __ Drop(1); | |
| 90 __ jmp(done); | |
| 91 __ Bind(&smi_to_double); | |
| 92 __ SmiUntag(EAX); | |
| 93 __ cvtsi2sd(XMM1, EAX); | |
| 94 __ jmp(&double_op); | |
| 95 __ Bind(&call_method); | |
| 96 } | |
| 97 | |
| 98 | |
| 99 void FlowGraphCompiler::GenerateCall(intptr_t token_pos, | |
| 100 intptr_t try_index, | |
| 101 const ExternalLabel* label, | |
| 102 PcDescriptors::Kind kind) { | |
| 103 ASSERT(frame_register_allocator()->IsSpilled()); | |
| 104 __ call(label); | |
| 105 AddCurrentDescriptor(kind, AstNode::kNoId, token_pos, try_index); | |
| 106 } | |
| 107 | |
| 108 | |
| 109 void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid, | |
| 110 intptr_t token_pos, | |
| 111 intptr_t try_index, | |
| 112 const RuntimeEntry& entry) { | |
| 113 ASSERT(frame_register_allocator()->IsSpilled()); | |
| 114 __ CallRuntime(entry); | |
| 115 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_pos, try_index); | |
| 116 } | |
| 117 | |
| 118 | |
| 119 void FlowGraphCompiler::CopyParameters() { | |
| 120 const Function& function = parsed_function().function(); | |
| 121 const bool is_native_instance_closure = | |
| 122 function.is_native() && function.IsImplicitInstanceClosureFunction(); | |
| 123 LocalScope* scope = parsed_function().node_sequence()->scope(); | |
| 124 const int num_fixed_params = function.num_fixed_parameters(); | |
| 125 const int num_opt_params = function.num_optional_parameters(); | |
| 126 int implicit_this_param_pos = is_native_instance_closure ? -1 : 0; | |
| 127 ASSERT(parsed_function().first_parameter_index() == | |
| 128 ParsedFunction::kFirstLocalSlotIndex + implicit_this_param_pos); | |
| 129 // Copy positional arguments. | |
| 130 // Check that no fewer than num_fixed_params positional arguments are passed | |
| 131 // in and that no more than num_params arguments are passed in. | |
| 132 // Passed argument i at fp[1 + argc - i] | |
| 133 // copied to fp[ParsedFunction::kFirstLocalSlotIndex - i]. | |
| 134 const int num_params = num_fixed_params + num_opt_params; | |
| 135 | |
| 136 // Total number of args is the first Smi in args descriptor array (EDX). | |
| 137 __ movl(EBX, FieldAddress(EDX, Array::data_offset())); | |
| 138 // Check that num_args <= num_params. | |
| 139 Label wrong_num_arguments; | |
| 140 __ cmpl(EBX, Immediate(Smi::RawValue(num_params))); | |
| 141 __ j(GREATER, &wrong_num_arguments); | |
| 142 // Number of positional args is the second Smi in descriptor array (EDX). | |
| 143 __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize))); | |
| 144 // Check that num_pos_args >= num_fixed_params. | |
| 145 __ cmpl(ECX, Immediate(Smi::RawValue(num_fixed_params))); | |
| 146 __ j(LESS, &wrong_num_arguments); | |
| 147 // Since EBX and ECX are Smi, use TIMES_2 instead of TIMES_4. | |
| 148 // Let EBX point to the last passed positional argument, i.e. to | |
| 149 // fp[1 + num_args - (num_pos_args - 1)]. | |
| 150 __ subl(EBX, ECX); | |
| 151 __ leal(EBX, Address(EBP, EBX, TIMES_2, 2 * kWordSize)); | |
| 152 | |
| 153 // Let EDI point to the last copied positional argument, i.e. to | |
| 154 // fp[ParsedFunction::kFirstLocalSlotIndex - (num_pos_args - 1)]. | |
| 155 const int index = | |
| 156 ParsedFunction::kFirstLocalSlotIndex + 1 + implicit_this_param_pos; | |
| 157 // First copy captured receiver if function is an implicit native closure. | |
| 158 if (is_native_instance_closure) { | |
| 159 __ movl(EAX, FieldAddress(CTX, Context::variable_offset(0))); | |
| 160 __ movl(Address(EBP, (index * kWordSize)), EAX); | |
| 161 } | |
| 162 __ leal(EDI, Address(EBP, (index * kWordSize))); | |
| 163 __ subl(EDI, ECX); // ECX is a Smi, subtract twice for TIMES_4 scaling. | |
| 164 __ subl(EDI, ECX); | |
| 165 __ SmiUntag(ECX); | |
| 166 Label loop, loop_condition; | |
| 167 __ jmp(&loop_condition, Assembler::kNearJump); | |
| 168 // We do not use the final allocation index of the variable here, i.e. | |
| 169 // scope->VariableAt(i)->index(), because captured variables still need | |
| 170 // to be copied to the context that is not yet allocated. | |
| 171 const Address argument_addr(EBX, ECX, TIMES_4, 0); | |
| 172 const Address copy_addr(EDI, ECX, TIMES_4, 0); | |
| 173 __ Bind(&loop); | |
| 174 __ movl(EAX, argument_addr); | |
| 175 __ movl(copy_addr, EAX); | |
| 176 __ Bind(&loop_condition); | |
| 177 __ decl(ECX); | |
| 178 __ j(POSITIVE, &loop, Assembler::kNearJump); | |
| 179 | |
| 180 // Copy or initialize optional named arguments. | |
| 181 Label all_arguments_processed; | |
| 182 const Immediate raw_null = | |
| 183 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 184 if (num_opt_params > 0) { | |
| 185 // Start by alphabetically sorting the names of the optional parameters. | |
| 186 LocalVariable** opt_param = new LocalVariable*[num_opt_params]; | |
| 187 int* opt_param_position = new int[num_opt_params]; | |
| 188 for (int pos = num_fixed_params; pos < num_params; pos++) { | |
| 189 LocalVariable* parameter = scope->VariableAt(pos); | |
| 190 const String& opt_param_name = parameter->name(); | |
| 191 int i = pos - num_fixed_params; | |
| 192 while (--i >= 0) { | |
| 193 LocalVariable* param_i = opt_param[i]; | |
| 194 const intptr_t result = opt_param_name.CompareTo(param_i->name()); | |
| 195 ASSERT(result != 0); | |
| 196 if (result > 0) break; | |
| 197 opt_param[i + 1] = opt_param[i]; | |
| 198 opt_param_position[i + 1] = opt_param_position[i]; | |
| 199 } | |
| 200 opt_param[i + 1] = parameter; | |
| 201 opt_param_position[i + 1] = pos; | |
| 202 } | |
| 203 // Generate code handling each optional parameter in alphabetical order. | |
| 204 // Total number of args is the first Smi in args descriptor array (EDX). | |
| 205 __ movl(EBX, FieldAddress(EDX, Array::data_offset())); | |
| 206 // Number of positional args is the second Smi in descriptor array (EDX). | |
| 207 __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize))); | |
| 208 __ SmiUntag(ECX); | |
| 209 // Let EBX point to the first passed argument, i.e. to fp[1 + argc - 0]. | |
| 210 __ leal(EBX, Address(EBP, EBX, TIMES_2, kWordSize)); // EBX is Smi. | |
| 211 // Let EDI point to the name/pos pair of the first named argument. | |
| 212 __ leal(EDI, FieldAddress(EDX, Array::data_offset() + (2 * kWordSize))); | |
| 213 for (int i = 0; i < num_opt_params; i++) { | |
| 214 // Handle this optional parameter only if k or fewer positional arguments | |
| 215 // have been passed, where k is the position of this optional parameter in | |
| 216 // the formal parameter list. | |
| 217 Label load_default_value, assign_optional_parameter, next_parameter; | |
| 218 const int param_pos = opt_param_position[i]; | |
| 219 __ cmpl(ECX, Immediate(param_pos)); | |
| 220 __ j(GREATER, &next_parameter, Assembler::kNearJump); | |
| 221 // Check if this named parameter was passed in. | |
| 222 __ movl(EAX, Address(EDI, 0)); // Load EAX with the name of the argument. | |
| 223 __ CompareObject(EAX, opt_param[i]->name()); | |
| 224 __ j(NOT_EQUAL, &load_default_value, Assembler::kNearJump); | |
| 225 // Load EAX with passed-in argument at provided arg_pos, i.e. at | |
| 226 // fp[1 + argc - arg_pos]. | |
| 227 __ movl(EAX, Address(EDI, kWordSize)); // EAX is arg_pos as Smi. | |
| 228 __ addl(EDI, Immediate(2 * kWordSize)); // Point to next name/pos pair. | |
| 229 __ negl(EAX); | |
| 230 Address argument_addr(EBX, EAX, TIMES_2, 0); // EAX is a negative Smi. | |
| 231 __ movl(EAX, argument_addr); | |
| 232 __ jmp(&assign_optional_parameter, Assembler::kNearJump); | |
| 233 __ Bind(&load_default_value); | |
| 234 // Load EAX with default argument at pos. | |
| 235 const Object& value = Object::ZoneHandle( | |
| 236 parsed_function().default_parameter_values().At( | |
| 237 param_pos - num_fixed_params)); | |
| 238 __ LoadObject(EAX, value); | |
| 239 __ Bind(&assign_optional_parameter); | |
| 240 // Assign EAX to fp[ParsedFunction::kFirstLocalSlotIndex - param_pos]. | |
| 241 // We do not use the final allocation index of the variable here, i.e. | |
| 242 // scope->VariableAt(i)->index(), because captured variables still need | |
| 243 // to be copied to the context that is not yet allocated. | |
| 244 intptr_t computed_param_pos = (ParsedFunction::kFirstLocalSlotIndex - | |
| 245 param_pos + implicit_this_param_pos); | |
| 246 const Address param_addr(EBP, (computed_param_pos * kWordSize)); | |
| 247 __ movl(param_addr, EAX); | |
| 248 __ Bind(&next_parameter); | |
| 249 } | |
| 250 delete[] opt_param; | |
| 251 delete[] opt_param_position; | |
| 252 // Check that EDI now points to the null terminator in the array descriptor. | |
| 253 __ cmpl(Address(EDI, 0), raw_null); | |
| 254 __ j(EQUAL, &all_arguments_processed, Assembler::kNearJump); | |
| 255 } else { | |
| 256 ASSERT(is_native_instance_closure); | |
| 257 __ jmp(&all_arguments_processed, Assembler::kNearJump); | |
| 258 } | |
| 259 | |
| 260 __ Bind(&wrong_num_arguments); | |
| 261 if (StackSize() != 0) { | |
| 262 // We need to unwind the space we reserved for locals and copied parameters. | |
| 263 // The NoSuchMethodFunction stub does not expect to see that area on the | |
| 264 // stack. | |
| 265 __ addl(ESP, Immediate(StackSize() * kWordSize)); | |
| 266 } | |
| 267 if (function.IsClosureFunction()) { | |
| 268 GenerateCallRuntime(AstNode::kNoId, | |
| 269 0, | |
| 270 CatchClauseNode::kInvalidTryIndex, | |
| 271 kClosureArgumentMismatchRuntimeEntry); | |
| 272 } else { | |
| 273 // Invoke noSuchMethod function. | |
| 274 const int kNumArgsChecked = 1; | |
| 275 ICData& ic_data = ICData::ZoneHandle(); | |
| 276 ic_data = ICData::New(function, | |
| 277 String::Handle(function.name()), | |
| 278 AstNode::kNoId, | |
| 279 kNumArgsChecked); | |
| 280 __ LoadObject(ECX, ic_data); | |
| 281 // EBP - 4 : PC marker, allows easy identification of RawInstruction obj. | |
| 282 // EBP : points to previous frame pointer. | |
| 283 // EBP + 4 : points to return address. | |
| 284 // EBP + 8 : address of last argument (arg n-1). | |
| 285 // ESP + 8 + 4*(n-1) : address of first argument (arg 0). | |
| 286 // ECX : ic-data. | |
| 287 // EDX : arguments descriptor array. | |
| 288 __ call(&StubCode::CallNoSuchMethodFunctionLabel()); | |
| 289 } | |
| 290 | |
| 291 if (FLAG_trace_functions) { | |
| 292 __ pushl(EAX); // Preserve result. | |
| 293 __ PushObject(Function::ZoneHandle(function.raw())); | |
| 294 GenerateCallRuntime(AstNode::kNoId, | |
| 295 0, | |
| 296 CatchClauseNode::kInvalidTryIndex, | |
| 297 kTraceFunctionExitRuntimeEntry); | |
| 298 __ popl(EAX); // Remove argument. | |
| 299 __ popl(EAX); // Restore result. | |
| 300 } | |
| 301 __ LeaveFrame(); | |
| 302 __ ret(); | |
| 303 | |
| 304 __ Bind(&all_arguments_processed); | |
| 305 // Nullify originally passed arguments only after they have been copied and | |
| 306 // checked, otherwise noSuchMethod would not see their original values. | |
| 307 // This step can be skipped in case we decide that formal parameters are | |
| 308 // implicitly final, since garbage collecting the unmodified value is not | |
| 309 // an issue anymore. | |
| 310 | |
| 311 // EDX : arguments descriptor array. | |
| 312 // Total number of args is the first Smi in args descriptor array (EDX). | |
| 313 __ movl(ECX, FieldAddress(EDX, Array::data_offset())); | |
| 314 __ SmiUntag(ECX); | |
| 315 Label null_args_loop, null_args_loop_condition; | |
| 316 __ jmp(&null_args_loop_condition, Assembler::kNearJump); | |
| 317 const Address original_argument_addr(EBP, ECX, TIMES_4, 2 * kWordSize); | |
| 318 __ Bind(&null_args_loop); | |
| 319 __ movl(original_argument_addr, raw_null); | |
| 320 __ Bind(&null_args_loop_condition); | |
| 321 __ decl(ECX); | |
| 322 __ j(POSITIVE, &null_args_loop, Assembler::kNearJump); | |
| 323 } | |
| 324 | |
| 325 | |
| 326 void FlowGraphCompiler::CompileGraph() { | |
| 327 InitCompiler(); | |
| 328 if (TryIntrinsify()) { | |
| 329 __ int3(); | |
| 330 __ jmp(&StubCode::FixCallersTargetLabel()); | |
| 331 return; | |
| 332 } | |
| 333 // Specialized version of entry code from CodeGenerator::GenerateEntryCode. | |
| 334 const Function& function = parsed_function().function(); | |
| 335 | |
| 336 const int parameter_count = function.num_fixed_parameters(); | |
| 337 const int num_copied_params = parsed_function().copied_parameter_count(); | |
| 338 const int local_count = parsed_function().stack_local_count(); | |
| 339 AssemblerMacros::EnterDartFrame(assembler(), (StackSize() * kWordSize)); | |
| 340 // We check the number of passed arguments when we have to copy them due to | |
| 341 // the presence of optional named parameters. | |
| 342 // No such checking code is generated if only fixed parameters are declared, | |
| 343 // unless we are debug mode or unless we are compiling a closure. | |
| 344 if (num_copied_params == 0) { | |
| 345 #ifdef DEBUG | |
| 346 const bool check_arguments = true; | |
| 347 #else | |
| 348 const bool check_arguments = function.IsClosureFunction(); | |
| 349 #endif | |
| 350 if (check_arguments) { | |
| 351 // Check that num_fixed <= argc <= num_params. | |
| 352 Label argc_in_range; | |
| 353 // Total number of args is the first Smi in args descriptor array (EDX). | |
| 354 __ movl(EAX, FieldAddress(EDX, Array::data_offset())); | |
| 355 __ cmpl(EAX, Immediate(Smi::RawValue(parameter_count))); | |
| 356 __ j(EQUAL, &argc_in_range, Assembler::kNearJump); | |
| 357 if (function.IsClosureFunction()) { | |
| 358 GenerateCallRuntime(AstNode::kNoId, | |
| 359 function.token_pos(), | |
| 360 CatchClauseNode::kInvalidTryIndex, | |
| 361 kClosureArgumentMismatchRuntimeEntry); | |
| 362 } else { | |
| 363 __ Stop("Wrong number of arguments"); | |
| 364 } | |
| 365 __ Bind(&argc_in_range); | |
| 366 } | |
| 367 } else { | |
| 368 CopyParameters(); | |
| 369 } | |
| 370 // Initialize (non-argument) stack allocated locals to null. | |
| 371 if (local_count > 0) { | |
| 372 const Immediate raw_null = | |
| 373 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 374 __ movl(EAX, raw_null); | |
| 375 const int base = parsed_function().first_stack_local_index(); | |
| 376 for (int i = 0; i < local_count; ++i) { | |
| 377 // Subtract index i (locals lie at lower addresses than EBP). | |
| 378 __ movl(Address(EBP, (base - i) * kWordSize), EAX); | |
| 379 } | |
| 380 } | |
| 381 | |
| 382 // Generate stack overflow check. | |
| 383 __ cmpl(ESP, | |
| 384 Address::Absolute(Isolate::Current()->stack_limit_address())); | |
| 385 Label no_stack_overflow; | |
| 386 __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump); | |
| 387 GenerateCallRuntime(AstNode::kNoId, | |
| 388 function.token_pos(), | |
| 389 CatchClauseNode::kInvalidTryIndex, | |
| 390 kStackOverflowRuntimeEntry); | |
| 391 __ Bind(&no_stack_overflow); | |
| 392 | |
| 393 if (FLAG_print_scopes) { | |
| 394 // Print the function scope (again) after generating the prologue in order | |
| 395 // to see annotations such as allocation indices of locals. | |
| 396 if (FLAG_print_ast) { | |
| 397 // Second printing. | |
| 398 OS::Print("Annotated "); | |
| 399 } | |
| 400 AstPrinter::PrintFunctionScope(parsed_function()); | |
| 401 } | |
| 402 | |
| 403 VisitBlocks(); | |
| 404 | |
| 405 __ int3(); | |
| 406 GenerateDeferredCode(); | |
| 407 // Emit function patching code. This will be swapped with the first 5 bytes | |
| 408 // at entry point. | |
| 409 pc_descriptors_list()->AddDescriptor(PcDescriptors::kPatchCode, | |
| 410 assembler()->CodeSize(), | |
| 411 AstNode::kNoId, | |
| 412 0, | |
| 413 -1); | |
| 414 __ jmp(&StubCode::FixCallersTargetLabel()); | |
| 415 } | |
| 416 | |
| 417 | |
| 418 intptr_t FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label, | |
| 419 const ICData& ic_data, | |
| 420 const Array& arguments_descriptor, | |
| 421 intptr_t argument_count) { | |
| 422 __ LoadObject(ECX, ic_data); | |
| 423 __ LoadObject(EDX, arguments_descriptor); | |
| 424 | |
| 425 __ call(target_label); | |
| 426 const intptr_t descr_offset = assembler()->CodeSize(); | |
| 427 __ Drop(argument_count); | |
| 428 return descr_offset; | |
| 429 } | |
| 430 | |
| 431 | |
| 432 intptr_t FlowGraphCompiler::EmitStaticCall(const Function& function, | |
| 433 const Array& arguments_descriptor, | |
| 434 intptr_t argument_count) { | |
| 435 __ LoadObject(ECX, function); | |
| 436 __ LoadObject(EDX, arguments_descriptor); | |
| 437 __ call(&StubCode::CallStaticFunctionLabel()); | |
| 438 const intptr_t descr_offset = assembler()->CodeSize(); | |
| 439 __ Drop(argument_count); | |
| 440 return descr_offset; | |
| 441 } | |
| 442 | |
| 443 | 48 |
| 444 // Fall through if bool_register contains null. | 49 // Fall through if bool_register contains null. |
| 445 void FlowGraphCompiler::GenerateBoolToJump(Register bool_register, | 50 void FlowGraphCompiler::GenerateBoolToJump(Register bool_register, |
| 446 Label* is_true, | 51 Label* is_true, |
| 447 Label* is_false) { | 52 Label* is_false) { |
| 448 const Immediate raw_null = | 53 const Immediate raw_null = |
| 449 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 54 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 450 Label fall_through; | 55 Label fall_through; |
| 451 __ cmpl(bool_register, raw_null); | 56 __ cmpl(bool_register, raw_null); |
| 452 __ j(EQUAL, &fall_through, Assembler::kNearJump); | 57 __ j(EQUAL, &fall_through, Assembler::kNearJump); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 493 __ popl(instance_reg); // Restore receiver. | 98 __ popl(instance_reg); // Restore receiver. |
| 494 __ popl(temp_reg); // Discard. | 99 __ popl(temp_reg); // Discard. |
| 495 GenerateBoolToJump(ECX, is_instance_lbl, is_not_instance_lbl); | 100 GenerateBoolToJump(ECX, is_instance_lbl, is_not_instance_lbl); |
| 496 return type_test_cache.raw(); | 101 return type_test_cache.raw(); |
| 497 } | 102 } |
| 498 | 103 |
| 499 | 104 |
| 500 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if | 105 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if |
| 501 // type test is conclusive, otherwise fallthrough if a type test could not | 106 // type test is conclusive, otherwise fallthrough if a type test could not |
| 502 // be completed. | 107 // be completed. |
| 503 // EAX: instance (must survive), clobbers ECX, EDI | 108 // EAX: instance (must survive). |
| 109 // Clobbers ECX, EDI. | |
| 504 RawSubtypeTestCache* | 110 RawSubtypeTestCache* |
| 505 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( | 111 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest( |
| 506 intptr_t cid, | 112 intptr_t cid, |
| 507 intptr_t token_pos, | 113 intptr_t token_pos, |
| 508 const AbstractType& type, | 114 const AbstractType& type, |
| 509 Label* is_instance_lbl, | 115 Label* is_instance_lbl, |
| 510 Label* is_not_instance_lbl) { | 116 Label* is_not_instance_lbl) { |
| 511 ASSERT(type.IsInstantiated()); | 117 ASSERT(type.IsInstantiated()); |
| 512 const Class& type_class = Class::ZoneHandle(type.type_class()); | 118 const Class& type_class = Class::ZoneHandle(type.type_class()); |
| 513 ASSERT(type_class.HasTypeArguments()); | 119 ASSERT(type_class.HasTypeArguments()); |
| 514 const Register kInstanceReg = EAX; | 120 const Register kInstanceReg = EAX; |
| 515 // A Smi object cannot be the instance of a parameterized class. | 121 // A Smi object cannot be the instance of a parameterized class. |
| 516 __ testl(kInstanceReg, Immediate(kSmiTagMask)); | 122 __ testl(kInstanceReg, Immediate(kSmiTagMask)); |
| 517 __ j(ZERO, is_not_instance_lbl); | 123 __ j(ZERO, is_not_instance_lbl); |
| 518 const AbstractTypeArguments& type_arguments = | 124 const AbstractTypeArguments& type_arguments = |
| 519 AbstractTypeArguments::ZoneHandle(type.arguments()); | 125 AbstractTypeArguments::ZoneHandle(type.arguments()); |
| 520 const bool is_raw_type = type_arguments.IsNull() || | 126 const bool is_raw_type = type_arguments.IsNull() || |
| 521 type_arguments.IsRaw(type_arguments.Length()); | 127 type_arguments.IsRaw(type_arguments.Length()); |
| 522 if (is_raw_type) { | 128 if (is_raw_type) { |
| 523 const Register kClassIdReg = ECX; | 129 const Register kClassIdReg = ECX; |
| 524 // Dynamic type argument, check only classes. | 130 // Dynamic type argument, check only classes. |
| 131 // List is a very common case. | |
| 525 __ LoadClassId(kClassIdReg, kInstanceReg); | 132 __ LoadClassId(kClassIdReg, kInstanceReg); |
| 526 if (!type_class.is_interface()) { | 133 if (!type_class.is_interface()) { |
| 527 __ cmpl(kClassIdReg, Immediate(type_class.id())); | 134 __ cmpl(kClassIdReg, Immediate(type_class.id())); |
| 528 __ j(EQUAL, is_instance_lbl); | 135 __ j(EQUAL, is_instance_lbl); |
| 529 } | 136 } |
| 530 if (type.IsListInterface()) { | 137 if (type.IsListInterface()) { |
| 531 GenerateListTypeCheck(kClassIdReg, is_instance_lbl); | 138 GenerateListTypeCheck(kClassIdReg, is_instance_lbl); |
| 532 } | 139 } |
| 533 return GenerateSubtype1TestCacheLookup( | 140 return GenerateSubtype1TestCacheLookup( |
| 534 cid, token_pos, type_class, is_instance_lbl, is_not_instance_lbl); | 141 cid, token_pos, type_class, is_instance_lbl, is_not_instance_lbl); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 569 for (intptr_t i = 0; i < class_ids.length(); i++) { | 176 for (intptr_t i = 0; i < class_ids.length(); i++) { |
| 570 __ cmpl(class_id_reg, Immediate(class_ids[i])); | 177 __ cmpl(class_id_reg, Immediate(class_ids[i])); |
| 571 __ j(EQUAL, is_equal_lbl); | 178 __ j(EQUAL, is_equal_lbl); |
| 572 } | 179 } |
| 573 __ jmp(is_not_equal_lbl); | 180 __ jmp(is_not_equal_lbl); |
| 574 } | 181 } |
| 575 | 182 |
| 576 | 183 |
| 577 // Testing against an instantiated type with no arguments, without | 184 // Testing against an instantiated type with no arguments, without |
| 578 // SubtypeTestCache. | 185 // SubtypeTestCache. |
| 579 // EAX: instance to test against (preserved). Clobbers ECX, EDI. | 186 // EAX: instance to test against (preserved). |
| 187 // Clobbers ECX, EDI. | |
| 580 void FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( | 188 void FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest( |
| 581 intptr_t cid, | 189 intptr_t cid, |
| 582 intptr_t token_pos, | 190 intptr_t token_pos, |
| 583 const AbstractType& type, | 191 const AbstractType& type, |
| 584 Label* is_instance_lbl, | 192 Label* is_instance_lbl, |
| 585 Label* is_not_instance_lbl) { | 193 Label* is_not_instance_lbl) { |
| 586 ASSERT(type.IsInstantiated()); | 194 ASSERT(type.IsInstantiated()); |
| 587 const Class& type_class = Class::Handle(type.type_class()); | 195 const Class& type_class = Class::Handle(type.type_class()); |
| 588 ASSERT(!type_class.HasTypeArguments()); | 196 ASSERT(!type_class.HasTypeArguments()); |
| 589 | 197 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 605 } | 213 } |
| 606 // Compare if the classes are equal. | 214 // Compare if the classes are equal. |
| 607 __ Bind(&compare_classes); | 215 __ Bind(&compare_classes); |
| 608 const Register kClassIdReg = ECX; | 216 const Register kClassIdReg = ECX; |
| 609 __ LoadClassId(kClassIdReg, kInstanceReg); | 217 __ LoadClassId(kClassIdReg, kInstanceReg); |
| 610 // If type is an interface, we can skip the class equality check. | 218 // If type is an interface, we can skip the class equality check. |
| 611 if (!type_class.is_interface()) { | 219 if (!type_class.is_interface()) { |
| 612 __ cmpl(kClassIdReg, Immediate(type_class.id())); | 220 __ cmpl(kClassIdReg, Immediate(type_class.id())); |
| 613 __ j(EQUAL, is_instance_lbl); | 221 __ j(EQUAL, is_instance_lbl); |
| 614 } | 222 } |
| 223 // Bool interface can be implemented only by core class Bool. | |
| 615 // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces). | 224 // (see ClassFinalizer::ResolveInterfaces for list of restricted interfaces). |
| 616 // Bool interface can be implemented only by core class Bool. | |
| 617 if (type.IsBoolInterface()) { | 225 if (type.IsBoolInterface()) { |
| 618 __ cmpl(kClassIdReg, Immediate(kBool)); | 226 __ cmpl(kClassIdReg, Immediate(kBool)); |
| 619 __ j(EQUAL, is_instance_lbl); | 227 __ j(EQUAL, is_instance_lbl); |
| 620 __ jmp(is_not_instance_lbl); | 228 __ jmp(is_not_instance_lbl); |
| 621 return; | 229 return; |
| 622 } | 230 } |
| 623 if (type.IsFunctionInterface()) { | 231 if (type.IsFunctionInterface()) { |
| 624 // Check if instance is a closure. | 232 // Check if instance is a closure. |
| 625 const Immediate raw_null = | 233 const Immediate raw_null = |
| 626 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 234 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 627 __ LoadClassById(EDI, kClassIdReg); | 235 __ LoadClassById(EDI, kClassIdReg); |
| 628 __ movl(EDI, FieldAddress(EDI, Class::signature_function_offset())); | 236 __ movl(EDI, FieldAddress(EDI, Class::signature_function_offset())); |
| 629 __ cmpl(EDI, raw_null); | 237 __ cmpl(EDI, raw_null); |
| 630 __ j(NOT_EQUAL, is_instance_lbl); | 238 __ j(NOT_EQUAL, is_instance_lbl); |
| 631 __ jmp(is_not_instance_lbl); | 239 __ jmp(is_not_instance_lbl); |
| 632 return; | 240 return; |
| 633 } | 241 } |
| 634 // Custom checking for numbers (Smi, Mint, Bigint and Double). | 242 // Custom checking for numbers (Smi, Mint, Bigint and Double). |
| 635 // Note that instance is not Smi(checked above). | 243 // Note that instance is not Smi(checked above). |
| 636 if (type.IsSubtypeOf( | 244 if (type.IsSubtypeOf( |
| 637 Type::Handle(Type::NumberInterface()), &malformed_error)) { | 245 Type::Handle(Type::NumberInterface()), &malformed_error)) { |
| 638 GenerateNumberTypeCheck( | 246 GenerateNumberTypeCheck( |
| 639 kClassIdReg, type, is_instance_lbl, is_not_instance_lbl); | 247 kClassIdReg, type, is_instance_lbl, is_not_instance_lbl); |
| 640 return; | 248 return; |
| 641 } | 249 } |
| 642 if (type.IsStringInterface()) { | 250 if (type.IsStringInterface()) { |
| 643 GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl); | 251 GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl); |
| 644 return; | 252 return; |
| 645 } | 253 } |
| 254 // Otherwise fallthrough. | |
| 255 } | |
| 256 | |
| 257 | |
| 258 // Uses SubtypeTestCache to store instance class and result. | |
| 259 // EAX: instance to test. | |
| 260 // Clobbers EDI, ECX. | |
| 261 // Immediate class test already done. | |
| 262 // TODO(srdjan): Implement a quicker subtype check, as type test | |
| 263 // arrays can grow too high, but they may be useful when optimizing | |
| 264 // code (type-feedback). | |
| 265 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( | |
| 266 intptr_t cid, | |
| 267 intptr_t token_pos, | |
| 268 const Class& type_class, | |
| 269 Label* is_instance_lbl, | |
| 270 Label* is_not_instance_lbl) { | |
| 271 const Register kInstanceReg = EAX; | |
| 272 __ LoadClass(ECX, kInstanceReg, EDI); | |
| 273 // ECX: instance class. | |
| 274 // Check immediate superclass equality. | |
| 275 __ movl(EDI, FieldAddress(ECX, Class::super_type_offset())); | |
| 276 __ movl(EDI, FieldAddress(EDI, Type::type_class_offset())); | |
| 277 __ CompareObject(EDI, type_class); | |
| 278 __ j(EQUAL, is_instance_lbl); | |
| 279 | |
| 280 const Register kTypeArgumentsReg = kNoRegister; | |
| 281 const Register kTempReg = EDI; | |
| 282 return GenerateCallSubtypeTestStub(kTestTypeOneArg, | |
| 283 kInstanceReg, | |
| 284 kTypeArgumentsReg, | |
| 285 kTempReg, | |
| 286 is_instance_lbl, | |
| 287 is_not_instance_lbl); | |
| 646 } | 288 } |
| 647 | 289 |
| 648 | 290 |
| 649 // Generates inlined check if 'type' is a type parameter or type itsef | 291 // Generates inlined check if 'type' is a type parameter or type itsef |
| 650 // EAX: instance (preserved). Clobbers EDX, EDI, ECX. | 292 // EAX: instance (preserved). |
| 293 // Clobbers EDX, EDI, ECX. | |
| 651 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( | 294 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest( |
| 652 intptr_t cid, | 295 intptr_t cid, |
| 653 intptr_t token_pos, | 296 intptr_t token_pos, |
| 654 const AbstractType& type, | 297 const AbstractType& type, |
| 655 Label* is_instance_lbl, | 298 Label* is_instance_lbl, |
| 656 Label* is_not_instance_lbl) { | 299 Label* is_not_instance_lbl) { |
| 657 ASSERT(!type.IsInstantiated()); | 300 ASSERT(!type.IsInstantiated()); |
| 658 // Skip check if destination is a dynamic type. | 301 // Skip check if destination is a dynamic type. |
| 659 const Immediate raw_null = | 302 const Immediate raw_null = |
| 660 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 303 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 661 if (type.IsTypeParameter()) { | 304 if (type.IsTypeParameter()) { |
| 662 // Load instantiator (or null) and instantiator type arguments on stack. | 305 // Load instantiator (or null) and instantiator type arguments on stack. |
| 663 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. | 306 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. |
| 664 // EDX: instantiator type arguments. | 307 // EDX: instantiator type arguments. |
| 665 // Check if type argument is Dynamic. | 308 // Check if type argument is Dynamic. |
| 666 __ cmpl(EDX, raw_null); | 309 __ cmpl(EDX, raw_null); |
| 667 __ j(EQUAL, is_instance_lbl); | 310 __ j(EQUAL, is_instance_lbl); |
| 668 // Can handle only type arguments that are instances of TypeArguments. | 311 // Can handle only type arguments that are instances of TypeArguments. |
| 669 // (runtime checks canonicalize type arguments). | 312 // (runtime checks canonicalize type arguments). |
| 670 Label fall_through; | 313 Label fall_through; |
| 671 __ CompareClassId(EDX, kTypeArguments, EDI); | 314 __ CompareClassId(EDX, kTypeArguments, EDI); |
| 672 __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump); | 315 __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump); |
| 673 | |
| 674 __ movl(EDI, | 316 __ movl(EDI, |
| 675 FieldAddress(EDX, TypeArguments::type_at_offset(type.Index()))); | 317 FieldAddress(EDX, TypeArguments::type_at_offset(type.Index()))); |
| 676 // EDI: concrete type of type. | 318 // EDI: concrete type of type. |
| 677 // Check if type argument is dynamic. | 319 // Check if type argument is dynamic. |
| 678 __ CompareObject(EDI, Type::ZoneHandle(Type::DynamicType())); | 320 __ CompareObject(EDI, Type::ZoneHandle(Type::DynamicType())); |
| 679 __ j(EQUAL, is_instance_lbl); | 321 __ j(EQUAL, is_instance_lbl); |
| 680 __ cmpl(EDI, raw_null); | 322 __ cmpl(EDI, raw_null); |
| 681 __ j(EQUAL, is_instance_lbl); | 323 __ j(EQUAL, is_instance_lbl); |
| 682 const Type& object_type = Type::ZoneHandle(Type::ObjectType()); | 324 const Type& object_type = Type::ZoneHandle(Type::ObjectType()); |
| 683 __ CompareObject(EDI, object_type); | 325 __ CompareObject(EDI, object_type); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 724 kInstanceReg, | 366 kInstanceReg, |
| 725 kTypeArgumentsReg, | 367 kTypeArgumentsReg, |
| 726 kTempReg, | 368 kTempReg, |
| 727 is_instance_lbl, | 369 is_instance_lbl, |
| 728 is_not_instance_lbl); | 370 is_not_instance_lbl); |
| 729 } | 371 } |
| 730 return SubtypeTestCache::null(); | 372 return SubtypeTestCache::null(); |
| 731 } | 373 } |
| 732 | 374 |
| 733 | 375 |
| 734 // Uses SubtypeTestCache to store instance class and result. | |
| 735 // EAX: instance to test. Clobbers EDI, ECX. | |
| 736 // Immediate class test already done. | |
| 737 // TODO(srdjan): Implement a quicker subtype check, as type test | |
| 738 // arrays can grow too high, but they may be useful when optimizing | |
| 739 // code (type-feedback). | |
| 740 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup( | |
| 741 intptr_t cid, | |
| 742 intptr_t token_pos, | |
| 743 const Class& type_class, | |
| 744 Label* is_instance_lbl, | |
| 745 Label* is_not_instance_lbl) { | |
| 746 const Register kInstanceReg = EAX; | |
| 747 __ LoadClass(ECX, kInstanceReg, EDI); | |
| 748 // ECX: instance class. | |
| 749 // Check immediate superclass equality. | |
| 750 __ movl(EDI, FieldAddress(ECX, Class::super_type_offset())); | |
| 751 __ movl(EDI, FieldAddress(EDI, Type::type_class_offset())); | |
| 752 __ CompareObject(EDI, type_class); | |
| 753 __ j(EQUAL, is_instance_lbl); | |
| 754 | |
| 755 const Register kTypeArgumentsReg = kNoRegister; | |
| 756 const Register kTempReg = EDI; | |
| 757 return GenerateCallSubtypeTestStub(kTestTypeOneArg, | |
| 758 kInstanceReg, | |
| 759 kTypeArgumentsReg, | |
| 760 kTempReg, | |
| 761 is_instance_lbl, | |
| 762 is_not_instance_lbl); | |
| 763 } | |
| 764 | |
| 765 | |
| 766 // Inputs: | 376 // Inputs: |
| 767 // - EAX: instance to test against (preserved). | 377 // - EAX: instance to test against (preserved). |
| 768 // - EDX: optional instantiator type arguments (preserved). | 378 // - EDX: optional instantiator type arguments (preserved). |
| 379 // Clobbers ECX, EDI. | |
| 769 // Returns: | 380 // Returns: |
| 770 // - preserved instance in EAX and optional instantiator type arguments in EDX. | 381 // - preserved instance in EAX and optional instantiator type arguments in EDX. |
| 771 // Note that this inlined code must be followed by the runtime_call code, as it | 382 // Note that this inlined code must be followed by the runtime_call code, as it |
| 772 // may fall through to it. Otherwise, this inline code will jump to the label | 383 // may fall through to it. Otherwise, this inline code will jump to the label |
| 773 // is_instance or to the label is_not_instance. | 384 // is_instance or to the label is_not_instance. |
| 774 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( | 385 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( |
| 775 intptr_t cid, | 386 intptr_t cid, |
| 776 intptr_t token_pos, | 387 intptr_t token_pos, |
| 777 const AbstractType& type, | 388 const AbstractType& type, |
| 778 Label* is_instance_lbl, | 389 Label* is_instance_lbl, |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 814 | 425 |
| 815 // If instanceof type test cannot be performed successfully at compile time and | 426 // If instanceof type test cannot be performed successfully at compile time and |
| 816 // therefore eliminated, optimize it by adding inlined tests for: | 427 // therefore eliminated, optimize it by adding inlined tests for: |
| 817 // - NULL -> return false. | 428 // - NULL -> return false. |
| 818 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 429 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 819 // - Class equality (only if class is not parameterized). | 430 // - Class equality (only if class is not parameterized). |
| 820 // Inputs: | 431 // Inputs: |
| 821 // - EAX: object. | 432 // - EAX: object. |
| 822 // - EDX: instantiator type arguments or raw_null. | 433 // - EDX: instantiator type arguments or raw_null. |
| 823 // - ECX: instantiator or raw_null. | 434 // - ECX: instantiator or raw_null. |
| 435 // Clobbers ECX and EDX. | |
| 824 // Returns: | 436 // Returns: |
| 825 // - true or false in EAX. | 437 // - true or false in EAX. |
| 826 void FlowGraphCompiler::GenerateInstanceOf(intptr_t cid, | 438 void FlowGraphCompiler::GenerateInstanceOf(intptr_t cid, |
| 827 intptr_t token_pos, | 439 intptr_t token_pos, |
| 828 intptr_t try_index, | 440 intptr_t try_index, |
| 829 const AbstractType& type, | 441 const AbstractType& type, |
| 830 bool negate_result) { | 442 bool negate_result) { |
| 831 ASSERT(type.IsFinalized() && !type.IsMalformed()); | 443 ASSERT(type.IsFinalized() && !type.IsMalformed()); |
| 832 | 444 |
| 833 const Immediate raw_null = | 445 const Immediate raw_null = |
| 834 Immediate(reinterpret_cast<intptr_t>(Object::null())); | 446 Immediate(reinterpret_cast<intptr_t>(Object::null())); |
| 835 Label is_instance, is_not_instance; | 447 Label is_instance, is_not_instance; |
| 836 __ pushl(ECX); // Store instantiator on stack. | 448 __ pushl(ECX); // Store instantiator on stack. |
| 837 __ pushl(EDX); // Store instantiator type arguments. | 449 __ pushl(EDX); // Store instantiator type arguments. |
| 838 // If type is instantiated and non-parameterized, we can inline code | 450 // If type is instantiated and non-parameterized, we can inline code |
| 839 // checking whether the tested instance is a Smi. | 451 // checking whether the tested instance is a Smi. |
| 840 if (type.IsInstantiated()) { | 452 if (type.IsInstantiated()) { |
| 841 // A null object is only an instance of Object and Dynamic, which has | 453 // A null object is only an instance of Object and Dynamic, which has |
| 842 // already been checked above (if the type is instantiated). So we can | 454 // already been checked above (if the type is instantiated). So we can |
| 843 // return false here if the instance is null (and if the type is | 455 // return false here if the instance is null (and if the type is |
| 844 // instantiated). | 456 // instantiated). |
| 845 // We can only inline this null check if the type is instantiated at compile | 457 // We can only inline this null check if the type is instantiated at compile |
| 846 // time, since an uninstantiated type at compile time could be Object or | 458 // time, since an uninstantiated type at compile time could be Object or |
| 847 // Dynamic at run time. | 459 // Dynamic at run time. |
| 848 __ cmpl(EAX, raw_null); | 460 __ cmpl(EAX, raw_null); |
| 849 __ j(EQUAL, &is_not_instance); | 461 __ j(EQUAL, &is_not_instance); |
| 850 } | 462 } |
| 851 // TODO(srdjan): Enable inlined checks. | 463 |
| 852 // Generate inline instanceof test. | 464 // Generate inline instanceof test. |
| 853 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); | 465 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| 854 test_cache = GenerateInlineInstanceof(cid, token_pos, type, | 466 test_cache = GenerateInlineInstanceof(cid, token_pos, type, |
| 855 &is_instance, &is_not_instance); | 467 &is_instance, &is_not_instance); |
| 856 | 468 |
| 857 // Generate runtime call. | 469 // Generate runtime call. |
| 858 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. | 470 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. |
| 859 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. | 471 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. |
| 860 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 472 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 861 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location. | 473 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location. |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 941 token_pos, | 553 token_pos, |
| 942 try_index, | 554 try_index, |
| 943 kMalformedTypeErrorRuntimeEntry); | 555 kMalformedTypeErrorRuntimeEntry); |
| 944 // We should never return here. | 556 // We should never return here. |
| 945 __ int3(); | 557 __ int3(); |
| 946 | 558 |
| 947 __ Bind(&is_assignable); // For a null object. | 559 __ Bind(&is_assignable); // For a null object. |
| 948 return; | 560 return; |
| 949 } | 561 } |
| 950 | 562 |
| 951 // TODO(srdjan): Enable subtype test cache. | |
| 952 // Generate inline type check, linking to runtime call if not assignable. | 563 // Generate inline type check, linking to runtime call if not assignable. |
| 953 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); | 564 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| 954 test_cache = GenerateInlineInstanceof(cid, token_pos, dst_type, | 565 test_cache = GenerateInlineInstanceof(cid, token_pos, dst_type, |
| 955 &is_assignable, &runtime_call); | 566 &is_assignable, &runtime_call); |
| 956 | 567 |
| 957 __ Bind(&runtime_call); | 568 __ Bind(&runtime_call); |
| 958 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. | 569 __ movl(EDX, Address(ESP, 0)); // Get instantiator type arguments. |
| 959 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. | 570 __ movl(ECX, Address(ESP, kWordSize)); // Get instantiator. |
| 960 __ PushObject(Object::ZoneHandle()); // Make room for the result. | 571 __ PushObject(Object::ZoneHandle()); // Make room for the result. |
| 961 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location. | 572 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location. |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 988 | 599 |
| 989 frame_register_allocator()->AllocateRegisters(instr); | 600 frame_register_allocator()->AllocateRegisters(instr); |
| 990 | 601 |
| 991 // TODO(vegorov): adjust assertion when we start removing comparison from the | 602 // TODO(vegorov): adjust assertion when we start removing comparison from the |
| 992 // graph when it is merged with a branch. | 603 // graph when it is merged with a branch. |
| 993 ASSERT(locs->is_call() || | 604 ASSERT(locs->is_call() || |
| 994 (instr->IsBranch() && instr->AsBranch()->is_fused_with_comparison()) || | 605 (instr->IsBranch() && instr->AsBranch()->is_fused_with_comparison()) || |
| 995 (locs->input_count() == instr->InputCount())); | 606 (locs->input_count() == instr->InputCount())); |
| 996 } | 607 } |
| 997 | 608 |
| 609 | |
| 610 void FlowGraphCompiler::CopyParameters() { | |
| 611 const Function& function = parsed_function().function(); | |
| 612 const bool is_native_instance_closure = | |
| 613 function.is_native() && function.IsImplicitInstanceClosureFunction(); | |
| 614 LocalScope* scope = parsed_function().node_sequence()->scope(); | |
| 615 const int num_fixed_params = function.num_fixed_parameters(); | |
| 616 const int num_opt_params = function.num_optional_parameters(); | |
| 617 int implicit_this_param_pos = is_native_instance_closure ? -1 : 0; | |
| 618 ASSERT(parsed_function().first_parameter_index() == | |
| 619 ParsedFunction::kFirstLocalSlotIndex + implicit_this_param_pos); | |
| 620 // Copy positional arguments. | |
| 621 // Check that no fewer than num_fixed_params positional arguments are passed | |
| 622 // in and that no more than num_params arguments are passed in. | |
| 623 // Passed argument i at fp[1 + argc - i] | |
| 624 // copied to fp[ParsedFunction::kFirstLocalSlotIndex - i]. | |
| 625 const int num_params = num_fixed_params + num_opt_params; | |
| 626 | |
| 627 // Total number of args is the first Smi in args descriptor array (EDX). | |
| 628 __ movl(EBX, FieldAddress(EDX, Array::data_offset())); | |
| 629 // Check that num_args <= num_params. | |
| 630 Label wrong_num_arguments; | |
| 631 __ cmpl(EBX, Immediate(Smi::RawValue(num_params))); | |
| 632 __ j(GREATER, &wrong_num_arguments); | |
| 633 // Number of positional args is the second Smi in descriptor array (EDX). | |
| 634 __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize))); | |
| 635 // Check that num_pos_args >= num_fixed_params. | |
| 636 __ cmpl(ECX, Immediate(Smi::RawValue(num_fixed_params))); | |
| 637 __ j(LESS, &wrong_num_arguments); | |
| 638 | |
| 639 // Since EBX and ECX are Smi, use TIMES_2 instead of TIMES_4. | |
| 640 // Let EBX point to the last passed positional argument, i.e. to | |
| 641 // fp[1 + num_args - (num_pos_args - 1)]. | |
| 642 __ subl(EBX, ECX); | |
| 643 __ leal(EBX, Address(EBP, EBX, TIMES_2, 2 * kWordSize)); | |
| 644 | |
| 645 // Let EDI point to the last copied positional argument, i.e. to | |
| 646 // fp[ParsedFunction::kFirstLocalSlotIndex - (num_pos_args - 1)]. | |
| 647 const int index = | |
| 648 ParsedFunction::kFirstLocalSlotIndex + 1 + implicit_this_param_pos; | |
| 649 // First copy captured receiver if function is an implicit native closure. | |
| 650 if (is_native_instance_closure) { | |
| 651 __ movl(EAX, FieldAddress(CTX, Context::variable_offset(0))); | |
| 652 __ movl(Address(EBP, (index * kWordSize)), EAX); | |
| 653 } | |
| 654 __ leal(EDI, Address(EBP, (index * kWordSize))); | |
| 655 __ subl(EDI, ECX); // ECX is a Smi, subtract twice for TIMES_4 scaling. | |
| 656 __ subl(EDI, ECX); | |
| 657 __ SmiUntag(ECX); | |
| 658 Label loop, loop_condition; | |
| 659 __ jmp(&loop_condition, Assembler::kNearJump); | |
| 660 // We do not use the final allocation index of the variable here, i.e. | |
| 661 // scope->VariableAt(i)->index(), because captured variables still need | |
| 662 // to be copied to the context that is not yet allocated. | |
| 663 const Address argument_addr(EBX, ECX, TIMES_4, 0); | |
| 664 const Address copy_addr(EDI, ECX, TIMES_4, 0); | |
| 665 __ Bind(&loop); | |
| 666 __ movl(EAX, argument_addr); | |
| 667 __ movl(copy_addr, EAX); | |
| 668 __ Bind(&loop_condition); | |
| 669 __ decl(ECX); | |
| 670 __ j(POSITIVE, &loop, Assembler::kNearJump); | |
| 671 | |
| 672 // Copy or initialize optional named arguments. | |
| 673 const Immediate raw_null = | |
| 674 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 675 Label all_arguments_processed; | |
| 676 if (num_opt_params > 0) { | |
| 677 // Start by alphabetically sorting the names of the optional parameters. | |
| 678 LocalVariable** opt_param = new LocalVariable*[num_opt_params]; | |
| 679 int* opt_param_position = new int[num_opt_params]; | |
| 680 for (int pos = num_fixed_params; pos < num_params; pos++) { | |
| 681 LocalVariable* parameter = scope->VariableAt(pos); | |
| 682 const String& opt_param_name = parameter->name(); | |
| 683 int i = pos - num_fixed_params; | |
| 684 while (--i >= 0) { | |
| 685 LocalVariable* param_i = opt_param[i]; | |
| 686 const intptr_t result = opt_param_name.CompareTo(param_i->name()); | |
| 687 ASSERT(result != 0); | |
| 688 if (result > 0) break; | |
| 689 opt_param[i + 1] = opt_param[i]; | |
| 690 opt_param_position[i + 1] = opt_param_position[i]; | |
| 691 } | |
| 692 opt_param[i + 1] = parameter; | |
| 693 opt_param_position[i + 1] = pos; | |
| 694 } | |
| 695 // Generate code handling each optional parameter in alphabetical order. | |
| 696 // Total number of args is the first Smi in args descriptor array (EDX). | |
| 697 __ movl(EBX, FieldAddress(EDX, Array::data_offset())); | |
| 698 // Number of positional args is the second Smi in descriptor array (EDX). | |
| 699 __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize))); | |
| 700 __ SmiUntag(ECX); | |
| 701 // Let EBX point to the first passed argument, i.e. to fp[1 + argc - 0]. | |
| 702 __ leal(EBX, Address(EBP, EBX, TIMES_2, kWordSize)); // EBX is Smi. | |
| 703 // Let EDI point to the name/pos pair of the first named argument. | |
| 704 __ leal(EDI, FieldAddress(EDX, Array::data_offset() + (2 * kWordSize))); | |
| 705 for (int i = 0; i < num_opt_params; i++) { | |
| 706 // Handle this optional parameter only if k or fewer positional arguments | |
| 707 // have been passed, where k is the position of this optional parameter in | |
| 708 // the formal parameter list. | |
| 709 Label load_default_value, assign_optional_parameter, next_parameter; | |
| 710 const int param_pos = opt_param_position[i]; | |
| 711 __ cmpl(ECX, Immediate(param_pos)); | |
| 712 __ j(GREATER, &next_parameter, Assembler::kNearJump); | |
| 713 // Check if this named parameter was passed in. | |
| 714 __ movl(EAX, Address(EDI, 0)); // Load EAX with the name of the argument. | |
| 715 __ CompareObject(EAX, opt_param[i]->name()); | |
| 716 __ j(NOT_EQUAL, &load_default_value, Assembler::kNearJump); | |
| 717 // Load EAX with passed-in argument at provided arg_pos, i.e. at | |
| 718 // fp[1 + argc - arg_pos]. | |
| 719 __ movl(EAX, Address(EDI, kWordSize)); // EAX is arg_pos as Smi. | |
| 720 __ addl(EDI, Immediate(2 * kWordSize)); // Point to next name/pos pair. | |
| 721 __ negl(EAX); | |
| 722 Address argument_addr(EBX, EAX, TIMES_2, 0); // EAX is a negative Smi. | |
| 723 __ movl(EAX, argument_addr); | |
| 724 __ jmp(&assign_optional_parameter, Assembler::kNearJump); | |
| 725 __ Bind(&load_default_value); | |
| 726 // Load EAX with default argument at pos. | |
| 727 const Object& value = Object::ZoneHandle( | |
| 728 parsed_function().default_parameter_values().At( | |
| 729 param_pos - num_fixed_params)); | |
| 730 __ LoadObject(EAX, value); | |
| 731 __ Bind(&assign_optional_parameter); | |
| 732 // Assign EAX to fp[ParsedFunction::kFirstLocalSlotIndex - param_pos]. | |
| 733 // We do not use the final allocation index of the variable here, i.e. | |
| 734 // scope->VariableAt(i)->index(), because captured variables still need | |
| 735 // to be copied to the context that is not yet allocated. | |
| 736 intptr_t computed_param_pos = (ParsedFunction::kFirstLocalSlotIndex - | |
| 737 param_pos + implicit_this_param_pos); | |
| 738 const Address param_addr(EBP, (computed_param_pos * kWordSize)); | |
| 739 __ movl(param_addr, EAX); | |
| 740 __ Bind(&next_parameter); | |
| 741 } | |
| 742 delete[] opt_param; | |
| 743 delete[] opt_param_position; | |
| 744 // Check that EDI now points to the null terminator in the array descriptor. | |
| 745 __ cmpl(Address(EDI, 0), raw_null); | |
| 746 __ j(EQUAL, &all_arguments_processed, Assembler::kNearJump); | |
| 747 } else { | |
| 748 ASSERT(is_native_instance_closure); | |
| 749 __ jmp(&all_arguments_processed, Assembler::kNearJump); | |
| 750 } | |
| 751 | |
| 752 __ Bind(&wrong_num_arguments); | |
| 753 if (StackSize() != 0) { | |
| 754 // We need to unwind the space we reserved for locals and copied parameters. | |
| 755 // The NoSuchMethodFunction stub does not expect to see that area on the | |
| 756 // stack. | |
| 757 __ addl(ESP, Immediate(StackSize() * kWordSize)); | |
| 758 } | |
| 759 if (function.IsClosureFunction()) { | |
| 760 GenerateCallRuntime(AstNode::kNoId, | |
| 761 0, | |
| 762 CatchClauseNode::kInvalidTryIndex, | |
| 763 kClosureArgumentMismatchRuntimeEntry); | |
| 764 } else { | |
| 765 // Invoke noSuchMethod function. | |
| 766 const int kNumArgsChecked = 1; | |
| 767 ICData& ic_data = ICData::ZoneHandle(); | |
| 768 ic_data = ICData::New(function, | |
| 769 String::Handle(function.name()), | |
| 770 AstNode::kNoId, | |
| 771 kNumArgsChecked); | |
| 772 __ LoadObject(ECX, ic_data); | |
| 773 // EBP - 4 : PC marker, allows easy identification of RawInstruction obj. | |
| 774 // EBP : points to previous frame pointer. | |
| 775 // EBP + 4 : points to return address. | |
| 776 // EBP + 8 : address of last argument (arg n-1). | |
| 777 // ESP + 8 + 4*(n-1) : address of first argument (arg 0). | |
| 778 // ECX : ic-data. | |
| 779 // EDX : arguments descriptor array. | |
| 780 __ call(&StubCode::CallNoSuchMethodFunctionLabel()); | |
| 781 } | |
| 782 | |
| 783 if (FLAG_trace_functions) { | |
| 784 __ pushl(EAX); // Preserve result. | |
| 785 __ PushObject(Function::ZoneHandle(function.raw())); | |
| 786 GenerateCallRuntime(AstNode::kNoId, | |
| 787 0, | |
| 788 CatchClauseNode::kInvalidTryIndex, | |
| 789 kTraceFunctionExitRuntimeEntry); | |
| 790 __ popl(EAX); // Remove argument. | |
| 791 __ popl(EAX); // Restore result. | |
| 792 } | |
| 793 __ LeaveFrame(); | |
| 794 __ ret(); | |
| 795 | |
| 796 __ Bind(&all_arguments_processed); | |
| 797 // Nullify originally passed arguments only after they have been copied and | |
| 798 // checked, otherwise noSuchMethod would not see their original values. | |
| 799 // This step can be skipped in case we decide that formal parameters are | |
| 800 // implicitly final, since garbage collecting the unmodified value is not | |
| 801 // an issue anymore. | |
| 802 | |
| 803 // EDX : arguments descriptor array. | |
| 804 // Total number of args is the first Smi in args descriptor array (EDX). | |
| 805 __ movl(ECX, FieldAddress(EDX, Array::data_offset())); | |
| 806 __ SmiUntag(ECX); | |
| 807 Label null_args_loop, null_args_loop_condition; | |
| 808 __ jmp(&null_args_loop_condition, Assembler::kNearJump); | |
| 809 const Address original_argument_addr(EBP, ECX, TIMES_4, 2 * kWordSize); | |
| 810 __ Bind(&null_args_loop); | |
| 811 __ movl(original_argument_addr, raw_null); | |
| 812 __ Bind(&null_args_loop_condition); | |
| 813 __ decl(ECX); | |
| 814 __ j(POSITIVE, &null_args_loop, Assembler::kNearJump); | |
| 815 } | |
| 816 | |
| 817 | |
| 818 void FlowGraphCompiler::GenerateInlinedGetter(intptr_t offset) { | |
| 819 // TOS: return address. | |
| 820 // +1 : receiver. | |
| 821 // Sequence node has one return node, its input is load field node. | |
| 822 __ movl(EAX, Address(ESP, 1 * kWordSize)); | |
| 823 __ movl(EAX, FieldAddress(EAX, offset)); | |
| 824 __ ret(); | |
| 825 } | |
| 826 | |
| 827 | |
| 828 void FlowGraphCompiler::GenerateInlinedSetter(intptr_t offset) { | |
| 829 // TOS: return address. | |
| 830 // +1 : value | |
| 831 // +2 : receiver. | |
| 832 // Sequence node has one store node and one return NULL node. | |
| 833 __ movl(EAX, Address(ESP, 2 * kWordSize)); // Receiver. | |
| 834 __ movl(EBX, Address(ESP, 1 * kWordSize)); // Value. | |
| 835 __ StoreIntoObject(EAX, FieldAddress(EAX, offset), EBX); | |
| 836 const Immediate raw_null = | |
| 837 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 838 __ movl(EAX, raw_null); | |
| 839 __ ret(); | |
| 840 } | |
| 841 | |
| 842 | |
| 843 void FlowGraphCompiler::GenerateInlinedMathSqrt(Label* done) { | |
| 844 Label smi_to_double, double_op, call_method; | |
| 845 __ movl(EAX, Address(ESP, 0)); | |
| 846 __ testl(EAX, Immediate(kSmiTagMask)); | |
| 847 __ j(ZERO, &smi_to_double); | |
| 848 __ CompareClassId(EAX, kDouble, EBX); | |
| 849 __ j(NOT_EQUAL, &call_method); | |
| 850 __ movsd(XMM1, FieldAddress(EAX, Double::value_offset())); | |
| 851 __ Bind(&double_op); | |
| 852 __ sqrtsd(XMM0, XMM1); | |
| 853 AssemblerMacros::TryAllocate(assembler_, | |
| 854 double_class_, | |
| 855 &call_method, | |
| 856 EAX); // Result register. | |
| 857 __ movsd(FieldAddress(EAX, Double::value_offset()), XMM0); | |
| 858 __ Drop(1); | |
| 859 __ jmp(done); | |
| 860 __ Bind(&smi_to_double); | |
| 861 __ SmiUntag(EAX); | |
| 862 __ cvtsi2sd(XMM1, EAX); | |
| 863 __ jmp(&double_op); | |
| 864 __ Bind(&call_method); | |
| 865 } | |
| 866 | |
| 867 | |
| 868 void FlowGraphCompiler::CompileGraph() { | |
| 869 InitCompiler(); | |
| 870 if (TryIntrinsify()) { | |
| 871 // Although this intrinsified code will never be patched, it must satisfy | |
| 872 // CodePatcher::CodeIsPatchable, which verifies that this code has a minimum | |
| 873 // code size. | |
| 874 __ int3(); | |
| 875 __ jmp(&StubCode::FixCallersTargetLabel()); | |
| 876 return; | |
| 877 } | |
| 878 // Specialized version of entry code from CodeGenerator::GenerateEntryCode. | |
| 879 const Function& function = parsed_function().function(); | |
| 880 | |
| 881 const int parameter_count = function.num_fixed_parameters(); | |
| 882 const int num_copied_params = parsed_function().copied_parameter_count(); | |
| 883 const int local_count = parsed_function().stack_local_count(); | |
| 884 AssemblerMacros::EnterDartFrame(assembler(), (StackSize() * kWordSize)); | |
| 885 // We check the number of passed arguments when we have to copy them due to | |
| 886 // the presence of optional named parameters. | |
| 887 // No such checking code is generated if only fixed parameters are declared, | |
| 888 // unless we are debug mode or unless we are compiling a closure. | |
| 889 if (num_copied_params == 0) { | |
| 890 #ifdef DEBUG | |
| 891 const bool check_arguments = true; | |
| 892 #else | |
| 893 const bool check_arguments = function.IsClosureFunction(); | |
| 894 #endif | |
| 895 if (check_arguments) { | |
| 896 // Check that num_fixed <= argc <= num_params. | |
| 897 Label argc_in_range; | |
| 898 // Total number of args is the first Smi in args descriptor array (EDX). | |
| 899 __ movl(EAX, FieldAddress(EDX, Array::data_offset())); | |
| 900 __ cmpl(EAX, Immediate(Smi::RawValue(parameter_count))); | |
| 901 __ j(EQUAL, &argc_in_range, Assembler::kNearJump); | |
| 902 if (function.IsClosureFunction()) { | |
| 903 GenerateCallRuntime(AstNode::kNoId, | |
| 904 function.token_pos(), | |
| 905 CatchClauseNode::kInvalidTryIndex, | |
| 906 kClosureArgumentMismatchRuntimeEntry); | |
| 907 } else { | |
| 908 __ Stop("Wrong number of arguments"); | |
| 909 } | |
| 910 __ Bind(&argc_in_range); | |
| 911 } | |
| 912 } else { | |
| 913 CopyParameters(); | |
| 914 } | |
| 915 // Initialize (non-argument) stack allocated locals to null. | |
| 916 if (local_count > 0) { | |
| 917 const Immediate raw_null = | |
| 918 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 919 __ movl(EAX, raw_null); | |
| 920 const int base = parsed_function().first_stack_local_index(); | |
| 921 for (int i = 0; i < local_count; ++i) { | |
| 922 // Subtract index i (locals lie at lower addresses than EBP). | |
| 923 __ movl(Address(EBP, (base - i) * kWordSize), EAX); | |
| 924 } | |
| 925 } | |
| 926 | |
| 927 // Generate stack overflow check. | |
| 928 __ cmpl(ESP, | |
| 929 Address::Absolute(Isolate::Current()->stack_limit_address())); | |
| 930 Label no_stack_overflow; | |
| 931 __ j(ABOVE, &no_stack_overflow, Assembler::kNearJump); | |
| 932 GenerateCallRuntime(AstNode::kNoId, | |
| 933 function.token_pos(), | |
| 934 CatchClauseNode::kInvalidTryIndex, | |
| 935 kStackOverflowRuntimeEntry); | |
| 936 __ Bind(&no_stack_overflow); | |
| 937 | |
| 938 if (FLAG_print_scopes) { | |
| 939 // Print the function scope (again) after generating the prologue in order | |
| 940 // to see annotations such as allocation indices of locals. | |
| 941 if (FLAG_print_ast) { | |
| 942 // Second printing. | |
| 943 OS::Print("Annotated "); | |
| 944 } | |
| 945 AstPrinter::PrintFunctionScope(parsed_function()); | |
| 946 } | |
| 947 | |
| 948 VisitBlocks(); | |
| 949 | |
| 950 __ int3(); | |
| 951 GenerateDeferredCode(); | |
| 952 // Emit function patching code. This will be swapped with the first 5 bytes | |
| 953 // at entry point. | |
| 954 pc_descriptors_list()->AddDescriptor(PcDescriptors::kPatchCode, | |
| 955 assembler()->CodeSize(), | |
| 956 AstNode::kNoId, | |
| 957 0, | |
| 958 -1); | |
| 959 __ jmp(&StubCode::FixCallersTargetLabel()); | |
| 960 } | |
| 961 | |
| 962 | |
| 963 void FlowGraphCompiler::GenerateCall(intptr_t token_pos, | |
| 964 intptr_t try_index, | |
| 965 const ExternalLabel* label, | |
| 966 PcDescriptors::Kind kind) { | |
| 967 ASSERT(frame_register_allocator()->IsSpilled()); | |
| 968 __ call(label); | |
| 969 AddCurrentDescriptor(kind, AstNode::kNoId, token_pos, try_index); | |
| 970 } | |
| 971 | |
| 972 | |
| 973 void FlowGraphCompiler::GenerateCallRuntime(intptr_t cid, | |
| 974 intptr_t token_pos, | |
| 975 intptr_t try_index, | |
| 976 const RuntimeEntry& entry) { | |
| 977 ASSERT(frame_register_allocator()->IsSpilled()); | |
| 978 __ CallRuntime(entry); | |
| 979 AddCurrentDescriptor(PcDescriptors::kOther, cid, token_pos, try_index); | |
| 980 } | |
| 981 | |
| 982 | |
| 983 intptr_t FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label, | |
| 984 const ICData& ic_data, | |
| 985 const Array& arguments_descriptor, | |
| 986 intptr_t argument_count) { | |
| 987 __ LoadObject(ECX, ic_data); | |
| 988 __ LoadObject(EDX, arguments_descriptor); | |
| 989 | |
| 990 __ call(target_label); | |
| 991 const intptr_t descr_offset = assembler()->CodeSize(); | |
| 992 __ Drop(argument_count); | |
| 993 return descr_offset; | |
| 994 } | |
| 995 | |
| 996 | |
| 997 intptr_t FlowGraphCompiler::EmitStaticCall(const Function& function, | |
| 998 const Array& arguments_descriptor, | |
| 999 intptr_t argument_count) { | |
| 1000 __ LoadObject(ECX, function); | |
| 1001 __ LoadObject(EDX, arguments_descriptor); | |
| 1002 __ call(&StubCode::CallStaticFunctionLabel()); | |
| 1003 const intptr_t descr_offset = assembler()->CodeSize(); | |
| 1004 __ Drop(argument_count); | |
| 1005 return descr_offset; | |
| 1006 } | |
| 1007 | |
| 1008 | |
| 998 // Checks class id of instance against all 'class_ids'. Jump to 'deopt' label | 1009 // Checks class id of instance against all 'class_ids'. Jump to 'deopt' label |
| 999 // if no match or instance is Smi. | 1010 // if no match or instance is Smi. |
| 1000 void FlowGraphCompiler::EmitClassChecksNoSmi(const ICData& ic_data, | 1011 void FlowGraphCompiler::EmitClassChecksNoSmi(const ICData& ic_data, |
| 1001 Register instance_reg, | 1012 Register instance_reg, |
| 1002 Register temp_reg, | 1013 Register temp_reg, |
| 1003 Label* deopt) { | 1014 Label* deopt) { |
| 1004 Label ok; | 1015 Label ok; |
| 1005 ASSERT(ic_data.GetReceiverClassIdAt(0) != kSmi); | 1016 ASSERT(ic_data.GetReceiverClassIdAt(0) != kSmi); |
| 1006 __ testl(instance_reg, Immediate(kSmiTagMask)); | 1017 __ testl(instance_reg, Immediate(kSmiTagMask)); |
| 1007 __ j(ZERO, deopt); | 1018 __ j(ZERO, deopt); |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 1025 } | 1036 } |
| 1026 | 1037 |
| 1027 | 1038 |
| 1028 void FlowGraphCompiler::LoadDoubleOrSmiToXmm(XmmRegister result, | 1039 void FlowGraphCompiler::LoadDoubleOrSmiToXmm(XmmRegister result, |
| 1029 Register reg, | 1040 Register reg, |
| 1030 Register temp, | 1041 Register temp, |
| 1031 Label* not_double_or_smi) { | 1042 Label* not_double_or_smi) { |
| 1032 Label is_smi, done; | 1043 Label is_smi, done; |
| 1033 __ testl(reg, Immediate(kSmiTagMask)); | 1044 __ testl(reg, Immediate(kSmiTagMask)); |
| 1034 __ j(ZERO, &is_smi); | 1045 __ j(ZERO, &is_smi); |
| 1035 __ LoadClassId(temp, reg); | 1046 __ CompareClassId(reg, kDouble, temp); |
| 1036 __ cmpl(temp, Immediate(kDouble)); | |
| 1037 __ j(NOT_EQUAL, not_double_or_smi); | 1047 __ j(NOT_EQUAL, not_double_or_smi); |
| 1038 __ movsd(result, FieldAddress(reg, Double::value_offset())); | 1048 __ movsd(result, FieldAddress(reg, Double::value_offset())); |
| 1039 __ jmp(&done); | 1049 __ jmp(&done); |
| 1040 __ Bind(&is_smi); | 1050 __ Bind(&is_smi); |
| 1041 __ movl(temp, reg); | 1051 __ movl(temp, reg); |
| 1042 __ SmiUntag(temp); | 1052 __ SmiUntag(temp); |
| 1043 __ cvtsi2sd(result, temp); | 1053 __ cvtsi2sd(result, temp); |
| 1044 __ Bind(&done); | 1054 __ Bind(&done); |
| 1045 } | 1055 } |
| 1046 | 1056 |
| 1047 | 1057 |
| 1048 #undef __ | 1058 #undef __ |
| 1049 | 1059 |
| 1050 } // namespace dart | 1060 } // namespace dart |
| 1051 | 1061 |
| 1052 #endif // defined TARGET_ARCH_IA32 | 1062 #endif // defined TARGET_ARCH_IA32 |
| OLD | NEW |