| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. | |
| 6 #if defined(TARGET_ARCH_IA32) | |
| 7 | |
| 8 #include "vm/code_generator.h" | |
| 9 | |
| 10 #include "lib/error.h" | |
| 11 #include "vm/assembler_macros.h" | |
| 12 #include "vm/ast_printer.h" | |
| 13 #include "vm/class_finalizer.h" | |
| 14 #include "vm/code_descriptors.h" | |
| 15 #include "vm/dart_entry.h" | |
| 16 #include "vm/debugger.h" | |
| 17 #include "vm/intrinsifier.h" | |
| 18 #include "vm/longjump.h" | |
| 19 #include "vm/object.h" | |
| 20 #include "vm/object_store.h" | |
| 21 #include "vm/parser.h" | |
| 22 #include "vm/resolver.h" | |
| 23 #include "vm/stub_code.h" | |
| 24 | |
| 25 namespace dart { | |
| 26 | |
| 27 DECLARE_FLAG(bool, print_ast); | |
| 28 DECLARE_FLAG(bool, print_scopes); | |
| 29 DECLARE_FLAG(bool, trace_functions); | |
| 30 DEFINE_FLAG(bool, print_ic_in_optimized, false, | |
| 31 "Debugging helper to identify potential performance pitfalls."); | |
| 32 DECLARE_FLAG(int, optimization_counter_threshold); | |
| 33 DECLARE_FLAG(bool, enable_type_checks); | |
| 34 DECLARE_FLAG(bool, trace_compiler); | |
| 35 DECLARE_FLAG(bool, intrinsify); | |
| 36 DECLARE_FLAG(bool, trace_functions); | |
| 37 | |
| 38 | |
| 39 #define __ assembler_-> | |
| 40 | |
| 41 | |
| 42 CodeGeneratorState::CodeGeneratorState(CodeGenerator* codegen) | |
| 43 : StackResource(Isolate::Current()), | |
| 44 codegen_(codegen), | |
| 45 parent_(codegen->state()) { | |
| 46 if (parent_ != NULL) { | |
| 47 root_node_ = parent_->root_node_; | |
| 48 current_try_index_ = parent_->current_try_index_; | |
| 49 } else { | |
| 50 root_node_ = NULL; | |
| 51 current_try_index_ = CatchClauseNode::kInvalidTryIndex; | |
| 52 } | |
| 53 codegen_->set_state(this); | |
| 54 } | |
| 55 | |
| 56 | |
| 57 CodeGeneratorState::~CodeGeneratorState() { | |
| 58 codegen_->set_state(parent_); | |
| 59 } | |
| 60 | |
| 61 | |
| 62 CodeGenerator::CodeGenerator(Assembler* assembler, | |
| 63 const ParsedFunction& parsed_function) | |
| 64 : assembler_(assembler), | |
| 65 parsed_function_(parsed_function), | |
| 66 locals_space_size_(-1), | |
| 67 state_(NULL), | |
| 68 pc_descriptors_list_(NULL), | |
| 69 stackmap_builder_(NULL), | |
| 70 exception_handlers_list_(NULL), | |
| 71 try_index_(CatchClauseNode::kInvalidTryIndex), | |
| 72 context_level_(0) { | |
| 73 ASSERT(assembler_ != NULL); | |
| 74 ASSERT(parsed_function.node_sequence() != NULL); | |
| 75 ASSERT(Isolate::Current()->long_jump_base()->IsSafeToJump()); | |
| 76 } | |
| 77 | |
| 78 | |
| 79 void CodeGenerator::InitGenerator() { | |
| 80 pc_descriptors_list_ = new DescriptorList(); | |
| 81 // We do not build any stack maps in the unoptimizing compiler. | |
| 82 exception_handlers_list_ = new ExceptionHandlerList(); | |
| 83 } | |
| 84 | |
| 85 | |
| 86 void CodeGenerator::IntrinsifyGetter() { | |
| 87 // TOS: return address. | |
| 88 // +1 : receiver. | |
| 89 // Sequence node has one return node, its input is oad field node. | |
| 90 const SequenceNode& sequence_node = *parsed_function_.node_sequence(); | |
| 91 ASSERT(sequence_node.length() == 1); | |
| 92 ASSERT(sequence_node.NodeAt(0)->IsReturnNode()); | |
| 93 const ReturnNode& return_node = *sequence_node.NodeAt(0)->AsReturnNode(); | |
| 94 ASSERT(return_node.value()->IsLoadInstanceFieldNode()); | |
| 95 const LoadInstanceFieldNode& load_node = | |
| 96 *return_node.value()->AsLoadInstanceFieldNode(); | |
| 97 __ movl(EAX, Address(ESP, 1 * kWordSize)); | |
| 98 __ movl(EAX, FieldAddress(EAX, load_node.field().Offset())); | |
| 99 __ ret(); | |
| 100 } | |
| 101 | |
| 102 | |
| 103 void CodeGenerator::IntrinsifySetter() { | |
| 104 // TOS: return address. | |
| 105 // +1 : value | |
| 106 // +2 : receiver. | |
| 107 // Sequence node has one store node and one return NULL node. | |
| 108 const SequenceNode& sequence_node = *parsed_function_.node_sequence(); | |
| 109 ASSERT(sequence_node.length() == 2); | |
| 110 ASSERT(sequence_node.NodeAt(0)->IsStoreInstanceFieldNode()); | |
| 111 ASSERT(sequence_node.NodeAt(1)->IsReturnNode()); | |
| 112 const StoreInstanceFieldNode& store_node = | |
| 113 *sequence_node.NodeAt(0)->AsStoreInstanceFieldNode(); | |
| 114 __ movl(EAX, Address(ESP, 2 * kWordSize)); // Receiver. | |
| 115 __ movl(EBX, Address(ESP, 1 * kWordSize)); // Value. | |
| 116 __ StoreIntoObject(EAX, FieldAddress(EAX, store_node.field().Offset()), EBX); | |
| 117 const Immediate raw_null = | |
| 118 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 119 __ movl(EAX, raw_null); | |
| 120 __ ret(); | |
| 121 } | |
| 122 | |
| 123 | |
| 124 | |
| 125 bool CodeGenerator::TryIntrinsify() { | |
| 126 if (!CanOptimize()) return false; | |
| 127 // Intrinsification skips arguments checks, therefore disable if in checked | |
| 128 // mode. | |
| 129 if (FLAG_intrinsify && !FLAG_trace_functions && !FLAG_enable_type_checks) { | |
| 130 if ((parsed_function_.function().kind() == RawFunction::kImplicitGetter)) { | |
| 131 IntrinsifyGetter(); | |
| 132 return true; | |
| 133 } | |
| 134 if ((parsed_function_.function().kind() == RawFunction::kImplicitSetter)) { | |
| 135 IntrinsifySetter(); | |
| 136 return true; | |
| 137 } | |
| 138 } | |
| 139 // Even if an intrinsified version of the function was successfully | |
| 140 // generated, it may fall through to the non-intrinsified method body. | |
| 141 if (!FLAG_trace_functions) { | |
| 142 return Intrinsifier::Intrinsify(parsed_function().function(), assembler_); | |
| 143 } | |
| 144 return false; | |
| 145 } | |
| 146 | |
| 147 | |
| 148 bool CodeGenerator::IsResultNeeded(AstNode* node) const { | |
| 149 return !state()->IsRootNode(node); | |
| 150 } | |
| 151 | |
| 152 | |
| 153 // NOTE: First 5 bytes of the code may be patched with a jump instruction. Do | |
| 154 // not emit any objects in the first 5 bytes. | |
| 155 void CodeGenerator::GenerateCode() { | |
| 156 InitGenerator(); | |
| 157 CodeGeneratorState codegen_state(this); | |
| 158 if (FLAG_print_scopes && FLAG_print_ast) { | |
| 159 // Print the function scope before code generation. | |
| 160 AstPrinter::PrintFunctionScope(parsed_function_); | |
| 161 } | |
| 162 if (FLAG_print_ast) { | |
| 163 // Print the function ast before code generation. | |
| 164 AstPrinter::PrintFunctionNodes(parsed_function_); | |
| 165 } | |
| 166 if (FLAG_trace_functions) { | |
| 167 // Preserve ECX (ic-data array or object) and EDX (arguments descriptor). | |
| 168 __ nop(2); // Make sure the code is patchable. | |
| 169 __ pushl(ECX); | |
| 170 __ pushl(EDX); | |
| 171 const Function& function = | |
| 172 Function::ZoneHandle(parsed_function_.function().raw()); | |
| 173 __ LoadObject(EAX, function); | |
| 174 __ pushl(EAX); | |
| 175 GenerateCallRuntime(AstNode::kNoId, | |
| 176 0, | |
| 177 kTraceFunctionEntryRuntimeEntry); | |
| 178 __ popl(EAX); | |
| 179 __ popl(EDX); | |
| 180 __ popl(ECX); | |
| 181 } | |
| 182 | |
| 183 const bool code_generation_finished = TryIntrinsify(); | |
| 184 // In some cases intrinsifier can generate all code and no AST based | |
| 185 // code generation is needed. In some cases slow-paths (e.g., overflows) are | |
| 186 // implemented by the AST based code generation and 'code_generation_finished' | |
| 187 // is false. | |
| 188 if (!code_generation_finished) { | |
| 189 GenerateEntryCode(); | |
| 190 if (FLAG_print_scopes) { | |
| 191 // Print the function scope (again) after generating the prologue in order | |
| 192 // to see annotations such as allocation indices of locals. | |
| 193 if (FLAG_print_ast) { | |
| 194 // Second printing. | |
| 195 OS::Print("Annotated "); | |
| 196 } | |
| 197 AstPrinter::PrintFunctionScope(parsed_function_); | |
| 198 } | |
| 199 parsed_function_.node_sequence()->Visit(this); | |
| 200 } | |
| 201 // End of code. | |
| 202 __ int3(); | |
| 203 GenerateDeferredCode(); | |
| 204 | |
| 205 // Emit function patching code. This will be swapped with the first 5 bytes | |
| 206 // at entry point. | |
| 207 pc_descriptors_list_->AddDescriptor(PcDescriptors::kPatchCode, | |
| 208 assembler_->CodeSize(), | |
| 209 AstNode::kNoId, | |
| 210 0, | |
| 211 -1); | |
| 212 __ jmp(&StubCode::FixCallersTargetLabel()); | |
| 213 } | |
| 214 | |
| 215 | |
| 216 void CodeGenerator::GenerateDeferredCode() { | |
| 217 } | |
| 218 | |
| 219 | |
| 220 void CodeGenerator::FinalizePcDescriptors(const Code& code) { | |
| 221 ASSERT(pc_descriptors_list_ != NULL); | |
| 222 const PcDescriptors& descriptors = PcDescriptors::Handle( | |
| 223 pc_descriptors_list_->FinalizePcDescriptors(code.EntryPoint())); | |
| 224 descriptors.Verify(parsed_function_.function().is_optimizable()); | |
| 225 code.set_pc_descriptors(descriptors); | |
| 226 } | |
| 227 | |
| 228 | |
| 229 void CodeGenerator::FinalizeStackmaps(const Code& code) { | |
| 230 if (stackmap_builder_ == NULL) { | |
| 231 // The unoptimizing compiler has no stack maps. | |
| 232 code.set_stackmaps(Array::Handle()); | |
| 233 } else { | |
| 234 // Finalize the stack map array and add it to the code object. | |
| 235 code.set_stackmaps( | |
| 236 Array::Handle(stackmap_builder_->FinalizeStackmaps(code))); | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 | |
| 241 void CodeGenerator::FinalizeVarDescriptors(const Code& code) { | |
| 242 const LocalVarDescriptors& var_descs = LocalVarDescriptors::Handle( | |
| 243 parsed_function_.node_sequence()->scope()->GetVarDescriptors( | |
| 244 parsed_function_.function())); | |
| 245 code.set_var_descriptors(var_descs); | |
| 246 } | |
| 247 | |
| 248 | |
| 249 void CodeGenerator::FinalizeExceptionHandlers(const Code& code) { | |
| 250 ASSERT(exception_handlers_list_ != NULL); | |
| 251 const ExceptionHandlers& handlers = ExceptionHandlers::Handle( | |
| 252 exception_handlers_list_->FinalizeExceptionHandlers(code.EntryPoint())); | |
| 253 code.set_exception_handlers(handlers); | |
| 254 } | |
| 255 | |
| 256 | |
| 257 void CodeGenerator::FinalizeComments(const Code& code) { | |
| 258 code.set_comments(assembler_->GetCodeComments()); | |
| 259 } | |
| 260 | |
| 261 | |
| 262 void CodeGenerator::GenerateLoadVariable(Register dst, | |
| 263 const LocalVariable& variable) { | |
| 264 if (variable.is_captured()) { | |
| 265 // The variable lives in the context. | |
| 266 intptr_t delta = context_level() - variable.owner()->context_level(); | |
| 267 ASSERT(delta >= 0); | |
| 268 Register base = CTX; | |
| 269 while (delta-- > 0) { | |
| 270 __ movl(dst, FieldAddress(base, Context::parent_offset())); | |
| 271 base = dst; | |
| 272 } | |
| 273 __ movl(dst, | |
| 274 FieldAddress(base, Context::variable_offset(variable.index()))); | |
| 275 } else { | |
| 276 // The variable lives in the current stack frame. | |
| 277 __ movl(dst, Address(EBP, variable.index() * kWordSize)); | |
| 278 } | |
| 279 } | |
| 280 | |
| 281 | |
| 282 void CodeGenerator::GenerateStoreVariable(const LocalVariable& variable, | |
| 283 Register src, | |
| 284 Register scratch) { | |
| 285 if (variable.is_captured()) { | |
| 286 // The variable lives in the context. | |
| 287 intptr_t delta = context_level() - variable.owner()->context_level(); | |
| 288 ASSERT(delta >= 0); | |
| 289 Register base = CTX; | |
| 290 while (delta-- > 0) { | |
| 291 __ movl(scratch, FieldAddress(base, Context::parent_offset())); | |
| 292 base = scratch; | |
| 293 } | |
| 294 __ StoreIntoObject( | |
| 295 base, | |
| 296 FieldAddress(base, Context::variable_offset(variable.index())), | |
| 297 src); | |
| 298 } else { | |
| 299 // The variable lives in the current stack frame. | |
| 300 __ movl(Address(EBP, variable.index() * kWordSize), src); | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 | |
| 305 void CodeGenerator::GeneratePushVariable(const LocalVariable& variable, | |
| 306 Register scratch) { | |
| 307 if (variable.is_captured()) { | |
| 308 // The variable lives in the context. | |
| 309 intptr_t delta = context_level() - variable.owner()->context_level(); | |
| 310 ASSERT(delta >= 0); | |
| 311 Register base = CTX; | |
| 312 while (delta-- > 0) { | |
| 313 __ movl(scratch, FieldAddress(base, Context::parent_offset())); | |
| 314 base = scratch; | |
| 315 } | |
| 316 __ pushl(FieldAddress(base, Context::variable_offset(variable.index()))); | |
| 317 } else { | |
| 318 // The variable lives in the current stack frame. | |
| 319 __ pushl(Address(EBP, variable.index() * kWordSize)); | |
| 320 } | |
| 321 } | |
| 322 | |
| 323 | |
| 324 void CodeGenerator::GenerateInstanceCall( | |
| 325 intptr_t node_id, | |
| 326 intptr_t token_pos, | |
| 327 const String& function_name, | |
| 328 int num_arguments, | |
| 329 const Array& optional_arguments_names, | |
| 330 intptr_t num_args_checked) { | |
| 331 if (FLAG_print_ic_in_optimized && IsOptimizing()) { | |
| 332 OS::Print("Generate IC in optimized code: id %d name: '%s'\n", | |
| 333 node_id, function_name.ToCString()); | |
| 334 } | |
| 335 ASSERT(num_args_checked > 0); // At least receiver check is necessary. | |
| 336 // Set up the function name and number of arguments (including the receiver) | |
| 337 // to the InstanceCall stub which will resolve the correct entrypoint for | |
| 338 // the operator and call it. | |
| 339 ICData& ic_data = ICData::ZoneHandle(); | |
| 340 ic_data = ICData::New(parsed_function().function(), | |
| 341 function_name, | |
| 342 node_id, | |
| 343 num_args_checked); | |
| 344 __ LoadObject(ECX, ic_data); | |
| 345 __ LoadObject(EDX, ArgumentsDescriptor(num_arguments, | |
| 346 optional_arguments_names)); | |
| 347 uword label_address = 0; | |
| 348 switch (num_args_checked) { | |
| 349 case 1: | |
| 350 label_address = StubCode::OneArgCheckInlineCacheEntryPoint(); | |
| 351 break; | |
| 352 case 2: | |
| 353 label_address = StubCode::TwoArgsCheckInlineCacheEntryPoint(); | |
| 354 break; | |
| 355 default: | |
| 356 UNIMPLEMENTED(); | |
| 357 } | |
| 358 ExternalLabel target_label("InlineCache", label_address); | |
| 359 | |
| 360 __ call(&target_label); | |
| 361 AddCurrentDescriptor(PcDescriptors::kIcCall, | |
| 362 node_id, | |
| 363 token_pos); | |
| 364 __ addl(ESP, Immediate(num_arguments * kWordSize)); | |
| 365 } | |
| 366 | |
| 367 | |
| 368 // Input parameters: | |
| 369 // ESP : points to return address. | |
| 370 // ESP + 4 : address of last argument (arg n-1). | |
| 371 // ESP + 4*n : address of first argument (arg 0). | |
| 372 // EDX : arguments descriptor array. | |
| 373 void CodeGenerator::GenerateEntryCode() { | |
| 374 const Immediate raw_null = | |
| 375 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 376 const Function& function = parsed_function_.function(); | |
| 377 const bool is_instance_native_closure = | |
| 378 function.is_native() && function.IsImplicitInstanceClosureFunction(); | |
| 379 | |
| 380 // 1. Compute the frame size and enter the frame (reserving local space | |
| 381 // for copied incoming and default arguments and stack-allocated local | |
| 382 // variables). | |
| 383 // | |
| 384 // TODO(regis): We may give up reserving space on stack for args/locals | |
| 385 // because pushes of initial values may be more effective than moves. | |
| 386 LocalScope* scope = parsed_function_.node_sequence()->scope(); | |
| 387 const int num_fixed_params = function.num_fixed_parameters(); | |
| 388 const int num_opt_params = function.num_optional_parameters(); | |
| 389 const int num_copied_params = parsed_function_.copied_parameter_count(); | |
| 390 const int stack_slot_count = | |
| 391 num_copied_params + parsed_function_.stack_local_count(); | |
| 392 set_locals_space_size(stack_slot_count * kWordSize); | |
| 393 AssemblerMacros::EnterDartFrame(assembler_, locals_space_size()); | |
| 394 | |
| 395 // 2. Optionally check if the number of arguments matches. We check the | |
| 396 // number of passed arguments when we have to copy them due to the | |
| 397 // presence of optional named parameters. No such checking code is | |
| 398 // generated if only fixed parameters are declared, unless we are in debug | |
| 399 // mode or unless we are compiling a closure. | |
| 400 if (num_copied_params == 0) { | |
| 401 ASSERT(num_opt_params == 0); | |
| 402 #if defined(DEBUG) | |
| 403 const bool check_arguments = true; // Always check arguments in debug mode. | |
| 404 #else | |
| 405 // The number of arguments passed to closure functions must always be | |
| 406 // checked here, because no resolving stub (normally responsible for the | |
| 407 // check) is involved in closure calls. | |
| 408 const bool check_arguments = function.IsClosureFunction(); | |
| 409 #endif | |
| 410 if (check_arguments) { | |
| 411 // Check that num_fixed <= argc <= num_params. | |
| 412 Label argc_in_range; | |
| 413 // Total number of args is the first Smi in args descriptor array (EDX). | |
| 414 __ movl(EAX, FieldAddress(EDX, Array::data_offset())); | |
| 415 __ cmpl(EAX, Immediate(Smi::RawValue(num_fixed_params))); | |
| 416 __ j(EQUAL, &argc_in_range, Assembler::kNearJump); | |
| 417 if (function.IsClosureFunction()) { | |
| 418 GenerateCallRuntime(AstNode::kNoId, | |
| 419 0, | |
| 420 kClosureArgumentMismatchRuntimeEntry); | |
| 421 } else { | |
| 422 __ Stop("Wrong number of arguments"); | |
| 423 } | |
| 424 __ Bind(&argc_in_range); | |
| 425 } | |
| 426 } else { | |
| 427 int implicit_this_param_pos = is_instance_native_closure ? -1 : 0; | |
| 428 ASSERT(parsed_function_.first_parameter_index() == | |
| 429 (ParsedFunction::kFirstLocalSlotIndex + implicit_this_param_pos)); | |
| 430 // Copy positional arguments. | |
| 431 // Check that no fewer than num_fixed_params positional arguments are passed | |
| 432 // in and that no more than num_params arguments are passed in. | |
| 433 // Passed argument i at fp[1 + argc - i] copied to | |
| 434 // fp[ParsedFunction::kFirstLocalSlotIndex - i]. | |
| 435 const int num_params = num_fixed_params + num_opt_params; | |
| 436 | |
| 437 // Total number of args is the first Smi in args descriptor array (EDX). | |
| 438 __ movl(EBX, FieldAddress(EDX, Array::data_offset())); | |
| 439 // Check that num_args <= num_params. | |
| 440 Label wrong_num_arguments; | |
| 441 __ cmpl(EBX, Immediate(Smi::RawValue(num_params))); | |
| 442 __ j(GREATER, &wrong_num_arguments); | |
| 443 // Number of positional args is the second Smi in descriptor array (EDX). | |
| 444 __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize))); | |
| 445 // Check that num_pos_args >= num_fixed_params. | |
| 446 __ cmpl(ECX, Immediate(Smi::RawValue(num_fixed_params))); | |
| 447 __ j(LESS, &wrong_num_arguments); | |
| 448 | |
| 449 // Since EBX and ECX are Smi, use TIMES_2 instead of TIMES_4. | |
| 450 // Let EBX point to the last passed positional argument, i.e. to | |
| 451 // fp[1 + num_args - (num_pos_args - 1)]. | |
| 452 __ subl(EBX, ECX); | |
| 453 __ leal(EBX, Address(EBP, EBX, TIMES_2, 2 * kWordSize)); | |
| 454 // Let EDI point to the last copied positional argument, i.e. to | |
| 455 // fp[ParsedFunction::kFirstLocalSlotIndex - (num_pos_args - 1)]. | |
| 456 const int index = | |
| 457 ParsedFunction::kFirstLocalSlotIndex + 1 + implicit_this_param_pos; | |
| 458 // First copy captured receiver if function is an implicit native closure. | |
| 459 if (is_instance_native_closure) { | |
| 460 __ movl(EAX, FieldAddress(CTX, Context::variable_offset(0))); | |
| 461 __ movl(Address(EBP, (index * kWordSize)), EAX); | |
| 462 } | |
| 463 __ leal(EDI, Address(EBP, (index * kWordSize))); | |
| 464 __ subl(EDI, ECX); // ECX is a Smi, subtract twice for TIMES_4 scaling. | |
| 465 __ subl(EDI, ECX); | |
| 466 __ SmiUntag(ECX); | |
| 467 Label loop, loop_condition; | |
| 468 __ jmp(&loop_condition, Assembler::kNearJump); | |
| 469 // We do not use the final allocation index of the variable here, i.e. | |
| 470 // scope->VariableAt(i)->index(), because captured variables still need | |
| 471 // to be copied to the context that is not yet allocated. | |
| 472 const Address argument_addr(EBX, ECX, TIMES_4, 0); | |
| 473 const Address copy_addr(EDI, ECX, TIMES_4, 0); | |
| 474 __ Bind(&loop); | |
| 475 __ movl(EAX, argument_addr); | |
| 476 __ movl(copy_addr, EAX); | |
| 477 __ Bind(&loop_condition); | |
| 478 __ decl(ECX); | |
| 479 __ j(POSITIVE, &loop, Assembler::kNearJump); | |
| 480 | |
| 481 // Copy or initialize optional named arguments. | |
| 482 Label all_arguments_processed; | |
| 483 if (num_opt_params > 0) { | |
| 484 // Start by alphabetically sorting the names of the optional parameters. | |
| 485 LocalVariable** opt_param = new LocalVariable*[num_opt_params]; | |
| 486 int* opt_param_position = new int[num_opt_params]; | |
| 487 for (int pos = num_fixed_params; pos < num_params; pos++) { | |
| 488 LocalVariable* parameter = scope->VariableAt(pos); | |
| 489 const String& opt_param_name = parameter->name(); | |
| 490 int i = pos - num_fixed_params; | |
| 491 while (--i >= 0) { | |
| 492 LocalVariable* param_i = opt_param[i]; | |
| 493 const intptr_t result = opt_param_name.CompareTo(param_i->name()); | |
| 494 ASSERT(result != 0); | |
| 495 if (result > 0) break; | |
| 496 opt_param[i + 1] = opt_param[i]; | |
| 497 opt_param_position[i + 1] = opt_param_position[i]; | |
| 498 } | |
| 499 opt_param[i + 1] = parameter; | |
| 500 opt_param_position[i + 1] = pos; | |
| 501 } | |
| 502 // Generate code handling each optional parameter in alphabetical order. | |
| 503 // Total number of args is the first Smi in args descriptor array (EDX). | |
| 504 __ movl(EBX, FieldAddress(EDX, Array::data_offset())); | |
| 505 // Number of positional args is the second Smi in descriptor array (EDX). | |
| 506 __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize))); | |
| 507 __ SmiUntag(ECX); | |
| 508 // Let EBX point to the first passed argument, i.e. to fp[1 + argc - 0]. | |
| 509 __ leal(EBX, Address(EBP, EBX, TIMES_2, kWordSize)); | |
| 510 // Let EDI point to the name/pos pair of the first named argument. | |
| 511 __ leal(EDI, FieldAddress(EDX, Array::data_offset() + (2 * kWordSize))); | |
| 512 for (int i = 0; i < num_opt_params; i++) { | |
| 513 // Handle this optional parameter only if k or fewer positional args | |
| 514 // have been passed, where k is the position of this optional parameter | |
| 515 // in the formal parameter list. | |
| 516 Label load_default_value, assign_optional_parameter, next_parameter; | |
| 517 const int param_pos = opt_param_position[i]; | |
| 518 __ cmpl(ECX, Immediate(param_pos)); | |
| 519 __ j(GREATER, &next_parameter, Assembler::kNearJump); | |
| 520 // Check if this named parameter was passed in. | |
| 521 __ movl(EAX, Address(EDI, 0)); // Load EAX with the name of the arg. | |
| 522 __ CompareObject(EAX, opt_param[i]->name()); | |
| 523 __ j(NOT_EQUAL, &load_default_value, Assembler::kNearJump); | |
| 524 // Load EAX with passed-in argument at provided arg_pos, i.e. at | |
| 525 // fp[1 + argc - arg_pos]. | |
| 526 __ movl(EAX, Address(EDI, kWordSize)); // EAX is arg_pos as Smi. | |
| 527 __ addl(EDI, Immediate(2 * kWordSize)); // Point to next name/pos pair. | |
| 528 __ negl(EAX); | |
| 529 Address argument_addr(EBX, EAX, TIMES_2, 0); // EAX is a negative Smi. | |
| 530 __ movl(EAX, argument_addr); | |
| 531 __ jmp(&assign_optional_parameter, Assembler::kNearJump); | |
| 532 __ Bind(&load_default_value); | |
| 533 // Load EAX with default argument at pos. | |
| 534 const Object& value = Object::ZoneHandle( | |
| 535 parsed_function_.default_parameter_values().At( | |
| 536 param_pos - num_fixed_params)); | |
| 537 __ LoadObject(EAX, value); | |
| 538 __ Bind(&assign_optional_parameter); | |
| 539 // Assign EAX to fp[ParsedFunction::kFirstLocalSlotIndex - param_pos]. | |
| 540 // We do not use the final allocation index of the variable here, i.e. | |
| 541 // scope->VariableAt(i)->index(), because captured variables still need | |
| 542 // to be copied to the context that is not yet allocated. | |
| 543 intptr_t computed_param_pos = (ParsedFunction::kFirstLocalSlotIndex - | |
| 544 param_pos + implicit_this_param_pos); | |
| 545 const Address param_addr(EBP, (computed_param_pos * kWordSize)); | |
| 546 __ movl(param_addr, EAX); | |
| 547 __ Bind(&next_parameter); | |
| 548 } | |
| 549 delete[] opt_param; | |
| 550 delete[] opt_param_position; | |
| 551 // Check that EDI now points to null terminator in the array descriptor. | |
| 552 __ cmpl(Address(EDI, 0), raw_null); | |
| 553 __ j(EQUAL, &all_arguments_processed, Assembler::kNearJump); | |
| 554 } else { | |
| 555 ASSERT(is_instance_native_closure); | |
| 556 __ jmp(&all_arguments_processed, Assembler::kNearJump); | |
| 557 } | |
| 558 | |
| 559 __ Bind(&wrong_num_arguments); | |
| 560 if (locals_space_size() != 0) { | |
| 561 // We need to unwind the space we reserved for locals and copied | |
| 562 // parameters. The NoSuchMethodFunction stub does not expect to | |
| 563 // see that area on the stack. | |
| 564 __ addl(ESP, Immediate(locals_space_size())); | |
| 565 } | |
| 566 if (function.IsClosureFunction()) { | |
| 567 GenerateCallRuntime(AstNode::kNoId, | |
| 568 0, | |
| 569 kClosureArgumentMismatchRuntimeEntry); | |
| 570 } else { | |
| 571 // Invoke noSuchMethod function. | |
| 572 const int kNumArgsChecked = 1; | |
| 573 ICData& ic_data = ICData::ZoneHandle(); | |
| 574 ic_data = ICData::New(parsed_function().function(), | |
| 575 String::Handle(function.name()), | |
| 576 AstNode::kNoId, | |
| 577 kNumArgsChecked); | |
| 578 __ LoadObject(ECX, ic_data); | |
| 579 // EBP - 4 : PC marker, allows easy identification of RawInstruction obj. | |
| 580 // EBP : points to previous frame pointer. | |
| 581 // EBP + 4 : points to return address. | |
| 582 // EBP + 8 : address of last argument (arg n-1). | |
| 583 // ESP + 8 + 4*(n-1) : address of first argument (arg 0). | |
| 584 // ECX : ic-data. | |
| 585 // EDX : arguments descriptor array. | |
| 586 __ call(&StubCode::CallNoSuchMethodFunctionLabel()); | |
| 587 } | |
| 588 | |
| 589 if (FLAG_trace_functions) { | |
| 590 __ pushl(EAX); // Preserve result. | |
| 591 __ PushObject(Function::ZoneHandle(function.raw())); | |
| 592 GenerateCallRuntime(AstNode::kNoId, | |
| 593 0, | |
| 594 kTraceFunctionExitRuntimeEntry); | |
| 595 __ popl(EAX); // Remove argument. | |
| 596 __ popl(EAX); // Restore result. | |
| 597 } | |
| 598 __ LeaveFrame(); | |
| 599 __ ret(); | |
| 600 | |
| 601 __ Bind(&all_arguments_processed); | |
| 602 // Nullify originally passed arguments only after they have been copied and | |
| 603 // checked, otherwise noSuchMethod would not see their original values. | |
| 604 // This step can be skipped in case we decide that formal parameters are | |
| 605 // implicitly final, since garbage collecting the unmodified value is not | |
| 606 // an issue anymore. | |
| 607 | |
| 608 // EDX : arguments descriptor array. | |
| 609 // Total number of args is the first Smi in args descriptor array (EDX). | |
| 610 __ movl(ECX, FieldAddress(EDX, Array::data_offset())); | |
| 611 __ SmiUntag(ECX); | |
| 612 Label null_args_loop, null_args_loop_condition; | |
| 613 __ jmp(&null_args_loop_condition, Assembler::kNearJump); | |
| 614 const Address original_argument_addr(EBP, ECX, TIMES_4, 2 * kWordSize); | |
| 615 __ Bind(&null_args_loop); | |
| 616 __ movl(original_argument_addr, raw_null); | |
| 617 __ Bind(&null_args_loop_condition); | |
| 618 __ decl(ECX); | |
| 619 __ j(POSITIVE, &null_args_loop, Assembler::kNearJump); | |
| 620 } | |
| 621 | |
| 622 // 3. Initialize (non-argument) stack-allocated locals to null. | |
| 623 // | |
| 624 // TODO(regis): For now, always unroll the init loop. Decide later above | |
| 625 // which threshold to implement a loop. Consider emitting pushes instead | |
| 626 // of moves. | |
| 627 const int base = parsed_function_.first_stack_local_index(); | |
| 628 for (int index = 0; index < parsed_function_.stack_local_count(); ++index) { | |
| 629 if (index == 0) { | |
| 630 __ movl(EAX, raw_null); | |
| 631 } | |
| 632 __ movl(Address(EBP, (base - index) * kWordSize), EAX); | |
| 633 } | |
| 634 | |
| 635 // 4. Generate the stack overflow check. | |
| 636 __ cmpl(ESP, | |
| 637 Address::Absolute(Isolate::Current()->stack_limit_address())); | |
| 638 Label no_stack_overflow; | |
| 639 __ j(ABOVE, &no_stack_overflow); | |
| 640 GenerateCallRuntime(AstNode::kNoId, | |
| 641 0, | |
| 642 kStackOverflowRuntimeEntry); | |
| 643 __ Bind(&no_stack_overflow); | |
| 644 } | |
| 645 | |
| 646 | |
| 647 void CodeGenerator::GenerateReturnEpilog(ReturnNode* node) { | |
| 648 // Unchain the context(s) up to context level 0. | |
| 649 intptr_t current_context_level = context_level(); | |
| 650 ASSERT(current_context_level >= 0); | |
| 651 if (parsed_function_.saved_context_var() != NULL) { | |
| 652 // CTX on entry was saved, but not linked as context parent. | |
| 653 GenerateLoadVariable(CTX, *parsed_function_.saved_context_var()); | |
| 654 } else { | |
| 655 while (current_context_level-- > 0) { | |
| 656 __ movl(CTX, FieldAddress(CTX, Context::parent_offset())); | |
| 657 } | |
| 658 } | |
| 659 #ifdef DEBUG | |
| 660 // Check that the entry stack size matches the exit stack size. | |
| 661 __ leal(EDX, Address(EBP, kLocalsOffsetFromFP)); | |
| 662 __ subl(EDX, ESP); | |
| 663 ASSERT(locals_space_size() >= 0); | |
| 664 __ cmpl(EDX, Immediate(locals_space_size())); | |
| 665 Label wrong_stack; | |
| 666 __ j(NOT_EQUAL, &wrong_stack, Assembler::kNearJump); | |
| 667 #endif // DEBUG. | |
| 668 | |
| 669 if (!IsOptimizing()) { | |
| 670 // Count only in unoptimized code. | |
| 671 // TODO(srdjan): Replace the counting code with a type feedback | |
| 672 // collection and counting stub. | |
| 673 const Function& function = | |
| 674 Function::ZoneHandle(parsed_function_.function().raw()); | |
| 675 __ LoadObject(EBX, function); | |
| 676 __ incl(FieldAddress(EBX, Function::usage_counter_offset())); | |
| 677 if (CodeGenerator::CanOptimize()) { | |
| 678 // Do not optimize if usage count must be reported. | |
| 679 __ cmpl(FieldAddress(EBX, Function::usage_counter_offset()), | |
| 680 Immediate(FLAG_optimization_counter_threshold)); | |
| 681 Label not_yet_hot; | |
| 682 __ j(LESS_EQUAL, ¬_yet_hot, Assembler::kNearJump); | |
| 683 __ pushl(EAX); // Preserve result. | |
| 684 __ pushl(EBX); // Argument for runtime: function to optimize. | |
| 685 __ CallRuntime(kOptimizeInvokedFunctionRuntimeEntry); | |
| 686 __ popl(EBX); // Remove argument. | |
| 687 __ popl(EAX); // Restore result. | |
| 688 __ Bind(¬_yet_hot); | |
| 689 } | |
| 690 } | |
| 691 if (FLAG_trace_functions) { | |
| 692 const Function& function = | |
| 693 Function::ZoneHandle(parsed_function_.function().raw()); | |
| 694 __ LoadObject(EBX, function); | |
| 695 __ pushl(EAX); // Preserve result. | |
| 696 __ pushl(EBX); | |
| 697 GenerateCallRuntime(AstNode::kNoId, | |
| 698 0, | |
| 699 kTraceFunctionExitRuntimeEntry); | |
| 700 __ popl(EAX); // Remove argument. | |
| 701 __ popl(EAX); // Restore result. | |
| 702 } | |
| 703 __ LeaveFrame(); | |
| 704 __ ret(); | |
| 705 // Add a NOP to make return code pattern 5 bytes long for patching | |
| 706 // in breakpoints during debugging. | |
| 707 __ nop(1); | |
| 708 AddCurrentDescriptor(PcDescriptors::kReturn, | |
| 709 node->id(), | |
| 710 node->token_pos()); | |
| 711 | |
| 712 #ifdef DEBUG | |
| 713 __ Bind(&wrong_stack); | |
| 714 __ Stop("Exit stack size does not match the entry stack size."); | |
| 715 #endif // DEBUG. | |
| 716 } | |
| 717 | |
| 718 | |
| 719 void CodeGenerator::VisitReturnNode(ReturnNode* node) { | |
| 720 ASSERT(!IsResultNeeded(node)); | |
| 721 ASSERT(node->value() != NULL); | |
| 722 | |
| 723 if (!node->value()->IsLiteralNode()) { | |
| 724 node->value()->Visit(this); | |
| 725 // The result of the return value is now on top of the stack. | |
| 726 } | |
| 727 | |
| 728 // Generate inlined code for all finally blocks as we are about to transfer | |
| 729 // control out of the 'try' blocks if any. | |
| 730 for (intptr_t i = 0; i < node->inlined_finally_list_length(); i++) { | |
| 731 node->InlinedFinallyNodeAt(i)->Visit(this); | |
| 732 } | |
| 733 | |
| 734 if (node->value()->IsLiteralNode()) { | |
| 735 // Load literal value into EAX. | |
| 736 const Object& literal = node->value()->AsLiteralNode()->literal(); | |
| 737 if (literal.IsSmi()) { | |
| 738 __ movl(EAX, Immediate(reinterpret_cast<int32_t>(literal.raw()))); | |
| 739 } else { | |
| 740 __ LoadObject(EAX, literal); | |
| 741 } | |
| 742 } else { | |
| 743 // Pop the previously evaluated result value into EAX. | |
| 744 __ popl(EAX); | |
| 745 } | |
| 746 | |
| 747 // Generate type check. | |
| 748 if (FLAG_enable_type_checks) { | |
| 749 const RawFunction::Kind kind = parsed_function().function().kind(); | |
| 750 const bool is_implicit_getter = | |
| 751 (kind == RawFunction::kImplicitGetter) || | |
| 752 (kind == RawFunction::kConstImplicitGetter); | |
| 753 const bool is_static = parsed_function().function().is_static(); | |
| 754 // Implicit getters do not need a type check at return, unless they compute | |
| 755 // the initial value of a static field. | |
| 756 if (is_static || !is_implicit_getter) { | |
| 757 GenerateAssertAssignable( | |
| 758 node->id(), | |
| 759 node->value()->token_pos(), | |
| 760 node->value(), | |
| 761 AbstractType::ZoneHandle(parsed_function().function().result_type()), | |
| 762 String::ZoneHandle(String::NewSymbol("function result"))); | |
| 763 } | |
| 764 } | |
| 765 GenerateReturnEpilog(node); | |
| 766 } | |
| 767 | |
| 768 | |
| 769 void CodeGenerator::VisitLiteralNode(LiteralNode* node) { | |
| 770 if (!IsResultNeeded(node)) return; | |
| 771 __ PushObject(node->literal()); | |
| 772 } | |
| 773 | |
| 774 | |
| 775 void CodeGenerator::VisitTypeNode(TypeNode* node) { | |
| 776 // Type nodes are handled specially by the code generator. | |
| 777 UNREACHABLE(); | |
| 778 } | |
| 779 | |
| 780 | |
| 781 void CodeGenerator::VisitAssignableNode(AssignableNode* node) { | |
| 782 ASSERT(FLAG_enable_type_checks); | |
| 783 node->expr()->Visit(this); | |
| 784 __ popl(EAX); | |
| 785 GenerateAssertAssignable(node->id(), | |
| 786 node->token_pos(), | |
| 787 node->expr(), | |
| 788 node->type(), | |
| 789 node->dst_name()); | |
| 790 if (IsResultNeeded(node)) { | |
| 791 __ pushl(EAX); | |
| 792 } | |
| 793 } | |
| 794 | |
| 795 | |
| 796 void CodeGenerator::VisitClosureNode(ClosureNode* node) { | |
| 797 const Immediate raw_null = | |
| 798 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 799 const Function& function = node->function(); | |
| 800 if (function.IsNonImplicitClosureFunction()) { | |
| 801 // The context scope may have already been set by the new non-optimizing | |
| 802 // compiler. If it was not, set it here. | |
| 803 if (function.context_scope() == ContextScope::null()) { | |
| 804 const intptr_t current_context_level = context_level(); | |
| 805 const ContextScope& context_scope = ContextScope::ZoneHandle( | |
| 806 node->scope()->PreserveOuterScope(current_context_level)); | |
| 807 ASSERT(!function.HasCode()); | |
| 808 function.set_context_scope(context_scope); | |
| 809 } | |
| 810 __ pushl(raw_null); // No receiver. | |
| 811 } else if (function.IsImplicitInstanceClosureFunction()) { | |
| 812 node->receiver()->Visit(this); | |
| 813 } else { | |
| 814 __ pushl(raw_null); // No receiver. | |
| 815 } | |
| 816 ASSERT(function.context_scope() != ContextScope::null()); | |
| 817 | |
| 818 // The function type of a closure may have type arguments. In that case, pass | |
| 819 // the type arguments of the instantiator. | |
| 820 const Class& cls = Class::Handle(function.signature_class()); | |
| 821 ASSERT(!cls.IsNull()); | |
| 822 const bool requires_type_arguments = cls.HasTypeArguments(); | |
| 823 if (requires_type_arguments) { | |
| 824 ASSERT(!function.IsImplicitStaticClosureFunction()); | |
| 825 const bool kPushInstantiator = false; | |
| 826 GenerateInstantiatorTypeArguments(node->token_pos(), kPushInstantiator); | |
| 827 } else { | |
| 828 __ pushl(raw_null); // No type arguments. | |
| 829 } | |
| 830 const Code& stub = Code::Handle( | |
| 831 StubCode::GetAllocationStubForClosure(function)); | |
| 832 const ExternalLabel label(function.ToCString(), stub.EntryPoint()); | |
| 833 GenerateCall(node->token_pos(), &label, PcDescriptors::kOther); | |
| 834 __ popl(ECX); // Pop type arguments. | |
| 835 __ popl(ECX); // Pop receiver. | |
| 836 if (IsResultNeeded(node)) { | |
| 837 __ pushl(EAX); | |
| 838 } | |
| 839 } | |
| 840 | |
| 841 | |
| 842 void CodeGenerator::VisitPrimaryNode(PrimaryNode* node) { | |
| 843 // PrimaryNodes are temporary during parsing. | |
| 844 UNREACHABLE(); | |
| 845 } | |
| 846 | |
| 847 | |
| 848 void CodeGenerator::VisitCloneContextNode(CloneContextNode *node) { | |
| 849 __ PushObject(Object::ZoneHandle()); // Make room for the result. | |
| 850 __ pushl(CTX); | |
| 851 GenerateCallRuntime(node->id(), | |
| 852 node->token_pos(), kCloneContextRuntimeEntry); | |
| 853 __ popl(EAX); | |
| 854 __ popl(CTX); // result: cloned context. Set as current context. | |
| 855 } | |
| 856 | |
| 857 | |
| 858 void CodeGenerator::VisitSequenceNode(SequenceNode* node_sequence) { | |
| 859 CodeGeneratorState codegen_state(this); | |
| 860 LocalScope* scope = node_sequence->scope(); | |
| 861 const intptr_t num_context_variables = | |
| 862 (scope != NULL) ? scope->num_context_variables() : 0; | |
| 863 intptr_t previous_context_level = context_level(); | |
| 864 if (num_context_variables > 0) { | |
| 865 // The loop local scope declares variables that are captured. | |
| 866 // Allocate and chain a new context. | |
| 867 __ movl(EDX, Immediate(num_context_variables)); | |
| 868 const ExternalLabel label("alloc_context", | |
| 869 StubCode::AllocateContextEntryPoint()); | |
| 870 GenerateCall(node_sequence->token_pos(), &label, PcDescriptors::kOther); | |
| 871 | |
| 872 // If this node_sequence is the body of the function being compiled, and if | |
| 873 // this function is not a closure, do not link the current context as the | |
| 874 // parent of the newly allocated context, as it is not accessible. Instead, | |
| 875 // save it in a pre-allocated variable and restore it on exit. | |
| 876 if ((node_sequence == parsed_function_.node_sequence()) && | |
| 877 (parsed_function_.saved_context_var() != NULL)) { | |
| 878 GenerateStoreVariable( | |
| 879 *parsed_function_.saved_context_var(), CTX, kNoRegister); | |
| 880 __ StoreIntoObjectNoBarrier(EAX, | |
| 881 FieldAddress(EAX, Context::parent_offset()), | |
| 882 Object::ZoneHandle()); | |
| 883 } else { | |
| 884 // Chain the new context in EAX to its parent in CTX. | |
| 885 __ StoreIntoObject(EAX, FieldAddress(EAX, Context::parent_offset()), CTX); | |
| 886 } | |
| 887 | |
| 888 // Set new context as current context. | |
| 889 __ movl(CTX, EAX); | |
| 890 set_context_level(scope->context_level()); | |
| 891 | |
| 892 // If this node_sequence is the body of the function being compiled, copy | |
| 893 // the captured parameters from the frame into the context. | |
| 894 if (node_sequence == parsed_function_.node_sequence()) { | |
| 895 ASSERT(scope->context_level() == 1); | |
| 896 const Immediate raw_null = | |
| 897 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 898 const Function& function = parsed_function_.function(); | |
| 899 const int num_params = function.NumberOfParameters(); | |
| 900 int param_frame_index = (num_params == function.num_fixed_parameters()) ? | |
| 901 (1 + num_params) : ParsedFunction::kFirstLocalSlotIndex; | |
| 902 for (int pos = 0; pos < num_params; param_frame_index--, pos++) { | |
| 903 LocalVariable* parameter = scope->VariableAt(pos); | |
| 904 ASSERT(parameter->owner() == scope); | |
| 905 if (parameter->is_captured()) { | |
| 906 // Copy parameter from local frame to current context. | |
| 907 const Address local_addr(EBP, param_frame_index * kWordSize); | |
| 908 __ movl(EAX, local_addr); | |
| 909 GenerateStoreVariable(*parameter, EAX, EDX); | |
| 910 // Write NULL to the source location to detect buggy accesses and | |
| 911 // allow GC of passed value if it gets overwritten by a new value in | |
| 912 // the function. | |
| 913 __ movl(local_addr, raw_null); | |
| 914 } | |
| 915 } | |
| 916 } | |
| 917 } | |
| 918 // If this node_sequence is the body of the function being compiled, generate | |
| 919 // code checking the type of the actual arguments. | |
| 920 if (FLAG_enable_type_checks && | |
| 921 (node_sequence == parsed_function_.node_sequence())) { | |
| 922 GenerateArgumentTypeChecks(); | |
| 923 } | |
| 924 for (int i = 0; i < node_sequence->length(); i++) { | |
| 925 AstNode* child_node = node_sequence->NodeAt(i); | |
| 926 state()->set_root_node(child_node); | |
| 927 child_node->Visit(this); | |
| 928 } | |
| 929 | |
| 930 // Unchain the previously allocated context. | |
| 931 if ((node_sequence == parsed_function_.node_sequence()) && | |
| 932 (parsed_function_.saved_context_var() != NULL)) { | |
| 933 ASSERT(num_context_variables > 0); | |
| 934 GenerateLoadVariable(CTX, *parsed_function_.saved_context_var()); | |
| 935 } else if (num_context_variables > 0) { | |
| 936 __ movl(CTX, FieldAddress(CTX, Context::parent_offset())); | |
| 937 } | |
| 938 | |
| 939 // If this node sequence is labeled, a break out of the sequence will have | |
| 940 // taken care of unchaining the context. | |
| 941 if (node_sequence->label() != NULL) { | |
| 942 __ Bind(node_sequence->label()->break_label()); | |
| 943 // Outermost sequence cannot have a label. | |
| 944 ASSERT(node_sequence != parsed_function_.node_sequence()); | |
| 945 } | |
| 946 set_context_level(previous_context_level); | |
| 947 } | |
| 948 | |
| 949 | |
| 950 void CodeGenerator::VisitArgumentListNode(ArgumentListNode* arguments) { | |
| 951 for (int i = 0; i < arguments->length(); i++) { | |
| 952 AstNode* argument = arguments->NodeAt(i); | |
| 953 argument->Visit(this); | |
| 954 } | |
| 955 } | |
| 956 | |
| 957 | |
| 958 void CodeGenerator::VisitArrayNode(ArrayNode* node) { | |
| 959 // Evaluate the array elements. | |
| 960 for (int i = 0; i < node->length(); i++) { | |
| 961 AstNode* element = node->ElementAt(i); | |
| 962 element->Visit(this); | |
| 963 } | |
| 964 | |
| 965 const AbstractTypeArguments& element_type = node->type_arguments(); | |
| 966 const bool instantiate_type_arguments = true; | |
| 967 GenerateTypeArguments(node->id(), | |
| 968 node->token_pos(), | |
| 969 element_type, | |
| 970 instantiate_type_arguments); | |
| 971 __ popl(ECX); | |
| 972 __ movl(EDX, Immediate(Smi::RawValue(node->length()))); | |
| 973 | |
| 974 // Allocate the array. | |
| 975 // EDX : Array length as Smi. | |
| 976 // ECX : element type for the array. | |
| 977 GenerateCall(node->token_pos(), | |
| 978 &StubCode::AllocateArrayLabel(), | |
| 979 PcDescriptors::kOther); | |
| 980 | |
| 981 // Pop the element values from the stack into the array. | |
| 982 __ leal(ECX, FieldAddress(EAX, Array::data_offset())); | |
| 983 for (int i = node->length() - 1; i >= 0; i--) { | |
| 984 __ popl(Address(ECX, i * kWordSize)); | |
| 985 } | |
| 986 | |
| 987 if (IsResultNeeded(node)) { | |
| 988 __ pushl(EAX); | |
| 989 } | |
| 990 } | |
| 991 | |
| 992 | |
| 993 void CodeGenerator::VisitLoadLocalNode(LoadLocalNode* node) { | |
| 994 if (node->HasPseudo()) { | |
| 995 node->pseudo()->Visit(this); | |
| 996 __ popl(EAX); // Discard result. | |
| 997 } | |
| 998 // Load the value of the local variable and push it onto the expression stack. | |
| 999 if (IsResultNeeded(node)) { | |
| 1000 GeneratePushVariable(node->local(), EAX); | |
| 1001 } | |
| 1002 } | |
| 1003 | |
| 1004 | |
| 1005 void CodeGenerator::VisitStoreLocalNode(StoreLocalNode* node) { | |
| 1006 node->value()->Visit(this); | |
| 1007 __ popl(EAX); | |
| 1008 if (FLAG_enable_type_checks) { | |
| 1009 GenerateAssertAssignable(node->id(), | |
| 1010 node->value()->token_pos(), | |
| 1011 node->value(), | |
| 1012 node->local().type(), | |
| 1013 node->local().name()); | |
| 1014 } | |
| 1015 GenerateStoreVariable(node->local(), EAX, EDX); | |
| 1016 if (IsResultNeeded(node)) { | |
| 1017 __ pushl(EAX); | |
| 1018 } | |
| 1019 } | |
| 1020 | |
| 1021 | |
| 1022 void CodeGenerator::VisitLoadInstanceFieldNode(LoadInstanceFieldNode* node) { | |
| 1023 node->instance()->Visit(this); | |
| 1024 MarkDeoptPoint(node->id(), node->token_pos()); | |
| 1025 __ popl(EAX); // Instance. | |
| 1026 __ movl(EAX, FieldAddress(EAX, node->field().Offset())); | |
| 1027 if (IsResultNeeded(node)) { | |
| 1028 __ pushl(EAX); | |
| 1029 } | |
| 1030 } | |
| 1031 | |
| 1032 | |
| 1033 void CodeGenerator::VisitStoreInstanceFieldNode(StoreInstanceFieldNode* node) { | |
| 1034 node->instance()->Visit(this); | |
| 1035 node->value()->Visit(this); | |
| 1036 MarkDeoptPoint(node->id(), node->token_pos()); | |
| 1037 __ popl(EAX); // Value. | |
| 1038 if (FLAG_enable_type_checks) { | |
| 1039 GenerateAssertAssignable(node->id(), | |
| 1040 node->value()->token_pos(), | |
| 1041 node->value(), | |
| 1042 AbstractType::ZoneHandle(node->field().type()), | |
| 1043 String::ZoneHandle(node->field().name())); | |
| 1044 } | |
| 1045 __ popl(EDX); // Instance. | |
| 1046 __ StoreIntoObject(EDX, FieldAddress(EDX, node->field().Offset()), EAX); | |
| 1047 ASSERT(!IsResultNeeded(node)); | |
| 1048 } | |
| 1049 | |
| 1050 | |
| 1051 // Expects array and index on stack and returns result in EAX. | |
| 1052 void CodeGenerator::GenerateLoadIndexed(intptr_t node_id, | |
| 1053 intptr_t token_pos) { | |
| 1054 // Invoke the [] operator on the receiver object with the index as argument. | |
| 1055 const String& operator_name = | |
| 1056 String::ZoneHandle(String::NewSymbol(Token::Str(Token::kINDEX))); | |
| 1057 const int kNumArguments = 2; // Receiver and index. | |
| 1058 const Array& kNoArgumentNames = Array::Handle(); | |
| 1059 const int kNumArgumentsChecked = 1; | |
| 1060 GenerateInstanceCall(node_id, | |
| 1061 token_pos, | |
| 1062 operator_name, | |
| 1063 kNumArguments, | |
| 1064 kNoArgumentNames, | |
| 1065 kNumArgumentsChecked); | |
| 1066 } | |
| 1067 | |
| 1068 | |
| 1069 void CodeGenerator::VisitLoadIndexedNode(LoadIndexedNode* node) { | |
| 1070 node->array()->Visit(this); | |
| 1071 // Now compute the index. | |
| 1072 node->index_expr()->Visit(this); | |
| 1073 MarkDeoptPoint(node->id(), node->token_pos()); | |
| 1074 GenerateLoadIndexed(node->id(), node->token_pos()); | |
| 1075 // Result is in EAX. | |
| 1076 if (IsResultNeeded(node)) { | |
| 1077 __ pushl(EAX); | |
| 1078 } | |
| 1079 } | |
| 1080 | |
| 1081 | |
| 1082 // Expected arguments. | |
| 1083 // TOS(0): value. | |
| 1084 // TOS(1): index. | |
| 1085 // TOS(2): array. | |
| 1086 void CodeGenerator::GenerateStoreIndexed(intptr_t node_id, | |
| 1087 intptr_t token_pos, | |
| 1088 bool preserve_value) { | |
| 1089 // It is not necessary to generate a type test of the assigned value here, | |
| 1090 // because the []= operator will check the type of its incoming arguments. | |
| 1091 if (preserve_value) { | |
| 1092 __ popl(EAX); | |
| 1093 __ popl(EDX); | |
| 1094 __ popl(ECX); | |
| 1095 __ pushl(EAX); // Preserve stored value. | |
| 1096 __ pushl(ECX); // Restore arguments. | |
| 1097 __ pushl(EDX); | |
| 1098 __ pushl(EAX); | |
| 1099 } | |
| 1100 // Invoke the []= operator on the receiver object with index and | |
| 1101 // value as arguments. | |
| 1102 const String& operator_name = | |
| 1103 String::ZoneHandle(String::NewSymbol(Token::Str(Token::kASSIGN_INDEX))); | |
| 1104 const int kNumArguments = 3; // Receiver, index and value. | |
| 1105 const Array& kNoArgumentNames = Array::Handle(); | |
| 1106 const int kNumArgumentsChecked = 1; | |
| 1107 GenerateInstanceCall(node_id, | |
| 1108 token_pos, | |
| 1109 operator_name, | |
| 1110 kNumArguments, | |
| 1111 kNoArgumentNames, | |
| 1112 kNumArgumentsChecked); | |
| 1113 } | |
| 1114 | |
| 1115 | |
| 1116 void CodeGenerator::VisitStoreIndexedNode(StoreIndexedNode* node) { | |
| 1117 // Compute the receiver object and pass as first argument to call. | |
| 1118 node->array()->Visit(this); | |
| 1119 // Now compute the index. | |
| 1120 node->index_expr()->Visit(this); | |
| 1121 // Finally compute the value to assign. | |
| 1122 node->value()->Visit(this); | |
| 1123 MarkDeoptPoint(node->id(), node->token_pos()); | |
| 1124 GenerateStoreIndexed(node->id(), node->token_pos(), IsResultNeeded(node)); | |
| 1125 } | |
| 1126 | |
| 1127 | |
| 1128 void CodeGenerator::VisitLoadStaticFieldNode(LoadStaticFieldNode* node) { | |
| 1129 MarkDeoptPoint(node->id(), node->token_pos()); | |
| 1130 __ LoadObject(EDX, node->field()); | |
| 1131 __ movl(EAX, FieldAddress(EDX, Field::value_offset())); | |
| 1132 if (IsResultNeeded(node)) { | |
| 1133 __ pushl(EAX); | |
| 1134 } | |
| 1135 } | |
| 1136 | |
| 1137 | |
| 1138 void CodeGenerator::VisitStoreStaticFieldNode(StoreStaticFieldNode* node) { | |
| 1139 node->value()->Visit(this); | |
| 1140 MarkDeoptPoint(node->id(), node->token_pos()); | |
| 1141 __ popl(EAX); // Value. | |
| 1142 if (FLAG_enable_type_checks) { | |
| 1143 GenerateAssertAssignable(node->id(), | |
| 1144 node->value()->token_pos(), | |
| 1145 node->value(), | |
| 1146 AbstractType::ZoneHandle(node->field().type()), | |
| 1147 String::ZoneHandle(node->field().name())); | |
| 1148 } | |
| 1149 __ LoadObject(EDX, node->field()); | |
| 1150 __ StoreIntoObject(EDX, FieldAddress(EDX, Field::value_offset()), EAX); | |
| 1151 if (IsResultNeeded(node)) { | |
| 1152 // The result is the input value. | |
| 1153 __ pushl(EAX); | |
| 1154 } | |
| 1155 } | |
| 1156 | |
| 1157 | |
| 1158 void CodeGenerator::GenerateLogicalNotOp(UnaryOpNode* node) { | |
| 1159 // Generate false if operand is true, otherwise generate true. | |
| 1160 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 1161 const Bool& bool_false = Bool::ZoneHandle(Bool::False()); | |
| 1162 node->operand()->Visit(this); | |
| 1163 MarkDeoptPoint(node->id(), node->token_pos()); | |
| 1164 Label done; | |
| 1165 GenerateConditionTypeCheck(node->id(), node->operand()->token_pos()); | |
| 1166 __ popl(EDX); | |
| 1167 __ LoadObject(EAX, bool_true); | |
| 1168 __ cmpl(EAX, EDX); | |
| 1169 __ j(NOT_EQUAL, &done, Assembler::kNearJump); | |
| 1170 __ LoadObject(EAX, bool_false); | |
| 1171 __ Bind(&done); | |
| 1172 if (IsResultNeeded(node)) { | |
| 1173 __ pushl(EAX); | |
| 1174 } | |
| 1175 } | |
| 1176 | |
| 1177 | |
| 1178 void CodeGenerator::VisitUnaryOpNode(UnaryOpNode* node) { | |
| 1179 if (node->kind() == Token::kNOT) { | |
| 1180 // "!" cannot be overloaded, therefore inline it. | |
| 1181 GenerateLogicalNotOp(node); | |
| 1182 return; | |
| 1183 } | |
| 1184 node->operand()->Visit(this); | |
| 1185 if (node->kind() == Token::kADD) { | |
| 1186 // TODO(srdjan): Remove this as it is not part of Dart language any longer. | |
| 1187 // Unary operator '+' does not exist, it's a NOP, skip it. | |
| 1188 if (!IsResultNeeded(node)) { | |
| 1189 __ popl(EAX); | |
| 1190 } | |
| 1191 return; | |
| 1192 } | |
| 1193 MarkDeoptPoint(node->id(), node->token_pos()); | |
| 1194 String& operator_name = String::ZoneHandle(); | |
| 1195 if (node->kind() == Token::kSUB) { | |
| 1196 operator_name = String::NewSymbol(Token::Str(Token::kNEGATE)); | |
| 1197 } else { | |
| 1198 operator_name = String::NewSymbol(node->Name()); | |
| 1199 } | |
| 1200 const int kNumberOfArguments = 1; | |
| 1201 const Array& kNoArgumentNames = Array::Handle(); | |
| 1202 const int kNumArgumentsChecked = 1; | |
| 1203 GenerateInstanceCall(node->id(), | |
| 1204 node->token_pos(), | |
| 1205 operator_name, | |
| 1206 kNumberOfArguments, | |
| 1207 kNoArgumentNames, | |
| 1208 kNumArgumentsChecked); | |
| 1209 if (IsResultNeeded(node)) { | |
| 1210 __ pushl(EAX); | |
| 1211 } | |
| 1212 } | |
| 1213 | |
| 1214 | |
| 1215 // Instance to test is in EAX. Test if its class is in subtype test cache array | |
| 1216 // and use the result in the array to jump to one of the labels. Fall through | |
| 1217 // if the instance is not in the cache array. | |
| 1218 // TODO(srdjan): Implement a quicker subtype check, as type test | |
| 1219 // arrays can grow too high, but they may be useful when optimizing | |
| 1220 // code (type-feedback). | |
| 1221 RawSubtypeTestCache* CodeGenerator::GenerateSubtype1TestCacheLookup( | |
| 1222 intptr_t node_id, | |
| 1223 intptr_t token_pos, | |
| 1224 const Class& type_class, | |
| 1225 Label* is_instance_lbl, | |
| 1226 Label* is_not_instance_lbl) { | |
| 1227 const SubtypeTestCache& type_test_cache = | |
| 1228 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); | |
| 1229 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 1230 const Immediate raw_null = | |
| 1231 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 1232 // Check immediate equality. | |
| 1233 __ LoadClass(ECX, EAX, EDI); | |
| 1234 // ECX: instance class. | |
| 1235 __ CompareObject(ECX, type_class); | |
| 1236 __ j(EQUAL, is_instance_lbl); | |
| 1237 | |
| 1238 // Check immediate superclass equality. | |
| 1239 __ movl(EDI, FieldAddress(ECX, Class::super_type_offset())); | |
| 1240 __ movl(EDI, FieldAddress(EDI, Type::type_class_offset())); | |
| 1241 __ CompareObject(EDI, type_class); | |
| 1242 __ j(EQUAL, is_instance_lbl); | |
| 1243 | |
| 1244 __ LoadObject(EDX, type_test_cache); | |
| 1245 __ pushl(EDX); // Cache array. | |
| 1246 __ pushl(EAX); // Instance. | |
| 1247 __ pushl(raw_null); // Unused | |
| 1248 __ call(&StubCode::Subtype1TestCacheLabel()); | |
| 1249 __ popl(EAX); // Discard. | |
| 1250 __ popl(EAX); // Restore receiver. | |
| 1251 __ popl(EDX); // Discard. | |
| 1252 // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False. | |
| 1253 | |
| 1254 Label runtime_call; | |
| 1255 __ cmpl(ECX, raw_null); | |
| 1256 __ j(EQUAL, &runtime_call, Assembler::kNearJump); | |
| 1257 __ CompareObject(ECX, bool_true); | |
| 1258 __ j(EQUAL, is_instance_lbl); | |
| 1259 __ jmp(is_not_instance_lbl); | |
| 1260 __ Bind(&runtime_call); | |
| 1261 return type_test_cache.raw(); | |
| 1262 } | |
| 1263 | |
| 1264 | |
| 1265 // Inline tests according to the 'type' being tested. Jump to labels | |
| 1266 // if we can compute the type-test, otherwise fallthrough. | |
| 1267 // EAX: instance to be tested, must be preserved. | |
| 1268 // Clobbers many registers. | |
| 1269 RawSubtypeTestCache* CodeGenerator::GenerateInlineInstanceof( | |
| 1270 intptr_t node_id, | |
| 1271 intptr_t token_pos, | |
| 1272 const AbstractType& type, | |
| 1273 Label* is_instance_lbl, | |
| 1274 Label* is_not_instance_lbl) { | |
| 1275 if (type.IsInstantiated()) { | |
| 1276 const Class& type_class = Class::ZoneHandle(type.type_class()); | |
| 1277 // A Smi object cannot be the instance of a parameterized class. | |
| 1278 // A class equality check is only applicable with a dst type of a | |
| 1279 // non-parameterized class or with a raw dst type of a parameterized class. | |
| 1280 if (type_class.HasTypeArguments()) { | |
| 1281 return GenerateInstantiatedTypeWithArgumentsTest(node_id, | |
| 1282 token_pos, | |
| 1283 type, | |
| 1284 is_instance_lbl, | |
| 1285 is_not_instance_lbl); | |
| 1286 // Fall through to runtime call. | |
| 1287 } else { | |
| 1288 GenerateInstantiatedTypeNoArgumentsTest(node_id, | |
| 1289 token_pos, | |
| 1290 type, | |
| 1291 is_instance_lbl, | |
| 1292 is_not_instance_lbl); | |
| 1293 // If test non-conclusive so far, try the inlined type-test cache. | |
| 1294 // 'type' is known at compile time. | |
| 1295 return GenerateSubtype1TestCacheLookup( | |
| 1296 node_id, token_pos, type_class, | |
| 1297 is_instance_lbl, is_not_instance_lbl); | |
| 1298 } | |
| 1299 } else { | |
| 1300 return GenerateUninstantiatedTypeTest(type, | |
| 1301 node_id, | |
| 1302 token_pos, | |
| 1303 is_instance_lbl, | |
| 1304 is_not_instance_lbl); | |
| 1305 } | |
| 1306 return SubtypeTestCache::null(); | |
| 1307 } | |
| 1308 | |
| 1309 | |
| 1310 // If instanceof type test cannot be performed successfully at compile time and | |
| 1311 // therefore eliminated, optimize it by adding inlined tests for: | |
| 1312 // - NULL -> return false. | |
| 1313 // - Smi -> compile time subtype check (only if dst class is not parameterized). | |
| 1314 // - Class equality (only if class is not parameterized). | |
| 1315 // Inputs: | |
| 1316 // - EAX: object. | |
| 1317 // Destroys ECX. | |
| 1318 // Returns: | |
| 1319 // - true or false on stack. | |
| 1320 void CodeGenerator::GenerateInstanceOf(intptr_t node_id, | |
| 1321 intptr_t token_pos, | |
| 1322 AstNode* value, | |
| 1323 const AbstractType& type, | |
| 1324 bool negate_result) { | |
| 1325 ASSERT(type.IsFinalized() && !type.IsMalformed()); | |
| 1326 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 1327 const Bool& bool_false = Bool::ZoneHandle(Bool::False()); | |
| 1328 | |
| 1329 // All objects are instances of type T if Object type is a subtype of type T. | |
| 1330 const Type& object_type = | |
| 1331 Type::Handle(Isolate::Current()->object_store()->object_type()); | |
| 1332 Error& malformed_error = Error::Handle(); | |
| 1333 if (type.IsInstantiated() && | |
| 1334 object_type.IsSubtypeOf(type, &malformed_error)) { | |
| 1335 __ PushObject(negate_result ? bool_false : bool_true); | |
| 1336 return; | |
| 1337 } | |
| 1338 | |
| 1339 // Eliminate the test if it can be performed successfully at compile time. | |
| 1340 if ((value != NULL) && value->IsLiteralNode() && type.IsInstantiated()) { | |
| 1341 const Instance& literal_value = value->AsLiteralNode()->literal(); | |
| 1342 const Class& cls = Class::Handle(literal_value.clazz()); | |
| 1343 if (cls.IsNullClass()) { | |
| 1344 ASSERT(literal_value.IsNull() || | |
| 1345 (literal_value.raw() == Object::sentinel()) || | |
| 1346 (literal_value.raw() == Object::transition_sentinel())); | |
| 1347 // A null object is only an instance of Object and Dynamic, which has | |
| 1348 // already been checked above (if the type is instantiated). So we can | |
| 1349 // return false here if the instance is null (and if the type is | |
| 1350 // instantiated). | |
| 1351 __ PushObject(negate_result ? bool_true : bool_false); | |
| 1352 } else { | |
| 1353 Error& malformed_error = Error::Handle(); | |
| 1354 if (literal_value.IsInstanceOf(type, | |
| 1355 TypeArguments::Handle(), | |
| 1356 &malformed_error)) { | |
| 1357 __ PushObject(negate_result ? bool_false : bool_true); | |
| 1358 } else { | |
| 1359 ASSERT(malformed_error.IsNull()); | |
| 1360 __ PushObject(negate_result ? bool_true : bool_false); | |
| 1361 } | |
| 1362 } | |
| 1363 return; | |
| 1364 } | |
| 1365 | |
| 1366 Label is_instance_of, is_not_instance_of; | |
| 1367 const Immediate raw_null = | |
| 1368 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 1369 Label done; | |
| 1370 // If type is instantiated and non-parameterized, we can inline code | |
| 1371 // checking whether the tested instance is a Smi. | |
| 1372 if (type.IsInstantiated()) { | |
| 1373 // A null object is only an instance of Object and Dynamic, which has | |
| 1374 // already been checked above (if the type is instantiated). So we can | |
| 1375 // return false here if the instance is null (and if the type is | |
| 1376 // instantiated). | |
| 1377 // We can only inline this null check if the type is instantiated at compile | |
| 1378 // time, since an uninstantiated type at compile time could be Object or | |
| 1379 // Dynamic at run time. | |
| 1380 Label non_null; | |
| 1381 __ cmpl(EAX, raw_null); | |
| 1382 __ j(NOT_EQUAL, &non_null, Assembler::kNearJump); | |
| 1383 __ PushObject(negate_result ? bool_true : bool_false); | |
| 1384 __ jmp(&done); | |
| 1385 __ Bind(&non_null); | |
| 1386 } | |
| 1387 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); | |
| 1388 test_cache = GenerateInlineInstanceof(node_id, token_pos, type, | |
| 1389 &is_instance_of, &is_not_instance_of); | |
| 1390 | |
| 1391 __ PushObject(Object::ZoneHandle()); // Make room for the result. | |
| 1392 const Immediate location = Immediate(Smi::RawValue(token_pos)); | |
| 1393 const Immediate node_id_as_smi = Immediate(Smi::RawValue(node_id)); | |
| 1394 __ pushl(location); // Push the source location. | |
| 1395 __ pushl(node_id_as_smi); // node-id. | |
| 1396 __ pushl(EAX); // Push the instance. | |
| 1397 __ PushObject(type); // Push the type. | |
| 1398 if (type.IsInstantiated()) { | |
| 1399 __ pushl(raw_null); // Null instantiator. | |
| 1400 __ pushl(raw_null); // Null instantiator. | |
| 1401 } else { | |
| 1402 const bool kPushInstantiator = true; | |
| 1403 GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator); | |
| 1404 } | |
| 1405 __ LoadObject(EAX, test_cache); | |
| 1406 __ pushl(EAX); | |
| 1407 GenerateCallRuntime(node_id, token_pos, kInstanceofRuntimeEntry); | |
| 1408 // Pop the two parameters supplied to the runtime entry. The result of the | |
| 1409 // instanceof runtime call will be left as the result of the operation. | |
| 1410 __ addl(ESP, Immediate(7 * kWordSize)); | |
| 1411 if (negate_result) { | |
| 1412 __ popl(EDX); | |
| 1413 __ CompareObject(EDX, bool_false); | |
| 1414 __ j(EQUAL, &is_not_instance_of, Assembler::kNearJump); | |
| 1415 // Fall through to is_instance_of. | |
| 1416 } else { | |
| 1417 __ jmp(&done); | |
| 1418 } | |
| 1419 | |
| 1420 __ Bind(&is_instance_of); | |
| 1421 __ PushObject(negate_result ? bool_false: bool_true); | |
| 1422 __ jmp(&done, Assembler::kNearJump); | |
| 1423 __ Bind(&is_not_instance_of); | |
| 1424 __ PushObject(negate_result ? bool_true: bool_false); | |
| 1425 __ Bind(&done); | |
| 1426 } | |
| 1427 | |
| 1428 | |
| 1429 // EAX: instance to test. | |
| 1430 // Clobbers: ECX. | |
| 1431 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if | |
| 1432 // type test is conclusive, otherwise fallthrough if a type test could not | |
| 1433 // be completed. | |
| 1434 RawSubtypeTestCache* CodeGenerator::GenerateInstantiatedTypeWithArgumentsTest( | |
| 1435 intptr_t node_id, | |
| 1436 intptr_t token_pos, | |
| 1437 const AbstractType& type, | |
| 1438 Label* is_instance_lbl, | |
| 1439 Label* is_not_instance_lbl) { | |
| 1440 ASSERT(type.IsInstantiated()); | |
| 1441 const Class& type_class = Class::ZoneHandle(type.type_class()); | |
| 1442 ASSERT(type_class.HasTypeArguments()); | |
| 1443 // A Smi object cannot be the instance of a parameterized class. | |
| 1444 // A class equality check is only applicable with a dst type of a | |
| 1445 // non-parameterized class or with a raw dst type of a parameterized class. | |
| 1446 __ testl(EAX, Immediate(kSmiTagMask)); | |
| 1447 __ j(ZERO, is_not_instance_lbl); | |
| 1448 const AbstractTypeArguments& type_arguments = | |
| 1449 AbstractTypeArguments::ZoneHandle(type.arguments()); | |
| 1450 const bool is_raw_type = type_arguments.IsNull() || | |
| 1451 type_arguments.IsRaw(type_arguments.Length()); | |
| 1452 if (is_raw_type) { | |
| 1453 // Dynamic type argument, check only classes. | |
| 1454 __ LoadClassId(ECX, EAX); | |
| 1455 if (!type_class.is_interface()) { | |
| 1456 __ cmpl(ECX, Immediate(type_class.id())); | |
| 1457 __ j(EQUAL, is_instance_lbl); | |
| 1458 } | |
| 1459 if (type.IsListInterface()) { | |
| 1460 // TODO(srdjan) also accept List<Object>. | |
| 1461 __ cmpl(ECX, Immediate(kArray)); | |
| 1462 __ j(EQUAL, is_instance_lbl); | |
| 1463 __ cmpl(ECX, Immediate(kGrowableObjectArray)); | |
| 1464 __ j(EQUAL, is_instance_lbl); | |
| 1465 } | |
| 1466 return | |
| 1467 GenerateSubtype1TestCacheLookup(node_id, token_pos, type_class, | |
| 1468 is_instance_lbl, is_not_instance_lbl); | |
| 1469 } | |
| 1470 // If one type argument only, quick check. | |
| 1471 if (type_arguments.Length() == 1) { | |
| 1472 const AbstractType& tp_argument = AbstractType::ZoneHandle( | |
| 1473 type_arguments.TypeAt(0)); | |
| 1474 if (tp_argument.IsType()) { | |
| 1475 // Malformed type has been caught in the caller chain of this function. | |
| 1476 ASSERT(tp_argument.HasResolvedTypeClass()); | |
| 1477 // Check if type argument is dynamic or Object. | |
| 1478 const Type& object_type = | |
| 1479 Type::Handle(Isolate::Current()->object_store()->object_type()); | |
| 1480 Error& malformed_error = Error::Handle(); | |
| 1481 if (object_type.IsSubtypeOf(tp_argument, &malformed_error)) { | |
| 1482 // Instance class test only necessary. | |
| 1483 return GenerateSubtype1TestCacheLookup( | |
| 1484 node_id, | |
| 1485 token_pos, | |
| 1486 type_class, | |
| 1487 is_instance_lbl, | |
| 1488 is_not_instance_lbl); | |
| 1489 } | |
| 1490 } | |
| 1491 } | |
| 1492 const SubtypeTestCache& type_test_cache = | |
| 1493 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); | |
| 1494 Label inlined_check, fall_through; | |
| 1495 const Immediate raw_null = | |
| 1496 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 1497 __ LoadObject(EDX, type_test_cache); | |
| 1498 __ pushl(EDX); // Subtype test cache. | |
| 1499 __ pushl(EAX); // Instance. | |
| 1500 __ pushl(raw_null); // Unused. | |
| 1501 __ call(&StubCode::Subtype2TestCacheLabel()); | |
| 1502 __ popl(EAX); // Discard. | |
| 1503 __ popl(EAX); // Restore receiver. | |
| 1504 __ popl(EDX); // Discard. | |
| 1505 // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False. | |
| 1506 | |
| 1507 __ cmpl(ECX, raw_null); | |
| 1508 __ j(EQUAL, &fall_through, Assembler::kNearJump); | |
| 1509 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 1510 __ CompareObject(ECX, bool_true); | |
| 1511 __ j(EQUAL, is_instance_lbl); | |
| 1512 __ jmp(is_not_instance_lbl); | |
| 1513 __ Bind(&fall_through); | |
| 1514 return type_test_cache.raw(); | |
| 1515 } | |
| 1516 | |
| 1517 | |
| 1518 // EAX: instance to test. | |
| 1519 // Clobbers: EBX, ECX, EDX. | |
| 1520 void CodeGenerator::GenerateInstantiatedTypeNoArgumentsTest( | |
| 1521 intptr_t node_id, | |
| 1522 intptr_t token_pos, | |
| 1523 const AbstractType& type, | |
| 1524 Label* is_instance_lbl, | |
| 1525 Label* is_not_instance_lbl) { | |
| 1526 ASSERT(type.IsInstantiated()); | |
| 1527 const Class& type_class = Class::Handle(type.type_class()); | |
| 1528 ASSERT(!type_class.HasTypeArguments()); | |
| 1529 | |
| 1530 Label compare_classes; | |
| 1531 __ testl(EAX, Immediate(kSmiTagMask)); | |
| 1532 __ j(NOT_ZERO, &compare_classes, Assembler::kNearJump); | |
| 1533 // Instance is Smi, check directly. | |
| 1534 const Class& smi_class = Class::Handle(Smi::Class()); | |
| 1535 // TODO(regis): We should introduce a SmiType. | |
| 1536 Error& malformed_error = Error::Handle(); | |
| 1537 if (smi_class.IsSubtypeOf(TypeArguments::Handle(), | |
| 1538 type_class, | |
| 1539 TypeArguments::Handle(), | |
| 1540 &malformed_error)) { | |
| 1541 __ jmp(is_instance_lbl); | |
| 1542 } else { | |
| 1543 __ jmp(is_not_instance_lbl); | |
| 1544 } | |
| 1545 // Compare if the classes are equal. | |
| 1546 __ Bind(&compare_classes); | |
| 1547 | |
| 1548 // Checking against interface. | |
| 1549 // However, for specific core library interfaces, we can check for | |
| 1550 // specific core library classes. | |
| 1551 if (type.IsBoolInterface()) { | |
| 1552 __ CompareClassId(EAX, kBool, ECX); | |
| 1553 __ j(EQUAL, is_instance_lbl); | |
| 1554 __ jmp(is_not_instance_lbl); | |
| 1555 return; | |
| 1556 } | |
| 1557 // If type is an interface, we can skip the class equality check, | |
| 1558 // because instances cannot be of an interface type. | |
| 1559 if (!type_class.is_interface()) { | |
| 1560 __ CompareClassId(EAX, type_class.id(), ECX); | |
| 1561 __ j(EQUAL, is_instance_lbl); | |
| 1562 } | |
| 1563 if (type.IsSubtypeOf( | |
| 1564 Type::Handle(Type::NumberInterface()), &malformed_error)) { | |
| 1565 // Custom checking for numbers (Smi, Mint, Bigint and Double) | |
| 1566 __ LoadClassId(ECX, EAX); | |
| 1567 | |
| 1568 if (type.IsIntInterface() || type.IsNumberInterface()) { | |
| 1569 // We already checked for Smi above. | |
| 1570 __ cmpl(ECX, Immediate(kMint)); | |
| 1571 __ j(EQUAL, is_instance_lbl); | |
| 1572 __ cmpl(ECX, Immediate(kBigint)); | |
| 1573 __ j(EQUAL, is_instance_lbl); | |
| 1574 if (type.IsIntInterface()) { | |
| 1575 __ jmp(is_not_instance_lbl); | |
| 1576 } | |
| 1577 } | |
| 1578 if (type.IsDoubleInterface() || type.IsNumberInterface()) { | |
| 1579 __ cmpl(ECX, Immediate(kDouble)); | |
| 1580 __ j(EQUAL, is_instance_lbl); | |
| 1581 __ jmp(is_not_instance_lbl); | |
| 1582 } | |
| 1583 } else if (type.IsStringInterface()) { | |
| 1584 __ LoadClassId(ECX, EAX); | |
| 1585 __ cmpl(ECX, Immediate(kOneByteString)); | |
| 1586 __ j(EQUAL, is_instance_lbl); | |
| 1587 __ cmpl(ECX, Immediate(kTwoByteString)); | |
| 1588 __ j(EQUAL, is_instance_lbl); | |
| 1589 __ cmpl(ECX, Immediate(kFourByteString)); | |
| 1590 __ j(EQUAL, is_instance_lbl); | |
| 1591 } else if (type.IsFunctionInterface()) { | |
| 1592 // Check if instance is a closure. | |
| 1593 const Immediate raw_null = | |
| 1594 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 1595 __ LoadClass(ECX, EAX, EBX); | |
| 1596 __ movl(ECX, FieldAddress(ECX, Class::signature_function_offset())); | |
| 1597 __ cmpl(ECX, raw_null); | |
| 1598 __ j(NOT_EQUAL, is_instance_lbl); | |
| 1599 } | |
| 1600 // Otherwise fallthrough. | |
| 1601 } | |
| 1602 | |
| 1603 | |
| 1604 // EAX: instance to test. | |
| 1605 // Clobbers: EBX, EDX, ECX. | |
| 1606 RawSubtypeTestCache* CodeGenerator::GenerateUninstantiatedTypeTest( | |
| 1607 const AbstractType& type, | |
| 1608 intptr_t node_id, | |
| 1609 intptr_t token_pos, | |
| 1610 Label* is_instance_lbl, | |
| 1611 Label* is_not_instance_lbl) { | |
| 1612 ASSERT(!type.IsInstantiated()); | |
| 1613 // Skip check if destination is a dynamic type. | |
| 1614 const Immediate raw_null = | |
| 1615 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 1616 if (type.IsTypeParameter()) { | |
| 1617 // EAX must be preserved! | |
| 1618 Label fall_through; | |
| 1619 const bool kPushInstantiator = false; | |
| 1620 GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator); | |
| 1621 // Type arguments are on stack. | |
| 1622 __ popl(EBX); | |
| 1623 // Check if type argument is dynamic. | |
| 1624 __ cmpl(EBX, raw_null); | |
| 1625 __ j(EQUAL, is_instance_lbl); | |
| 1626 | |
| 1627 // EBX: instantiator type arguments. | |
| 1628 // For now handle only TypeArguments and bail out if InstantiatedTypeArgs. | |
| 1629 // We expect that frequently checked objects wull have their type arguments | |
| 1630 // converted into instance of TypeArguments. | |
| 1631 __ CompareClassId(EBX, kTypeArguments, EDX); | |
| 1632 __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump); | |
| 1633 | |
| 1634 __ movl(EDX, | |
| 1635 FieldAddress(EBX, TypeArguments::type_at_offset(type.Index()))); | |
| 1636 // EDX: concrete type of type. | |
| 1637 // Check if type argument is dynamic. | |
| 1638 __ CompareObject(EDX, Type::ZoneHandle(Type::DynamicType())); | |
| 1639 __ j(EQUAL, is_instance_lbl); | |
| 1640 __ cmpl(EDX, raw_null); | |
| 1641 __ j(EQUAL, is_instance_lbl); | |
| 1642 | |
| 1643 // For Smi check quickly against int and num interfaces. | |
| 1644 Label not_smi; | |
| 1645 __ testl(EAX, Immediate(kSmiTagMask)); // Value is Smi? | |
| 1646 __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump); | |
| 1647 __ CompareObject(EDX, Type::ZoneHandle(Type::IntInterface())); | |
| 1648 __ j(EQUAL, is_instance_lbl); | |
| 1649 __ CompareObject(EDX, Type::ZoneHandle(Type::NumberInterface())); | |
| 1650 __ j(EQUAL, is_instance_lbl); | |
| 1651 __ jmp(&fall_through); | |
| 1652 | |
| 1653 __ Bind(¬_smi); | |
| 1654 // EBX: instantiator type arguments. | |
| 1655 // EAX: instance | |
| 1656 // The instantiated type parameter may not be a Type, but could be an | |
| 1657 // InstantiatedType. It is therefore necessary to check its class. | |
| 1658 const SubtypeTestCache& type_test_cache = | |
| 1659 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); | |
| 1660 __ LoadObject(ECX, type_test_cache); | |
| 1661 __ pushl(ECX); // Subtype test cache. | |
| 1662 __ pushl(EAX); // Instance. | |
| 1663 __ pushl(EBX); // Instantator type arguments. | |
| 1664 __ call(&StubCode::Subtype3TestCacheLabel()); | |
| 1665 __ popl(EDX); // Discard type arguments. | |
| 1666 __ popl(EAX); // Restore receiver. | |
| 1667 __ popl(EDX); // Discard subtype test cache. | |
| 1668 // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False. | |
| 1669 __ cmpl(ECX, raw_null); | |
| 1670 __ j(EQUAL, &fall_through, Assembler::kNearJump); | |
| 1671 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 1672 __ CompareObject(ECX, bool_true); | |
| 1673 __ j(EQUAL, is_instance_lbl); | |
| 1674 __ jmp(is_not_instance_lbl); | |
| 1675 __ Bind(&fall_through); | |
| 1676 return type_test_cache.raw(); | |
| 1677 } | |
| 1678 if (type.IsType()) { | |
| 1679 Label fall_through; | |
| 1680 __ testl(EAX, Immediate(kSmiTagMask)); // Is instance Smi? | |
| 1681 __ j(ZERO, is_not_instance_lbl); | |
| 1682 // Uninstantiated type class is known at compile time, but the type | |
| 1683 // arguments are determined at runtime by the instantiator. | |
| 1684 const SubtypeTestCache& type_test_cache = | |
| 1685 SubtypeTestCache::ZoneHandle(SubtypeTestCache::New()); | |
| 1686 __ LoadObject(EDX, type_test_cache); | |
| 1687 __ pushl(EDX); // Subtype test cache. | |
| 1688 __ pushl(EAX); // Instance. | |
| 1689 const bool kPushInstantiator = false; | |
| 1690 GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator); | |
| 1691 __ call(&StubCode::Subtype3TestCacheLabel()); | |
| 1692 __ popl(EDX); // Discard type arguments. | |
| 1693 __ popl(EAX); // Restore receiver. | |
| 1694 __ popl(EDX); // Discard subtype test cache. | |
| 1695 // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False. | |
| 1696 __ cmpl(ECX, raw_null); | |
| 1697 __ j(EQUAL, &fall_through, Assembler::kNearJump); | |
| 1698 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 1699 __ CompareObject(ECX, bool_true); | |
| 1700 __ j(EQUAL, is_instance_lbl); | |
| 1701 __ jmp(is_not_instance_lbl); | |
| 1702 __ Bind(&fall_through); | |
| 1703 return type_test_cache.raw(); | |
| 1704 } | |
| 1705 return SubtypeTestCache::null(); | |
| 1706 } | |
| 1707 | |
| 1708 | |
| 1709 // If type check cannot be performed successfully at compile time and therefore | |
| 1710 // eliminated, optimize it by adding inlined tests for: | |
| 1711 // - NULL -> return NULL. | |
| 1712 // - Smi -> compile time subtype check (only if dst class is not parameterized). | |
| 1713 // - Class equality (only if class is not parameterized). | |
| 1714 // Inputs: | |
| 1715 // - EAX: object. | |
| 1716 // Destroys ECX and EDX. | |
| 1717 // Returns: | |
| 1718 // - object in EAX for successful assignable check (or throws TypeError). | |
| 1719 // Performance notes: positive checks must be quick, negative checks can be slow | |
| 1720 // as they throw an exception. | |
| 1721 void CodeGenerator::GenerateAssertAssignable(intptr_t node_id, | |
| 1722 intptr_t token_pos, | |
| 1723 AstNode* value, | |
| 1724 const AbstractType& dst_type, | |
| 1725 const String& dst_name) { | |
| 1726 ASSERT(FLAG_enable_type_checks); | |
| 1727 ASSERT(token_pos >= 0); | |
| 1728 ASSERT(!dst_type.IsNull()); | |
| 1729 ASSERT(dst_type.IsFinalized()); | |
| 1730 | |
| 1731 // Any expression is assignable to the Dynamic type and to the Object type. | |
| 1732 // Skip the test. | |
| 1733 if (!dst_type.IsMalformed() && | |
| 1734 (dst_type.IsDynamicType() || dst_type.IsObjectType())) { | |
| 1735 return; | |
| 1736 } | |
| 1737 | |
| 1738 // It is a compile-time error to explicitly return a value (including null) | |
| 1739 // from a void function. However, functions that do not explicitly return a | |
| 1740 // value, implicitly return null. This includes void functions. Therefore, we | |
| 1741 // skip the type test here and trust the parser to only return null in void | |
| 1742 // function. | |
| 1743 if (dst_type.IsVoidType()) { | |
| 1744 return; | |
| 1745 } | |
| 1746 | |
| 1747 // Eliminate the test if it can be performed successfully at compile time. | |
| 1748 if ((value != NULL) && value->IsLiteralNode()) { | |
| 1749 const Instance& literal_value = value->AsLiteralNode()->literal(); | |
| 1750 const Class& cls = Class::Handle(literal_value.clazz()); | |
| 1751 if (cls.IsNullClass()) { | |
| 1752 ASSERT(literal_value.IsNull() || | |
| 1753 (literal_value.raw() == Object::sentinel()) || | |
| 1754 (literal_value.raw() == Object::transition_sentinel())); | |
| 1755 return; | |
| 1756 } | |
| 1757 Error& malformed_error = Error::Handle(); | |
| 1758 if (!dst_type.IsMalformed() && | |
| 1759 dst_type.IsInstantiated() && | |
| 1760 literal_value.IsInstanceOf(dst_type, | |
| 1761 TypeArguments::Handle(), | |
| 1762 &malformed_error)) { | |
| 1763 return; | |
| 1764 } | |
| 1765 } | |
| 1766 | |
| 1767 // A null object is always assignable and is returned as result. | |
| 1768 const Immediate raw_null = | |
| 1769 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 1770 Label done, runtime_call; | |
| 1771 __ cmpl(EAX, raw_null); | |
| 1772 __ j(EQUAL, &done); | |
| 1773 | |
| 1774 // Generate throw new TypeError() if the type is malformed. | |
| 1775 if (dst_type.IsMalformed()) { | |
| 1776 const Error& error = Error::Handle(dst_type.malformed_error()); | |
| 1777 const String& error_message = String::ZoneHandle( | |
| 1778 String::NewSymbol(error.ToErrorCString())); | |
| 1779 __ PushObject(Object::ZoneHandle()); // Make room for the result. | |
| 1780 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location. | |
| 1781 __ pushl(EAX); // Push the source object. | |
| 1782 __ PushObject(dst_name); // Push the name of the destination. | |
| 1783 __ PushObject(error_message); | |
| 1784 GenerateCallRuntime(node_id, token_pos, kMalformedTypeErrorRuntimeEntry); | |
| 1785 // We should never return here. | |
| 1786 __ int3(); | |
| 1787 | |
| 1788 __ Bind(&done); // For a null object. | |
| 1789 return; | |
| 1790 } | |
| 1791 | |
| 1792 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); | |
| 1793 test_cache = GenerateInlineInstanceof(node_id, token_pos, dst_type, | |
| 1794 &done, &runtime_call); | |
| 1795 | |
| 1796 __ Bind(&runtime_call); | |
| 1797 __ PushObject(Object::ZoneHandle()); // Make room for the result. | |
| 1798 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location. | |
| 1799 __ pushl(Immediate(Smi::RawValue(node_id))); // node-id. | |
| 1800 __ pushl(EAX); // Push the source object. | |
| 1801 __ PushObject(dst_type); // Push the type of the destination. | |
| 1802 if (dst_type.IsInstantiated()) { | |
| 1803 __ pushl(raw_null); // Null instantiator. | |
| 1804 __ pushl(raw_null); // Null instantiator type argument. | |
| 1805 } else { | |
| 1806 const bool kPushInstantiator = true; | |
| 1807 GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator); | |
| 1808 } | |
| 1809 __ PushObject(dst_name); // Push the name of the destination. | |
| 1810 __ LoadObject(EAX, test_cache); | |
| 1811 __ pushl(EAX); | |
| 1812 GenerateCallRuntime(node_id, token_pos, kTypeCheckRuntimeEntry); | |
| 1813 // Pop the parameters supplied to the runtime entry. The result of the | |
| 1814 // type check runtime call is the checked value. | |
| 1815 __ addl(ESP, Immediate(8 * kWordSize)); | |
| 1816 __ popl(EAX); | |
| 1817 | |
| 1818 // EAX: value. | |
| 1819 __ Bind(&done); | |
| 1820 } | |
| 1821 | |
| 1822 | |
| 1823 void CodeGenerator::GenerateArgumentTypeChecks() { | |
| 1824 const Function& function = parsed_function_.function(); | |
| 1825 const SequenceNode& sequence_node = *parsed_function_.node_sequence(); | |
| 1826 LocalScope* scope = sequence_node.scope(); | |
| 1827 const int num_fixed_params = function.num_fixed_parameters(); | |
| 1828 const int num_opt_params = function.num_optional_parameters(); | |
| 1829 ASSERT(num_fixed_params + num_opt_params <= scope->num_variables()); | |
| 1830 for (intptr_t i = 0; i < num_fixed_params + num_opt_params; i++) { | |
| 1831 LocalVariable* parameter = scope->VariableAt(i); | |
| 1832 GenerateLoadVariable(EAX, *parameter); | |
| 1833 GenerateAssertAssignable(sequence_node.ParameterIdAt(i), | |
| 1834 parameter->token_pos(), | |
| 1835 NULL, | |
| 1836 parameter->type(), | |
| 1837 parameter->name()); | |
| 1838 } | |
| 1839 } | |
| 1840 | |
| 1841 | |
| 1842 void CodeGenerator::GenerateConditionTypeCheck(intptr_t node_id, | |
| 1843 intptr_t token_pos) { | |
| 1844 if (!FLAG_enable_type_checks) { | |
| 1845 return; | |
| 1846 } | |
| 1847 | |
| 1848 // Check that the type of the object on the stack is allowed in conditional | |
| 1849 // context. | |
| 1850 // Call the runtime if the object is null or not of type bool. | |
| 1851 const Immediate raw_null = | |
| 1852 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 1853 Label runtime_call, done; | |
| 1854 __ movl(EAX, Address(ESP, 0)); | |
| 1855 __ cmpl(EAX, raw_null); | |
| 1856 __ j(EQUAL, &runtime_call, Assembler::kNearJump); | |
| 1857 __ testl(EAX, Immediate(kSmiTagMask)); | |
| 1858 __ j(ZERO, &runtime_call, Assembler::kNearJump); // Call runtime for Smi. | |
| 1859 // This check should pass if the receiver's class implements the interface | |
| 1860 // 'bool'. Check only class 'Bool' since it is the only legal implementation | |
| 1861 // of the interface 'bool'. | |
| 1862 __ CompareClassId(EAX, kBool, ECX); | |
| 1863 __ j(EQUAL, &done, Assembler::kNearJump); | |
| 1864 | |
| 1865 __ Bind(&runtime_call); | |
| 1866 __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location. | |
| 1867 __ pushl(EAX); // Push the source object. | |
| 1868 GenerateCallRuntime(node_id, token_pos, kConditionTypeErrorRuntimeEntry); | |
| 1869 // We should never return here. | |
| 1870 __ int3(); | |
| 1871 | |
| 1872 __ Bind(&done); | |
| 1873 } | |
| 1874 | |
| 1875 | |
| 1876 void CodeGenerator::VisitComparisonNode(ComparisonNode* node) { | |
| 1877 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 1878 const Bool& bool_false = Bool::ZoneHandle(Bool::False()); | |
| 1879 node->left()->Visit(this); | |
| 1880 | |
| 1881 // The instanceof operator needs special handling. | |
| 1882 if (Token::IsTypeTestOperator(node->kind())) { | |
| 1883 __ popl(EAX); // Left operand. | |
| 1884 ASSERT(node->right()->IsTypeNode()); | |
| 1885 GenerateInstanceOf(node->id(), | |
| 1886 node->token_pos(), | |
| 1887 node->left(), | |
| 1888 node->right()->AsTypeNode()->type(), | |
| 1889 (node->kind() == Token::kISNOT)); | |
| 1890 if (!IsResultNeeded(node)) { | |
| 1891 __ popl(EAX); // Pop the result of the instanceof operation. | |
| 1892 } | |
| 1893 return; | |
| 1894 } | |
| 1895 | |
| 1896 if (Token::IsTypeCastOperator(node->kind())) { | |
| 1897 // This code generator is not maintained anymore. | |
| 1898 UNIMPLEMENTED(); | |
| 1899 } | |
| 1900 | |
| 1901 node->right()->Visit(this); | |
| 1902 // Both left and right values on stack. | |
| 1903 | |
| 1904 // '===' and '!==' are not overloadable. | |
| 1905 if ((node->kind() == Token::kEQ_STRICT) || | |
| 1906 (node->kind() == Token::kNE_STRICT)) { | |
| 1907 __ popl(EDX); // Right operand. | |
| 1908 __ popl(EAX); // Left operand. | |
| 1909 if (!IsResultNeeded(node)) { | |
| 1910 return; | |
| 1911 } | |
| 1912 Label load_true, done; | |
| 1913 __ cmpl(EAX, EDX); | |
| 1914 if (node->kind() == Token::kEQ_STRICT) { | |
| 1915 __ j(EQUAL, &load_true, Assembler::kNearJump); | |
| 1916 } else { | |
| 1917 __ j(NOT_EQUAL, &load_true, Assembler::kNearJump); | |
| 1918 } | |
| 1919 __ LoadObject(EAX, bool_false); | |
| 1920 __ jmp(&done, Assembler::kNearJump); | |
| 1921 __ Bind(&load_true); | |
| 1922 __ LoadObject(EAX, bool_true); | |
| 1923 __ Bind(&done); | |
| 1924 // Result is in EAX. | |
| 1925 __ pushl(EAX); | |
| 1926 return; | |
| 1927 } | |
| 1928 | |
| 1929 MarkDeoptPoint(node->id(), node->token_pos()); | |
| 1930 | |
| 1931 // '!=' not overloadable, always implements negation of '=='. | |
| 1932 // Call operator for '=='. | |
| 1933 if ((node->kind() == Token::kEQ) || (node->kind() == Token::kNE)) { | |
| 1934 // Null is a special receiver with a special type and frequently used on | |
| 1935 // operators "==" and "!=". Emit inlined code for null so that it does not | |
| 1936 // pollute type information at call site. | |
| 1937 Label null_done; | |
| 1938 { | |
| 1939 const Immediate raw_null = | |
| 1940 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 1941 Label non_null_compare, load_true; | |
| 1942 // Check if left argument is null. | |
| 1943 __ cmpl(Address(ESP, 1 * kWordSize), raw_null); | |
| 1944 __ j(NOT_EQUAL, &non_null_compare, Assembler::kNearJump); | |
| 1945 // Comparison with NULL is "===". | |
| 1946 // Load/remove arguments. | |
| 1947 __ popl(EDX); | |
| 1948 __ popl(EAX); | |
| 1949 __ cmpl(EAX, EDX); | |
| 1950 if (node->kind() == Token::kEQ) { | |
| 1951 __ j(EQUAL, &load_true, Assembler::kNearJump); | |
| 1952 } else { | |
| 1953 __ j(NOT_EQUAL, &load_true, Assembler::kNearJump); | |
| 1954 } | |
| 1955 __ LoadObject(EAX, bool_false); | |
| 1956 __ jmp(&null_done, Assembler::kNearJump); | |
| 1957 __ Bind(&load_true); | |
| 1958 __ LoadObject(EAX, bool_true); | |
| 1959 __ jmp(&null_done, Assembler::kNearJump); | |
| 1960 __ Bind(&non_null_compare); | |
| 1961 } | |
| 1962 // Do '==' first then negate if necessary, | |
| 1963 const String& operator_name = String::ZoneHandle(String::NewSymbol("==")); | |
| 1964 const int kNumberOfArguments = 2; | |
| 1965 const Array& kNoArgumentNames = Array::Handle(); | |
| 1966 const int kNumArgumentsChecked = 1; | |
| 1967 GenerateInstanceCall(node->id(), | |
| 1968 node->token_pos(), | |
| 1969 operator_name, | |
| 1970 kNumberOfArguments, | |
| 1971 kNoArgumentNames, | |
| 1972 kNumArgumentsChecked); | |
| 1973 | |
| 1974 // Result is in EAX. No need to negate if result is not needed. | |
| 1975 if ((node->kind() == Token::kNE) && IsResultNeeded(node)) { | |
| 1976 // Negate result. | |
| 1977 Label load_true, done; | |
| 1978 __ LoadObject(EDX, bool_false); | |
| 1979 __ cmpl(EAX, EDX); | |
| 1980 __ j(EQUAL, &load_true, Assembler::kNearJump); | |
| 1981 __ movl(EAX, EDX); // false. | |
| 1982 __ jmp(&done, Assembler::kNearJump); | |
| 1983 __ Bind(&load_true); | |
| 1984 __ LoadObject(EAX, bool_true); | |
| 1985 __ Bind(&done); | |
| 1986 } | |
| 1987 __ Bind(&null_done); | |
| 1988 // Result is in EAX. | |
| 1989 if (IsResultNeeded(node)) { | |
| 1990 __ pushl(EAX); | |
| 1991 } | |
| 1992 return; | |
| 1993 } | |
| 1994 | |
| 1995 // Call operator. | |
| 1996 GenerateBinaryOperatorCall(node->id(), node->token_pos(), node->Name()); | |
| 1997 // Result is in EAX. | |
| 1998 if (IsResultNeeded(node)) { | |
| 1999 __ pushl(EAX); | |
| 2000 } | |
| 2001 } | |
| 2002 | |
| 2003 | |
| 2004 void CodeGenerator::HandleBackwardBranch( | |
| 2005 intptr_t loop_id, intptr_t token_pos) { | |
| 2006 // Use stack overflow check to eventually stop execution of loops. | |
| 2007 // This is necessary only if a loop does not have calls. | |
| 2008 __ cmpl(ESP, | |
| 2009 Address::Absolute(Isolate::Current()->stack_limit_address())); | |
| 2010 Label no_stack_overflow; | |
| 2011 __ j(ABOVE, &no_stack_overflow); | |
| 2012 GenerateCallRuntime(loop_id, | |
| 2013 token_pos, | |
| 2014 kStackOverflowRuntimeEntry); | |
| 2015 __ Bind(&no_stack_overflow); | |
| 2016 } | |
| 2017 | |
| 2018 | |
| 2019 void CodeGenerator::VisitWhileNode(WhileNode* node) { | |
| 2020 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 2021 SourceLabel* label = node->label(); | |
| 2022 __ Bind(label->continue_label()); | |
| 2023 node->condition()->Visit(this); | |
| 2024 GenerateConditionTypeCheck(node->id(), node->condition()->token_pos()); | |
| 2025 __ popl(EAX); | |
| 2026 __ LoadObject(EDX, bool_true); | |
| 2027 __ cmpl(EAX, EDX); | |
| 2028 __ j(NOT_EQUAL, label->break_label()); | |
| 2029 node->body()->Visit(this); | |
| 2030 HandleBackwardBranch(node->id(), node->token_pos()); | |
| 2031 __ jmp(label->continue_label()); | |
| 2032 __ Bind(label->break_label()); | |
| 2033 } | |
| 2034 | |
| 2035 | |
| 2036 void CodeGenerator::VisitDoWhileNode(DoWhileNode* node) { | |
| 2037 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 2038 SourceLabel* label = node->label(); | |
| 2039 Label loop; | |
| 2040 __ Bind(&loop); | |
| 2041 node->body()->Visit(this); | |
| 2042 HandleBackwardBranch(node->id(), node->token_pos()); | |
| 2043 __ Bind(label->continue_label()); | |
| 2044 node->condition()->Visit(this); | |
| 2045 GenerateConditionTypeCheck(node->id(), node->condition()->token_pos()); | |
| 2046 __ popl(EAX); | |
| 2047 __ LoadObject(EDX, bool_true); | |
| 2048 __ cmpl(EAX, EDX); | |
| 2049 __ j(EQUAL, &loop); | |
| 2050 __ Bind(label->break_label()); | |
| 2051 } | |
| 2052 | |
| 2053 | |
| 2054 void CodeGenerator::VisitForNode(ForNode* node) { | |
| 2055 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 2056 node->initializer()->Visit(this); | |
| 2057 SourceLabel* label = node->label(); | |
| 2058 Label loop; | |
| 2059 __ Bind(&loop); | |
| 2060 if (node->condition() != NULL) { | |
| 2061 node->condition()->Visit(this); | |
| 2062 GenerateConditionTypeCheck(node->id(), node->condition()->token_pos()); | |
| 2063 __ popl(EAX); | |
| 2064 __ LoadObject(EDX, bool_true); | |
| 2065 __ cmpl(EAX, EDX); | |
| 2066 __ j(NOT_EQUAL, label->break_label()); | |
| 2067 } | |
| 2068 node->body()->Visit(this); | |
| 2069 HandleBackwardBranch(node->id(), node->token_pos()); | |
| 2070 __ Bind(label->continue_label()); | |
| 2071 node->increment()->Visit(this); | |
| 2072 __ jmp(&loop); | |
| 2073 __ Bind(label->break_label()); | |
| 2074 } | |
| 2075 | |
| 2076 | |
| 2077 void CodeGenerator::VisitJumpNode(JumpNode* node) { | |
| 2078 SourceLabel* label = node->label(); | |
| 2079 | |
| 2080 // Generate inlined code for all finally blocks as we may transfer | |
| 2081 // control out of the 'try' blocks if any. | |
| 2082 for (intptr_t i = 0; i < node->inlined_finally_list_length(); i++) { | |
| 2083 node->InlinedFinallyNodeAt(i)->Visit(this); | |
| 2084 } | |
| 2085 | |
| 2086 // Unchain the context(s) up to the outer context level of the scope which | |
| 2087 // contains the destination label. | |
| 2088 ASSERT(label->owner() != NULL); | |
| 2089 intptr_t target_context_level = 0; | |
| 2090 LocalScope* target_scope = label->owner(); | |
| 2091 if (target_scope->num_context_variables() > 0) { | |
| 2092 // The scope of the target label allocates a context, therefore its outer | |
| 2093 // scope is at a lower context level. | |
| 2094 target_context_level = target_scope->context_level() - 1; | |
| 2095 } else { | |
| 2096 // The scope of the target label does not allocate a context, so its outer | |
| 2097 // scope is at the same context level. Find it. | |
| 2098 while ((target_scope != NULL) && | |
| 2099 (target_scope->num_context_variables() == 0)) { | |
| 2100 target_scope = target_scope->parent(); | |
| 2101 } | |
| 2102 if (target_scope != NULL) { | |
| 2103 target_context_level = target_scope->context_level(); | |
| 2104 } | |
| 2105 } | |
| 2106 ASSERT(target_context_level >= 0); | |
| 2107 int current_context_level = context_level(); | |
| 2108 ASSERT(current_context_level >= target_context_level); | |
| 2109 while (current_context_level-- > target_context_level) { | |
| 2110 __ movl(CTX, FieldAddress(CTX, Context::parent_offset())); | |
| 2111 } | |
| 2112 | |
| 2113 if (node->kind() == Token::kBREAK) { | |
| 2114 __ jmp(label->break_label()); | |
| 2115 } else { | |
| 2116 __ jmp(label->continue_label()); | |
| 2117 } | |
| 2118 } | |
| 2119 | |
| 2120 | |
| 2121 void CodeGenerator::VisitConditionalExprNode(ConditionalExprNode* node) { | |
| 2122 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 2123 Label false_label, done; | |
| 2124 node->condition()->Visit(this); | |
| 2125 GenerateConditionTypeCheck(node->id(), node->condition()->token_pos()); | |
| 2126 __ popl(EAX); | |
| 2127 __ LoadObject(EDX, bool_true); | |
| 2128 __ cmpl(EAX, EDX); | |
| 2129 __ j(NOT_EQUAL, &false_label); | |
| 2130 node->true_expr()->Visit(this); | |
| 2131 __ jmp(&done); | |
| 2132 __ Bind(&false_label); | |
| 2133 node->false_expr()->Visit(this); | |
| 2134 __ Bind(&done); | |
| 2135 if (!IsResultNeeded(node)) { | |
| 2136 __ popl(EAX); | |
| 2137 } | |
| 2138 } | |
| 2139 | |
| 2140 | |
| 2141 void CodeGenerator::VisitSwitchNode(SwitchNode *node) { | |
| 2142 SourceLabel* label = node->label(); | |
| 2143 node->body()->Visit(this); | |
| 2144 __ Bind(label->break_label()); | |
| 2145 } | |
| 2146 | |
| 2147 | |
| 2148 void CodeGenerator::VisitCaseNode(CaseNode* node) { | |
| 2149 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 2150 Label case_statements, end_case; | |
| 2151 | |
| 2152 for (int i = 0; i < node->case_expressions()->length(); i++) { | |
| 2153 // Load case expression onto stack. | |
| 2154 AstNode* case_expr = node->case_expressions()->NodeAt(i); | |
| 2155 case_expr->Visit(this); | |
| 2156 __ popl(EAX); | |
| 2157 __ CompareObject(EAX, bool_true); | |
| 2158 // Jump to case clause code if case expression equals switch expression | |
| 2159 __ j(EQUAL, &case_statements); | |
| 2160 } | |
| 2161 // If this case clause contains the default label, fall through to | |
| 2162 // case clause code, else skip this clause. | |
| 2163 if (!node->contains_default()) { | |
| 2164 __ jmp(&end_case); | |
| 2165 } | |
| 2166 | |
| 2167 // If there is a label associated with this case clause, bind it. | |
| 2168 if (node->label() != NULL) { | |
| 2169 __ Bind(node->label()->continue_label()); | |
| 2170 } | |
| 2171 | |
| 2172 // Generate code for case clause statements. The parser guarantees that | |
| 2173 // the code contains a jump, so we should never fall through the end | |
| 2174 // of the statements. | |
| 2175 __ Bind(&case_statements); | |
| 2176 node->statements()->Visit(this); | |
| 2177 __ Bind(&end_case); | |
| 2178 } | |
| 2179 | |
| 2180 | |
| 2181 void CodeGenerator::VisitIfNode(IfNode* node) { | |
| 2182 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 2183 Label false_label; | |
| 2184 node->condition()->Visit(this); | |
| 2185 GenerateConditionTypeCheck(node->id(), node->condition()->token_pos()); | |
| 2186 __ popl(EAX); | |
| 2187 __ LoadObject(EDX, bool_true); | |
| 2188 __ cmpl(EAX, EDX); | |
| 2189 __ j(NOT_EQUAL, &false_label); | |
| 2190 node->true_branch()->Visit(this); | |
| 2191 if (node->false_branch() != NULL) { | |
| 2192 Label done; | |
| 2193 __ jmp(&done); | |
| 2194 __ Bind(&false_label); | |
| 2195 node->false_branch()->Visit(this); | |
| 2196 __ Bind(&done); | |
| 2197 } else { | |
| 2198 __ Bind(&false_label); | |
| 2199 } | |
| 2200 } | |
| 2201 | |
| 2202 | |
| 2203 // Operators '&&' and '||' are not overloadabled, inline them. | |
| 2204 void CodeGenerator::GenerateLogicalAndOrOp(BinaryOpNode* node) { | |
| 2205 // Generate true if (left == true) op (right == true), otherwise generate | |
| 2206 // false, with op being either || or &&. | |
| 2207 const Bool& bool_true = Bool::ZoneHandle(Bool::True()); | |
| 2208 const Bool& bool_false = Bool::ZoneHandle(Bool::False()); | |
| 2209 Label load_false, done; | |
| 2210 node->left()->Visit(this); | |
| 2211 GenerateConditionTypeCheck(node->id(), node->left()->token_pos()); | |
| 2212 __ popl(EAX); | |
| 2213 __ LoadObject(EDX, bool_true); | |
| 2214 __ cmpl(EAX, EDX); | |
| 2215 if (node->kind() == Token::kAND) { | |
| 2216 __ j(NOT_EQUAL, &load_false); | |
| 2217 } else { | |
| 2218 ASSERT(node->kind() == Token::kOR); | |
| 2219 __ j(EQUAL, &done); | |
| 2220 } | |
| 2221 node->right()->Visit(this); | |
| 2222 GenerateConditionTypeCheck(node->id(), node->right()->token_pos()); | |
| 2223 __ popl(EAX); | |
| 2224 __ LoadObject(EDX, bool_true); | |
| 2225 __ cmpl(EAX, EDX); | |
| 2226 __ j(EQUAL, &done); | |
| 2227 __ Bind(&load_false); | |
| 2228 __ LoadObject(EAX, bool_false); | |
| 2229 __ Bind(&done); | |
| 2230 if (IsResultNeeded(node)) { | |
| 2231 __ pushl(EAX); | |
| 2232 } | |
| 2233 } | |
| 2234 | |
| 2235 | |
| 2236 // Expect receiver(left operand) and right operand on stack. | |
| 2237 // Return result in EAX. | |
| 2238 void CodeGenerator::GenerateBinaryOperatorCall(intptr_t node_id, | |
| 2239 intptr_t token_pos, | |
| 2240 const char* name) { | |
| 2241 const String& operator_name = String::ZoneHandle(String::NewSymbol(name)); | |
| 2242 const int kNumberOfArguments = 2; | |
| 2243 const Array& kNoArgumentNames = Array::Handle(); | |
| 2244 const int kNumArgumentsChecked = 2; | |
| 2245 GenerateInstanceCall(node_id, | |
| 2246 token_pos, | |
| 2247 operator_name, | |
| 2248 kNumberOfArguments, | |
| 2249 kNoArgumentNames, | |
| 2250 kNumArgumentsChecked); | |
| 2251 } | |
| 2252 | |
| 2253 | |
| 2254 void CodeGenerator::VisitBinaryOpNode(BinaryOpNode* node) { | |
| 2255 if ((node->kind() == Token::kAND) || (node->kind() == Token::kOR)) { | |
| 2256 // Operators "&&" and "||" cannot be overloaded, therefore inline them | |
| 2257 // instead of calling the operator. | |
| 2258 GenerateLogicalAndOrOp(node); | |
| 2259 return; | |
| 2260 } | |
| 2261 node->left()->Visit(this); | |
| 2262 node->right()->Visit(this); | |
| 2263 MarkDeoptPoint(node->id(), node->token_pos()); | |
| 2264 GenerateBinaryOperatorCall(node->id(), node->token_pos(), node->Name()); | |
| 2265 if (IsResultNeeded(node)) { | |
| 2266 __ pushl(EAX); | |
| 2267 } | |
| 2268 } | |
| 2269 | |
| 2270 | |
| 2271 void CodeGenerator::VisitStringConcatNode(StringConcatNode* node) { | |
| 2272 const String& cls_name = String::Handle(String::NewSymbol("StringBase")); | |
| 2273 const Library& core_lib = Library::Handle( | |
| 2274 Isolate::Current()->object_store()->core_library()); | |
| 2275 const Class& cls = Class::Handle(core_lib.LookupClass(cls_name)); | |
| 2276 ASSERT(!cls.IsNull()); | |
| 2277 const String& func_name = String::Handle(String::NewSymbol("_interpolate")); | |
| 2278 const int number_of_parameters = 1; | |
| 2279 const Function& interpol_func = Function::ZoneHandle( | |
| 2280 Resolver::ResolveStatic(cls, func_name, | |
| 2281 number_of_parameters, | |
| 2282 Array::Handle(), | |
| 2283 Resolver::kIsQualified)); | |
| 2284 ASSERT(!interpol_func.IsNull()); | |
| 2285 | |
| 2286 // First try to concatenate and canonicalize the values at compile time. | |
| 2287 bool compile_time_interpolation = true; | |
| 2288 Array& literals = Array::Handle(Array::New(node->values()->length())); | |
| 2289 for (int i = 0; i < node->values()->length(); i++) { | |
| 2290 if (node->values()->ElementAt(i)->IsLiteralNode()) { | |
| 2291 LiteralNode* lit = node->values()->ElementAt(i)->AsLiteralNode(); | |
| 2292 literals.SetAt(i, lit->literal()); | |
| 2293 } else { | |
| 2294 compile_time_interpolation = false; | |
| 2295 break; | |
| 2296 } | |
| 2297 } | |
| 2298 if (compile_time_interpolation) { | |
| 2299 if (!IsResultNeeded(node)) { | |
| 2300 return; | |
| 2301 } | |
| 2302 // Build argument array to pass to the interpolation function. | |
| 2303 GrowableArray<const Object*> interpolate_arg; | |
| 2304 interpolate_arg.Add(&literals); | |
| 2305 const Array& kNoArgumentNames = Array::Handle(); | |
| 2306 // Call the interpolation function. | |
| 2307 String& concatenated = String::ZoneHandle(); | |
| 2308 concatenated ^= DartEntry::InvokeStatic(interpol_func, | |
| 2309 interpolate_arg, | |
| 2310 kNoArgumentNames); | |
| 2311 if (concatenated.IsUnhandledException()) { | |
| 2312 // TODO(hausner): Shouldn't we generate a throw? | |
| 2313 // Then remove unused CodeGenerator::ErrorMsg(). | |
| 2314 ErrorMsg(node->token_pos(), | |
| 2315 "Exception thrown in CodeGenerator::VisitStringConcatNode"); | |
| 2316 } | |
| 2317 ASSERT(!concatenated.IsNull()); | |
| 2318 concatenated = String::NewSymbol(concatenated); | |
| 2319 | |
| 2320 __ LoadObject(EAX, concatenated); | |
| 2321 __ pushl(EAX); | |
| 2322 return; | |
| 2323 } | |
| 2324 | |
| 2325 // Could not concatenate at compile time, generate a call to | |
| 2326 // interpolation function. | |
| 2327 ArgumentListNode* interpol_arg = new ArgumentListNode(node->token_pos()); | |
| 2328 interpol_arg->Add(node->values()); | |
| 2329 node->values()->Visit(this); | |
| 2330 __ LoadObject(ECX, interpol_func); | |
| 2331 __ LoadObject(EDX, ArgumentsDescriptor(interpol_arg->length(), | |
| 2332 interpol_arg->names())); | |
| 2333 GenerateCall(node->token_pos(), | |
| 2334 &StubCode::CallStaticFunctionLabel(), | |
| 2335 PcDescriptors::kFuncCall); | |
| 2336 __ addl(ESP, Immediate(interpol_arg->length() * kWordSize)); | |
| 2337 // Result is in EAX. | |
| 2338 if (IsResultNeeded(node)) { | |
| 2339 __ pushl(EAX); | |
| 2340 } | |
| 2341 } | |
| 2342 | |
| 2343 | |
| 2344 void CodeGenerator::VisitInstanceCallNode(InstanceCallNode* node) { | |
| 2345 const int number_of_arguments = node->arguments()->length() + 1; | |
| 2346 // Compute the receiver object and pass it as first argument to call. | |
| 2347 node->receiver()->Visit(this); | |
| 2348 // Now compute rest of the arguments to the call. | |
| 2349 node->arguments()->Visit(this); | |
| 2350 // Some method may be inlined using type feedback, therefore this may be a | |
| 2351 // deoptimization point. | |
| 2352 MarkDeoptPoint(node->id(), node->token_pos()); | |
| 2353 const int kNumArgumentsChecked = 1; | |
| 2354 GenerateInstanceCall(node->id(), | |
| 2355 node->token_pos(), | |
| 2356 node->function_name(), | |
| 2357 number_of_arguments, | |
| 2358 node->arguments()->names(), | |
| 2359 kNumArgumentsChecked); | |
| 2360 // Result is in EAX. | |
| 2361 if (IsResultNeeded(node)) { | |
| 2362 __ pushl(EAX); | |
| 2363 } | |
| 2364 } | |
| 2365 | |
| 2366 | |
| 2367 void CodeGenerator::VisitStaticCallNode(StaticCallNode* node) { | |
| 2368 node->arguments()->Visit(this); | |
| 2369 __ LoadObject(ECX, node->function()); | |
| 2370 __ LoadObject(EDX, ArgumentsDescriptor(node->arguments()->length(), | |
| 2371 node->arguments()->names())); | |
| 2372 GenerateCall(node->token_pos(), | |
| 2373 &StubCode::CallStaticFunctionLabel(), | |
| 2374 PcDescriptors::kFuncCall); | |
| 2375 __ addl(ESP, Immediate(node->arguments()->length() * kWordSize)); | |
| 2376 // Result is in EAX. | |
| 2377 if (IsResultNeeded(node)) { | |
| 2378 __ pushl(EAX); | |
| 2379 } | |
| 2380 } | |
| 2381 | |
| 2382 | |
| 2383 void CodeGenerator::VisitClosureCallNode(ClosureCallNode* node) { | |
| 2384 // The spec states that the closure is evaluated before the arguments. | |
| 2385 // Preserve the current context, since it will be overridden by the closure | |
| 2386 // context during the call. | |
| 2387 __ pushl(CTX); | |
| 2388 // Compute the closure object and pass it as first argument to the stub. | |
| 2389 node->closure()->Visit(this); | |
| 2390 // Now compute the arguments to the call. | |
| 2391 node->arguments()->Visit(this); | |
| 2392 // Set up the number of arguments (excluding the closure) to the ClosureCall | |
| 2393 // stub which will setup the closure context and jump to the entrypoint of the | |
| 2394 // closure function (the function will be compiled if it has not already been | |
| 2395 // compiled). | |
| 2396 // NOTE: The stub accesses the closure before the parameter list. | |
| 2397 __ LoadObject(EDX, ArgumentsDescriptor(node->arguments()->length(), | |
| 2398 node->arguments()->names())); | |
| 2399 GenerateCall(node->token_pos(), | |
| 2400 &StubCode::CallClosureFunctionLabel(), | |
| 2401 PcDescriptors::kOther); | |
| 2402 __ addl(ESP, Immediate((node->arguments()->length() + 1) * kWordSize)); | |
| 2403 // Restore the context. | |
| 2404 __ popl(CTX); | |
| 2405 // Result is in EAX. | |
| 2406 if (IsResultNeeded(node)) { | |
| 2407 __ pushl(EAX); | |
| 2408 } | |
| 2409 } | |
| 2410 | |
| 2411 | |
| 2412 // Pushes either the instantiator or null. | |
| 2413 // Pushes the type arguments of the instantiator on the stack. | |
| 2414 // Destroys EBX, ECX. | |
| 2415 void CodeGenerator::GenerateInstantiatorTypeArguments(intptr_t token_pos, | |
| 2416 bool push_instantiator) { | |
| 2417 if (push_instantiator) { | |
| 2418 const Immediate raw_null = | |
| 2419 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 2420 __ pushl(raw_null); // Initial instantiator. | |
| 2421 } | |
| 2422 const Class& instantiator_class = Class::Handle( | |
| 2423 parsed_function().function().owner()); | |
| 2424 if (instantiator_class.NumTypeParameters() == 0) { | |
| 2425 // The type arguments are compile time constants. | |
| 2426 AbstractTypeArguments& type_arguments = AbstractTypeArguments::ZoneHandle(); | |
| 2427 // TODO(regis): Temporary type should be allocated in new gen heap. | |
| 2428 Type& type = Type::Handle( | |
| 2429 Type::New(instantiator_class, type_arguments, token_pos)); | |
| 2430 type ^= ClassFinalizer::FinalizeType( | |
| 2431 instantiator_class, type, ClassFinalizer::kFinalizeWellFormed); | |
| 2432 type_arguments = type.arguments(); | |
| 2433 __ PushObject(type_arguments); | |
| 2434 } else { | |
| 2435 ASSERT(parsed_function().instantiator() != NULL); | |
| 2436 parsed_function().instantiator()->Visit(this); | |
| 2437 Function& outer_function = | |
| 2438 Function::Handle(parsed_function().function().raw()); | |
| 2439 while (outer_function.IsLocalFunction()) { | |
| 2440 outer_function = outer_function.parent_function(); | |
| 2441 } | |
| 2442 if (!outer_function.IsFactory()) { | |
| 2443 if (push_instantiator) { | |
| 2444 __ popl(EBX); // Pop instantiator. | |
| 2445 __ popl(ECX); // Discard null instantiator. | |
| 2446 __ pushl(EBX); // Replace it with the real one. | |
| 2447 } else { | |
| 2448 __ popl(EBX); // Pop instantiator. | |
| 2449 } | |
| 2450 // EBX: instantiator. | |
| 2451 // The instantiator is the receiver of the caller, which is not a factory. | |
| 2452 // The receiver cannot be null; extract its AbstractTypeArguments object. | |
| 2453 // Note that in the factory case, the instantiator is the first parameter | |
| 2454 // of the factory, i.e. already an AbstractTypeArguments object. | |
| 2455 intptr_t type_arguments_instance_field_offset = | |
| 2456 instantiator_class.type_arguments_instance_field_offset(); | |
| 2457 ASSERT(type_arguments_instance_field_offset != Class::kNoTypeArguments); | |
| 2458 __ movl(EBX, FieldAddress(EBX, type_arguments_instance_field_offset)); | |
| 2459 __ pushl(EBX); | |
| 2460 } | |
| 2461 } | |
| 2462 } | |
| 2463 | |
| 2464 | |
| 2465 // Pushes the type arguments on the stack in preparation of an allocation call. | |
| 2466 // If instantiate_type_arguments is true, the instantiated type arguments | |
| 2467 // are pushed on the stack (after an instantiation run time call, if necessary). | |
| 2468 // If instantiate_type_arguments is false, the (possibly uninstantiated) type | |
| 2469 // arguments are pushed on the stack, as well as the type arguments of the | |
| 2470 // instantiator (or the special kNoInstantiator Smi marker, if the type | |
| 2471 // arguments are instantiated). | |
| 2472 void CodeGenerator::GenerateTypeArguments( | |
| 2473 intptr_t node_id, | |
| 2474 intptr_t token_pos, | |
| 2475 const AbstractTypeArguments& type_arguments, | |
| 2476 bool instantiate_type_arguments) { | |
| 2477 const Immediate raw_null = | |
| 2478 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 2479 if (type_arguments.IsNull() || type_arguments.IsInstantiated()) { | |
| 2480 // The type arguments are instantiated. | |
| 2481 __ PushObject(type_arguments); | |
| 2482 if (!instantiate_type_arguments) { | |
| 2483 // The type arguments of the instantiator are not needed, since the | |
| 2484 // type arguments are instantiated. | |
| 2485 __ pushl(Immediate(Smi::RawValue(StubCode::kNoInstantiator))); | |
| 2486 } | |
| 2487 } else { | |
| 2488 // The type arguments are uninstantiated. | |
| 2489 const bool kPushInstantiator = false; | |
| 2490 GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator); | |
| 2491 __ popl(EAX); // Pop instantiator. | |
| 2492 // EAX is the instantiator AbstractTypeArguments object (or null). | |
| 2493 // If the instantiator is null and if the type argument vector | |
| 2494 // instantiated from null becomes a vector of Dynamic, then use null as | |
| 2495 // the type arguments. | |
| 2496 Label type_arguments_instantiated; | |
| 2497 const intptr_t len = type_arguments.Length(); | |
| 2498 if (type_arguments.IsRawInstantiatedRaw(len)) { | |
| 2499 __ cmpl(EAX, raw_null); | |
| 2500 __ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump); | |
| 2501 } | |
| 2502 // Instantiate non-null type arguments. | |
| 2503 if (type_arguments.IsUninstantiatedIdentity()) { | |
| 2504 // Check if the instantiator type argument vector is a TypeArguments of a | |
| 2505 // matching length and, if so, use it as the instantiated type_arguments. | |
| 2506 // No need to check RAX for null (again), because a null instance will | |
| 2507 // have the wrong class (Null instead of TypeArguments). | |
| 2508 Label type_arguments_uninstantiated; | |
| 2509 __ CompareClassId(EAX, kTypeArguments, ECX); | |
| 2510 __ j(NOT_EQUAL, &type_arguments_uninstantiated, Assembler::kNearJump); | |
| 2511 __ cmpl(FieldAddress(EAX, TypeArguments::length_offset()), | |
| 2512 Immediate(Smi::RawValue(len))); | |
| 2513 __ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump); | |
| 2514 __ Bind(&type_arguments_uninstantiated); | |
| 2515 } | |
| 2516 if (instantiate_type_arguments) { | |
| 2517 // A runtime call to instantiate the type arguments is required. | |
| 2518 __ PushObject(Object::ZoneHandle()); // Make room for the result. | |
| 2519 __ PushObject(type_arguments); | |
| 2520 __ pushl(EAX); // Push instantiator type arguments. | |
| 2521 GenerateCallRuntime(node_id, | |
| 2522 token_pos, | |
| 2523 kInstantiateTypeArgumentsRuntimeEntry); | |
| 2524 __ popl(EAX); // Pop instantiator type arguments. | |
| 2525 __ popl(EAX); // Pop uninstantiated type arguments. | |
| 2526 __ popl(EAX); // Pop instantiated type arguments. | |
| 2527 __ Bind(&type_arguments_instantiated); | |
| 2528 __ pushl(EAX); // Instantiated type arguments. | |
| 2529 } else { | |
| 2530 // The allocation stub will instantiate the type arguments. | |
| 2531 __ PushObject(type_arguments); | |
| 2532 __ pushl(EAX); // Instantiator type arguments. | |
| 2533 Label type_arguments_pushed; | |
| 2534 __ jmp(&type_arguments_pushed, Assembler::kNearJump); | |
| 2535 | |
| 2536 __ Bind(&type_arguments_instantiated); | |
| 2537 __ pushl(EAX); // Instantiated type arguments. | |
| 2538 __ pushl(Immediate(Smi::RawValue(StubCode::kNoInstantiator))); | |
| 2539 __ Bind(&type_arguments_pushed); | |
| 2540 } | |
| 2541 } | |
| 2542 } | |
| 2543 | |
| 2544 | |
| 2545 void CodeGenerator::VisitConstructorCallNode(ConstructorCallNode* node) { | |
| 2546 if (node->constructor().IsFactory()) { | |
| 2547 const bool instantiate_type_arguments = true; // First argument to factory. | |
| 2548 GenerateTypeArguments(node->id(), | |
| 2549 node->token_pos(), | |
| 2550 node->type_arguments(), | |
| 2551 instantiate_type_arguments); | |
| 2552 // The top of stack is an instantiated AbstractTypeArguments object | |
| 2553 // (or null). | |
| 2554 int num_args = node->arguments()->length() + 1; // +1 to include type args. | |
| 2555 node->arguments()->Visit(this); | |
| 2556 // Call the factory. | |
| 2557 __ LoadObject(ECX, node->constructor()); | |
| 2558 __ LoadObject(EDX, ArgumentsDescriptor(num_args, | |
| 2559 node->arguments()->names())); | |
| 2560 GenerateCall(node->token_pos(), | |
| 2561 &StubCode::CallStaticFunctionLabel(), | |
| 2562 PcDescriptors::kFuncCall); | |
| 2563 // Factory constructor returns object in EAX. | |
| 2564 __ addl(ESP, Immediate(num_args * kWordSize)); | |
| 2565 if (IsResultNeeded(node)) { | |
| 2566 __ pushl(EAX); | |
| 2567 } | |
| 2568 return; | |
| 2569 } | |
| 2570 | |
| 2571 const Class& cls = Class::ZoneHandle(node->constructor().owner()); | |
| 2572 const bool requires_type_arguments = cls.HasTypeArguments(); | |
| 2573 const bool instantiate_type_arguments = false; // Done in stub or runtime. | |
| 2574 if (requires_type_arguments) { | |
| 2575 GenerateTypeArguments(node->id(), | |
| 2576 node->token_pos(), | |
| 2577 node->type_arguments(), | |
| 2578 instantiate_type_arguments); | |
| 2579 } | |
| 2580 | |
| 2581 // If cls is parameterized, the type arguments and the instantiator's | |
| 2582 // type arguments are on the stack. | |
| 2583 // In checked mode, if the type arguments are uninstantiated, they may need to | |
| 2584 // be checked against declared bounds at run time. | |
| 2585 Error& malformed_error = Error::Handle(); | |
| 2586 if (FLAG_enable_type_checks && | |
| 2587 requires_type_arguments && | |
| 2588 !node->type_arguments().IsNull() && | |
| 2589 !node->type_arguments().IsInstantiated() && | |
| 2590 !node->type_arguments().IsWithinBoundsOf(cls, | |
| 2591 node->type_arguments(), | |
| 2592 &malformed_error)) { | |
| 2593 // The uninstantiated type arguments cannot be verified to be within their | |
| 2594 // bounds at compile time, so verify them at runtime. | |
| 2595 // Although the type arguments may be uninstantiated at compile time, they | |
| 2596 // may represent the identity vector and may be replaced by the instantiated | |
| 2597 // type arguments of the instantiator at run time. | |
| 2598 __ popl(ECX); // Pop instantiator type arguments. | |
| 2599 __ popl(EAX); // Pop type arguments. | |
| 2600 | |
| 2601 // Push the result place holder initialized to NULL. | |
| 2602 __ PushObject(Object::ZoneHandle()); | |
| 2603 __ pushl(Immediate(Smi::RawValue(node->token_pos()))); | |
| 2604 __ PushObject(cls); | |
| 2605 __ pushl(EAX); // Push type arguments. | |
| 2606 __ pushl(ECX); // Push instantiator type arguments. | |
| 2607 GenerateCallRuntime(node->id(), | |
| 2608 node->token_pos(), | |
| 2609 kAllocateObjectWithBoundsCheckRuntimeEntry); | |
| 2610 __ popl(ECX); // Pop instantiator type arguments. | |
| 2611 __ popl(ECX); // Pop type arguments. | |
| 2612 __ popl(ECX); // Pop class. | |
| 2613 __ popl(ECX); // Pop source location. | |
| 2614 __ popl(EAX); // Pop new instance. | |
| 2615 } else { | |
| 2616 const Code& stub = Code::Handle(StubCode::GetAllocationStubForClass(cls)); | |
| 2617 const ExternalLabel label(cls.ToCString(), stub.EntryPoint()); | |
| 2618 GenerateCall(node->token_pos(), &label, PcDescriptors::kOther); | |
| 2619 if (requires_type_arguments) { | |
| 2620 __ popl(ECX); // Pop instantiator type arguments. | |
| 2621 __ popl(ECX); // Pop type arguments. | |
| 2622 } | |
| 2623 } | |
| 2624 | |
| 2625 if (IsResultNeeded(node)) { | |
| 2626 __ pushl(EAX); // Set up return value from allocate. | |
| 2627 } | |
| 2628 | |
| 2629 // First argument(this) for constructor call which follows. | |
| 2630 __ pushl(EAX); | |
| 2631 // Second argument is the implicit construction phase parameter. | |
| 2632 // Run both the constructor initializer list and the constructor body. | |
| 2633 __ pushl(Immediate(Smi::RawValue(Function::kCtorPhaseAll))); | |
| 2634 | |
| 2635 // Now setup rest of the arguments for the constructor call. | |
| 2636 node->arguments()->Visit(this); | |
| 2637 | |
| 2638 // Call the constructor. | |
| 2639 // +2 to include implicit receiver and phase arguments. | |
| 2640 int num_args = node->arguments()->length() + 2; | |
| 2641 __ LoadObject(ECX, node->constructor()); | |
| 2642 __ LoadObject(EDX, ArgumentsDescriptor(num_args, node->arguments()->names())); | |
| 2643 GenerateCall(node->token_pos(), | |
| 2644 &StubCode::CallStaticFunctionLabel(), | |
| 2645 PcDescriptors::kFuncCall); | |
| 2646 // Constructors do not return any value. | |
| 2647 | |
| 2648 // Pop out all the other arguments on the stack. | |
| 2649 __ addl(ESP, Immediate(num_args * kWordSize)); | |
| 2650 } | |
| 2651 | |
| 2652 | |
| 2653 // Expects receiver on stack, returns result in EAX.. | |
| 2654 void CodeGenerator::GenerateInstanceGetterCall(intptr_t node_id, | |
| 2655 intptr_t token_pos, | |
| 2656 const String& field_name) { | |
| 2657 const String& getter_name = | |
| 2658 String::ZoneHandle(Field::GetterSymbol(field_name)); | |
| 2659 const int kNumberOfArguments = 1; | |
| 2660 const Array& kNoArgumentNames = Array::Handle(); | |
| 2661 const int kNumArgumentsChecked = 1; | |
| 2662 GenerateInstanceCall(node_id, | |
| 2663 token_pos, | |
| 2664 getter_name, | |
| 2665 kNumberOfArguments, | |
| 2666 kNoArgumentNames, | |
| 2667 kNumArgumentsChecked); | |
| 2668 } | |
| 2669 | |
| 2670 | |
| 2671 // Call to the instance getter. | |
| 2672 void CodeGenerator::VisitInstanceGetterNode(InstanceGetterNode* node) { | |
| 2673 node->receiver()->Visit(this); | |
| 2674 MarkDeoptPoint(node->id(), node->token_pos()); | |
| 2675 GenerateInstanceGetterCall(node->id(), | |
| 2676 node->token_pos(), | |
| 2677 node->field_name()); | |
| 2678 if (IsResultNeeded(node)) { | |
| 2679 __ pushl(EAX); | |
| 2680 } | |
| 2681 } | |
| 2682 | |
| 2683 | |
| 2684 // Expects receiver and value on stack. | |
| 2685 void CodeGenerator::GenerateInstanceSetterCall(intptr_t node_id, | |
| 2686 intptr_t token_pos, | |
| 2687 const String& field_name) { | |
| 2688 const String& setter_name = | |
| 2689 String::ZoneHandle(Field::SetterSymbol(field_name)); | |
| 2690 const int kNumberOfArguments = 2; // receiver + value. | |
| 2691 const Array& kNoArgumentNames = Array::Handle(); | |
| 2692 const int kNumArgumentsChecked = 1; | |
| 2693 GenerateInstanceCall(node_id, | |
| 2694 token_pos, | |
| 2695 setter_name, | |
| 2696 kNumberOfArguments, | |
| 2697 kNoArgumentNames, | |
| 2698 kNumArgumentsChecked); | |
| 2699 } | |
| 2700 | |
| 2701 | |
| 2702 // The call to the instance setter implements the assignment to a field. | |
| 2703 // The result of the assignment to a field is the value being stored. | |
| 2704 void CodeGenerator::VisitInstanceSetterNode(InstanceSetterNode* node) { | |
| 2705 // Compute the receiver object and pass it as first argument to call. | |
| 2706 node->receiver()->Visit(this); | |
| 2707 node->value()->Visit(this); | |
| 2708 MarkDeoptPoint(node->id(), node->token_pos()); | |
| 2709 if (IsResultNeeded(node)) { | |
| 2710 __ popl(EAX); // value. | |
| 2711 __ popl(EDX); // receiver. | |
| 2712 __ pushl(EAX); // Preserve value. | |
| 2713 __ pushl(EDX); // arg0: receiver. | |
| 2714 __ pushl(EAX); // arg1: value. | |
| 2715 } | |
| 2716 // It is not necessary to generate a type test of the assigned value here, | |
| 2717 // because the setter will check the type of its incoming arguments. | |
| 2718 GenerateInstanceSetterCall(node->id(), | |
| 2719 node->token_pos(), | |
| 2720 node->field_name()); | |
| 2721 } | |
| 2722 | |
| 2723 | |
| 2724 // Return result in EAX. | |
| 2725 void CodeGenerator::GenerateStaticGetterCall(intptr_t token_pos, | |
| 2726 const Class& field_class, | |
| 2727 const String& field_name) { | |
| 2728 const String& getter_name = String::Handle(Field::GetterName(field_name)); | |
| 2729 const Function& function = | |
| 2730 Function::ZoneHandle(field_class.LookupStaticFunction(getter_name)); | |
| 2731 ASSERT(!function.IsNull()); | |
| 2732 __ LoadObject(ECX, function); | |
| 2733 const int kNumberOfArguments = 0; | |
| 2734 const Array& kNoArgumentNames = Array::Handle(); | |
| 2735 __ LoadObject(EDX, ArgumentsDescriptor(kNumberOfArguments, kNoArgumentNames)); | |
| 2736 GenerateCall(token_pos, | |
| 2737 &StubCode::CallStaticFunctionLabel(), | |
| 2738 PcDescriptors::kFuncCall); | |
| 2739 // No arguments were pushed, hence nothing to pop. | |
| 2740 } | |
| 2741 | |
| 2742 | |
| 2743 // Call to static getter. | |
| 2744 void CodeGenerator::VisitStaticGetterNode(StaticGetterNode* node) { | |
| 2745 GenerateStaticGetterCall(node->token_pos(), | |
| 2746 node->cls(), | |
| 2747 node->field_name()); | |
| 2748 // Result is in EAX. | |
| 2749 if (IsResultNeeded(node)) { | |
| 2750 __ pushl(EAX); | |
| 2751 } | |
| 2752 } | |
| 2753 | |
| 2754 | |
| 2755 // Expects value on stack. | |
| 2756 void CodeGenerator::GenerateStaticSetterCall(intptr_t token_pos, | |
| 2757 const Class& field_class, | |
| 2758 const String& field_name) { | |
| 2759 const String& setter_name = String::Handle(Field::SetterName(field_name)); | |
| 2760 const Function& function = | |
| 2761 Function::ZoneHandle(field_class.LookupStaticFunction(setter_name)); | |
| 2762 ASSERT(!function.IsNull()); | |
| 2763 __ LoadObject(ECX, function); | |
| 2764 const int kNumberOfArguments = 1; // value. | |
| 2765 const Array& kNoArgumentNames = Array::Handle(); | |
| 2766 __ LoadObject(EDX, ArgumentsDescriptor(kNumberOfArguments, kNoArgumentNames)); | |
| 2767 GenerateCall(token_pos, | |
| 2768 &StubCode::CallStaticFunctionLabel(), | |
| 2769 PcDescriptors::kFuncCall); | |
| 2770 __ addl(ESP, Immediate(kNumberOfArguments * kWordSize)); | |
| 2771 } | |
| 2772 | |
| 2773 | |
| 2774 // The call to static setter implements assignment to a static field. | |
| 2775 // The result of the assignment is the value being stored. | |
| 2776 void CodeGenerator::VisitStaticSetterNode(StaticSetterNode* node) { | |
| 2777 node->value()->Visit(this); | |
| 2778 if (IsResultNeeded(node)) { | |
| 2779 // Preserve the original value when returning from setter. | |
| 2780 __ movl(EAX, Address(ESP, 0)); | |
| 2781 __ pushl(EAX); // arg0: value. | |
| 2782 } | |
| 2783 // It is not necessary to generate a type test of the assigned value here, | |
| 2784 // because the setter will check the type of its incoming arguments. | |
| 2785 GenerateStaticSetterCall(node->token_pos(), | |
| 2786 node->cls(), | |
| 2787 node->field_name()); | |
| 2788 } | |
| 2789 | |
| 2790 | |
| 2791 void CodeGenerator::VisitNativeBodyNode(NativeBodyNode* node) { | |
| 2792 intptr_t argument_count = node->argument_count(); | |
| 2793 if (node->is_native_instance_closure()) { | |
| 2794 argument_count += 1; | |
| 2795 } | |
| 2796 | |
| 2797 // Push the result place holder initialized to NULL. | |
| 2798 __ PushObject(Object::ZoneHandle()); | |
| 2799 // Pass a pointer to the first argument in EAX. | |
| 2800 if (!node->has_optional_parameters() && !node->is_native_instance_closure()) { | |
| 2801 __ leal(EAX, Address(EBP, (1 + argument_count) * kWordSize)); | |
| 2802 } else { | |
| 2803 __ leal(EAX, | |
| 2804 Address(EBP, ParsedFunction::kFirstLocalSlotIndex * kWordSize)); | |
| 2805 } | |
| 2806 __ movl(ECX, Immediate(reinterpret_cast<uword>(node->native_c_function()))); | |
| 2807 __ movl(EDX, Immediate(argument_count)); | |
| 2808 GenerateCall(node->token_pos(), | |
| 2809 &StubCode::CallNativeCFunctionLabel(), | |
| 2810 PcDescriptors::kOther); | |
| 2811 // Result is on the stack. | |
| 2812 if (!IsResultNeeded(node)) { | |
| 2813 __ popl(EAX); | |
| 2814 } | |
| 2815 } | |
| 2816 | |
| 2817 | |
| 2818 void CodeGenerator::VisitCatchClauseNode(CatchClauseNode* node) { | |
| 2819 // NOTE: The implicit variables ':saved_context', ':exception_var' | |
| 2820 // and ':stacktrace_var' can never be captured variables. | |
| 2821 // Restore CTX from local variable ':saved_context'. | |
| 2822 GenerateLoadVariable(CTX, node->context_var()); | |
| 2823 | |
| 2824 // Restore ESP from EBP as we are coming from a throw and the code for | |
| 2825 // popping arguments has not been run. | |
| 2826 ASSERT(locals_space_size() >= 0); | |
| 2827 intptr_t offset_size = -locals_space_size() + kLocalsOffsetFromFP; | |
| 2828 __ leal(ESP, Address(EBP, offset_size)); | |
| 2829 | |
| 2830 // The JumpToExceptionHandler trampoline code sets up | |
| 2831 // - the exception object in EAX (kExceptionObjectReg) | |
| 2832 // - the stacktrace object in register EDX (kStackTraceObjectReg) | |
| 2833 // We now setup the exception object and the trace object | |
| 2834 // so that the handler code has access to these objects. | |
| 2835 GenerateStoreVariable(node->exception_var(), | |
| 2836 kExceptionObjectReg, | |
| 2837 kNoRegister); | |
| 2838 GenerateStoreVariable(node->stacktrace_var(), | |
| 2839 kStackTraceObjectReg, | |
| 2840 kNoRegister); | |
| 2841 | |
| 2842 // Now generate code for the catch handler block. | |
| 2843 node->VisitChildren(this); | |
| 2844 } | |
| 2845 | |
| 2846 | |
| 2847 void CodeGenerator::VisitTryCatchNode(TryCatchNode* node) { | |
| 2848 CodeGeneratorState codegen_state(this); | |
| 2849 int outer_try_index = state()->try_index(); | |
| 2850 // We are about to generate code for a new try block, generate an | |
| 2851 // unique 'try index' for this block and set that try index in | |
| 2852 // the code generator state. | |
| 2853 int try_index = generate_next_try_index(); | |
| 2854 state()->set_try_index(try_index); | |
| 2855 exception_handlers_list_->AddHandler(try_index, -1); | |
| 2856 | |
| 2857 // Preserve CTX into local variable '%saved_context'. | |
| 2858 GenerateStoreVariable(node->context_var(), CTX, kNoRegister); | |
| 2859 | |
| 2860 node->try_block()->Visit(this); | |
| 2861 | |
| 2862 // We are done generating code for the try block. | |
| 2863 ASSERT(state()->try_index() > CatchClauseNode::kInvalidTryIndex); | |
| 2864 ASSERT(try_index == state()->try_index()); | |
| 2865 state()->set_try_index(outer_try_index); | |
| 2866 | |
| 2867 CatchClauseNode* catch_block = node->catch_block(); | |
| 2868 if (catch_block != NULL) { | |
| 2869 // Jump over the catch handler block, when exceptions are thrown we | |
| 2870 // will end up at the next instruction. | |
| 2871 __ jmp(node->end_catch_label()->continue_label()); | |
| 2872 | |
| 2873 // Set the corresponding try index for this catch block so | |
| 2874 // that we can set the appropriate handler pc when we generate | |
| 2875 // code for this catch block. | |
| 2876 catch_block->set_try_index(try_index); | |
| 2877 | |
| 2878 // Set the handler pc for this try index in the exception handler | |
| 2879 // table. | |
| 2880 exception_handlers_list_->SetPcOffset(try_index, assembler_->CodeSize()); | |
| 2881 | |
| 2882 // Generate code for the catch block. | |
| 2883 catch_block->Visit(this); | |
| 2884 | |
| 2885 // Bind the end of catch blocks label here. | |
| 2886 __ Bind(node->end_catch_label()->continue_label()); | |
| 2887 } | |
| 2888 | |
| 2889 // Generate code for the finally block if one exists. | |
| 2890 if (node->finally_block() != NULL) { | |
| 2891 node->finally_block()->Visit(this); | |
| 2892 } | |
| 2893 } | |
| 2894 | |
| 2895 | |
| 2896 void CodeGenerator::VisitThrowNode(ThrowNode* node) { | |
| 2897 node->exception()->Visit(this); | |
| 2898 // Exception object is on TOS. | |
| 2899 if (node->stacktrace() != NULL) { | |
| 2900 node->stacktrace()->Visit(this); | |
| 2901 GenerateCallRuntime(node->id(), node->token_pos(), kReThrowRuntimeEntry); | |
| 2902 } else { | |
| 2903 GenerateCallRuntime(node->id(), node->token_pos(), kThrowRuntimeEntry); | |
| 2904 } | |
| 2905 // We should never return here. | |
| 2906 __ int3(); | |
| 2907 } | |
| 2908 | |
| 2909 | |
| 2910 void CodeGenerator::VisitInlinedFinallyNode(InlinedFinallyNode* node) { | |
| 2911 int try_index = state()->try_index(); | |
| 2912 if (try_index >= 0) { | |
| 2913 // We are about to generate code for an inlined finally block. Exceptions | |
| 2914 // thrown in this block of code should be treated as though they are | |
| 2915 // thrown not from the current try block but the outer try block if any. | |
| 2916 // the code generator state. | |
| 2917 state()->set_try_index((try_index - 1)); | |
| 2918 } | |
| 2919 | |
| 2920 // Restore CTX from local variable ':saved_context'. | |
| 2921 GenerateLoadVariable(CTX, node->context_var()); | |
| 2922 node->finally_block()->Visit(this); | |
| 2923 | |
| 2924 if (try_index >= 0) { | |
| 2925 state()->set_try_index(try_index); | |
| 2926 } | |
| 2927 } | |
| 2928 | |
| 2929 | |
| 2930 void CodeGenerator::GenerateCall(intptr_t token_pos, | |
| 2931 const ExternalLabel* ext_label, | |
| 2932 PcDescriptors::Kind desc_kind) { | |
| 2933 __ call(ext_label); | |
| 2934 AddCurrentDescriptor(desc_kind, AstNode::kNoId, token_pos); | |
| 2935 } | |
| 2936 | |
| 2937 | |
| 2938 void CodeGenerator::GenerateCallRuntime(intptr_t node_id, | |
| 2939 intptr_t token_pos, | |
| 2940 const RuntimeEntry& entry) { | |
| 2941 __ CallRuntime(entry); | |
| 2942 AddCurrentDescriptor(PcDescriptors::kOther, node_id, token_pos); | |
| 2943 } | |
| 2944 | |
| 2945 | |
| 2946 void CodeGenerator::MarkDeoptPoint(intptr_t node_id, | |
| 2947 intptr_t token_pos) { | |
| 2948 ASSERT(node_id != AstNode::kNoId); | |
| 2949 AddCurrentDescriptor(PcDescriptors::kDeopt, node_id, token_pos); | |
| 2950 } | |
| 2951 | |
| 2952 | |
| 2953 // Uses current pc position and try-index. | |
| 2954 void CodeGenerator::AddCurrentDescriptor(PcDescriptors::Kind kind, | |
| 2955 intptr_t node_id, | |
| 2956 intptr_t token_pos) { | |
| 2957 pc_descriptors_list_->AddDescriptor(kind, | |
| 2958 assembler_->CodeSize(), | |
| 2959 node_id, | |
| 2960 token_pos, | |
| 2961 state()->try_index()); | |
| 2962 } | |
| 2963 | |
| 2964 | |
| 2965 void CodeGenerator::ErrorMsg(intptr_t token_pos, const char* format, ...) { | |
| 2966 va_list args; | |
| 2967 va_start(args, format); | |
| 2968 const Class& cls = Class::Handle(parsed_function_.function().owner()); | |
| 2969 const Script& script = Script::Handle(cls.script()); | |
| 2970 const Error& error = Error::Handle( | |
| 2971 Parser::FormatError(script, token_pos, "Error", format, args)); | |
| 2972 va_end(args); | |
| 2973 Isolate::Current()->long_jump_base()->Jump(1, error); | |
| 2974 UNREACHABLE(); | |
| 2975 } | |
| 2976 | |
| 2977 } // namespace dart | |
| 2978 | |
| 2979 #endif // defined TARGET_ARCH_IA32 | |
| OLD | NEW |