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

Unified Diff: runtime/vm/code_generator_ia32.cc

Issue 10665038: Remove old code generator. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: runtime/vm/code_generator_ia32.cc
===================================================================
--- runtime/vm/code_generator_ia32.cc (revision 9089)
+++ runtime/vm/code_generator_ia32.cc (working copy)
@@ -1,2979 +0,0 @@
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-#include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32.
-#if defined(TARGET_ARCH_IA32)
-
-#include "vm/code_generator.h"
-
-#include "lib/error.h"
-#include "vm/assembler_macros.h"
-#include "vm/ast_printer.h"
-#include "vm/class_finalizer.h"
-#include "vm/code_descriptors.h"
-#include "vm/dart_entry.h"
-#include "vm/debugger.h"
-#include "vm/intrinsifier.h"
-#include "vm/longjump.h"
-#include "vm/object.h"
-#include "vm/object_store.h"
-#include "vm/parser.h"
-#include "vm/resolver.h"
-#include "vm/stub_code.h"
-
-namespace dart {
-
-DECLARE_FLAG(bool, print_ast);
-DECLARE_FLAG(bool, print_scopes);
-DECLARE_FLAG(bool, trace_functions);
-DEFINE_FLAG(bool, print_ic_in_optimized, false,
- "Debugging helper to identify potential performance pitfalls.");
-DECLARE_FLAG(int, optimization_counter_threshold);
-DECLARE_FLAG(bool, enable_type_checks);
-DECLARE_FLAG(bool, trace_compiler);
-DECLARE_FLAG(bool, intrinsify);
-DECLARE_FLAG(bool, trace_functions);
-
-
-#define __ assembler_->
-
-
-CodeGeneratorState::CodeGeneratorState(CodeGenerator* codegen)
- : StackResource(Isolate::Current()),
- codegen_(codegen),
- parent_(codegen->state()) {
- if (parent_ != NULL) {
- root_node_ = parent_->root_node_;
- current_try_index_ = parent_->current_try_index_;
- } else {
- root_node_ = NULL;
- current_try_index_ = CatchClauseNode::kInvalidTryIndex;
- }
- codegen_->set_state(this);
-}
-
-
-CodeGeneratorState::~CodeGeneratorState() {
- codegen_->set_state(parent_);
-}
-
-
-CodeGenerator::CodeGenerator(Assembler* assembler,
- const ParsedFunction& parsed_function)
- : assembler_(assembler),
- parsed_function_(parsed_function),
- locals_space_size_(-1),
- state_(NULL),
- pc_descriptors_list_(NULL),
- stackmap_builder_(NULL),
- exception_handlers_list_(NULL),
- try_index_(CatchClauseNode::kInvalidTryIndex),
- context_level_(0) {
- ASSERT(assembler_ != NULL);
- ASSERT(parsed_function.node_sequence() != NULL);
- ASSERT(Isolate::Current()->long_jump_base()->IsSafeToJump());
-}
-
-
-void CodeGenerator::InitGenerator() {
- pc_descriptors_list_ = new DescriptorList();
- // We do not build any stack maps in the unoptimizing compiler.
- exception_handlers_list_ = new ExceptionHandlerList();
-}
-
-
-void CodeGenerator::IntrinsifyGetter() {
- // TOS: return address.
- // +1 : receiver.
- // Sequence node has one return node, its input is oad field node.
- const SequenceNode& sequence_node = *parsed_function_.node_sequence();
- ASSERT(sequence_node.length() == 1);
- ASSERT(sequence_node.NodeAt(0)->IsReturnNode());
- const ReturnNode& return_node = *sequence_node.NodeAt(0)->AsReturnNode();
- ASSERT(return_node.value()->IsLoadInstanceFieldNode());
- const LoadInstanceFieldNode& load_node =
- *return_node.value()->AsLoadInstanceFieldNode();
- __ movl(EAX, Address(ESP, 1 * kWordSize));
- __ movl(EAX, FieldAddress(EAX, load_node.field().Offset()));
- __ ret();
-}
-
-
-void CodeGenerator::IntrinsifySetter() {
- // TOS: return address.
- // +1 : value
- // +2 : receiver.
- // Sequence node has one store node and one return NULL node.
- const SequenceNode& sequence_node = *parsed_function_.node_sequence();
- ASSERT(sequence_node.length() == 2);
- ASSERT(sequence_node.NodeAt(0)->IsStoreInstanceFieldNode());
- ASSERT(sequence_node.NodeAt(1)->IsReturnNode());
- const StoreInstanceFieldNode& store_node =
- *sequence_node.NodeAt(0)->AsStoreInstanceFieldNode();
- __ movl(EAX, Address(ESP, 2 * kWordSize)); // Receiver.
- __ movl(EBX, Address(ESP, 1 * kWordSize)); // Value.
- __ StoreIntoObject(EAX, FieldAddress(EAX, store_node.field().Offset()), EBX);
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ movl(EAX, raw_null);
- __ ret();
-}
-
-
-
-bool CodeGenerator::TryIntrinsify() {
- if (!CanOptimize()) return false;
- // Intrinsification skips arguments checks, therefore disable if in checked
- // mode.
- if (FLAG_intrinsify && !FLAG_trace_functions && !FLAG_enable_type_checks) {
- if ((parsed_function_.function().kind() == RawFunction::kImplicitGetter)) {
- IntrinsifyGetter();
- return true;
- }
- if ((parsed_function_.function().kind() == RawFunction::kImplicitSetter)) {
- IntrinsifySetter();
- return true;
- }
- }
- // Even if an intrinsified version of the function was successfully
- // generated, it may fall through to the non-intrinsified method body.
- if (!FLAG_trace_functions) {
- return Intrinsifier::Intrinsify(parsed_function().function(), assembler_);
- }
- return false;
-}
-
-
-bool CodeGenerator::IsResultNeeded(AstNode* node) const {
- return !state()->IsRootNode(node);
-}
-
-
-// NOTE: First 5 bytes of the code may be patched with a jump instruction. Do
-// not emit any objects in the first 5 bytes.
-void CodeGenerator::GenerateCode() {
- InitGenerator();
- CodeGeneratorState codegen_state(this);
- if (FLAG_print_scopes && FLAG_print_ast) {
- // Print the function scope before code generation.
- AstPrinter::PrintFunctionScope(parsed_function_);
- }
- if (FLAG_print_ast) {
- // Print the function ast before code generation.
- AstPrinter::PrintFunctionNodes(parsed_function_);
- }
- if (FLAG_trace_functions) {
- // Preserve ECX (ic-data array or object) and EDX (arguments descriptor).
- __ nop(2); // Make sure the code is patchable.
- __ pushl(ECX);
- __ pushl(EDX);
- const Function& function =
- Function::ZoneHandle(parsed_function_.function().raw());
- __ LoadObject(EAX, function);
- __ pushl(EAX);
- GenerateCallRuntime(AstNode::kNoId,
- 0,
- kTraceFunctionEntryRuntimeEntry);
- __ popl(EAX);
- __ popl(EDX);
- __ popl(ECX);
- }
-
- const bool code_generation_finished = TryIntrinsify();
- // In some cases intrinsifier can generate all code and no AST based
- // code generation is needed. In some cases slow-paths (e.g., overflows) are
- // implemented by the AST based code generation and 'code_generation_finished'
- // is false.
- if (!code_generation_finished) {
- GenerateEntryCode();
- if (FLAG_print_scopes) {
- // Print the function scope (again) after generating the prologue in order
- // to see annotations such as allocation indices of locals.
- if (FLAG_print_ast) {
- // Second printing.
- OS::Print("Annotated ");
- }
- AstPrinter::PrintFunctionScope(parsed_function_);
- }
- parsed_function_.node_sequence()->Visit(this);
- }
- // End of code.
- __ int3();
- GenerateDeferredCode();
-
- // Emit function patching code. This will be swapped with the first 5 bytes
- // at entry point.
- pc_descriptors_list_->AddDescriptor(PcDescriptors::kPatchCode,
- assembler_->CodeSize(),
- AstNode::kNoId,
- 0,
- -1);
- __ jmp(&StubCode::FixCallersTargetLabel());
-}
-
-
-void CodeGenerator::GenerateDeferredCode() {
-}
-
-
-void CodeGenerator::FinalizePcDescriptors(const Code& code) {
- ASSERT(pc_descriptors_list_ != NULL);
- const PcDescriptors& descriptors = PcDescriptors::Handle(
- pc_descriptors_list_->FinalizePcDescriptors(code.EntryPoint()));
- descriptors.Verify(parsed_function_.function().is_optimizable());
- code.set_pc_descriptors(descriptors);
-}
-
-
-void CodeGenerator::FinalizeStackmaps(const Code& code) {
- if (stackmap_builder_ == NULL) {
- // The unoptimizing compiler has no stack maps.
- code.set_stackmaps(Array::Handle());
- } else {
- // Finalize the stack map array and add it to the code object.
- code.set_stackmaps(
- Array::Handle(stackmap_builder_->FinalizeStackmaps(code)));
- }
-}
-
-
-void CodeGenerator::FinalizeVarDescriptors(const Code& code) {
- const LocalVarDescriptors& var_descs = LocalVarDescriptors::Handle(
- parsed_function_.node_sequence()->scope()->GetVarDescriptors(
- parsed_function_.function()));
- code.set_var_descriptors(var_descs);
-}
-
-
-void CodeGenerator::FinalizeExceptionHandlers(const Code& code) {
- ASSERT(exception_handlers_list_ != NULL);
- const ExceptionHandlers& handlers = ExceptionHandlers::Handle(
- exception_handlers_list_->FinalizeExceptionHandlers(code.EntryPoint()));
- code.set_exception_handlers(handlers);
-}
-
-
-void CodeGenerator::FinalizeComments(const Code& code) {
- code.set_comments(assembler_->GetCodeComments());
-}
-
-
-void CodeGenerator::GenerateLoadVariable(Register dst,
- const LocalVariable& variable) {
- if (variable.is_captured()) {
- // The variable lives in the context.
- intptr_t delta = context_level() - variable.owner()->context_level();
- ASSERT(delta >= 0);
- Register base = CTX;
- while (delta-- > 0) {
- __ movl(dst, FieldAddress(base, Context::parent_offset()));
- base = dst;
- }
- __ movl(dst,
- FieldAddress(base, Context::variable_offset(variable.index())));
- } else {
- // The variable lives in the current stack frame.
- __ movl(dst, Address(EBP, variable.index() * kWordSize));
- }
-}
-
-
-void CodeGenerator::GenerateStoreVariable(const LocalVariable& variable,
- Register src,
- Register scratch) {
- if (variable.is_captured()) {
- // The variable lives in the context.
- intptr_t delta = context_level() - variable.owner()->context_level();
- ASSERT(delta >= 0);
- Register base = CTX;
- while (delta-- > 0) {
- __ movl(scratch, FieldAddress(base, Context::parent_offset()));
- base = scratch;
- }
- __ StoreIntoObject(
- base,
- FieldAddress(base, Context::variable_offset(variable.index())),
- src);
- } else {
- // The variable lives in the current stack frame.
- __ movl(Address(EBP, variable.index() * kWordSize), src);
- }
-}
-
-
-void CodeGenerator::GeneratePushVariable(const LocalVariable& variable,
- Register scratch) {
- if (variable.is_captured()) {
- // The variable lives in the context.
- intptr_t delta = context_level() - variable.owner()->context_level();
- ASSERT(delta >= 0);
- Register base = CTX;
- while (delta-- > 0) {
- __ movl(scratch, FieldAddress(base, Context::parent_offset()));
- base = scratch;
- }
- __ pushl(FieldAddress(base, Context::variable_offset(variable.index())));
- } else {
- // The variable lives in the current stack frame.
- __ pushl(Address(EBP, variable.index() * kWordSize));
- }
-}
-
-
-void CodeGenerator::GenerateInstanceCall(
- intptr_t node_id,
- intptr_t token_pos,
- const String& function_name,
- int num_arguments,
- const Array& optional_arguments_names,
- intptr_t num_args_checked) {
- if (FLAG_print_ic_in_optimized && IsOptimizing()) {
- OS::Print("Generate IC in optimized code: id %d name: '%s'\n",
- node_id, function_name.ToCString());
- }
- ASSERT(num_args_checked > 0); // At least receiver check is necessary.
- // Set up the function name and number of arguments (including the receiver)
- // to the InstanceCall stub which will resolve the correct entrypoint for
- // the operator and call it.
- ICData& ic_data = ICData::ZoneHandle();
- ic_data = ICData::New(parsed_function().function(),
- function_name,
- node_id,
- num_args_checked);
- __ LoadObject(ECX, ic_data);
- __ LoadObject(EDX, ArgumentsDescriptor(num_arguments,
- optional_arguments_names));
- uword label_address = 0;
- switch (num_args_checked) {
- case 1:
- label_address = StubCode::OneArgCheckInlineCacheEntryPoint();
- break;
- case 2:
- label_address = StubCode::TwoArgsCheckInlineCacheEntryPoint();
- break;
- default:
- UNIMPLEMENTED();
- }
- ExternalLabel target_label("InlineCache", label_address);
-
- __ call(&target_label);
- AddCurrentDescriptor(PcDescriptors::kIcCall,
- node_id,
- token_pos);
- __ addl(ESP, Immediate(num_arguments * kWordSize));
-}
-
-
-// Input parameters:
-// ESP : points to return address.
-// ESP + 4 : address of last argument (arg n-1).
-// ESP + 4*n : address of first argument (arg 0).
-// EDX : arguments descriptor array.
-void CodeGenerator::GenerateEntryCode() {
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- const Function& function = parsed_function_.function();
- const bool is_instance_native_closure =
- function.is_native() && function.IsImplicitInstanceClosureFunction();
-
- // 1. Compute the frame size and enter the frame (reserving local space
- // for copied incoming and default arguments and stack-allocated local
- // variables).
- //
- // TODO(regis): We may give up reserving space on stack for args/locals
- // because pushes of initial values may be more effective than moves.
- LocalScope* scope = parsed_function_.node_sequence()->scope();
- const int num_fixed_params = function.num_fixed_parameters();
- const int num_opt_params = function.num_optional_parameters();
- const int num_copied_params = parsed_function_.copied_parameter_count();
- const int stack_slot_count =
- num_copied_params + parsed_function_.stack_local_count();
- set_locals_space_size(stack_slot_count * kWordSize);
- AssemblerMacros::EnterDartFrame(assembler_, locals_space_size());
-
- // 2. Optionally check if the number of arguments matches. We check the
- // number of passed arguments when we have to copy them due to the
- // presence of optional named parameters. No such checking code is
- // generated if only fixed parameters are declared, unless we are in debug
- // mode or unless we are compiling a closure.
- if (num_copied_params == 0) {
- ASSERT(num_opt_params == 0);
-#if defined(DEBUG)
- const bool check_arguments = true; // Always check arguments in debug mode.
-#else
- // The number of arguments passed to closure functions must always be
- // checked here, because no resolving stub (normally responsible for the
- // check) is involved in closure calls.
- const bool check_arguments = function.IsClosureFunction();
-#endif
- if (check_arguments) {
- // Check that num_fixed <= argc <= num_params.
- Label argc_in_range;
- // Total number of args is the first Smi in args descriptor array (EDX).
- __ movl(EAX, FieldAddress(EDX, Array::data_offset()));
- __ cmpl(EAX, Immediate(Smi::RawValue(num_fixed_params)));
- __ j(EQUAL, &argc_in_range, Assembler::kNearJump);
- if (function.IsClosureFunction()) {
- GenerateCallRuntime(AstNode::kNoId,
- 0,
- kClosureArgumentMismatchRuntimeEntry);
- } else {
- __ Stop("Wrong number of arguments");
- }
- __ Bind(&argc_in_range);
- }
- } else {
- int implicit_this_param_pos = is_instance_native_closure ? -1 : 0;
- ASSERT(parsed_function_.first_parameter_index() ==
- (ParsedFunction::kFirstLocalSlotIndex + implicit_this_param_pos));
- // Copy positional arguments.
- // Check that no fewer than num_fixed_params positional arguments are passed
- // in and that no more than num_params arguments are passed in.
- // Passed argument i at fp[1 + argc - i] copied to
- // fp[ParsedFunction::kFirstLocalSlotIndex - i].
- const int num_params = num_fixed_params + num_opt_params;
-
- // Total number of args is the first Smi in args descriptor array (EDX).
- __ movl(EBX, FieldAddress(EDX, Array::data_offset()));
- // Check that num_args <= num_params.
- Label wrong_num_arguments;
- __ cmpl(EBX, Immediate(Smi::RawValue(num_params)));
- __ j(GREATER, &wrong_num_arguments);
- // Number of positional args is the second Smi in descriptor array (EDX).
- __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize)));
- // Check that num_pos_args >= num_fixed_params.
- __ cmpl(ECX, Immediate(Smi::RawValue(num_fixed_params)));
- __ j(LESS, &wrong_num_arguments);
-
- // Since EBX and ECX are Smi, use TIMES_2 instead of TIMES_4.
- // Let EBX point to the last passed positional argument, i.e. to
- // fp[1 + num_args - (num_pos_args - 1)].
- __ subl(EBX, ECX);
- __ leal(EBX, Address(EBP, EBX, TIMES_2, 2 * kWordSize));
- // Let EDI point to the last copied positional argument, i.e. to
- // fp[ParsedFunction::kFirstLocalSlotIndex - (num_pos_args - 1)].
- const int index =
- ParsedFunction::kFirstLocalSlotIndex + 1 + implicit_this_param_pos;
- // First copy captured receiver if function is an implicit native closure.
- if (is_instance_native_closure) {
- __ movl(EAX, FieldAddress(CTX, Context::variable_offset(0)));
- __ movl(Address(EBP, (index * kWordSize)), EAX);
- }
- __ leal(EDI, Address(EBP, (index * kWordSize)));
- __ subl(EDI, ECX); // ECX is a Smi, subtract twice for TIMES_4 scaling.
- __ subl(EDI, ECX);
- __ SmiUntag(ECX);
- Label loop, loop_condition;
- __ jmp(&loop_condition, Assembler::kNearJump);
- // We do not use the final allocation index of the variable here, i.e.
- // scope->VariableAt(i)->index(), because captured variables still need
- // to be copied to the context that is not yet allocated.
- const Address argument_addr(EBX, ECX, TIMES_4, 0);
- const Address copy_addr(EDI, ECX, TIMES_4, 0);
- __ Bind(&loop);
- __ movl(EAX, argument_addr);
- __ movl(copy_addr, EAX);
- __ Bind(&loop_condition);
- __ decl(ECX);
- __ j(POSITIVE, &loop, Assembler::kNearJump);
-
- // Copy or initialize optional named arguments.
- Label all_arguments_processed;
- if (num_opt_params > 0) {
- // Start by alphabetically sorting the names of the optional parameters.
- LocalVariable** opt_param = new LocalVariable*[num_opt_params];
- int* opt_param_position = new int[num_opt_params];
- for (int pos = num_fixed_params; pos < num_params; pos++) {
- LocalVariable* parameter = scope->VariableAt(pos);
- const String& opt_param_name = parameter->name();
- int i = pos - num_fixed_params;
- while (--i >= 0) {
- LocalVariable* param_i = opt_param[i];
- const intptr_t result = opt_param_name.CompareTo(param_i->name());
- ASSERT(result != 0);
- if (result > 0) break;
- opt_param[i + 1] = opt_param[i];
- opt_param_position[i + 1] = opt_param_position[i];
- }
- opt_param[i + 1] = parameter;
- opt_param_position[i + 1] = pos;
- }
- // Generate code handling each optional parameter in alphabetical order.
- // Total number of args is the first Smi in args descriptor array (EDX).
- __ movl(EBX, FieldAddress(EDX, Array::data_offset()));
- // Number of positional args is the second Smi in descriptor array (EDX).
- __ movl(ECX, FieldAddress(EDX, Array::data_offset() + (1 * kWordSize)));
- __ SmiUntag(ECX);
- // Let EBX point to the first passed argument, i.e. to fp[1 + argc - 0].
- __ leal(EBX, Address(EBP, EBX, TIMES_2, kWordSize));
- // Let EDI point to the name/pos pair of the first named argument.
- __ leal(EDI, FieldAddress(EDX, Array::data_offset() + (2 * kWordSize)));
- for (int i = 0; i < num_opt_params; i++) {
- // Handle this optional parameter only if k or fewer positional args
- // have been passed, where k is the position of this optional parameter
- // in the formal parameter list.
- Label load_default_value, assign_optional_parameter, next_parameter;
- const int param_pos = opt_param_position[i];
- __ cmpl(ECX, Immediate(param_pos));
- __ j(GREATER, &next_parameter, Assembler::kNearJump);
- // Check if this named parameter was passed in.
- __ movl(EAX, Address(EDI, 0)); // Load EAX with the name of the arg.
- __ CompareObject(EAX, opt_param[i]->name());
- __ j(NOT_EQUAL, &load_default_value, Assembler::kNearJump);
- // Load EAX with passed-in argument at provided arg_pos, i.e. at
- // fp[1 + argc - arg_pos].
- __ movl(EAX, Address(EDI, kWordSize)); // EAX is arg_pos as Smi.
- __ addl(EDI, Immediate(2 * kWordSize)); // Point to next name/pos pair.
- __ negl(EAX);
- Address argument_addr(EBX, EAX, TIMES_2, 0); // EAX is a negative Smi.
- __ movl(EAX, argument_addr);
- __ jmp(&assign_optional_parameter, Assembler::kNearJump);
- __ Bind(&load_default_value);
- // Load EAX with default argument at pos.
- const Object& value = Object::ZoneHandle(
- parsed_function_.default_parameter_values().At(
- param_pos - num_fixed_params));
- __ LoadObject(EAX, value);
- __ Bind(&assign_optional_parameter);
- // Assign EAX to fp[ParsedFunction::kFirstLocalSlotIndex - param_pos].
- // We do not use the final allocation index of the variable here, i.e.
- // scope->VariableAt(i)->index(), because captured variables still need
- // to be copied to the context that is not yet allocated.
- intptr_t computed_param_pos = (ParsedFunction::kFirstLocalSlotIndex -
- param_pos + implicit_this_param_pos);
- const Address param_addr(EBP, (computed_param_pos * kWordSize));
- __ movl(param_addr, EAX);
- __ Bind(&next_parameter);
- }
- delete[] opt_param;
- delete[] opt_param_position;
- // Check that EDI now points to null terminator in the array descriptor.
- __ cmpl(Address(EDI, 0), raw_null);
- __ j(EQUAL, &all_arguments_processed, Assembler::kNearJump);
- } else {
- ASSERT(is_instance_native_closure);
- __ jmp(&all_arguments_processed, Assembler::kNearJump);
- }
-
- __ Bind(&wrong_num_arguments);
- if (locals_space_size() != 0) {
- // We need to unwind the space we reserved for locals and copied
- // parameters. The NoSuchMethodFunction stub does not expect to
- // see that area on the stack.
- __ addl(ESP, Immediate(locals_space_size()));
- }
- if (function.IsClosureFunction()) {
- GenerateCallRuntime(AstNode::kNoId,
- 0,
- kClosureArgumentMismatchRuntimeEntry);
- } else {
- // Invoke noSuchMethod function.
- const int kNumArgsChecked = 1;
- ICData& ic_data = ICData::ZoneHandle();
- ic_data = ICData::New(parsed_function().function(),
- String::Handle(function.name()),
- AstNode::kNoId,
- kNumArgsChecked);
- __ LoadObject(ECX, ic_data);
- // EBP - 4 : PC marker, allows easy identification of RawInstruction obj.
- // EBP : points to previous frame pointer.
- // EBP + 4 : points to return address.
- // EBP + 8 : address of last argument (arg n-1).
- // ESP + 8 + 4*(n-1) : address of first argument (arg 0).
- // ECX : ic-data.
- // EDX : arguments descriptor array.
- __ call(&StubCode::CallNoSuchMethodFunctionLabel());
- }
-
- if (FLAG_trace_functions) {
- __ pushl(EAX); // Preserve result.
- __ PushObject(Function::ZoneHandle(function.raw()));
- GenerateCallRuntime(AstNode::kNoId,
- 0,
- kTraceFunctionExitRuntimeEntry);
- __ popl(EAX); // Remove argument.
- __ popl(EAX); // Restore result.
- }
- __ LeaveFrame();
- __ ret();
-
- __ Bind(&all_arguments_processed);
- // Nullify originally passed arguments only after they have been copied and
- // checked, otherwise noSuchMethod would not see their original values.
- // This step can be skipped in case we decide that formal parameters are
- // implicitly final, since garbage collecting the unmodified value is not
- // an issue anymore.
-
- // EDX : arguments descriptor array.
- // Total number of args is the first Smi in args descriptor array (EDX).
- __ movl(ECX, FieldAddress(EDX, Array::data_offset()));
- __ SmiUntag(ECX);
- Label null_args_loop, null_args_loop_condition;
- __ jmp(&null_args_loop_condition, Assembler::kNearJump);
- const Address original_argument_addr(EBP, ECX, TIMES_4, 2 * kWordSize);
- __ Bind(&null_args_loop);
- __ movl(original_argument_addr, raw_null);
- __ Bind(&null_args_loop_condition);
- __ decl(ECX);
- __ j(POSITIVE, &null_args_loop, Assembler::kNearJump);
- }
-
- // 3. Initialize (non-argument) stack-allocated locals to null.
- //
- // TODO(regis): For now, always unroll the init loop. Decide later above
- // which threshold to implement a loop. Consider emitting pushes instead
- // of moves.
- const int base = parsed_function_.first_stack_local_index();
- for (int index = 0; index < parsed_function_.stack_local_count(); ++index) {
- if (index == 0) {
- __ movl(EAX, raw_null);
- }
- __ movl(Address(EBP, (base - index) * kWordSize), EAX);
- }
-
- // 4. Generate the stack overflow check.
- __ cmpl(ESP,
- Address::Absolute(Isolate::Current()->stack_limit_address()));
- Label no_stack_overflow;
- __ j(ABOVE, &no_stack_overflow);
- GenerateCallRuntime(AstNode::kNoId,
- 0,
- kStackOverflowRuntimeEntry);
- __ Bind(&no_stack_overflow);
-}
-
-
-void CodeGenerator::GenerateReturnEpilog(ReturnNode* node) {
- // Unchain the context(s) up to context level 0.
- intptr_t current_context_level = context_level();
- ASSERT(current_context_level >= 0);
- if (parsed_function_.saved_context_var() != NULL) {
- // CTX on entry was saved, but not linked as context parent.
- GenerateLoadVariable(CTX, *parsed_function_.saved_context_var());
- } else {
- while (current_context_level-- > 0) {
- __ movl(CTX, FieldAddress(CTX, Context::parent_offset()));
- }
- }
-#ifdef DEBUG
- // Check that the entry stack size matches the exit stack size.
- __ leal(EDX, Address(EBP, kLocalsOffsetFromFP));
- __ subl(EDX, ESP);
- ASSERT(locals_space_size() >= 0);
- __ cmpl(EDX, Immediate(locals_space_size()));
- Label wrong_stack;
- __ j(NOT_EQUAL, &wrong_stack, Assembler::kNearJump);
-#endif // DEBUG.
-
- if (!IsOptimizing()) {
- // Count only in unoptimized code.
- // TODO(srdjan): Replace the counting code with a type feedback
- // collection and counting stub.
- const Function& function =
- Function::ZoneHandle(parsed_function_.function().raw());
- __ LoadObject(EBX, function);
- __ incl(FieldAddress(EBX, Function::usage_counter_offset()));
- if (CodeGenerator::CanOptimize()) {
- // Do not optimize if usage count must be reported.
- __ cmpl(FieldAddress(EBX, Function::usage_counter_offset()),
- Immediate(FLAG_optimization_counter_threshold));
- Label not_yet_hot;
- __ j(LESS_EQUAL, &not_yet_hot, Assembler::kNearJump);
- __ pushl(EAX); // Preserve result.
- __ pushl(EBX); // Argument for runtime: function to optimize.
- __ CallRuntime(kOptimizeInvokedFunctionRuntimeEntry);
- __ popl(EBX); // Remove argument.
- __ popl(EAX); // Restore result.
- __ Bind(&not_yet_hot);
- }
- }
- if (FLAG_trace_functions) {
- const Function& function =
- Function::ZoneHandle(parsed_function_.function().raw());
- __ LoadObject(EBX, function);
- __ pushl(EAX); // Preserve result.
- __ pushl(EBX);
- GenerateCallRuntime(AstNode::kNoId,
- 0,
- kTraceFunctionExitRuntimeEntry);
- __ popl(EAX); // Remove argument.
- __ popl(EAX); // Restore result.
- }
- __ LeaveFrame();
- __ ret();
- // Add a NOP to make return code pattern 5 bytes long for patching
- // in breakpoints during debugging.
- __ nop(1);
- AddCurrentDescriptor(PcDescriptors::kReturn,
- node->id(),
- node->token_pos());
-
-#ifdef DEBUG
- __ Bind(&wrong_stack);
- __ Stop("Exit stack size does not match the entry stack size.");
-#endif // DEBUG.
-}
-
-
-void CodeGenerator::VisitReturnNode(ReturnNode* node) {
- ASSERT(!IsResultNeeded(node));
- ASSERT(node->value() != NULL);
-
- if (!node->value()->IsLiteralNode()) {
- node->value()->Visit(this);
- // The result of the return value is now on top of the stack.
- }
-
- // Generate inlined code for all finally blocks as we are about to transfer
- // control out of the 'try' blocks if any.
- for (intptr_t i = 0; i < node->inlined_finally_list_length(); i++) {
- node->InlinedFinallyNodeAt(i)->Visit(this);
- }
-
- if (node->value()->IsLiteralNode()) {
- // Load literal value into EAX.
- const Object& literal = node->value()->AsLiteralNode()->literal();
- if (literal.IsSmi()) {
- __ movl(EAX, Immediate(reinterpret_cast<int32_t>(literal.raw())));
- } else {
- __ LoadObject(EAX, literal);
- }
- } else {
- // Pop the previously evaluated result value into EAX.
- __ popl(EAX);
- }
-
- // Generate type check.
- if (FLAG_enable_type_checks) {
- const RawFunction::Kind kind = parsed_function().function().kind();
- const bool is_implicit_getter =
- (kind == RawFunction::kImplicitGetter) ||
- (kind == RawFunction::kConstImplicitGetter);
- const bool is_static = parsed_function().function().is_static();
- // Implicit getters do not need a type check at return, unless they compute
- // the initial value of a static field.
- if (is_static || !is_implicit_getter) {
- GenerateAssertAssignable(
- node->id(),
- node->value()->token_pos(),
- node->value(),
- AbstractType::ZoneHandle(parsed_function().function().result_type()),
- String::ZoneHandle(String::NewSymbol("function result")));
- }
- }
- GenerateReturnEpilog(node);
-}
-
-
-void CodeGenerator::VisitLiteralNode(LiteralNode* node) {
- if (!IsResultNeeded(node)) return;
- __ PushObject(node->literal());
-}
-
-
-void CodeGenerator::VisitTypeNode(TypeNode* node) {
- // Type nodes are handled specially by the code generator.
- UNREACHABLE();
-}
-
-
-void CodeGenerator::VisitAssignableNode(AssignableNode* node) {
- ASSERT(FLAG_enable_type_checks);
- node->expr()->Visit(this);
- __ popl(EAX);
- GenerateAssertAssignable(node->id(),
- node->token_pos(),
- node->expr(),
- node->type(),
- node->dst_name());
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-void CodeGenerator::VisitClosureNode(ClosureNode* node) {
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- const Function& function = node->function();
- if (function.IsNonImplicitClosureFunction()) {
- // The context scope may have already been set by the new non-optimizing
- // compiler. If it was not, set it here.
- if (function.context_scope() == ContextScope::null()) {
- const intptr_t current_context_level = context_level();
- const ContextScope& context_scope = ContextScope::ZoneHandle(
- node->scope()->PreserveOuterScope(current_context_level));
- ASSERT(!function.HasCode());
- function.set_context_scope(context_scope);
- }
- __ pushl(raw_null); // No receiver.
- } else if (function.IsImplicitInstanceClosureFunction()) {
- node->receiver()->Visit(this);
- } else {
- __ pushl(raw_null); // No receiver.
- }
- ASSERT(function.context_scope() != ContextScope::null());
-
- // The function type of a closure may have type arguments. In that case, pass
- // the type arguments of the instantiator.
- const Class& cls = Class::Handle(function.signature_class());
- ASSERT(!cls.IsNull());
- const bool requires_type_arguments = cls.HasTypeArguments();
- if (requires_type_arguments) {
- ASSERT(!function.IsImplicitStaticClosureFunction());
- const bool kPushInstantiator = false;
- GenerateInstantiatorTypeArguments(node->token_pos(), kPushInstantiator);
- } else {
- __ pushl(raw_null); // No type arguments.
- }
- const Code& stub = Code::Handle(
- StubCode::GetAllocationStubForClosure(function));
- const ExternalLabel label(function.ToCString(), stub.EntryPoint());
- GenerateCall(node->token_pos(), &label, PcDescriptors::kOther);
- __ popl(ECX); // Pop type arguments.
- __ popl(ECX); // Pop receiver.
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-void CodeGenerator::VisitPrimaryNode(PrimaryNode* node) {
- // PrimaryNodes are temporary during parsing.
- UNREACHABLE();
-}
-
-
-void CodeGenerator::VisitCloneContextNode(CloneContextNode *node) {
- __ PushObject(Object::ZoneHandle()); // Make room for the result.
- __ pushl(CTX);
- GenerateCallRuntime(node->id(),
- node->token_pos(), kCloneContextRuntimeEntry);
- __ popl(EAX);
- __ popl(CTX); // result: cloned context. Set as current context.
-}
-
-
-void CodeGenerator::VisitSequenceNode(SequenceNode* node_sequence) {
- CodeGeneratorState codegen_state(this);
- LocalScope* scope = node_sequence->scope();
- const intptr_t num_context_variables =
- (scope != NULL) ? scope->num_context_variables() : 0;
- intptr_t previous_context_level = context_level();
- if (num_context_variables > 0) {
- // The loop local scope declares variables that are captured.
- // Allocate and chain a new context.
- __ movl(EDX, Immediate(num_context_variables));
- const ExternalLabel label("alloc_context",
- StubCode::AllocateContextEntryPoint());
- GenerateCall(node_sequence->token_pos(), &label, PcDescriptors::kOther);
-
- // If this node_sequence is the body of the function being compiled, and if
- // this function is not a closure, do not link the current context as the
- // parent of the newly allocated context, as it is not accessible. Instead,
- // save it in a pre-allocated variable and restore it on exit.
- if ((node_sequence == parsed_function_.node_sequence()) &&
- (parsed_function_.saved_context_var() != NULL)) {
- GenerateStoreVariable(
- *parsed_function_.saved_context_var(), CTX, kNoRegister);
- __ StoreIntoObjectNoBarrier(EAX,
- FieldAddress(EAX, Context::parent_offset()),
- Object::ZoneHandle());
- } else {
- // Chain the new context in EAX to its parent in CTX.
- __ StoreIntoObject(EAX, FieldAddress(EAX, Context::parent_offset()), CTX);
- }
-
- // Set new context as current context.
- __ movl(CTX, EAX);
- set_context_level(scope->context_level());
-
- // If this node_sequence is the body of the function being compiled, copy
- // the captured parameters from the frame into the context.
- if (node_sequence == parsed_function_.node_sequence()) {
- ASSERT(scope->context_level() == 1);
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- const Function& function = parsed_function_.function();
- const int num_params = function.NumberOfParameters();
- int param_frame_index = (num_params == function.num_fixed_parameters()) ?
- (1 + num_params) : ParsedFunction::kFirstLocalSlotIndex;
- for (int pos = 0; pos < num_params; param_frame_index--, pos++) {
- LocalVariable* parameter = scope->VariableAt(pos);
- ASSERT(parameter->owner() == scope);
- if (parameter->is_captured()) {
- // Copy parameter from local frame to current context.
- const Address local_addr(EBP, param_frame_index * kWordSize);
- __ movl(EAX, local_addr);
- GenerateStoreVariable(*parameter, EAX, EDX);
- // Write NULL to the source location to detect buggy accesses and
- // allow GC of passed value if it gets overwritten by a new value in
- // the function.
- __ movl(local_addr, raw_null);
- }
- }
- }
- }
- // If this node_sequence is the body of the function being compiled, generate
- // code checking the type of the actual arguments.
- if (FLAG_enable_type_checks &&
- (node_sequence == parsed_function_.node_sequence())) {
- GenerateArgumentTypeChecks();
- }
- for (int i = 0; i < node_sequence->length(); i++) {
- AstNode* child_node = node_sequence->NodeAt(i);
- state()->set_root_node(child_node);
- child_node->Visit(this);
- }
-
- // Unchain the previously allocated context.
- if ((node_sequence == parsed_function_.node_sequence()) &&
- (parsed_function_.saved_context_var() != NULL)) {
- ASSERT(num_context_variables > 0);
- GenerateLoadVariable(CTX, *parsed_function_.saved_context_var());
- } else if (num_context_variables > 0) {
- __ movl(CTX, FieldAddress(CTX, Context::parent_offset()));
- }
-
- // If this node sequence is labeled, a break out of the sequence will have
- // taken care of unchaining the context.
- if (node_sequence->label() != NULL) {
- __ Bind(node_sequence->label()->break_label());
- // Outermost sequence cannot have a label.
- ASSERT(node_sequence != parsed_function_.node_sequence());
- }
- set_context_level(previous_context_level);
-}
-
-
-void CodeGenerator::VisitArgumentListNode(ArgumentListNode* arguments) {
- for (int i = 0; i < arguments->length(); i++) {
- AstNode* argument = arguments->NodeAt(i);
- argument->Visit(this);
- }
-}
-
-
-void CodeGenerator::VisitArrayNode(ArrayNode* node) {
- // Evaluate the array elements.
- for (int i = 0; i < node->length(); i++) {
- AstNode* element = node->ElementAt(i);
- element->Visit(this);
- }
-
- const AbstractTypeArguments& element_type = node->type_arguments();
- const bool instantiate_type_arguments = true;
- GenerateTypeArguments(node->id(),
- node->token_pos(),
- element_type,
- instantiate_type_arguments);
- __ popl(ECX);
- __ movl(EDX, Immediate(Smi::RawValue(node->length())));
-
- // Allocate the array.
- // EDX : Array length as Smi.
- // ECX : element type for the array.
- GenerateCall(node->token_pos(),
- &StubCode::AllocateArrayLabel(),
- PcDescriptors::kOther);
-
- // Pop the element values from the stack into the array.
- __ leal(ECX, FieldAddress(EAX, Array::data_offset()));
- for (int i = node->length() - 1; i >= 0; i--) {
- __ popl(Address(ECX, i * kWordSize));
- }
-
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-void CodeGenerator::VisitLoadLocalNode(LoadLocalNode* node) {
- if (node->HasPseudo()) {
- node->pseudo()->Visit(this);
- __ popl(EAX); // Discard result.
- }
- // Load the value of the local variable and push it onto the expression stack.
- if (IsResultNeeded(node)) {
- GeneratePushVariable(node->local(), EAX);
- }
-}
-
-
-void CodeGenerator::VisitStoreLocalNode(StoreLocalNode* node) {
- node->value()->Visit(this);
- __ popl(EAX);
- if (FLAG_enable_type_checks) {
- GenerateAssertAssignable(node->id(),
- node->value()->token_pos(),
- node->value(),
- node->local().type(),
- node->local().name());
- }
- GenerateStoreVariable(node->local(), EAX, EDX);
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-void CodeGenerator::VisitLoadInstanceFieldNode(LoadInstanceFieldNode* node) {
- node->instance()->Visit(this);
- MarkDeoptPoint(node->id(), node->token_pos());
- __ popl(EAX); // Instance.
- __ movl(EAX, FieldAddress(EAX, node->field().Offset()));
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-void CodeGenerator::VisitStoreInstanceFieldNode(StoreInstanceFieldNode* node) {
- node->instance()->Visit(this);
- node->value()->Visit(this);
- MarkDeoptPoint(node->id(), node->token_pos());
- __ popl(EAX); // Value.
- if (FLAG_enable_type_checks) {
- GenerateAssertAssignable(node->id(),
- node->value()->token_pos(),
- node->value(),
- AbstractType::ZoneHandle(node->field().type()),
- String::ZoneHandle(node->field().name()));
- }
- __ popl(EDX); // Instance.
- __ StoreIntoObject(EDX, FieldAddress(EDX, node->field().Offset()), EAX);
- ASSERT(!IsResultNeeded(node));
-}
-
-
-// Expects array and index on stack and returns result in EAX.
-void CodeGenerator::GenerateLoadIndexed(intptr_t node_id,
- intptr_t token_pos) {
- // Invoke the [] operator on the receiver object with the index as argument.
- const String& operator_name =
- String::ZoneHandle(String::NewSymbol(Token::Str(Token::kINDEX)));
- const int kNumArguments = 2; // Receiver and index.
- const Array& kNoArgumentNames = Array::Handle();
- const int kNumArgumentsChecked = 1;
- GenerateInstanceCall(node_id,
- token_pos,
- operator_name,
- kNumArguments,
- kNoArgumentNames,
- kNumArgumentsChecked);
-}
-
-
-void CodeGenerator::VisitLoadIndexedNode(LoadIndexedNode* node) {
- node->array()->Visit(this);
- // Now compute the index.
- node->index_expr()->Visit(this);
- MarkDeoptPoint(node->id(), node->token_pos());
- GenerateLoadIndexed(node->id(), node->token_pos());
- // Result is in EAX.
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-// Expected arguments.
-// TOS(0): value.
-// TOS(1): index.
-// TOS(2): array.
-void CodeGenerator::GenerateStoreIndexed(intptr_t node_id,
- intptr_t token_pos,
- bool preserve_value) {
- // It is not necessary to generate a type test of the assigned value here,
- // because the []= operator will check the type of its incoming arguments.
- if (preserve_value) {
- __ popl(EAX);
- __ popl(EDX);
- __ popl(ECX);
- __ pushl(EAX); // Preserve stored value.
- __ pushl(ECX); // Restore arguments.
- __ pushl(EDX);
- __ pushl(EAX);
- }
- // Invoke the []= operator on the receiver object with index and
- // value as arguments.
- const String& operator_name =
- String::ZoneHandle(String::NewSymbol(Token::Str(Token::kASSIGN_INDEX)));
- const int kNumArguments = 3; // Receiver, index and value.
- const Array& kNoArgumentNames = Array::Handle();
- const int kNumArgumentsChecked = 1;
- GenerateInstanceCall(node_id,
- token_pos,
- operator_name,
- kNumArguments,
- kNoArgumentNames,
- kNumArgumentsChecked);
-}
-
-
-void CodeGenerator::VisitStoreIndexedNode(StoreIndexedNode* node) {
- // Compute the receiver object and pass as first argument to call.
- node->array()->Visit(this);
- // Now compute the index.
- node->index_expr()->Visit(this);
- // Finally compute the value to assign.
- node->value()->Visit(this);
- MarkDeoptPoint(node->id(), node->token_pos());
- GenerateStoreIndexed(node->id(), node->token_pos(), IsResultNeeded(node));
-}
-
-
-void CodeGenerator::VisitLoadStaticFieldNode(LoadStaticFieldNode* node) {
- MarkDeoptPoint(node->id(), node->token_pos());
- __ LoadObject(EDX, node->field());
- __ movl(EAX, FieldAddress(EDX, Field::value_offset()));
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-void CodeGenerator::VisitStoreStaticFieldNode(StoreStaticFieldNode* node) {
- node->value()->Visit(this);
- MarkDeoptPoint(node->id(), node->token_pos());
- __ popl(EAX); // Value.
- if (FLAG_enable_type_checks) {
- GenerateAssertAssignable(node->id(),
- node->value()->token_pos(),
- node->value(),
- AbstractType::ZoneHandle(node->field().type()),
- String::ZoneHandle(node->field().name()));
- }
- __ LoadObject(EDX, node->field());
- __ StoreIntoObject(EDX, FieldAddress(EDX, Field::value_offset()), EAX);
- if (IsResultNeeded(node)) {
- // The result is the input value.
- __ pushl(EAX);
- }
-}
-
-
-void CodeGenerator::GenerateLogicalNotOp(UnaryOpNode* node) {
- // Generate false if operand is true, otherwise generate true.
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- const Bool& bool_false = Bool::ZoneHandle(Bool::False());
- node->operand()->Visit(this);
- MarkDeoptPoint(node->id(), node->token_pos());
- Label done;
- GenerateConditionTypeCheck(node->id(), node->operand()->token_pos());
- __ popl(EDX);
- __ LoadObject(EAX, bool_true);
- __ cmpl(EAX, EDX);
- __ j(NOT_EQUAL, &done, Assembler::kNearJump);
- __ LoadObject(EAX, bool_false);
- __ Bind(&done);
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-void CodeGenerator::VisitUnaryOpNode(UnaryOpNode* node) {
- if (node->kind() == Token::kNOT) {
- // "!" cannot be overloaded, therefore inline it.
- GenerateLogicalNotOp(node);
- return;
- }
- node->operand()->Visit(this);
- if (node->kind() == Token::kADD) {
- // TODO(srdjan): Remove this as it is not part of Dart language any longer.
- // Unary operator '+' does not exist, it's a NOP, skip it.
- if (!IsResultNeeded(node)) {
- __ popl(EAX);
- }
- return;
- }
- MarkDeoptPoint(node->id(), node->token_pos());
- String& operator_name = String::ZoneHandle();
- if (node->kind() == Token::kSUB) {
- operator_name = String::NewSymbol(Token::Str(Token::kNEGATE));
- } else {
- operator_name = String::NewSymbol(node->Name());
- }
- const int kNumberOfArguments = 1;
- const Array& kNoArgumentNames = Array::Handle();
- const int kNumArgumentsChecked = 1;
- GenerateInstanceCall(node->id(),
- node->token_pos(),
- operator_name,
- kNumberOfArguments,
- kNoArgumentNames,
- kNumArgumentsChecked);
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-// Instance to test is in EAX. Test if its class is in subtype test cache array
-// and use the result in the array to jump to one of the labels. Fall through
-// if the instance is not in the cache array.
-// TODO(srdjan): Implement a quicker subtype check, as type test
-// arrays can grow too high, but they may be useful when optimizing
-// code (type-feedback).
-RawSubtypeTestCache* CodeGenerator::GenerateSubtype1TestCacheLookup(
- intptr_t node_id,
- intptr_t token_pos,
- const Class& type_class,
- Label* is_instance_lbl,
- Label* is_not_instance_lbl) {
- const SubtypeTestCache& type_test_cache =
- SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- // Check immediate equality.
- __ LoadClass(ECX, EAX, EDI);
- // ECX: instance class.
- __ CompareObject(ECX, type_class);
- __ j(EQUAL, is_instance_lbl);
-
- // Check immediate superclass equality.
- __ movl(EDI, FieldAddress(ECX, Class::super_type_offset()));
- __ movl(EDI, FieldAddress(EDI, Type::type_class_offset()));
- __ CompareObject(EDI, type_class);
- __ j(EQUAL, is_instance_lbl);
-
- __ LoadObject(EDX, type_test_cache);
- __ pushl(EDX); // Cache array.
- __ pushl(EAX); // Instance.
- __ pushl(raw_null); // Unused
- __ call(&StubCode::Subtype1TestCacheLabel());
- __ popl(EAX); // Discard.
- __ popl(EAX); // Restore receiver.
- __ popl(EDX); // Discard.
- // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False.
-
- Label runtime_call;
- __ cmpl(ECX, raw_null);
- __ j(EQUAL, &runtime_call, Assembler::kNearJump);
- __ CompareObject(ECX, bool_true);
- __ j(EQUAL, is_instance_lbl);
- __ jmp(is_not_instance_lbl);
- __ Bind(&runtime_call);
- return type_test_cache.raw();
-}
-
-
-// Inline tests according to the 'type' being tested. Jump to labels
-// if we can compute the type-test, otherwise fallthrough.
-// EAX: instance to be tested, must be preserved.
-// Clobbers many registers.
-RawSubtypeTestCache* CodeGenerator::GenerateInlineInstanceof(
- intptr_t node_id,
- intptr_t token_pos,
- const AbstractType& type,
- Label* is_instance_lbl,
- Label* is_not_instance_lbl) {
- if (type.IsInstantiated()) {
- const Class& type_class = Class::ZoneHandle(type.type_class());
- // A Smi object cannot be the instance of a parameterized class.
- // A class equality check is only applicable with a dst type of a
- // non-parameterized class or with a raw dst type of a parameterized class.
- if (type_class.HasTypeArguments()) {
- return GenerateInstantiatedTypeWithArgumentsTest(node_id,
- token_pos,
- type,
- is_instance_lbl,
- is_not_instance_lbl);
- // Fall through to runtime call.
- } else {
- GenerateInstantiatedTypeNoArgumentsTest(node_id,
- token_pos,
- type,
- is_instance_lbl,
- is_not_instance_lbl);
- // If test non-conclusive so far, try the inlined type-test cache.
- // 'type' is known at compile time.
- return GenerateSubtype1TestCacheLookup(
- node_id, token_pos, type_class,
- is_instance_lbl, is_not_instance_lbl);
- }
- } else {
- return GenerateUninstantiatedTypeTest(type,
- node_id,
- token_pos,
- is_instance_lbl,
- is_not_instance_lbl);
- }
- return SubtypeTestCache::null();
-}
-
-
-// If instanceof type test cannot be performed successfully at compile time and
-// therefore eliminated, optimize it by adding inlined tests for:
-// - NULL -> return false.
-// - Smi -> compile time subtype check (only if dst class is not parameterized).
-// - Class equality (only if class is not parameterized).
-// Inputs:
-// - EAX: object.
-// Destroys ECX.
-// Returns:
-// - true or false on stack.
-void CodeGenerator::GenerateInstanceOf(intptr_t node_id,
- intptr_t token_pos,
- AstNode* value,
- const AbstractType& type,
- bool negate_result) {
- ASSERT(type.IsFinalized() && !type.IsMalformed());
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- const Bool& bool_false = Bool::ZoneHandle(Bool::False());
-
- // All objects are instances of type T if Object type is a subtype of type T.
- const Type& object_type =
- Type::Handle(Isolate::Current()->object_store()->object_type());
- Error& malformed_error = Error::Handle();
- if (type.IsInstantiated() &&
- object_type.IsSubtypeOf(type, &malformed_error)) {
- __ PushObject(negate_result ? bool_false : bool_true);
- return;
- }
-
- // Eliminate the test if it can be performed successfully at compile time.
- if ((value != NULL) && value->IsLiteralNode() && type.IsInstantiated()) {
- const Instance& literal_value = value->AsLiteralNode()->literal();
- const Class& cls = Class::Handle(literal_value.clazz());
- if (cls.IsNullClass()) {
- ASSERT(literal_value.IsNull() ||
- (literal_value.raw() == Object::sentinel()) ||
- (literal_value.raw() == Object::transition_sentinel()));
- // A null object is only an instance of Object and Dynamic, which has
- // already been checked above (if the type is instantiated). So we can
- // return false here if the instance is null (and if the type is
- // instantiated).
- __ PushObject(negate_result ? bool_true : bool_false);
- } else {
- Error& malformed_error = Error::Handle();
- if (literal_value.IsInstanceOf(type,
- TypeArguments::Handle(),
- &malformed_error)) {
- __ PushObject(negate_result ? bool_false : bool_true);
- } else {
- ASSERT(malformed_error.IsNull());
- __ PushObject(negate_result ? bool_true : bool_false);
- }
- }
- return;
- }
-
- Label is_instance_of, is_not_instance_of;
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- Label done;
- // If type is instantiated and non-parameterized, we can inline code
- // checking whether the tested instance is a Smi.
- if (type.IsInstantiated()) {
- // A null object is only an instance of Object and Dynamic, which has
- // already been checked above (if the type is instantiated). So we can
- // return false here if the instance is null (and if the type is
- // instantiated).
- // We can only inline this null check if the type is instantiated at compile
- // time, since an uninstantiated type at compile time could be Object or
- // Dynamic at run time.
- Label non_null;
- __ cmpl(EAX, raw_null);
- __ j(NOT_EQUAL, &non_null, Assembler::kNearJump);
- __ PushObject(negate_result ? bool_true : bool_false);
- __ jmp(&done);
- __ Bind(&non_null);
- }
- SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle();
- test_cache = GenerateInlineInstanceof(node_id, token_pos, type,
- &is_instance_of, &is_not_instance_of);
-
- __ PushObject(Object::ZoneHandle()); // Make room for the result.
- const Immediate location = Immediate(Smi::RawValue(token_pos));
- const Immediate node_id_as_smi = Immediate(Smi::RawValue(node_id));
- __ pushl(location); // Push the source location.
- __ pushl(node_id_as_smi); // node-id.
- __ pushl(EAX); // Push the instance.
- __ PushObject(type); // Push the type.
- if (type.IsInstantiated()) {
- __ pushl(raw_null); // Null instantiator.
- __ pushl(raw_null); // Null instantiator.
- } else {
- const bool kPushInstantiator = true;
- GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator);
- }
- __ LoadObject(EAX, test_cache);
- __ pushl(EAX);
- GenerateCallRuntime(node_id, token_pos, kInstanceofRuntimeEntry);
- // Pop the two parameters supplied to the runtime entry. The result of the
- // instanceof runtime call will be left as the result of the operation.
- __ addl(ESP, Immediate(7 * kWordSize));
- if (negate_result) {
- __ popl(EDX);
- __ CompareObject(EDX, bool_false);
- __ j(EQUAL, &is_not_instance_of, Assembler::kNearJump);
- // Fall through to is_instance_of.
- } else {
- __ jmp(&done);
- }
-
- __ Bind(&is_instance_of);
- __ PushObject(negate_result ? bool_false: bool_true);
- __ jmp(&done, Assembler::kNearJump);
- __ Bind(&is_not_instance_of);
- __ PushObject(negate_result ? bool_true: bool_false);
- __ Bind(&done);
-}
-
-
-// EAX: instance to test.
-// Clobbers: ECX.
-// Jumps to labels 'is_instance' or 'is_not_instance' respectively, if
-// type test is conclusive, otherwise fallthrough if a type test could not
-// be completed.
-RawSubtypeTestCache* CodeGenerator::GenerateInstantiatedTypeWithArgumentsTest(
- intptr_t node_id,
- intptr_t token_pos,
- const AbstractType& type,
- Label* is_instance_lbl,
- Label* is_not_instance_lbl) {
- ASSERT(type.IsInstantiated());
- const Class& type_class = Class::ZoneHandle(type.type_class());
- ASSERT(type_class.HasTypeArguments());
- // A Smi object cannot be the instance of a parameterized class.
- // A class equality check is only applicable with a dst type of a
- // non-parameterized class or with a raw dst type of a parameterized class.
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(ZERO, is_not_instance_lbl);
- const AbstractTypeArguments& type_arguments =
- AbstractTypeArguments::ZoneHandle(type.arguments());
- const bool is_raw_type = type_arguments.IsNull() ||
- type_arguments.IsRaw(type_arguments.Length());
- if (is_raw_type) {
- // Dynamic type argument, check only classes.
- __ LoadClassId(ECX, EAX);
- if (!type_class.is_interface()) {
- __ cmpl(ECX, Immediate(type_class.id()));
- __ j(EQUAL, is_instance_lbl);
- }
- if (type.IsListInterface()) {
- // TODO(srdjan) also accept List<Object>.
- __ cmpl(ECX, Immediate(kArray));
- __ j(EQUAL, is_instance_lbl);
- __ cmpl(ECX, Immediate(kGrowableObjectArray));
- __ j(EQUAL, is_instance_lbl);
- }
- return
- GenerateSubtype1TestCacheLookup(node_id, token_pos, type_class,
- is_instance_lbl, is_not_instance_lbl);
- }
- // If one type argument only, quick check.
- if (type_arguments.Length() == 1) {
- const AbstractType& tp_argument = AbstractType::ZoneHandle(
- type_arguments.TypeAt(0));
- if (tp_argument.IsType()) {
- // Malformed type has been caught in the caller chain of this function.
- ASSERT(tp_argument.HasResolvedTypeClass());
- // Check if type argument is dynamic or Object.
- const Type& object_type =
- Type::Handle(Isolate::Current()->object_store()->object_type());
- Error& malformed_error = Error::Handle();
- if (object_type.IsSubtypeOf(tp_argument, &malformed_error)) {
- // Instance class test only necessary.
- return GenerateSubtype1TestCacheLookup(
- node_id,
- token_pos,
- type_class,
- is_instance_lbl,
- is_not_instance_lbl);
- }
- }
- }
- const SubtypeTestCache& type_test_cache =
- SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
- Label inlined_check, fall_through;
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ LoadObject(EDX, type_test_cache);
- __ pushl(EDX); // Subtype test cache.
- __ pushl(EAX); // Instance.
- __ pushl(raw_null); // Unused.
- __ call(&StubCode::Subtype2TestCacheLabel());
- __ popl(EAX); // Discard.
- __ popl(EAX); // Restore receiver.
- __ popl(EDX); // Discard.
- // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False.
-
- __ cmpl(ECX, raw_null);
- __ j(EQUAL, &fall_through, Assembler::kNearJump);
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- __ CompareObject(ECX, bool_true);
- __ j(EQUAL, is_instance_lbl);
- __ jmp(is_not_instance_lbl);
- __ Bind(&fall_through);
- return type_test_cache.raw();
-}
-
-
-// EAX: instance to test.
-// Clobbers: EBX, ECX, EDX.
-void CodeGenerator::GenerateInstantiatedTypeNoArgumentsTest(
- intptr_t node_id,
- intptr_t token_pos,
- const AbstractType& type,
- Label* is_instance_lbl,
- Label* is_not_instance_lbl) {
- ASSERT(type.IsInstantiated());
- const Class& type_class = Class::Handle(type.type_class());
- ASSERT(!type_class.HasTypeArguments());
-
- Label compare_classes;
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(NOT_ZERO, &compare_classes, Assembler::kNearJump);
- // Instance is Smi, check directly.
- const Class& smi_class = Class::Handle(Smi::Class());
- // TODO(regis): We should introduce a SmiType.
- Error& malformed_error = Error::Handle();
- if (smi_class.IsSubtypeOf(TypeArguments::Handle(),
- type_class,
- TypeArguments::Handle(),
- &malformed_error)) {
- __ jmp(is_instance_lbl);
- } else {
- __ jmp(is_not_instance_lbl);
- }
- // Compare if the classes are equal.
- __ Bind(&compare_classes);
-
- // Checking against interface.
- // However, for specific core library interfaces, we can check for
- // specific core library classes.
- if (type.IsBoolInterface()) {
- __ CompareClassId(EAX, kBool, ECX);
- __ j(EQUAL, is_instance_lbl);
- __ jmp(is_not_instance_lbl);
- return;
- }
- // If type is an interface, we can skip the class equality check,
- // because instances cannot be of an interface type.
- if (!type_class.is_interface()) {
- __ CompareClassId(EAX, type_class.id(), ECX);
- __ j(EQUAL, is_instance_lbl);
- }
- if (type.IsSubtypeOf(
- Type::Handle(Type::NumberInterface()), &malformed_error)) {
- // Custom checking for numbers (Smi, Mint, Bigint and Double)
- __ LoadClassId(ECX, EAX);
-
- if (type.IsIntInterface() || type.IsNumberInterface()) {
- // We already checked for Smi above.
- __ cmpl(ECX, Immediate(kMint));
- __ j(EQUAL, is_instance_lbl);
- __ cmpl(ECX, Immediate(kBigint));
- __ j(EQUAL, is_instance_lbl);
- if (type.IsIntInterface()) {
- __ jmp(is_not_instance_lbl);
- }
- }
- if (type.IsDoubleInterface() || type.IsNumberInterface()) {
- __ cmpl(ECX, Immediate(kDouble));
- __ j(EQUAL, is_instance_lbl);
- __ jmp(is_not_instance_lbl);
- }
- } else if (type.IsStringInterface()) {
- __ LoadClassId(ECX, EAX);
- __ cmpl(ECX, Immediate(kOneByteString));
- __ j(EQUAL, is_instance_lbl);
- __ cmpl(ECX, Immediate(kTwoByteString));
- __ j(EQUAL, is_instance_lbl);
- __ cmpl(ECX, Immediate(kFourByteString));
- __ j(EQUAL, is_instance_lbl);
- } else if (type.IsFunctionInterface()) {
- // Check if instance is a closure.
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ LoadClass(ECX, EAX, EBX);
- __ movl(ECX, FieldAddress(ECX, Class::signature_function_offset()));
- __ cmpl(ECX, raw_null);
- __ j(NOT_EQUAL, is_instance_lbl);
- }
- // Otherwise fallthrough.
-}
-
-
-// EAX: instance to test.
-// Clobbers: EBX, EDX, ECX.
-RawSubtypeTestCache* CodeGenerator::GenerateUninstantiatedTypeTest(
- const AbstractType& type,
- intptr_t node_id,
- intptr_t token_pos,
- Label* is_instance_lbl,
- Label* is_not_instance_lbl) {
- ASSERT(!type.IsInstantiated());
- // Skip check if destination is a dynamic type.
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- if (type.IsTypeParameter()) {
- // EAX must be preserved!
- Label fall_through;
- const bool kPushInstantiator = false;
- GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator);
- // Type arguments are on stack.
- __ popl(EBX);
- // Check if type argument is dynamic.
- __ cmpl(EBX, raw_null);
- __ j(EQUAL, is_instance_lbl);
-
- // EBX: instantiator type arguments.
- // For now handle only TypeArguments and bail out if InstantiatedTypeArgs.
- // We expect that frequently checked objects wull have their type arguments
- // converted into instance of TypeArguments.
- __ CompareClassId(EBX, kTypeArguments, EDX);
- __ j(NOT_EQUAL, &fall_through, Assembler::kNearJump);
-
- __ movl(EDX,
- FieldAddress(EBX, TypeArguments::type_at_offset(type.Index())));
- // EDX: concrete type of type.
- // Check if type argument is dynamic.
- __ CompareObject(EDX, Type::ZoneHandle(Type::DynamicType()));
- __ j(EQUAL, is_instance_lbl);
- __ cmpl(EDX, raw_null);
- __ j(EQUAL, is_instance_lbl);
-
- // For Smi check quickly against int and num interfaces.
- Label not_smi;
- __ testl(EAX, Immediate(kSmiTagMask)); // Value is Smi?
- __ j(NOT_ZERO, &not_smi, Assembler::kNearJump);
- __ CompareObject(EDX, Type::ZoneHandle(Type::IntInterface()));
- __ j(EQUAL, is_instance_lbl);
- __ CompareObject(EDX, Type::ZoneHandle(Type::NumberInterface()));
- __ j(EQUAL, is_instance_lbl);
- __ jmp(&fall_through);
-
- __ Bind(&not_smi);
- // EBX: instantiator type arguments.
- // EAX: instance
- // The instantiated type parameter may not be a Type, but could be an
- // InstantiatedType. It is therefore necessary to check its class.
- const SubtypeTestCache& type_test_cache =
- SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
- __ LoadObject(ECX, type_test_cache);
- __ pushl(ECX); // Subtype test cache.
- __ pushl(EAX); // Instance.
- __ pushl(EBX); // Instantator type arguments.
- __ call(&StubCode::Subtype3TestCacheLabel());
- __ popl(EDX); // Discard type arguments.
- __ popl(EAX); // Restore receiver.
- __ popl(EDX); // Discard subtype test cache.
- // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False.
- __ cmpl(ECX, raw_null);
- __ j(EQUAL, &fall_through, Assembler::kNearJump);
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- __ CompareObject(ECX, bool_true);
- __ j(EQUAL, is_instance_lbl);
- __ jmp(is_not_instance_lbl);
- __ Bind(&fall_through);
- return type_test_cache.raw();
- }
- if (type.IsType()) {
- Label fall_through;
- __ testl(EAX, Immediate(kSmiTagMask)); // Is instance Smi?
- __ j(ZERO, is_not_instance_lbl);
- // Uninstantiated type class is known at compile time, but the type
- // arguments are determined at runtime by the instantiator.
- const SubtypeTestCache& type_test_cache =
- SubtypeTestCache::ZoneHandle(SubtypeTestCache::New());
- __ LoadObject(EDX, type_test_cache);
- __ pushl(EDX); // Subtype test cache.
- __ pushl(EAX); // Instance.
- const bool kPushInstantiator = false;
- GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator);
- __ call(&StubCode::Subtype3TestCacheLabel());
- __ popl(EDX); // Discard type arguments.
- __ popl(EAX); // Restore receiver.
- __ popl(EDX); // Discard subtype test cache.
- // Result is in ECX: null -> not found, otherwise Bool::True or Bool::False.
- __ cmpl(ECX, raw_null);
- __ j(EQUAL, &fall_through, Assembler::kNearJump);
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- __ CompareObject(ECX, bool_true);
- __ j(EQUAL, is_instance_lbl);
- __ jmp(is_not_instance_lbl);
- __ Bind(&fall_through);
- return type_test_cache.raw();
- }
- return SubtypeTestCache::null();
-}
-
-
-// If type check cannot be performed successfully at compile time and therefore
-// eliminated, optimize it by adding inlined tests for:
-// - NULL -> return NULL.
-// - Smi -> compile time subtype check (only if dst class is not parameterized).
-// - Class equality (only if class is not parameterized).
-// Inputs:
-// - EAX: object.
-// Destroys ECX and EDX.
-// Returns:
-// - object in EAX for successful assignable check (or throws TypeError).
-// Performance notes: positive checks must be quick, negative checks can be slow
-// as they throw an exception.
-void CodeGenerator::GenerateAssertAssignable(intptr_t node_id,
- intptr_t token_pos,
- AstNode* value,
- const AbstractType& dst_type,
- const String& dst_name) {
- ASSERT(FLAG_enable_type_checks);
- ASSERT(token_pos >= 0);
- ASSERT(!dst_type.IsNull());
- ASSERT(dst_type.IsFinalized());
-
- // Any expression is assignable to the Dynamic type and to the Object type.
- // Skip the test.
- if (!dst_type.IsMalformed() &&
- (dst_type.IsDynamicType() || dst_type.IsObjectType())) {
- return;
- }
-
- // It is a compile-time error to explicitly return a value (including null)
- // from a void function. However, functions that do not explicitly return a
- // value, implicitly return null. This includes void functions. Therefore, we
- // skip the type test here and trust the parser to only return null in void
- // function.
- if (dst_type.IsVoidType()) {
- return;
- }
-
- // Eliminate the test if it can be performed successfully at compile time.
- if ((value != NULL) && value->IsLiteralNode()) {
- const Instance& literal_value = value->AsLiteralNode()->literal();
- const Class& cls = Class::Handle(literal_value.clazz());
- if (cls.IsNullClass()) {
- ASSERT(literal_value.IsNull() ||
- (literal_value.raw() == Object::sentinel()) ||
- (literal_value.raw() == Object::transition_sentinel()));
- return;
- }
- Error& malformed_error = Error::Handle();
- if (!dst_type.IsMalformed() &&
- dst_type.IsInstantiated() &&
- literal_value.IsInstanceOf(dst_type,
- TypeArguments::Handle(),
- &malformed_error)) {
- return;
- }
- }
-
- // A null object is always assignable and is returned as result.
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- Label done, runtime_call;
- __ cmpl(EAX, raw_null);
- __ j(EQUAL, &done);
-
- // Generate throw new TypeError() if the type is malformed.
- if (dst_type.IsMalformed()) {
- const Error& error = Error::Handle(dst_type.malformed_error());
- const String& error_message = String::ZoneHandle(
- String::NewSymbol(error.ToErrorCString()));
- __ PushObject(Object::ZoneHandle()); // Make room for the result.
- __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location.
- __ pushl(EAX); // Push the source object.
- __ PushObject(dst_name); // Push the name of the destination.
- __ PushObject(error_message);
- GenerateCallRuntime(node_id, token_pos, kMalformedTypeErrorRuntimeEntry);
- // We should never return here.
- __ int3();
-
- __ Bind(&done); // For a null object.
- return;
- }
-
- SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle();
- test_cache = GenerateInlineInstanceof(node_id, token_pos, dst_type,
- &done, &runtime_call);
-
- __ Bind(&runtime_call);
- __ PushObject(Object::ZoneHandle()); // Make room for the result.
- __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location.
- __ pushl(Immediate(Smi::RawValue(node_id))); // node-id.
- __ pushl(EAX); // Push the source object.
- __ PushObject(dst_type); // Push the type of the destination.
- if (dst_type.IsInstantiated()) {
- __ pushl(raw_null); // Null instantiator.
- __ pushl(raw_null); // Null instantiator type argument.
- } else {
- const bool kPushInstantiator = true;
- GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator);
- }
- __ PushObject(dst_name); // Push the name of the destination.
- __ LoadObject(EAX, test_cache);
- __ pushl(EAX);
- GenerateCallRuntime(node_id, token_pos, kTypeCheckRuntimeEntry);
- // Pop the parameters supplied to the runtime entry. The result of the
- // type check runtime call is the checked value.
- __ addl(ESP, Immediate(8 * kWordSize));
- __ popl(EAX);
-
- // EAX: value.
- __ Bind(&done);
-}
-
-
-void CodeGenerator::GenerateArgumentTypeChecks() {
- const Function& function = parsed_function_.function();
- const SequenceNode& sequence_node = *parsed_function_.node_sequence();
- LocalScope* scope = sequence_node.scope();
- const int num_fixed_params = function.num_fixed_parameters();
- const int num_opt_params = function.num_optional_parameters();
- ASSERT(num_fixed_params + num_opt_params <= scope->num_variables());
- for (intptr_t i = 0; i < num_fixed_params + num_opt_params; i++) {
- LocalVariable* parameter = scope->VariableAt(i);
- GenerateLoadVariable(EAX, *parameter);
- GenerateAssertAssignable(sequence_node.ParameterIdAt(i),
- parameter->token_pos(),
- NULL,
- parameter->type(),
- parameter->name());
- }
-}
-
-
-void CodeGenerator::GenerateConditionTypeCheck(intptr_t node_id,
- intptr_t token_pos) {
- if (!FLAG_enable_type_checks) {
- return;
- }
-
- // Check that the type of the object on the stack is allowed in conditional
- // context.
- // Call the runtime if the object is null or not of type bool.
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- Label runtime_call, done;
- __ movl(EAX, Address(ESP, 0));
- __ cmpl(EAX, raw_null);
- __ j(EQUAL, &runtime_call, Assembler::kNearJump);
- __ testl(EAX, Immediate(kSmiTagMask));
- __ j(ZERO, &runtime_call, Assembler::kNearJump); // Call runtime for Smi.
- // This check should pass if the receiver's class implements the interface
- // 'bool'. Check only class 'Bool' since it is the only legal implementation
- // of the interface 'bool'.
- __ CompareClassId(EAX, kBool, ECX);
- __ j(EQUAL, &done, Assembler::kNearJump);
-
- __ Bind(&runtime_call);
- __ pushl(Immediate(Smi::RawValue(token_pos))); // Source location.
- __ pushl(EAX); // Push the source object.
- GenerateCallRuntime(node_id, token_pos, kConditionTypeErrorRuntimeEntry);
- // We should never return here.
- __ int3();
-
- __ Bind(&done);
-}
-
-
-void CodeGenerator::VisitComparisonNode(ComparisonNode* node) {
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- const Bool& bool_false = Bool::ZoneHandle(Bool::False());
- node->left()->Visit(this);
-
- // The instanceof operator needs special handling.
- if (Token::IsTypeTestOperator(node->kind())) {
- __ popl(EAX); // Left operand.
- ASSERT(node->right()->IsTypeNode());
- GenerateInstanceOf(node->id(),
- node->token_pos(),
- node->left(),
- node->right()->AsTypeNode()->type(),
- (node->kind() == Token::kISNOT));
- if (!IsResultNeeded(node)) {
- __ popl(EAX); // Pop the result of the instanceof operation.
- }
- return;
- }
-
- if (Token::IsTypeCastOperator(node->kind())) {
- // This code generator is not maintained anymore.
- UNIMPLEMENTED();
- }
-
- node->right()->Visit(this);
- // Both left and right values on stack.
-
- // '===' and '!==' are not overloadable.
- if ((node->kind() == Token::kEQ_STRICT) ||
- (node->kind() == Token::kNE_STRICT)) {
- __ popl(EDX); // Right operand.
- __ popl(EAX); // Left operand.
- if (!IsResultNeeded(node)) {
- return;
- }
- Label load_true, done;
- __ cmpl(EAX, EDX);
- if (node->kind() == Token::kEQ_STRICT) {
- __ j(EQUAL, &load_true, Assembler::kNearJump);
- } else {
- __ j(NOT_EQUAL, &load_true, Assembler::kNearJump);
- }
- __ LoadObject(EAX, bool_false);
- __ jmp(&done, Assembler::kNearJump);
- __ Bind(&load_true);
- __ LoadObject(EAX, bool_true);
- __ Bind(&done);
- // Result is in EAX.
- __ pushl(EAX);
- return;
- }
-
- MarkDeoptPoint(node->id(), node->token_pos());
-
- // '!=' not overloadable, always implements negation of '=='.
- // Call operator for '=='.
- if ((node->kind() == Token::kEQ) || (node->kind() == Token::kNE)) {
- // Null is a special receiver with a special type and frequently used on
- // operators "==" and "!=". Emit inlined code for null so that it does not
- // pollute type information at call site.
- Label null_done;
- {
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- Label non_null_compare, load_true;
- // Check if left argument is null.
- __ cmpl(Address(ESP, 1 * kWordSize), raw_null);
- __ j(NOT_EQUAL, &non_null_compare, Assembler::kNearJump);
- // Comparison with NULL is "===".
- // Load/remove arguments.
- __ popl(EDX);
- __ popl(EAX);
- __ cmpl(EAX, EDX);
- if (node->kind() == Token::kEQ) {
- __ j(EQUAL, &load_true, Assembler::kNearJump);
- } else {
- __ j(NOT_EQUAL, &load_true, Assembler::kNearJump);
- }
- __ LoadObject(EAX, bool_false);
- __ jmp(&null_done, Assembler::kNearJump);
- __ Bind(&load_true);
- __ LoadObject(EAX, bool_true);
- __ jmp(&null_done, Assembler::kNearJump);
- __ Bind(&non_null_compare);
- }
- // Do '==' first then negate if necessary,
- const String& operator_name = String::ZoneHandle(String::NewSymbol("=="));
- const int kNumberOfArguments = 2;
- const Array& kNoArgumentNames = Array::Handle();
- const int kNumArgumentsChecked = 1;
- GenerateInstanceCall(node->id(),
- node->token_pos(),
- operator_name,
- kNumberOfArguments,
- kNoArgumentNames,
- kNumArgumentsChecked);
-
- // Result is in EAX. No need to negate if result is not needed.
- if ((node->kind() == Token::kNE) && IsResultNeeded(node)) {
- // Negate result.
- Label load_true, done;
- __ LoadObject(EDX, bool_false);
- __ cmpl(EAX, EDX);
- __ j(EQUAL, &load_true, Assembler::kNearJump);
- __ movl(EAX, EDX); // false.
- __ jmp(&done, Assembler::kNearJump);
- __ Bind(&load_true);
- __ LoadObject(EAX, bool_true);
- __ Bind(&done);
- }
- __ Bind(&null_done);
- // Result is in EAX.
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
- return;
- }
-
- // Call operator.
- GenerateBinaryOperatorCall(node->id(), node->token_pos(), node->Name());
- // Result is in EAX.
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-void CodeGenerator::HandleBackwardBranch(
- intptr_t loop_id, intptr_t token_pos) {
- // Use stack overflow check to eventually stop execution of loops.
- // This is necessary only if a loop does not have calls.
- __ cmpl(ESP,
- Address::Absolute(Isolate::Current()->stack_limit_address()));
- Label no_stack_overflow;
- __ j(ABOVE, &no_stack_overflow);
- GenerateCallRuntime(loop_id,
- token_pos,
- kStackOverflowRuntimeEntry);
- __ Bind(&no_stack_overflow);
-}
-
-
-void CodeGenerator::VisitWhileNode(WhileNode* node) {
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- SourceLabel* label = node->label();
- __ Bind(label->continue_label());
- node->condition()->Visit(this);
- GenerateConditionTypeCheck(node->id(), node->condition()->token_pos());
- __ popl(EAX);
- __ LoadObject(EDX, bool_true);
- __ cmpl(EAX, EDX);
- __ j(NOT_EQUAL, label->break_label());
- node->body()->Visit(this);
- HandleBackwardBranch(node->id(), node->token_pos());
- __ jmp(label->continue_label());
- __ Bind(label->break_label());
-}
-
-
-void CodeGenerator::VisitDoWhileNode(DoWhileNode* node) {
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- SourceLabel* label = node->label();
- Label loop;
- __ Bind(&loop);
- node->body()->Visit(this);
- HandleBackwardBranch(node->id(), node->token_pos());
- __ Bind(label->continue_label());
- node->condition()->Visit(this);
- GenerateConditionTypeCheck(node->id(), node->condition()->token_pos());
- __ popl(EAX);
- __ LoadObject(EDX, bool_true);
- __ cmpl(EAX, EDX);
- __ j(EQUAL, &loop);
- __ Bind(label->break_label());
-}
-
-
-void CodeGenerator::VisitForNode(ForNode* node) {
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- node->initializer()->Visit(this);
- SourceLabel* label = node->label();
- Label loop;
- __ Bind(&loop);
- if (node->condition() != NULL) {
- node->condition()->Visit(this);
- GenerateConditionTypeCheck(node->id(), node->condition()->token_pos());
- __ popl(EAX);
- __ LoadObject(EDX, bool_true);
- __ cmpl(EAX, EDX);
- __ j(NOT_EQUAL, label->break_label());
- }
- node->body()->Visit(this);
- HandleBackwardBranch(node->id(), node->token_pos());
- __ Bind(label->continue_label());
- node->increment()->Visit(this);
- __ jmp(&loop);
- __ Bind(label->break_label());
-}
-
-
-void CodeGenerator::VisitJumpNode(JumpNode* node) {
- SourceLabel* label = node->label();
-
- // Generate inlined code for all finally blocks as we may transfer
- // control out of the 'try' blocks if any.
- for (intptr_t i = 0; i < node->inlined_finally_list_length(); i++) {
- node->InlinedFinallyNodeAt(i)->Visit(this);
- }
-
- // Unchain the context(s) up to the outer context level of the scope which
- // contains the destination label.
- ASSERT(label->owner() != NULL);
- intptr_t target_context_level = 0;
- LocalScope* target_scope = label->owner();
- if (target_scope->num_context_variables() > 0) {
- // The scope of the target label allocates a context, therefore its outer
- // scope is at a lower context level.
- target_context_level = target_scope->context_level() - 1;
- } else {
- // The scope of the target label does not allocate a context, so its outer
- // scope is at the same context level. Find it.
- while ((target_scope != NULL) &&
- (target_scope->num_context_variables() == 0)) {
- target_scope = target_scope->parent();
- }
- if (target_scope != NULL) {
- target_context_level = target_scope->context_level();
- }
- }
- ASSERT(target_context_level >= 0);
- int current_context_level = context_level();
- ASSERT(current_context_level >= target_context_level);
- while (current_context_level-- > target_context_level) {
- __ movl(CTX, FieldAddress(CTX, Context::parent_offset()));
- }
-
- if (node->kind() == Token::kBREAK) {
- __ jmp(label->break_label());
- } else {
- __ jmp(label->continue_label());
- }
-}
-
-
-void CodeGenerator::VisitConditionalExprNode(ConditionalExprNode* node) {
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- Label false_label, done;
- node->condition()->Visit(this);
- GenerateConditionTypeCheck(node->id(), node->condition()->token_pos());
- __ popl(EAX);
- __ LoadObject(EDX, bool_true);
- __ cmpl(EAX, EDX);
- __ j(NOT_EQUAL, &false_label);
- node->true_expr()->Visit(this);
- __ jmp(&done);
- __ Bind(&false_label);
- node->false_expr()->Visit(this);
- __ Bind(&done);
- if (!IsResultNeeded(node)) {
- __ popl(EAX);
- }
-}
-
-
-void CodeGenerator::VisitSwitchNode(SwitchNode *node) {
- SourceLabel* label = node->label();
- node->body()->Visit(this);
- __ Bind(label->break_label());
-}
-
-
-void CodeGenerator::VisitCaseNode(CaseNode* node) {
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- Label case_statements, end_case;
-
- for (int i = 0; i < node->case_expressions()->length(); i++) {
- // Load case expression onto stack.
- AstNode* case_expr = node->case_expressions()->NodeAt(i);
- case_expr->Visit(this);
- __ popl(EAX);
- __ CompareObject(EAX, bool_true);
- // Jump to case clause code if case expression equals switch expression
- __ j(EQUAL, &case_statements);
- }
- // If this case clause contains the default label, fall through to
- // case clause code, else skip this clause.
- if (!node->contains_default()) {
- __ jmp(&end_case);
- }
-
- // If there is a label associated with this case clause, bind it.
- if (node->label() != NULL) {
- __ Bind(node->label()->continue_label());
- }
-
- // Generate code for case clause statements. The parser guarantees that
- // the code contains a jump, so we should never fall through the end
- // of the statements.
- __ Bind(&case_statements);
- node->statements()->Visit(this);
- __ Bind(&end_case);
-}
-
-
-void CodeGenerator::VisitIfNode(IfNode* node) {
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- Label false_label;
- node->condition()->Visit(this);
- GenerateConditionTypeCheck(node->id(), node->condition()->token_pos());
- __ popl(EAX);
- __ LoadObject(EDX, bool_true);
- __ cmpl(EAX, EDX);
- __ j(NOT_EQUAL, &false_label);
- node->true_branch()->Visit(this);
- if (node->false_branch() != NULL) {
- Label done;
- __ jmp(&done);
- __ Bind(&false_label);
- node->false_branch()->Visit(this);
- __ Bind(&done);
- } else {
- __ Bind(&false_label);
- }
-}
-
-
-// Operators '&&' and '||' are not overloadabled, inline them.
-void CodeGenerator::GenerateLogicalAndOrOp(BinaryOpNode* node) {
- // Generate true if (left == true) op (right == true), otherwise generate
- // false, with op being either || or &&.
- const Bool& bool_true = Bool::ZoneHandle(Bool::True());
- const Bool& bool_false = Bool::ZoneHandle(Bool::False());
- Label load_false, done;
- node->left()->Visit(this);
- GenerateConditionTypeCheck(node->id(), node->left()->token_pos());
- __ popl(EAX);
- __ LoadObject(EDX, bool_true);
- __ cmpl(EAX, EDX);
- if (node->kind() == Token::kAND) {
- __ j(NOT_EQUAL, &load_false);
- } else {
- ASSERT(node->kind() == Token::kOR);
- __ j(EQUAL, &done);
- }
- node->right()->Visit(this);
- GenerateConditionTypeCheck(node->id(), node->right()->token_pos());
- __ popl(EAX);
- __ LoadObject(EDX, bool_true);
- __ cmpl(EAX, EDX);
- __ j(EQUAL, &done);
- __ Bind(&load_false);
- __ LoadObject(EAX, bool_false);
- __ Bind(&done);
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-// Expect receiver(left operand) and right operand on stack.
-// Return result in EAX.
-void CodeGenerator::GenerateBinaryOperatorCall(intptr_t node_id,
- intptr_t token_pos,
- const char* name) {
- const String& operator_name = String::ZoneHandle(String::NewSymbol(name));
- const int kNumberOfArguments = 2;
- const Array& kNoArgumentNames = Array::Handle();
- const int kNumArgumentsChecked = 2;
- GenerateInstanceCall(node_id,
- token_pos,
- operator_name,
- kNumberOfArguments,
- kNoArgumentNames,
- kNumArgumentsChecked);
-}
-
-
-void CodeGenerator::VisitBinaryOpNode(BinaryOpNode* node) {
- if ((node->kind() == Token::kAND) || (node->kind() == Token::kOR)) {
- // Operators "&&" and "||" cannot be overloaded, therefore inline them
- // instead of calling the operator.
- GenerateLogicalAndOrOp(node);
- return;
- }
- node->left()->Visit(this);
- node->right()->Visit(this);
- MarkDeoptPoint(node->id(), node->token_pos());
- GenerateBinaryOperatorCall(node->id(), node->token_pos(), node->Name());
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-void CodeGenerator::VisitStringConcatNode(StringConcatNode* node) {
- const String& cls_name = String::Handle(String::NewSymbol("StringBase"));
- const Library& core_lib = Library::Handle(
- Isolate::Current()->object_store()->core_library());
- const Class& cls = Class::Handle(core_lib.LookupClass(cls_name));
- ASSERT(!cls.IsNull());
- const String& func_name = String::Handle(String::NewSymbol("_interpolate"));
- const int number_of_parameters = 1;
- const Function& interpol_func = Function::ZoneHandle(
- Resolver::ResolveStatic(cls, func_name,
- number_of_parameters,
- Array::Handle(),
- Resolver::kIsQualified));
- ASSERT(!interpol_func.IsNull());
-
- // First try to concatenate and canonicalize the values at compile time.
- bool compile_time_interpolation = true;
- Array& literals = Array::Handle(Array::New(node->values()->length()));
- for (int i = 0; i < node->values()->length(); i++) {
- if (node->values()->ElementAt(i)->IsLiteralNode()) {
- LiteralNode* lit = node->values()->ElementAt(i)->AsLiteralNode();
- literals.SetAt(i, lit->literal());
- } else {
- compile_time_interpolation = false;
- break;
- }
- }
- if (compile_time_interpolation) {
- if (!IsResultNeeded(node)) {
- return;
- }
- // Build argument array to pass to the interpolation function.
- GrowableArray<const Object*> interpolate_arg;
- interpolate_arg.Add(&literals);
- const Array& kNoArgumentNames = Array::Handle();
- // Call the interpolation function.
- String& concatenated = String::ZoneHandle();
- concatenated ^= DartEntry::InvokeStatic(interpol_func,
- interpolate_arg,
- kNoArgumentNames);
- if (concatenated.IsUnhandledException()) {
- // TODO(hausner): Shouldn't we generate a throw?
- // Then remove unused CodeGenerator::ErrorMsg().
- ErrorMsg(node->token_pos(),
- "Exception thrown in CodeGenerator::VisitStringConcatNode");
- }
- ASSERT(!concatenated.IsNull());
- concatenated = String::NewSymbol(concatenated);
-
- __ LoadObject(EAX, concatenated);
- __ pushl(EAX);
- return;
- }
-
- // Could not concatenate at compile time, generate a call to
- // interpolation function.
- ArgumentListNode* interpol_arg = new ArgumentListNode(node->token_pos());
- interpol_arg->Add(node->values());
- node->values()->Visit(this);
- __ LoadObject(ECX, interpol_func);
- __ LoadObject(EDX, ArgumentsDescriptor(interpol_arg->length(),
- interpol_arg->names()));
- GenerateCall(node->token_pos(),
- &StubCode::CallStaticFunctionLabel(),
- PcDescriptors::kFuncCall);
- __ addl(ESP, Immediate(interpol_arg->length() * kWordSize));
- // Result is in EAX.
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-void CodeGenerator::VisitInstanceCallNode(InstanceCallNode* node) {
- const int number_of_arguments = node->arguments()->length() + 1;
- // Compute the receiver object and pass it as first argument to call.
- node->receiver()->Visit(this);
- // Now compute rest of the arguments to the call.
- node->arguments()->Visit(this);
- // Some method may be inlined using type feedback, therefore this may be a
- // deoptimization point.
- MarkDeoptPoint(node->id(), node->token_pos());
- const int kNumArgumentsChecked = 1;
- GenerateInstanceCall(node->id(),
- node->token_pos(),
- node->function_name(),
- number_of_arguments,
- node->arguments()->names(),
- kNumArgumentsChecked);
- // Result is in EAX.
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-void CodeGenerator::VisitStaticCallNode(StaticCallNode* node) {
- node->arguments()->Visit(this);
- __ LoadObject(ECX, node->function());
- __ LoadObject(EDX, ArgumentsDescriptor(node->arguments()->length(),
- node->arguments()->names()));
- GenerateCall(node->token_pos(),
- &StubCode::CallStaticFunctionLabel(),
- PcDescriptors::kFuncCall);
- __ addl(ESP, Immediate(node->arguments()->length() * kWordSize));
- // Result is in EAX.
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-void CodeGenerator::VisitClosureCallNode(ClosureCallNode* node) {
- // The spec states that the closure is evaluated before the arguments.
- // Preserve the current context, since it will be overridden by the closure
- // context during the call.
- __ pushl(CTX);
- // Compute the closure object and pass it as first argument to the stub.
- node->closure()->Visit(this);
- // Now compute the arguments to the call.
- node->arguments()->Visit(this);
- // Set up the number of arguments (excluding the closure) to the ClosureCall
- // stub which will setup the closure context and jump to the entrypoint of the
- // closure function (the function will be compiled if it has not already been
- // compiled).
- // NOTE: The stub accesses the closure before the parameter list.
- __ LoadObject(EDX, ArgumentsDescriptor(node->arguments()->length(),
- node->arguments()->names()));
- GenerateCall(node->token_pos(),
- &StubCode::CallClosureFunctionLabel(),
- PcDescriptors::kOther);
- __ addl(ESP, Immediate((node->arguments()->length() + 1) * kWordSize));
- // Restore the context.
- __ popl(CTX);
- // Result is in EAX.
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-// Pushes either the instantiator or null.
-// Pushes the type arguments of the instantiator on the stack.
-// Destroys EBX, ECX.
-void CodeGenerator::GenerateInstantiatorTypeArguments(intptr_t token_pos,
- bool push_instantiator) {
- if (push_instantiator) {
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- __ pushl(raw_null); // Initial instantiator.
- }
- const Class& instantiator_class = Class::Handle(
- parsed_function().function().owner());
- if (instantiator_class.NumTypeParameters() == 0) {
- // The type arguments are compile time constants.
- AbstractTypeArguments& type_arguments = AbstractTypeArguments::ZoneHandle();
- // TODO(regis): Temporary type should be allocated in new gen heap.
- Type& type = Type::Handle(
- Type::New(instantiator_class, type_arguments, token_pos));
- type ^= ClassFinalizer::FinalizeType(
- instantiator_class, type, ClassFinalizer::kFinalizeWellFormed);
- type_arguments = type.arguments();
- __ PushObject(type_arguments);
- } else {
- ASSERT(parsed_function().instantiator() != NULL);
- parsed_function().instantiator()->Visit(this);
- Function& outer_function =
- Function::Handle(parsed_function().function().raw());
- while (outer_function.IsLocalFunction()) {
- outer_function = outer_function.parent_function();
- }
- if (!outer_function.IsFactory()) {
- if (push_instantiator) {
- __ popl(EBX); // Pop instantiator.
- __ popl(ECX); // Discard null instantiator.
- __ pushl(EBX); // Replace it with the real one.
- } else {
- __ popl(EBX); // Pop instantiator.
- }
- // EBX: instantiator.
- // The instantiator is the receiver of the caller, which is not a factory.
- // The receiver cannot be null; extract its AbstractTypeArguments object.
- // Note that in the factory case, the instantiator is the first parameter
- // of the factory, i.e. already an AbstractTypeArguments object.
- intptr_t type_arguments_instance_field_offset =
- instantiator_class.type_arguments_instance_field_offset();
- ASSERT(type_arguments_instance_field_offset != Class::kNoTypeArguments);
- __ movl(EBX, FieldAddress(EBX, type_arguments_instance_field_offset));
- __ pushl(EBX);
- }
- }
-}
-
-
-// Pushes the type arguments on the stack in preparation of an allocation call.
-// If instantiate_type_arguments is true, the instantiated type arguments
-// are pushed on the stack (after an instantiation run time call, if necessary).
-// If instantiate_type_arguments is false, the (possibly uninstantiated) type
-// arguments are pushed on the stack, as well as the type arguments of the
-// instantiator (or the special kNoInstantiator Smi marker, if the type
-// arguments are instantiated).
-void CodeGenerator::GenerateTypeArguments(
- intptr_t node_id,
- intptr_t token_pos,
- const AbstractTypeArguments& type_arguments,
- bool instantiate_type_arguments) {
- const Immediate raw_null =
- Immediate(reinterpret_cast<intptr_t>(Object::null()));
- if (type_arguments.IsNull() || type_arguments.IsInstantiated()) {
- // The type arguments are instantiated.
- __ PushObject(type_arguments);
- if (!instantiate_type_arguments) {
- // The type arguments of the instantiator are not needed, since the
- // type arguments are instantiated.
- __ pushl(Immediate(Smi::RawValue(StubCode::kNoInstantiator)));
- }
- } else {
- // The type arguments are uninstantiated.
- const bool kPushInstantiator = false;
- GenerateInstantiatorTypeArguments(token_pos, kPushInstantiator);
- __ popl(EAX); // Pop instantiator.
- // EAX is the instantiator AbstractTypeArguments object (or null).
- // If the instantiator is null and if the type argument vector
- // instantiated from null becomes a vector of Dynamic, then use null as
- // the type arguments.
- Label type_arguments_instantiated;
- const intptr_t len = type_arguments.Length();
- if (type_arguments.IsRawInstantiatedRaw(len)) {
- __ cmpl(EAX, raw_null);
- __ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump);
- }
- // Instantiate non-null type arguments.
- if (type_arguments.IsUninstantiatedIdentity()) {
- // Check if the instantiator type argument vector is a TypeArguments of a
- // matching length and, if so, use it as the instantiated type_arguments.
- // No need to check RAX for null (again), because a null instance will
- // have the wrong class (Null instead of TypeArguments).
- Label type_arguments_uninstantiated;
- __ CompareClassId(EAX, kTypeArguments, ECX);
- __ j(NOT_EQUAL, &type_arguments_uninstantiated, Assembler::kNearJump);
- __ cmpl(FieldAddress(EAX, TypeArguments::length_offset()),
- Immediate(Smi::RawValue(len)));
- __ j(EQUAL, &type_arguments_instantiated, Assembler::kNearJump);
- __ Bind(&type_arguments_uninstantiated);
- }
- if (instantiate_type_arguments) {
- // A runtime call to instantiate the type arguments is required.
- __ PushObject(Object::ZoneHandle()); // Make room for the result.
- __ PushObject(type_arguments);
- __ pushl(EAX); // Push instantiator type arguments.
- GenerateCallRuntime(node_id,
- token_pos,
- kInstantiateTypeArgumentsRuntimeEntry);
- __ popl(EAX); // Pop instantiator type arguments.
- __ popl(EAX); // Pop uninstantiated type arguments.
- __ popl(EAX); // Pop instantiated type arguments.
- __ Bind(&type_arguments_instantiated);
- __ pushl(EAX); // Instantiated type arguments.
- } else {
- // The allocation stub will instantiate the type arguments.
- __ PushObject(type_arguments);
- __ pushl(EAX); // Instantiator type arguments.
- Label type_arguments_pushed;
- __ jmp(&type_arguments_pushed, Assembler::kNearJump);
-
- __ Bind(&type_arguments_instantiated);
- __ pushl(EAX); // Instantiated type arguments.
- __ pushl(Immediate(Smi::RawValue(StubCode::kNoInstantiator)));
- __ Bind(&type_arguments_pushed);
- }
- }
-}
-
-
-void CodeGenerator::VisitConstructorCallNode(ConstructorCallNode* node) {
- if (node->constructor().IsFactory()) {
- const bool instantiate_type_arguments = true; // First argument to factory.
- GenerateTypeArguments(node->id(),
- node->token_pos(),
- node->type_arguments(),
- instantiate_type_arguments);
- // The top of stack is an instantiated AbstractTypeArguments object
- // (or null).
- int num_args = node->arguments()->length() + 1; // +1 to include type args.
- node->arguments()->Visit(this);
- // Call the factory.
- __ LoadObject(ECX, node->constructor());
- __ LoadObject(EDX, ArgumentsDescriptor(num_args,
- node->arguments()->names()));
- GenerateCall(node->token_pos(),
- &StubCode::CallStaticFunctionLabel(),
- PcDescriptors::kFuncCall);
- // Factory constructor returns object in EAX.
- __ addl(ESP, Immediate(num_args * kWordSize));
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
- return;
- }
-
- const Class& cls = Class::ZoneHandle(node->constructor().owner());
- const bool requires_type_arguments = cls.HasTypeArguments();
- const bool instantiate_type_arguments = false; // Done in stub or runtime.
- if (requires_type_arguments) {
- GenerateTypeArguments(node->id(),
- node->token_pos(),
- node->type_arguments(),
- instantiate_type_arguments);
- }
-
- // If cls is parameterized, the type arguments and the instantiator's
- // type arguments are on the stack.
- // In checked mode, if the type arguments are uninstantiated, they may need to
- // be checked against declared bounds at run time.
- Error& malformed_error = Error::Handle();
- if (FLAG_enable_type_checks &&
- requires_type_arguments &&
- !node->type_arguments().IsNull() &&
- !node->type_arguments().IsInstantiated() &&
- !node->type_arguments().IsWithinBoundsOf(cls,
- node->type_arguments(),
- &malformed_error)) {
- // The uninstantiated type arguments cannot be verified to be within their
- // bounds at compile time, so verify them at runtime.
- // Although the type arguments may be uninstantiated at compile time, they
- // may represent the identity vector and may be replaced by the instantiated
- // type arguments of the instantiator at run time.
- __ popl(ECX); // Pop instantiator type arguments.
- __ popl(EAX); // Pop type arguments.
-
- // Push the result place holder initialized to NULL.
- __ PushObject(Object::ZoneHandle());
- __ pushl(Immediate(Smi::RawValue(node->token_pos())));
- __ PushObject(cls);
- __ pushl(EAX); // Push type arguments.
- __ pushl(ECX); // Push instantiator type arguments.
- GenerateCallRuntime(node->id(),
- node->token_pos(),
- kAllocateObjectWithBoundsCheckRuntimeEntry);
- __ popl(ECX); // Pop instantiator type arguments.
- __ popl(ECX); // Pop type arguments.
- __ popl(ECX); // Pop class.
- __ popl(ECX); // Pop source location.
- __ popl(EAX); // Pop new instance.
- } else {
- const Code& stub = Code::Handle(StubCode::GetAllocationStubForClass(cls));
- const ExternalLabel label(cls.ToCString(), stub.EntryPoint());
- GenerateCall(node->token_pos(), &label, PcDescriptors::kOther);
- if (requires_type_arguments) {
- __ popl(ECX); // Pop instantiator type arguments.
- __ popl(ECX); // Pop type arguments.
- }
- }
-
- if (IsResultNeeded(node)) {
- __ pushl(EAX); // Set up return value from allocate.
- }
-
- // First argument(this) for constructor call which follows.
- __ pushl(EAX);
- // Second argument is the implicit construction phase parameter.
- // Run both the constructor initializer list and the constructor body.
- __ pushl(Immediate(Smi::RawValue(Function::kCtorPhaseAll)));
-
- // Now setup rest of the arguments for the constructor call.
- node->arguments()->Visit(this);
-
- // Call the constructor.
- // +2 to include implicit receiver and phase arguments.
- int num_args = node->arguments()->length() + 2;
- __ LoadObject(ECX, node->constructor());
- __ LoadObject(EDX, ArgumentsDescriptor(num_args, node->arguments()->names()));
- GenerateCall(node->token_pos(),
- &StubCode::CallStaticFunctionLabel(),
- PcDescriptors::kFuncCall);
- // Constructors do not return any value.
-
- // Pop out all the other arguments on the stack.
- __ addl(ESP, Immediate(num_args * kWordSize));
-}
-
-
-// Expects receiver on stack, returns result in EAX..
-void CodeGenerator::GenerateInstanceGetterCall(intptr_t node_id,
- intptr_t token_pos,
- const String& field_name) {
- const String& getter_name =
- String::ZoneHandle(Field::GetterSymbol(field_name));
- const int kNumberOfArguments = 1;
- const Array& kNoArgumentNames = Array::Handle();
- const int kNumArgumentsChecked = 1;
- GenerateInstanceCall(node_id,
- token_pos,
- getter_name,
- kNumberOfArguments,
- kNoArgumentNames,
- kNumArgumentsChecked);
-}
-
-
-// Call to the instance getter.
-void CodeGenerator::VisitInstanceGetterNode(InstanceGetterNode* node) {
- node->receiver()->Visit(this);
- MarkDeoptPoint(node->id(), node->token_pos());
- GenerateInstanceGetterCall(node->id(),
- node->token_pos(),
- node->field_name());
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-// Expects receiver and value on stack.
-void CodeGenerator::GenerateInstanceSetterCall(intptr_t node_id,
- intptr_t token_pos,
- const String& field_name) {
- const String& setter_name =
- String::ZoneHandle(Field::SetterSymbol(field_name));
- const int kNumberOfArguments = 2; // receiver + value.
- const Array& kNoArgumentNames = Array::Handle();
- const int kNumArgumentsChecked = 1;
- GenerateInstanceCall(node_id,
- token_pos,
- setter_name,
- kNumberOfArguments,
- kNoArgumentNames,
- kNumArgumentsChecked);
-}
-
-
-// The call to the instance setter implements the assignment to a field.
-// The result of the assignment to a field is the value being stored.
-void CodeGenerator::VisitInstanceSetterNode(InstanceSetterNode* node) {
- // Compute the receiver object and pass it as first argument to call.
- node->receiver()->Visit(this);
- node->value()->Visit(this);
- MarkDeoptPoint(node->id(), node->token_pos());
- if (IsResultNeeded(node)) {
- __ popl(EAX); // value.
- __ popl(EDX); // receiver.
- __ pushl(EAX); // Preserve value.
- __ pushl(EDX); // arg0: receiver.
- __ pushl(EAX); // arg1: value.
- }
- // It is not necessary to generate a type test of the assigned value here,
- // because the setter will check the type of its incoming arguments.
- GenerateInstanceSetterCall(node->id(),
- node->token_pos(),
- node->field_name());
-}
-
-
-// Return result in EAX.
-void CodeGenerator::GenerateStaticGetterCall(intptr_t token_pos,
- const Class& field_class,
- const String& field_name) {
- const String& getter_name = String::Handle(Field::GetterName(field_name));
- const Function& function =
- Function::ZoneHandle(field_class.LookupStaticFunction(getter_name));
- ASSERT(!function.IsNull());
- __ LoadObject(ECX, function);
- const int kNumberOfArguments = 0;
- const Array& kNoArgumentNames = Array::Handle();
- __ LoadObject(EDX, ArgumentsDescriptor(kNumberOfArguments, kNoArgumentNames));
- GenerateCall(token_pos,
- &StubCode::CallStaticFunctionLabel(),
- PcDescriptors::kFuncCall);
- // No arguments were pushed, hence nothing to pop.
-}
-
-
-// Call to static getter.
-void CodeGenerator::VisitStaticGetterNode(StaticGetterNode* node) {
- GenerateStaticGetterCall(node->token_pos(),
- node->cls(),
- node->field_name());
- // Result is in EAX.
- if (IsResultNeeded(node)) {
- __ pushl(EAX);
- }
-}
-
-
-// Expects value on stack.
-void CodeGenerator::GenerateStaticSetterCall(intptr_t token_pos,
- const Class& field_class,
- const String& field_name) {
- const String& setter_name = String::Handle(Field::SetterName(field_name));
- const Function& function =
- Function::ZoneHandle(field_class.LookupStaticFunction(setter_name));
- ASSERT(!function.IsNull());
- __ LoadObject(ECX, function);
- const int kNumberOfArguments = 1; // value.
- const Array& kNoArgumentNames = Array::Handle();
- __ LoadObject(EDX, ArgumentsDescriptor(kNumberOfArguments, kNoArgumentNames));
- GenerateCall(token_pos,
- &StubCode::CallStaticFunctionLabel(),
- PcDescriptors::kFuncCall);
- __ addl(ESP, Immediate(kNumberOfArguments * kWordSize));
-}
-
-
-// The call to static setter implements assignment to a static field.
-// The result of the assignment is the value being stored.
-void CodeGenerator::VisitStaticSetterNode(StaticSetterNode* node) {
- node->value()->Visit(this);
- if (IsResultNeeded(node)) {
- // Preserve the original value when returning from setter.
- __ movl(EAX, Address(ESP, 0));
- __ pushl(EAX); // arg0: value.
- }
- // It is not necessary to generate a type test of the assigned value here,
- // because the setter will check the type of its incoming arguments.
- GenerateStaticSetterCall(node->token_pos(),
- node->cls(),
- node->field_name());
-}
-
-
-void CodeGenerator::VisitNativeBodyNode(NativeBodyNode* node) {
- intptr_t argument_count = node->argument_count();
- if (node->is_native_instance_closure()) {
- argument_count += 1;
- }
-
- // Push the result place holder initialized to NULL.
- __ PushObject(Object::ZoneHandle());
- // Pass a pointer to the first argument in EAX.
- if (!node->has_optional_parameters() && !node->is_native_instance_closure()) {
- __ leal(EAX, Address(EBP, (1 + argument_count) * kWordSize));
- } else {
- __ leal(EAX,
- Address(EBP, ParsedFunction::kFirstLocalSlotIndex * kWordSize));
- }
- __ movl(ECX, Immediate(reinterpret_cast<uword>(node->native_c_function())));
- __ movl(EDX, Immediate(argument_count));
- GenerateCall(node->token_pos(),
- &StubCode::CallNativeCFunctionLabel(),
- PcDescriptors::kOther);
- // Result is on the stack.
- if (!IsResultNeeded(node)) {
- __ popl(EAX);
- }
-}
-
-
-void CodeGenerator::VisitCatchClauseNode(CatchClauseNode* node) {
- // NOTE: The implicit variables ':saved_context', ':exception_var'
- // and ':stacktrace_var' can never be captured variables.
- // Restore CTX from local variable ':saved_context'.
- GenerateLoadVariable(CTX, node->context_var());
-
- // Restore ESP from EBP as we are coming from a throw and the code for
- // popping arguments has not been run.
- ASSERT(locals_space_size() >= 0);
- intptr_t offset_size = -locals_space_size() + kLocalsOffsetFromFP;
- __ leal(ESP, Address(EBP, offset_size));
-
- // The JumpToExceptionHandler trampoline code sets up
- // - the exception object in EAX (kExceptionObjectReg)
- // - the stacktrace object in register EDX (kStackTraceObjectReg)
- // We now setup the exception object and the trace object
- // so that the handler code has access to these objects.
- GenerateStoreVariable(node->exception_var(),
- kExceptionObjectReg,
- kNoRegister);
- GenerateStoreVariable(node->stacktrace_var(),
- kStackTraceObjectReg,
- kNoRegister);
-
- // Now generate code for the catch handler block.
- node->VisitChildren(this);
-}
-
-
-void CodeGenerator::VisitTryCatchNode(TryCatchNode* node) {
- CodeGeneratorState codegen_state(this);
- int outer_try_index = state()->try_index();
- // We are about to generate code for a new try block, generate an
- // unique 'try index' for this block and set that try index in
- // the code generator state.
- int try_index = generate_next_try_index();
- state()->set_try_index(try_index);
- exception_handlers_list_->AddHandler(try_index, -1);
-
- // Preserve CTX into local variable '%saved_context'.
- GenerateStoreVariable(node->context_var(), CTX, kNoRegister);
-
- node->try_block()->Visit(this);
-
- // We are done generating code for the try block.
- ASSERT(state()->try_index() > CatchClauseNode::kInvalidTryIndex);
- ASSERT(try_index == state()->try_index());
- state()->set_try_index(outer_try_index);
-
- CatchClauseNode* catch_block = node->catch_block();
- if (catch_block != NULL) {
- // Jump over the catch handler block, when exceptions are thrown we
- // will end up at the next instruction.
- __ jmp(node->end_catch_label()->continue_label());
-
- // Set the corresponding try index for this catch block so
- // that we can set the appropriate handler pc when we generate
- // code for this catch block.
- catch_block->set_try_index(try_index);
-
- // Set the handler pc for this try index in the exception handler
- // table.
- exception_handlers_list_->SetPcOffset(try_index, assembler_->CodeSize());
-
- // Generate code for the catch block.
- catch_block->Visit(this);
-
- // Bind the end of catch blocks label here.
- __ Bind(node->end_catch_label()->continue_label());
- }
-
- // Generate code for the finally block if one exists.
- if (node->finally_block() != NULL) {
- node->finally_block()->Visit(this);
- }
-}
-
-
-void CodeGenerator::VisitThrowNode(ThrowNode* node) {
- node->exception()->Visit(this);
- // Exception object is on TOS.
- if (node->stacktrace() != NULL) {
- node->stacktrace()->Visit(this);
- GenerateCallRuntime(node->id(), node->token_pos(), kReThrowRuntimeEntry);
- } else {
- GenerateCallRuntime(node->id(), node->token_pos(), kThrowRuntimeEntry);
- }
- // We should never return here.
- __ int3();
-}
-
-
-void CodeGenerator::VisitInlinedFinallyNode(InlinedFinallyNode* node) {
- int try_index = state()->try_index();
- if (try_index >= 0) {
- // We are about to generate code for an inlined finally block. Exceptions
- // thrown in this block of code should be treated as though they are
- // thrown not from the current try block but the outer try block if any.
- // the code generator state.
- state()->set_try_index((try_index - 1));
- }
-
- // Restore CTX from local variable ':saved_context'.
- GenerateLoadVariable(CTX, node->context_var());
- node->finally_block()->Visit(this);
-
- if (try_index >= 0) {
- state()->set_try_index(try_index);
- }
-}
-
-
-void CodeGenerator::GenerateCall(intptr_t token_pos,
- const ExternalLabel* ext_label,
- PcDescriptors::Kind desc_kind) {
- __ call(ext_label);
- AddCurrentDescriptor(desc_kind, AstNode::kNoId, token_pos);
-}
-
-
-void CodeGenerator::GenerateCallRuntime(intptr_t node_id,
- intptr_t token_pos,
- const RuntimeEntry& entry) {
- __ CallRuntime(entry);
- AddCurrentDescriptor(PcDescriptors::kOther, node_id, token_pos);
-}
-
-
-void CodeGenerator::MarkDeoptPoint(intptr_t node_id,
- intptr_t token_pos) {
- ASSERT(node_id != AstNode::kNoId);
- AddCurrentDescriptor(PcDescriptors::kDeopt, node_id, token_pos);
-}
-
-
-// Uses current pc position and try-index.
-void CodeGenerator::AddCurrentDescriptor(PcDescriptors::Kind kind,
- intptr_t node_id,
- intptr_t token_pos) {
- pc_descriptors_list_->AddDescriptor(kind,
- assembler_->CodeSize(),
- node_id,
- token_pos,
- state()->try_index());
-}
-
-
-void CodeGenerator::ErrorMsg(intptr_t token_pos, const char* format, ...) {
- va_list args;
- va_start(args, format);
- const Class& cls = Class::Handle(parsed_function_.function().owner());
- const Script& script = Script::Handle(cls.script());
- const Error& error = Error::Handle(
- Parser::FormatError(script, token_pos, "Error", format, args));
- va_end(args);
- Isolate::Current()->long_jump_base()->Jump(1, error);
- UNREACHABLE();
-}
-
-} // namespace dart
-
-#endif // defined TARGET_ARCH_IA32

Powered by Google App Engine
This is Rietveld 408576698