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