// $Id: bytecode.cpp,v 1.185 2004/05/20 21:01:33 elliott-oss Exp $ // // This software is subject to the terms of the IBM Jikes Compiler // License Agreement available at the following URL: // http://ibm.com/developerworks/opensource/jikes. // Copyright (C) 1996, 2004 IBM Corporation and others. All Rights Reserved. // You must accept the terms of that agreement to use this software. // #include "bytecode.h" #include "ast.h" #include "class.h" #include "control.h" #include "semantic.h" #include "stream.h" #include "symbol.h" #include "table.h" #include "option.h" #ifdef HAVE_JIKES_NAMESPACE namespace Jikes { // Open namespace Jikes block #endif void ByteCode::GenerateCode() { AstClassBody* class_body = unit_type -> declaration; unsigned i; // // Process static variables. // for (i = 0; i < class_body -> NumClassVariables(); i++) { AstFieldDeclaration* field_decl = class_body -> ClassVariable(i); for (unsigned vi = 0; vi < field_decl -> NumVariableDeclarators(); vi++) { AstVariableDeclarator* vd = field_decl -> VariableDeclarator(vi); DeclareField(vd -> symbol); } } // // Process instance variables. We separate constant fields from others, // because in 1.4 or later, constant fields are initialized before the // call to super() in order to obey semantics of JLS 13.1. // Tuple constant_instance_fields (unit_type -> NumVariableSymbols()); for (i = 0; i < class_body -> NumInstanceVariables(); i++) { AstFieldDeclaration* field_decl = class_body -> InstanceVariable(i); for (unsigned vi = 0; vi < field_decl -> NumVariableDeclarators(); vi++) { AstVariableDeclarator* vd = field_decl -> VariableDeclarator(vi); VariableSymbol* vsym = vd -> symbol; DeclareField(vsym); if (vd -> variable_initializer_opt && vsym -> initial_value) { AstExpression* init; assert(init = vd -> variable_initializer_opt -> ExpressionCast()); assert(init -> IsConstant() && vd -> symbol -> ACC_FINAL()); constant_instance_fields.Next() = vd; } } } // // Process synthetic fields (this$0, local shadow parameters, $class..., // $array..., $noassert). // if (unit_type -> EnclosingType()) DeclareField(unit_type -> EnclosingInstance()); for (i = 0; i < unit_type -> NumConstructorParameters(); i++) DeclareField(unit_type -> ConstructorParameter(i)); for (i = 0; i < unit_type -> NumClassLiterals(); i++) DeclareField(unit_type -> ClassLiteral(i)); VariableSymbol* assert_variable = unit_type -> AssertVariable(); if (assert_variable) { assert(! control.option.noassert); DeclareField(assert_variable); if (control.option.target < JikesOption::SDK1_4) { semantic.ReportSemError(SemanticError::ASSERT_UNSUPPORTED_IN_TARGET, unit_type -> declaration, unit_type -> ContainingPackageName(), unit_type -> ExternalName()); assert_variable = NULL; } } // // Process declared methods. // for (i = 0; i < class_body -> NumMethods(); i++) { AstMethodDeclaration* method = class_body -> Method(i); if (method -> method_symbol) { int method_index = methods.NextIndex(); // index for method BeginMethod(method_index, method -> method_symbol); if (method -> method_body_opt) // not an abstract method ? EmitBlockStatement(method -> method_body_opt); EndMethod(method_index, method -> method_symbol); } } // // Process synthetic methods (access$..., class$). // for (i = 0; i < unit_type -> NumPrivateAccessMethods(); i++) { int method_index = methods.NextIndex(); // index for method MethodSymbol* method_sym = unit_type -> PrivateAccessMethod(i); AstMethodDeclaration* method = method_sym -> declaration -> MethodDeclarationCast(); assert(method); BeginMethod(method_index, method_sym); EmitBlockStatement(method -> method_body_opt); EndMethod(method_index, method_sym); } MethodSymbol* class_literal_sym = unit_type -> ClassLiteralMethod(); if (class_literal_sym) { int method_index = methods.NextIndex(); // index for method BeginMethod(method_index, class_literal_sym); GenerateClassAccessMethod(); EndMethod(method_index, class_literal_sym); } // // Process the instance initializer. // bool has_instance_initializer = false; if (unit_type -> instance_initializer_method) { AstMethodDeclaration* declaration = (AstMethodDeclaration*) unit_type -> instance_initializer_method -> declaration; AstBlock* init_block = declaration -> method_body_opt; if (! IsNop(init_block)) { int method_index = methods.NextIndex(); // index for method BeginMethod(method_index, unit_type -> instance_initializer_method); bool abrupt = EmitBlockStatement(init_block); if (! abrupt) PutOp(OP_RETURN); EndMethod(method_index, unit_type -> instance_initializer_method); has_instance_initializer = true; } } // // Process all constructors (including synthetic ones). // if (class_body -> default_constructor) CompileConstructor(class_body -> default_constructor, constant_instance_fields, has_instance_initializer); else { for (i = 0; i < class_body -> NumConstructors(); i++) CompileConstructor(class_body -> Constructor(i), constant_instance_fields, has_instance_initializer); } for (i = 0; i < unit_type -> NumPrivateAccessConstructors(); i++) { MethodSymbol* constructor_sym = unit_type -> PrivateAccessConstructor(i); AstConstructorDeclaration* constructor = constructor_sym -> declaration -> ConstructorDeclarationCast(); CompileConstructor(constructor, constant_instance_fields, has_instance_initializer); } // // Process the static initializer. // if (unit_type -> static_initializer_method) { AstMethodDeclaration* declaration = (AstMethodDeclaration*) unit_type -> static_initializer_method -> declaration; AstBlock* init_block = declaration -> method_body_opt; if (assert_variable || ! IsNop(init_block)) { int method_index = methods.NextIndex(); // index for method BeginMethod(method_index, unit_type -> static_initializer_method); if (assert_variable) GenerateAssertVariableInitializer(unit_type -> outermost_type, assert_variable); bool abrupt = EmitBlockStatement(init_block); if (! abrupt) PutOp(OP_RETURN); EndMethod(method_index, unit_type -> static_initializer_method); } } FinishCode(); // // Check for overflow. // if (constant_pool.Length() > 65535) { semantic.ReportSemError(SemanticError::CONSTANT_POOL_OVERFLOW, unit_type -> declaration, unit_type -> ContainingPackageName(), unit_type -> ExternalName()); } if (interfaces.Length() > 65535) { // Interface overflow implies constant pool overflow. semantic.ReportSemError(SemanticError::INTERFACES_OVERFLOW, unit_type -> declaration, unit_type -> ContainingPackageName(), unit_type -> ExternalName()); } if (fields.Length() > 65535) { // Field overflow implies constant pool overflow. semantic.ReportSemError(SemanticError::FIELDS_OVERFLOW, unit_type -> declaration, unit_type -> ContainingPackageName(), unit_type -> ExternalName()); } if (methods.Length() > 65535) { // Method overflow implies constant pool overflow. semantic.ReportSemError(SemanticError::METHODS_OVERFLOW, unit_type -> declaration, unit_type -> ContainingPackageName(), unit_type -> ExternalName()); } if (string_overflow) { semantic.ReportSemError(SemanticError::STRING_OVERFLOW, unit_type -> declaration, unit_type -> ContainingPackageName(), unit_type -> ExternalName()); } if (library_method_not_found) { semantic.ReportSemError(SemanticError::LIBRARY_METHOD_NOT_FOUND, unit_type -> declaration, unit_type -> ContainingPackageName(), unit_type -> ExternalName()); } if (semantic.NumErrors() == 0) Write(unit_type); #ifdef JIKES_DEBUG if (control.option.debug_dump_class) Print(); #endif // JIKES_DEBUG } // // initialized_fields is a list of fields needing code to initialize. // void ByteCode::CompileConstructor(AstConstructorDeclaration* constructor, Tuple& constants, bool has_instance_initializer) { MethodSymbol* method_symbol = constructor -> constructor_symbol; AstMethodBody* constructor_block = constructor -> constructor_body; int method_index = methods.NextIndex(); // index for method BeginMethod(method_index, method_symbol); // // Set up the index to account for this, this$0, and normal parameters, // so we know where the local variable shadows begin. // shadow_parameter_offset = unit_type -> EnclosingType() ? 2 : 1; if (unit_type -> NumConstructorParameters()) { for (unsigned j = 0; j < method_symbol -> NumFormalParameters(); j++) shadow_parameter_offset += GetTypeWords(method_symbol -> FormalParameter(j) -> Type()); } if (control.option.target < JikesOption::SDK1_4) { // // Prior to JDK 1.4, VMs incorrectly complained if shadow // initialization happened before the superconstructor, even though // the JVMS permits it. // if (constructor_block -> explicit_constructor_opt) EmitStatement(constructor_block -> explicit_constructor_opt); else assert(unit_type == control.Object() && "A constructor without an explicit constructor invocation"); } // // Supply synthetic field initialization unless constructor calls this(). // Also initialize all constants. // if (constructor_block -> explicit_constructor_opt && ! constructor_block -> explicit_constructor_opt -> ThisCallCast()) { if (unit_type -> EnclosingType()) { // // Initialize this$0 // VariableSymbol* this0_parameter = unit_type -> EnclosingInstance(); PutOp(OP_ALOAD_0); LoadLocal(1, this0_parameter -> Type()); PutOp(OP_PUTFIELD); PutU2(RegisterFieldref(this0_parameter)); } for (unsigned i = 0, index = shadow_parameter_offset; i < unit_type -> NumConstructorParameters(); i++) { VariableSymbol* shadow = unit_type -> ConstructorParameter(i); PutOp(OP_ALOAD_0); LoadLocal(index, shadow -> Type()); PutOp(OP_PUTFIELD); if (control.IsDoubleWordType(shadow -> Type())) ChangeStack(-1); PutU2(RegisterFieldref(shadow)); index += GetTypeWords(shadow -> Type()); } for (unsigned j = 0; j < constants.Length(); j ++) EmitStatement(constants[j]); } if (control.option.target >= JikesOption::SDK1_4) { // // Since JDK 1.4, VMs correctly allow shadow initialization before // the superconstructor, which is necessary to avoid null pointer // exceptions with polymorphic calls from the superconstructor. // if (constructor_block -> explicit_constructor_opt) EmitStatement(constructor_block -> explicit_constructor_opt); else assert(unit_type == control.Object() && "A constructor without an explicit constructor invocation"); } // // Compile instance initializers unless the constructor calls this(). // shadow_parameter_offset = 0; if (has_instance_initializer && constructor_block -> explicit_constructor_opt && ! constructor_block -> explicit_constructor_opt -> ThisCallCast()) { PutOp(OP_ALOAD_0); PutOp(OP_INVOKESPECIAL); CompleteCall(unit_type -> instance_initializer_method, 0); } EmitBlockStatement(constructor_block); EndMethod(method_index, method_symbol); } void ByteCode::DeclareField(VariableSymbol* symbol) { int field_index = fields.NextIndex(); // index for field fields[field_index] = new FieldInfo(); const TypeSymbol* type = symbol -> Type(); if (type -> num_dimensions > 255) { semantic.ReportSemError(SemanticError::ARRAY_OVERFLOW, symbol -> declarator); } fields[field_index] -> SetFlags(symbol -> Flags()); fields[field_index] -> SetNameIndex(RegisterName(symbol -> ExternalIdentity())); fields[field_index] -> SetDescriptorIndex(RegisterUtf8(type -> signature)); // // Any final field initialized with a constant must have a ConstantValue // attribute. However, the VM only reads this value for static fields. // if (symbol -> initial_value) { assert(symbol -> ACC_FINAL()); assert(type -> Primitive() || type == control.String()); u2 index = ((control.IsSimpleIntegerValueType(type) || type == control.boolean_type) ? RegisterInteger(DYNAMIC_CAST (symbol -> initial_value)) : type == control.String() ? RegisterString(DYNAMIC_CAST (symbol -> initial_value)) : type == control.float_type ? RegisterFloat(DYNAMIC_CAST (symbol -> initial_value)) : type == control.long_type ? RegisterLong(DYNAMIC_CAST (symbol -> initial_value)) : RegisterDouble(DYNAMIC_CAST (symbol -> initial_value))); u2 attribute_index = RegisterUtf8(control.ConstantValue_literal); fields[field_index] -> AddAttribute(new ConstantValueAttribute(attribute_index, index)); } if (symbol -> ACC_SYNTHETIC() && control.option.target < JikesOption::SDK1_5) { fields[field_index] -> AddAttribute(CreateSyntheticAttribute()); } if (symbol -> IsDeprecated()) fields[field_index] -> AddAttribute(CreateDeprecatedAttribute()); } void ByteCode::BeginMethod(int method_index, MethodSymbol* msym) { assert(msym); #ifdef DUMP if (control.option.g) Coutput << "(51) Generating code for method \"" << msym -> Name() << "\" in " << unit_type -> ContainingPackageName() << "/" << unit_type -> ExternalName() << endl; #endif // DUMP #ifdef JIKES_DEBUG if (control.option.debug_trace_stack_change) Coutput << endl << "Generating method " << unit_type -> ContainingPackageName() << '.' << unit_type -> ExternalName() << '.' << msym -> Name() << msym -> signature -> value << endl; #endif // JIKES_DEBUG MethodInitialization(); methods[method_index] = new MethodInfo(); methods[method_index] -> SetNameIndex(RegisterName(msym -> ExternalIdentity())); methods[method_index] -> SetDescriptorIndex(RegisterUtf8(msym -> signature)); methods[method_index] -> SetFlags(msym -> Flags()); if (msym -> ACC_SYNTHETIC() && control.option.target < JikesOption::SDK1_5) { methods[method_index] -> AddAttribute(CreateSyntheticAttribute()); } if (msym -> IsDeprecated()) methods[method_index] -> AddAttribute(CreateDeprecatedAttribute()); // // Generate throws attribute if method throws any exceptions // if (msym -> NumThrows()) { ExceptionsAttribute* exceptions_attribute = new ExceptionsAttribute(RegisterUtf8(control.Exceptions_literal)); for (unsigned i = 0; i < msym -> NumThrows(); i++) exceptions_attribute -> AddExceptionIndex(RegisterClass(msym -> Throws(i))); methods[method_index] -> AddAttribute(exceptions_attribute); } // // here if need code and associated attributes. // if (! (msym -> ACC_ABSTRACT() || msym -> ACC_NATIVE())) { method_stack = new MethodStack(msym -> max_block_depth, msym -> block_symbol -> max_variable_index); code_attribute = new CodeAttribute(RegisterUtf8(control.Code_literal), msym -> block_symbol -> max_variable_index); line_number = 0; line_number_table_attribute = new LineNumberTableAttribute (RegisterUtf8(control.LineNumberTable_literal)); local_variable_table_attribute = (control.option.g & JikesOption::VARS) ? (new LocalVariableTableAttribute (RegisterUtf8(control.LocalVariableTable_literal))) : (LocalVariableTableAttribute*) NULL; } if (msym -> Type() -> num_dimensions > 255) { assert(msym -> declaration -> MethodDeclarationCast()); Ast* type = ((AstMethodDeclaration*) msym -> declaration) -> type; semantic.ReportSemError(SemanticError::ARRAY_OVERFLOW, type); } VariableSymbol* parameter = NULL; for (unsigned i = 0; i < msym -> NumFormalParameters(); i++) { parameter = msym -> FormalParameter(i); if (parameter -> Type() -> num_dimensions > 255) { semantic.ReportSemError(SemanticError::ARRAY_OVERFLOW, parameter -> declarator); } } if (parameter) { int last_parameter_index = parameter -> LocalVariableIndex(); if (control.IsDoubleWordType(parameter -> Type())) last_parameter_index++; if (last_parameter_index >= 255) { assert(msym -> declaration); AstMethodDeclaration* method_declaration = msym -> declaration -> MethodDeclarationCast(); AstConstructorDeclaration* constructor_declaration = msym -> declaration -> ConstructorDeclarationCast(); AstMethodDeclarator* declarator = method_declaration ? method_declaration -> method_declarator : constructor_declaration -> constructor_declarator; semantic.ReportSemError(SemanticError::PARAMETER_OVERFLOW, declarator -> left_parenthesis_token, declarator -> right_parenthesis_token, msym -> Header(), unit_type -> ContainingPackageName(), unit_type -> ExternalName()); } } } void ByteCode::EndMethod(int method_index, MethodSymbol* msym) { assert(msym); if (! (msym -> ACC_ABSTRACT() || msym -> ACC_NATIVE())) { // // Make sure that no component in the code attribute exceeded its // limit. // if (msym -> block_symbol -> max_variable_index > 65535) { semantic.ReportSemError(SemanticError::LOCAL_VARIABLES_OVERFLOW, msym -> declaration, msym -> Header(), unit_type -> ContainingPackageName(), unit_type -> ExternalName()); } if (max_stack > 65535) { semantic.ReportSemError(SemanticError::STACK_OVERFLOW, msym -> declaration, msym -> Header(), unit_type -> ContainingPackageName(), unit_type -> ExternalName()); } if (code_attribute -> CodeLengthExceeded()) { semantic.ReportSemError(SemanticError::CODE_OVERFLOW, msym -> declaration, msym -> Header(), unit_type -> ContainingPackageName(), unit_type -> ExternalName()); } // // // code_attribute -> SetMaxStack(max_stack); // // Sanity check - make sure nothing jumped past here // assert((u2) last_label_pc < code_attribute -> CodeLength() || code_attribute -> CodeLength() == 0x0ffff); assert(stack_depth == 0); // // attribute length: // Need to review how to make attribute_name and attribute_length. // Only write line number attribute if there are line numbers to // write, and -g:lines is enabled. // if ((control.option.g & JikesOption::LINES) && line_number_table_attribute -> LineNumberTableLength()) { code_attribute -> AddAttribute(line_number_table_attribute); } else { // line_number_table_attribute not needed, so delete it now delete line_number_table_attribute; } // // Debug level -g:vars & not dealing with generated accessed method // if ((control.option.g & JikesOption::VARS) && (! msym -> accessed_member) && (msym -> Identity() != control.class_name_symbol)) { if (! msym -> ACC_STATIC()) // add 'this' to local variable table { local_variable_table_attribute -> AddLocalVariable(0, code_attribute -> CodeLength(), RegisterUtf8(control.this_name_symbol -> Utf8_literal), RegisterUtf8(msym -> containing_type -> signature), 0); } // // For a normal constructor or method. // for (unsigned i = 0; i < msym -> NumFormalParameters(); i++) { VariableSymbol* parameter = msym -> FormalParameter(i); local_variable_table_attribute -> AddLocalVariable(0, code_attribute -> CodeLength(), RegisterName(parameter -> ExternalIdentity()), RegisterUtf8(parameter -> Type() -> signature), parameter -> LocalVariableIndex()); } if (local_variable_table_attribute -> LocalVariableTableLength()) code_attribute -> AddAttribute(local_variable_table_attribute); else // local_variable_table_attribute not needed, so delete it now delete local_variable_table_attribute; } else delete local_variable_table_attribute; methods[method_index] -> AddAttribute(code_attribute); delete method_stack; } } // // This is called to initialize non-constant static fields, and all instance // fields, that were declared with optional initializers. // void ByteCode::InitializeVariable(AstVariableDeclarator* vd) { assert(vd -> variable_initializer_opt && vd -> symbol); AstExpression* expression = vd -> variable_initializer_opt -> ExpressionCast(); if (expression) { if (vd -> symbol -> ACC_STATIC()) assert(! vd -> symbol -> initial_value); else PutOp(OP_ALOAD_0); // load 'this' for instance variables EmitExpression(expression); } else { AstArrayInitializer* array_initializer = vd -> variable_initializer_opt -> ArrayInitializerCast(); assert(array_initializer); if (! vd -> symbol -> ACC_STATIC()) PutOp(OP_ALOAD_0); // load 'this' for instance variables InitializeArray(vd -> symbol -> Type(), array_initializer); } PutOp(vd -> symbol -> ACC_STATIC() ? OP_PUTSTATIC : OP_PUTFIELD); if (expression && control.IsDoubleWordType(expression -> Type())) ChangeStack(-1); PutU2(RegisterFieldref(vd -> symbol)); } void ByteCode::InitializeArray(const TypeSymbol* type, AstArrayInitializer* array_initializer, bool need_value) { TypeSymbol* subtype = type -> ArraySubtype(); if (need_value) { LoadImmediateInteger(array_initializer -> NumVariableInitializers()); EmitNewArray(1, type); // make the array } for (unsigned i = 0; i < array_initializer -> NumVariableInitializers(); i++) { Ast* entry = array_initializer -> VariableInitializer(i); AstExpression* expr = entry -> ExpressionCast(); if (expr && (IsZero(expr) || expr -> Type() == control.null_type)) { bool optimize; if (expr -> Type() == control.float_type) { FloatLiteralValue* value = DYNAMIC_CAST (expr -> value); optimize = value -> value.IsPositiveZero(); } else if (expr -> Type() == control.double_type) { DoubleLiteralValue* value = DYNAMIC_CAST (expr -> value); optimize = value -> value.IsPositiveZero(); } else optimize = true; if (optimize) { EmitExpression(expr, false); continue; } } if (need_value) { PutOp(OP_DUP); LoadImmediateInteger(i); } if (expr) EmitExpression(expr, need_value); else { assert(entry -> ArrayInitializerCast()); InitializeArray(subtype, entry -> ArrayInitializerCast(), need_value); } if (need_value) StoreArrayElement(subtype); } } // // Generate code for local variable declaration. // void ByteCode::DeclareLocalVariable(AstVariableDeclarator* declarator) { if (control.option.g & JikesOption::VARS) { #ifdef JIKES_DEBUG // Must be uninitialized. assert(method_stack -> StartPc(declarator -> symbol) == 0xFFFF); #endif // JIKES_DEBUG #ifdef DUMP Coutput << "(53) Variable \"" << declarator -> symbol -> Name() << "\" numbered " << declarator -> symbol -> LocalVariableIndex() << " was processed" << endl; #endif // DUMP method_stack -> StartPc(declarator -> symbol) = code_attribute -> CodeLength(); } TypeSymbol* type = declarator -> symbol -> Type(); if (type -> num_dimensions > 255) semantic.ReportSemError(SemanticError::ARRAY_OVERFLOW, declarator); if (declarator -> symbol -> initial_value) { // // Optimization: If we are not tracking local variable names, we do // not need to waste space on a constant as it is always inlined. // if (! (control.option.g & JikesOption::VARS)) return; LoadLiteral(declarator -> symbol -> initial_value, declarator -> symbol -> Type()); } else if (declarator -> variable_initializer_opt) { AstArrayCreationExpression* ace = declarator -> variable_initializer_opt -> ArrayCreationExpressionCast(); AstArrayInitializer* ai = declarator -> variable_initializer_opt -> ArrayInitializerCast(); if (ace) EmitArrayCreationExpression(ace); else if (ai) InitializeArray(type, ai); else // evaluation as expression { AstExpression* expr = (AstExpression*) declarator -> variable_initializer_opt; assert(declarator -> variable_initializer_opt -> ExpressionCast()); EmitExpression(expr); // // Prior to JDK 1.5, VMs incorrectly complained if assigning an // array type into an element of a null expression (in other // words, null was not being treated as compatible with a // multi-dimensional array on the aastore opcode). The // workaround requires a checkcast any time null might be // assigned to a multi-dimensional local variable or directly // used as an array access base. // if (control.option.target < JikesOption::SDK1_5 && IsMultiDimensionalArray(type) && (StripNops(expr) -> Type() == control.null_type)) { PutOp(OP_CHECKCAST); PutU2(RegisterClass(type)); } } } else return; // if nothing to initialize StoreLocal(declarator -> symbol -> LocalVariableIndex(), type); } // // JLS Chapter 13: Blocks and Statements // Statements control the sequence of evaluation of Java programs, // are executed for their effects and do not have values. // // Processing of loops requires a loop stack, especially to handle // break and continue statements. // Loops have three labels, LABEL_BEGIN for start of loop body, // LABEL_BREAK to leave the loop, and LABEL_CONTINUE to continue the iteration. // Each loop requires a break label; other labels are defined and used // as needed. // Labels allocated but never used incur no extra cost in the generated // byte code, only in additional execution expense during compilation. // // This method returns true if the statement is guaranteed to complete // abruptly (break, continue, throw, return, and special cases of if); it // allows some dead code elimination. // bool ByteCode::EmitStatement(AstStatement* statement) { if (! statement -> BlockCast()) { line_number_table_attribute -> AddLineNumber(code_attribute -> CodeLength(), semantic.lex_stream -> Line(statement -> LeftToken())); } assert(stack_depth == 0); // stack empty at start of statement switch (statement -> kind) { case Ast::METHOD_BODY: case Ast::BLOCK: // JLS 14.2 return EmitBlockStatement((AstBlock*) statement); case Ast::LOCAL_VARIABLE_DECLARATION: // JLS 14.3 { AstLocalVariableStatement* lvs = statement -> LocalVariableStatementCast(); for (unsigned i = 0; i < lvs -> NumVariableDeclarators(); i++) DeclareLocalVariable(lvs -> VariableDeclarator(i)); } return false; case Ast::EMPTY_STATEMENT: // JLS 14.5 return false; case Ast::EXPRESSION_STATEMENT: // JLS 14.7 EmitStatementExpression(statement -> ExpressionStatementCast() -> expression); return false; case Ast::IF: // JLS 14.8 { AstIfStatement* if_statement = (AstIfStatement*) statement; // Constant condition. if (IsOne(if_statement -> expression)) return EmitBlockStatement(if_statement -> true_statement); if (IsZero(if_statement -> expression)) { if (if_statement -> false_statement_opt) return EmitBlockStatement(if_statement -> false_statement_opt); return false; } // True and false parts. if (if_statement -> false_statement_opt && ! IsNop(if_statement -> false_statement_opt)) { if (IsNop(if_statement -> true_statement)) { Label label; EmitBranchIfExpression(if_statement -> expression, true, label, (if_statement -> false_statement_opt)); assert(stack_depth == 0); EmitBlockStatement(if_statement -> false_statement_opt); DefineLabel(label); CompleteLabel(label); return false; } Label label1, label2; bool abrupt; AstBlock* true_statement = if_statement -> true_statement; EmitBranchIfExpression(if_statement -> expression, false, label1, true_statement); assert(stack_depth == 0); abrupt = EmitBlockStatement(true_statement); if (! abrupt) EmitBranch(OP_GOTO, label2, if_statement -> false_statement_opt); DefineLabel(label1); abrupt &= EmitBlockStatement(if_statement -> false_statement_opt); if (! abrupt) { DefineLabel(label2); CompleteLabel(label2); } CompleteLabel(label1); return abrupt; } // No false part. if (IsNop(if_statement -> true_statement)) { EmitExpression(if_statement -> expression, false); return false; } Label label1; EmitBranchIfExpression(if_statement -> expression, false, label1, if_statement -> true_statement); assert(stack_depth == 0); EmitBlockStatement(if_statement -> true_statement); DefineLabel(label1); CompleteLabel(label1); return false; } case Ast::SWITCH: // JLS 14.9 return EmitSwitchStatement(statement -> SwitchStatementCast()); case Ast::SWITCH_BLOCK: // JLS 14.9 case Ast::SWITCH_LABEL: // // These nodes are handled by SwitchStatement and // are not directly visited. // assert(false && "faulty logic encountered"); return false; case Ast::WHILE: // JLS 14.10 { AstWhileStatement* wp = statement -> WhileStatementCast(); bool abrupt = false; // // Branch to continuation test. This test is placed after the // body of the loop we can fall through into it after each // loop iteration without the need for an additional branch, // unless the loop body always completes abruptly. // if (! wp -> statement -> can_complete_normally) { if (wp -> expression -> IsConstant()) { // must be true, or internal statement would be // unreachable assert(semantic.IsConstantTrue(wp -> expression)); abrupt = true; } else { line_number_table_attribute -> AddLineNumber(code_attribute -> CodeLength(), semantic.lex_stream -> Line(wp -> expression -> LeftToken())); EmitBranchIfExpression(wp -> expression, false, method_stack -> TopBreakLabel(), wp -> statement); } EmitBlockStatement(wp -> statement); assert(stack_depth == 0); return abrupt; } Label& continue_label = method_stack -> TopContinueLabel(); if (wp -> expression -> IsConstant()) { // must be true, or internal statement would be // unreachable assert(semantic.IsConstantTrue(wp -> expression)); abrupt = true; } else EmitBranch(OP_GOTO, continue_label, wp -> statement); Label begin_label; DefineLabel(begin_label); u2 begin_pc = code_attribute -> CodeLength(); abrupt |= EmitBlockStatement(wp -> statement); bool empty = (begin_pc == code_attribute -> CodeLength()); DefineLabel(continue_label); assert(stack_depth == 0); // // Reset the line number before evaluating the expression // line_number_table_attribute -> AddLineNumber(code_attribute -> CodeLength(), semantic.lex_stream -> Line(wp -> expression -> LeftToken())); EmitBranchIfExpression(wp -> expression, true, empty ? continue_label : begin_label, wp -> statement); CompleteLabel(begin_label); CompleteLabel(continue_label); return abrupt && ! wp -> can_complete_normally; } case Ast::DO: // JLS 14.11 { AstDoStatement* sp = statement -> DoStatementCast(); Label begin_label; DefineLabel(begin_label); bool abrupt = EmitBlockStatement(sp -> statement); if (IsLabelUsed(method_stack -> TopContinueLabel())) { DefineLabel(method_stack -> TopContinueLabel()); CompleteLabel(method_stack -> TopContinueLabel()); abrupt = false; } assert(stack_depth == 0); if (! abrupt) { // // Reset the line number before evaluating the expression // line_number_table_attribute -> AddLineNumber(code_attribute -> CodeLength(), semantic.lex_stream -> Line(sp -> expression -> LeftToken())); EmitBranchIfExpression(sp -> expression, true, begin_label, sp -> statement); } CompleteLabel(begin_label); return (abrupt || IsOne(sp -> expression)) && ! sp -> can_complete_normally; } case Ast::FOR: // JLS 14.12 { AstForStatement* for_statement = statement -> ForStatementCast(); bool abrupt = false; for (unsigned i = 0; i < for_statement -> NumForInitStatements(); i++) EmitStatement(for_statement -> ForInitStatement(i)); Label begin_label; Label test_label; // // The loop test is placed after the body, unless the body // always completes abruptly, to save an additional jump. // if (! for_statement -> statement -> can_complete_normally) { abrupt = true; if (for_statement -> end_expression_opt) { if (for_statement -> end_expression_opt -> IsConstant()) { // must be true, or internal statement would be // unreachable assert(semantic.IsConstantTrue(for_statement -> end_expression_opt)); } else { abrupt = false; line_number_table_attribute -> AddLineNumber(code_attribute -> CodeLength(), semantic.lex_stream -> Line(for_statement -> end_expression_opt -> LeftToken())); EmitBranchIfExpression(for_statement -> end_expression_opt, false, method_stack -> TopBreakLabel(), for_statement -> statement); } } EmitBlockStatement(for_statement -> statement); assert(stack_depth == 0); return abrupt; } Label& continue_label = method_stack -> TopContinueLabel(); if (for_statement -> end_expression_opt && ! for_statement -> end_expression_opt -> IsConstant()) { EmitBranch(OP_GOTO, (for_statement -> NumForUpdateStatements() ? test_label : continue_label), for_statement -> statement); } else abrupt = true; DefineLabel(begin_label); u2 begin_pc = code_attribute -> CodeLength(); abrupt |= EmitBlockStatement(for_statement -> statement); bool empty = (begin_pc == code_attribute -> CodeLength()); DefineLabel(continue_label); for (unsigned j = 0; j < for_statement -> NumForUpdateStatements(); j++) { EmitStatement(for_statement -> ForUpdateStatement(j)); } DefineLabel(test_label); CompleteLabel(test_label); AstExpression* end_expr = for_statement -> end_expression_opt; if (end_expr) { assert(stack_depth == 0); // // Reset the line number before evaluating the expression // line_number_table_attribute -> AddLineNumber(code_attribute -> CodeLength(), semantic.lex_stream -> Line(end_expr -> LeftToken())); EmitBranchIfExpression(end_expr, true, empty ? continue_label : begin_label, for_statement -> statement); } else EmitBranch(OP_GOTO, empty ? continue_label : begin_label, for_statement -> statement); CompleteLabel(continue_label); CompleteLabel(begin_label); return abrupt && ! for_statement -> can_complete_normally; } case Ast::FOREACH: // JSR 201 EmitForeachStatement((AstForeachStatement*) statement); return false; case Ast::BREAK: // JLS 14.13 { unsigned nesting_level = statement -> BreakStatementCast() -> nesting_level; AstBlock* over = method_stack -> Block(nesting_level); u2 jump_size = (over -> RightToken() - over -> LeftToken() < TOKEN_WIDTH_REQUIRING_GOTOW) ? 3 : 5; if (ProcessAbruptExit(nesting_level, jump_size)) { EmitBranch(OP_GOTO, method_stack -> BreakLabel(nesting_level), over); } return true; } case Ast::CONTINUE: // JLS 14.14 { unsigned nesting_level = statement -> ContinueStatementCast() -> nesting_level; AstBlock* over = method_stack -> Block(nesting_level); u2 jump_size = (over -> RightToken() - over -> LeftToken() < TOKEN_WIDTH_REQUIRING_GOTOW) ? 3 : 5; if (ProcessAbruptExit(nesting_level, jump_size)) { EmitBranch(OP_GOTO, method_stack -> ContinueLabel(nesting_level), over); } return true; } case Ast::RETURN: // JLS 14.15 EmitReturnStatement(statement -> ReturnStatementCast()); return true; case Ast::SUPER_CALL: EmitSuperInvocation((AstSuperCall*) statement); return false; case Ast::THIS_CALL: EmitThisInvocation((AstThisCall*) statement); return false; case Ast::THROW: // JLS 14.16 EmitExpression(statement -> ThrowStatementCast() -> expression); PutOp(OP_ATHROW); return true; case Ast::SYNCHRONIZED_STATEMENT: // JLS 14.17 return EmitSynchronizedStatement((AstSynchronizedStatement*) statement); case Ast::TRY: // JLS 14.18 EmitTryStatement((AstTryStatement*) statement); return ! statement -> can_complete_normally; case Ast::CATCH: // JLS 14.18 case Ast::FINALLY: // JLS 14.18 // handled by TryStatement assert(false && "should not get here"); return false; case Ast::ASSERT: // JDK 1.4 (JSR 41) EmitAssertStatement((AstAssertStatement*) statement); return false; case Ast::LOCAL_CLASS: // Class Declaration // // This is factored out by the front end; and so must be // skipped here (remember, interfaces cannot be declared locally). // return false; case Ast::VARIABLE_DECLARATOR: // // This is not really a statement, but we treat it as one to make // initializer blocks easier to intermix with variable declarations. // InitializeVariable((AstVariableDeclarator*) statement); return false; default: assert(false && "unknown statement kind"); return false; } } void ByteCode::EmitReturnStatement(AstReturnStatement* statement) { AstExpression* expression = statement -> expression_opt; if (! expression) { if (ProcessAbruptExit(method_stack -> NestingLevel(0), 1)) PutOp(OP_RETURN); } else { TypeSymbol* type = expression -> Type(); assert(type != control.void_type); EmitExpression(expression); if (ProcessAbruptExit(method_stack -> NestingLevel(0), 1, type)) GenerateReturn(type); } } bool ByteCode::EmitBlockStatement(AstBlock* block) { assert(stack_depth == 0); // stack empty at start of statement method_stack -> Push(block); bool abrupt = false; for (unsigned i = 0; i < block -> NumStatements() && ! abrupt; i++) abrupt = EmitStatement(block -> Statement(i)); // // If contained break statements jump out of this block, define the label. // if (IsLabelUsed(method_stack -> TopBreakLabel())) { DefineLabel(method_stack -> TopBreakLabel()); CompleteLabel(method_stack -> TopBreakLabel()); abrupt = false; } if (control.option.g & JikesOption::VARS) { for (unsigned i = 0; i < block -> NumLocallyDefinedVariables(); i++) { VariableSymbol* variable = block -> LocallyDefinedVariable(i); if (method_stack -> StartPc(variable) == 0xFFFF) // never used continue; #ifdef DUMP Coutput << "(56) The symbol \"" << variable -> Name() << "\" numbered " << variable -> LocalVariableIndex() << " was released" << endl; #endif // DUMP local_variable_table_attribute -> AddLocalVariable(method_stack -> StartPc(variable), code_attribute -> CodeLength(), RegisterName(variable -> ExternalIdentity()), RegisterUtf8(variable -> Type() -> signature), variable -> LocalVariableIndex()); } } method_stack -> Pop(); return abrupt; } void ByteCode::EmitStatementExpression(AstExpression* expression) { switch (expression -> kind) { case Ast::CALL: EmitMethodInvocation((AstMethodInvocation*) expression, false); break; case Ast::POST_UNARY: EmitPostUnaryExpression((AstPostUnaryExpression*) expression, false); break; case Ast::PRE_UNARY: EmitPreUnaryExpression((AstPreUnaryExpression*) expression, false); break; case Ast::ASSIGNMENT: EmitAssignmentExpression((AstAssignmentExpression*) expression, false); break; case Ast::CLASS_CREATION: EmitClassCreationExpression((AstClassCreationExpression*) expression, false); break; default: assert(false && "invalid statement expression kind"); } } // // Generate code for switch statement. Good code generation requires // detailed knowledge of the target machine. Lacking this, we simply // choose between LOOKUPSWITCH and TABLESWITCH by picking that // opcode that takes the least number of bytes in the byte code. // // With TABLESWITCH, a target must be provided for every entry in the range // low..high, even though the user may not have provided an explicit entry, // in which case the default action is to be taken. For example // switch (e) { // case 1:2:3: act1; break; // case 5:6: act2; break; // default: defact; break; // } // translates as // switch (e) { // case 1:2:3: act1; break; // case 4: goto defa: // case 5:6: act2; break; // defa: // default: defact; // } // bool ByteCode::EmitSwitchStatement(AstSwitchStatement* switch_statement) { AstBlock* switch_block = switch_statement -> switch_block; u2 op_start = code_attribute -> CodeLength(); unsigned i; bool abrupt; assert(stack_depth == 0); // stack empty at start of statement // // Optimization: When switching on a constant, emit only those blocks // that it will flow through. // switch (constant) { ... } => single code path // if (switch_statement -> expression -> IsConstant()) { CaseElement* target = switch_statement -> CaseForValue(DYNAMIC_CAST (switch_statement -> expression -> value) -> value); if (! target) return false; // // Bring all previously-declared variables into scope, then compile // until we run out of blocks or else complete abruptly. // method_stack -> Push(switch_block); for (i = 0; i < target -> block_index; i++) EmitSwitchBlockStatement(switch_statement -> Block(i), true); abrupt = false; for ( ; ! abrupt && i < switch_statement -> NumBlocks(); i++) { abrupt = EmitSwitchBlockStatement(switch_statement -> Block(i), abrupt); } CloseSwitchLocalVariables(switch_block, op_start); if (IsLabelUsed(method_stack -> TopBreakLabel())) { abrupt = false; DefineLabel(method_stack -> TopBreakLabel()); CompleteLabel(method_stack -> TopBreakLabel()); } method_stack -> Pop(); return abrupt; } // // Optimization: When there are zero blocks, emit the expression. // switch (expr) {} => expr; // if (! switch_statement -> NumBlocks()) { EmitExpression(switch_statement -> expression, false); return false; } // // Optimization: When there is one block labeled by default, emit it. // switch (expr) { default: block; } => expr, block // switch (expr) { case a: default: block; } => expr, block // if (switch_statement -> NumBlocks() == 1 && switch_statement -> DefaultCase()) { EmitExpression(switch_statement -> expression, false); method_stack -> Push(switch_block); abrupt = EmitSwitchBlockStatement(switch_statement -> Block(0), false); CloseSwitchLocalVariables(switch_block, op_start); if (IsLabelUsed(method_stack -> TopBreakLabel())) { abrupt = false; DefineLabel(method_stack -> TopBreakLabel()); CompleteLabel(method_stack -> TopBreakLabel()); } method_stack -> Pop(); return abrupt; } // // Optimization: If there is one non-default label, turn this into an // if statement. // if (switch_statement -> NumCases() == 1) { // // switch (expr) { case a: block; } => if (expr == a) block; // if (! switch_statement -> DefaultCase()) { EmitExpression(switch_statement -> expression); Label lab; if (switch_statement -> Case(0) -> value) { LoadImmediateInteger(switch_statement -> Case(0) -> value); EmitBranch(OP_IF_ICMPNE, lab, switch_block); } else EmitBranch(OP_IFNE, lab, switch_block); method_stack -> Push(switch_block); EmitSwitchBlockStatement(switch_statement -> Block(0), false); CloseSwitchLocalVariables(switch_block, op_start); if (IsLabelUsed(method_stack -> TopBreakLabel())) { DefineLabel(method_stack -> TopBreakLabel()); CompleteLabel(method_stack -> TopBreakLabel()); } method_stack -> Pop(); DefineLabel(lab); CompleteLabel(lab); return false; } // // TODO: Implement these optimizations. // switch (expr) { case a: fallthrough_block; default: block; } // => if (expr == a) fallthrough_block; block; // switch (expr) { case a: abrupt_block; default: block; } // => if (expr == a) abrupt_block; else block; // switch (expr) { default: fallthrough_block; case a: block; } // => if (expr != a) fallthrough_block; block; // switch (expr) { default: abrupt_block; case a: block; } // => if (expr != a) abrupt_block; else block; // } // // Use tableswitch if size of tableswitch case is no more than 32 bytes // (8 words) more code than lookup case. // bool use_lookup = true; // set if using LOOKUPSWITCH opcode unsigned ncases = switch_statement -> NumCases(); unsigned nlabels = ncases; i4 high = 0, low = 0; if (ncases) { low = switch_statement -> Case(0) -> value; high = switch_statement -> Case(ncases - 1) -> value; assert(low <= high); // // Workaround for Sun JVM TABLESWITCH bug in JDK 1.2, 1.3 // when case values of 0x7ffffff0 through 0x7fffffff are used. // Force the generation of a LOOKUPSWITCH in these circumstances. // if (high < 0x7ffffff0L || control.option.target >= JikesOption::SDK1_4) { // We want to compute (1 + (high - low + 1)) < (ncases * 2 + 8). // However, we must beware of integer overflow. i4 range = high - low + 1; if (range > 0 && (unsigned) range < (ncases * 2 + 8)) { use_lookup = false; // use tableswitch nlabels = range; assert(nlabels >= ncases); } } } // // Set up the environment for the switch block. This must be done before // emitting the expression, in case the expression is an assignment. // method_stack -> Push(switch_block); // // Reset the line number before evaluating the expression // line_number_table_attribute -> AddLineNumber(code_attribute -> CodeLength(), semantic.lex_stream -> Line(switch_statement -> expression -> LeftToken())); EmitExpression(switch_statement -> expression); PutOp(use_lookup ? OP_LOOKUPSWITCH : OP_TABLESWITCH); op_start = last_op_pc; // pc at start of instruction // // Supply any needed padding. // while (code_attribute -> CodeLength() % 4 != 0) PutU1(0); // // Note that if there is no default clause in switch statement, we create // one that corresponds to do nothing and branches to start of next // statement. The default label is case_labels[nlabels]. // Label* case_labels = new Label[nlabels + 1]; UseLabel(case_labels[nlabels], 4, code_attribute -> CodeLength() - op_start); if (use_lookup) { PutU4(ncases); for (i = 0; i < ncases; i++) { PutU4(switch_statement -> Case(i) -> value); UseLabel(case_labels[i], 4, code_attribute -> CodeLength() - op_start); } } else { PutU4(low); PutU4(high); for (i = 0; i < nlabels; i++) { UseLabel(case_labels[i], 4, code_attribute -> CodeLength() - op_start); } } // // March through switch block statements, compiling blocks in proper // order. We must respect order in which blocks are seen so that blocks // lacking a terminal break fall through to the proper place. // abrupt = false; for (i = 0; i < switch_block -> NumStatements(); i++) { AstSwitchBlockStatement* switch_block_statement = switch_statement -> Block(i); for (unsigned li = 0; li < switch_block_statement -> NumSwitchLabels(); li++) { AstSwitchLabel* switch_label = switch_block_statement -> SwitchLabel(li); if (use_lookup) DefineLabel(case_labels[switch_label -> map_index]); else if (switch_label -> expression_opt) { i4 value = DYNAMIC_CAST (switch_label -> expression_opt -> value) -> value; DefineLabel(case_labels[value - low]); } else { DefineLabel(case_labels[nlabels]); // // We must also point all inserted cases to the default. // unsigned j = 1; i4 k = low + 1; for ( ; j < switch_statement -> NumCases(); j++, k++) while (k != switch_statement -> Case(j) -> value) DefineLabel(case_labels[k++ - low]); } } abrupt = EmitSwitchBlockStatement(switch_block_statement, false); } CloseSwitchLocalVariables(switch_block, op_start); for (i = 0; i <= nlabels; i++) { if (! case_labels[i].defined) { abrupt = false; DefineLabel(case_labels[i]); } CompleteLabel(case_labels[i]); } // // If this switch statement was "broken", we define the break label here. // if (IsLabelUsed(method_stack -> TopBreakLabel())) { // need define only if used DefineLabel(method_stack -> TopBreakLabel()); CompleteLabel(method_stack -> TopBreakLabel()); abrupt = false; } delete [] case_labels; method_stack -> Pop(); assert(abrupt || switch_statement -> can_complete_normally); return abrupt; } bool ByteCode::EmitSwitchBlockStatement(AstSwitchBlockStatement* block, bool abrupt) { for (unsigned i = 0; i < block -> NumStatements(); i++) { if (! abrupt) abrupt = EmitStatement(block -> Statement(i)); else if (block -> Statement(i) -> LocalVariableStatementCast()) { // // In a switch statement, local variable declarations are // accessible in other case labels even if the declaration // itself is unreachable. // AstLocalVariableStatement* lvs = (AstLocalVariableStatement*) block -> Statement(i); for (unsigned j = 0; j < lvs -> NumVariableDeclarators(); j++) { AstVariableDeclarator* declarator = lvs -> VariableDeclarator(j); if (control.option.g & JikesOption::VARS) { method_stack -> StartPc(declarator -> symbol) = code_attribute -> CodeLength(); } if (declarator -> symbol -> Type() -> num_dimensions > 255) { semantic.ReportSemError(SemanticError::ARRAY_OVERFLOW, declarator); } } } } return abrupt; } void ByteCode::CloseSwitchLocalVariables(AstBlock* switch_block, u2 op_start) { if (control.option.g & JikesOption::VARS) { for (unsigned i = 0; i < switch_block -> NumLocallyDefinedVariables(); i++) { VariableSymbol* variable = switch_block -> LocallyDefinedVariable(i); if (method_stack -> StartPc(variable) > op_start) { if (method_stack -> StartPc(variable) == 0xFFFF) // never used continue; #ifdef DUMP Coutput << "(58) The symbol \"" << variable -> Name() << "\" numbered " << variable -> LocalVariableIndex() << " was released" << endl; #endif // DUMP local_variable_table_attribute -> AddLocalVariable(method_stack -> StartPc(variable), code_attribute -> CodeLength(), RegisterName(variable -> ExternalIdentity()), RegisterUtf8(variable -> Type() -> signature), variable -> LocalVariableIndex()); } } } } // // 13.18 The try statement // void ByteCode::EmitTryStatement(AstTryStatement* statement) { // // If the finally label in the surrounding block is used by a try // statement, it is cleared after the finally block associated with the // try statement has been processed. // assert(method_stack -> TopFinallyLabel().uses.Length() == 0); assert(method_stack -> TopFinallyLabel().defined == false); assert(method_stack -> TopFinallyLabel().definition == 0); u2 start_try_block_pc = code_attribute -> CodeLength(); // start pc assert(method_stack -> TopHandlerRangeStart().Length() == 0 && method_stack -> TopHandlerRangeEnd().Length() == 0); method_stack -> TopHandlerRangeStart().Push(start_try_block_pc); bool emit_finally_clause = statement -> finally_clause_opt && ! IsNop(statement -> finally_clause_opt -> block); // // If we determined the finally clause is a nop, remove the tag // TRY_CLAUSE_WITH_FINALLY so that abrupt completions do not emit JSR. // On the other hand, if the finally clause cannot complete normally, // change the tag to ABRUPT_TRY_FINALLY so that abrupt completions emit // a GOTO instead of a JSR. Also, mark a try block which has a catch // clause but no finally clause, in case an abrupt exit forces a split // in the range of protected code. // if (statement -> finally_clause_opt) if (! emit_finally_clause) statement -> block -> SetTag(AstBlock::NONE); else if (! statement -> finally_clause_opt -> block -> can_complete_normally) { statement -> block -> SetTag(AstBlock::ABRUPT_TRY_FINALLY); } if (statement -> block -> Tag() == AstBlock::NONE && statement -> NumCatchClauses()) { statement -> block -> SetTag(AstBlock::TRY_CLAUSE_WITH_CATCH); } bool abrupt = EmitBlockStatement(statement -> block); // // The computation of end_try_block_pc, the instruction following the last // instruction in the body of the try block, does not include the code, if // any, needed to call a finally block or skip to the end of the try // statement. // u2 end_try_block_pc = code_attribute -> CodeLength(); Tuple handler_starts(method_stack -> TopHandlerRangeStart()); Tuple handler_ends(method_stack -> TopHandlerRangeEnd()); handler_ends.Push(end_try_block_pc); assert(handler_starts.Length() == handler_ends.Length()); // // If try block is not empty, process catch clauses, including "special" // clause for finally. // if (start_try_block_pc != end_try_block_pc) { // Use the label in the block immediately enclosing try statement. Label& finally_label = method_stack -> TopFinallyLabel(); Label end_label; // // If try block completes normally, skip code for catch blocks. // if (! abrupt && (emit_finally_clause || statement -> NumCatchClauses())) { EmitBranch(OP_GOTO, end_label, statement); } for (unsigned i = 0; i < statement -> NumCatchClauses(); i++) { u2 handler_pc = code_attribute -> CodeLength(); AstCatchClause* catch_clause = statement -> CatchClause(i); VariableSymbol* parameter_symbol = catch_clause -> parameter_symbol; assert(stack_depth == 0); stack_depth = 1; // account for the exception already on the stack line_number_table_attribute -> AddLineNumber(code_attribute -> CodeLength(), semantic.lex_stream -> Line(catch_clause -> catch_token)); // // Unless debugging, we don't need to waste a variable on an // empty catch. // if ((control.option.g & JikesOption::VARS) || ! IsNop(catch_clause -> block)) { StoreLocal(parameter_symbol -> LocalVariableIndex(), parameter_symbol -> Type()); } else PutOp(OP_POP); u2 handler_type = RegisterClass(parameter_symbol -> Type()); for (int j = handler_starts.Length(); --j >= 0; ) { code_attribute -> AddException(handler_starts[j], handler_ends[j], handler_pc, handler_type); } // // If we determined the finally clause is a nop, remove the tag // TRY_CLAUSE_WITH_FINALLY so that abrupt completions do not emit // JSR. On the other hand, if the finally clause cannot complete // normally, change the tag to ABRUPT_TRY_FINALLY so that abrupt // completions emit a GOTO instead of a JSR. // if (statement -> finally_clause_opt) { if (! emit_finally_clause) catch_clause -> block -> SetTag(AstBlock::NONE); else if (! statement -> finally_clause_opt -> block -> can_complete_normally) { catch_clause -> block -> SetTag(AstBlock::ABRUPT_TRY_FINALLY); } } abrupt = EmitBlockStatement(catch_clause -> block); if (control.option.g & JikesOption::VARS) { local_variable_table_attribute -> AddLocalVariable(handler_pc, code_attribute -> CodeLength(), RegisterName(parameter_symbol -> ExternalIdentity()), RegisterUtf8(parameter_symbol -> Type() -> signature), parameter_symbol -> LocalVariableIndex()); } // // If catch block completes normally, skip further catch blocks. // if (! abrupt && (emit_finally_clause || i < (statement -> NumCatchClauses() - 1))) { EmitBranch(OP_GOTO, end_label, statement); } } // // If this try statement contains a finally clause, then ... // if (emit_finally_clause) { int variable_index = method_stack -> TopBlock() -> block_symbol -> helper_variable_index; u2 finally_start_pc = code_attribute -> CodeLength(); u2 special_end_pc = finally_start_pc; // // Emit code for "special" handler to make sure finally clause is // invoked in case an otherwise uncaught exception is thrown in the // try block, or an exception is thrown from within a catch block. // This must cover all instructions through the jsr, in case of // asynchronous exceptions. // assert(stack_depth == 0); stack_depth = 1; // account for the exception already on stack if (statement -> finally_clause_opt -> block -> can_complete_normally) { StoreLocal(variable_index, control.Throwable()); // Save, EmitBranch(OP_JSR, finally_label, statement); special_end_pc = code_attribute -> CodeLength(); LoadLocal(variable_index, control.Throwable()); // reload, and PutOp(OP_ATHROW); // rethrow exception. } else { // // Ignore the exception already on the stack, since we know // the finally clause overrides it. // PutOp(OP_POP); } method_stack -> TopHandlerRangeEnd().Push(special_end_pc); unsigned count = method_stack -> TopHandlerRangeStart().Length(); assert(count == method_stack -> TopHandlerRangeEnd().Length()); while (count--) { code_attribute -> AddException(method_stack -> TopHandlerRangeStart().Pop(), method_stack -> TopHandlerRangeEnd().Pop(), finally_start_pc, 0); } // // Generate code for finally clause. If the finally block can // complete normally, this is reached from a JSR, so save the // return address. Otherwise, this is reached from a GOTO. // DefineLabel(finally_label); assert(stack_depth == 0); if (statement -> finally_clause_opt -> block -> can_complete_normally) { stack_depth = 1; // account for the return location on stack StoreLocal(variable_index + 1, control.Object()); } else if (IsLabelUsed(end_label)) { DefineLabel(end_label); CompleteLabel(end_label); } EmitBlockStatement(statement -> finally_clause_opt -> block); // // If a finally block can complete normally, return to the saved // address of the caller. // if (statement -> finally_clause_opt -> block -> can_complete_normally) { PutOpWide(OP_RET, variable_index + 1); // // Now, if the try or catch blocks complete normally, execute // the finally block before advancing to next statement. We // need to trap one more possibility of an asynchronous // exception before the jsr has started. // if (IsLabelUsed(end_label)) { DefineLabel(end_label); CompleteLabel(end_label); EmitBranch(OP_JSR, finally_label, statement -> finally_clause_opt -> block); special_end_pc = code_attribute -> CodeLength(); code_attribute -> AddException(special_end_pc - 3, special_end_pc, finally_start_pc, 0); } } CompleteLabel(finally_label); } else { // // Finally block is not present, advance to next statement, and // clean up the handler start/end ranges. // assert(! IsLabelUsed(finally_label)); DefineLabel(end_label); CompleteLabel(end_label); method_stack -> TopHandlerRangeStart().Reset(); method_stack -> TopHandlerRangeEnd().Reset(); } } else { // // Try block was empty; skip all catch blocks, and a finally block // is treated normally. // method_stack -> TopHandlerRangeStart().Reset(); if (emit_finally_clause) EmitBlockStatement(statement -> finally_clause_opt -> block); } } // // Exit to block at level, freeing monitor locks and invoking finally // clauses as appropriate. The width is 1 for return, 3 for normal a normal // GOTO (from a break or continue), or 5 for a GOTO_W. The return is true // unless some intervening finally block cannot complete normally. // bool ByteCode::ProcessAbruptExit(unsigned level, u2 width, TypeSymbol* return_type) { int variable_index = -1; // // We must store the return value in a variable, rather than on the // stack, in case a finally block contains an embedded try-catch which // wipes out the stack. // if (return_type) { for (unsigned i = method_stack -> Size() - 1; i > 0 && method_stack -> NestingLevel(i) != level; i--) { unsigned nesting_level = method_stack -> NestingLevel(i); unsigned enclosing_level = method_stack -> NestingLevel(i - 1); AstBlock* block = method_stack -> Block(nesting_level); if (block -> Tag() == AstBlock::TRY_CLAUSE_WITH_FINALLY) { variable_index = method_stack -> Block(enclosing_level) -> block_symbol -> helper_variable_index + 2; } else if (block -> Tag() == AstBlock::ABRUPT_TRY_FINALLY) { variable_index = -1; PutOp(control.IsDoubleWordType(return_type) ? OP_POP2 : OP_POP); break; } } } if (variable_index >= 0) StoreLocal(variable_index, return_type); for (unsigned i = method_stack -> Size() - 1; i > 0 && method_stack -> NestingLevel(i) != level; i--) { unsigned nesting_level = method_stack -> NestingLevel(i); unsigned enclosing_level = method_stack -> NestingLevel(i - 1); AstBlock* block = method_stack -> Block(nesting_level); if (block -> Tag() == AstBlock::TRY_CLAUSE_WITH_FINALLY) { EmitBranch(OP_JSR, method_stack -> FinallyLabel(enclosing_level), method_stack -> Block(enclosing_level)); method_stack -> HandlerRangeEnd(enclosing_level). Push(code_attribute -> CodeLength()); } else if (block -> Tag() == AstBlock::ABRUPT_TRY_FINALLY) { // // Ignore the width of the abrupt instruction, because the abrupt // finally preempts it. // width = 0; EmitBranch(OP_GOTO, method_stack -> FinallyLabel(enclosing_level), method_stack -> Block(enclosing_level)); method_stack -> HandlerRangeEnd(enclosing_level). Push(code_attribute -> CodeLength()); break; } else if (block -> Tag() == AstBlock::SYNCHRONIZED) { // // This code must be safe for asynchronous exceptions. Note that // we are splitting the range of instructions covered by the // synchronized statement catchall handler. // int variable_index = method_stack -> Block(enclosing_level) -> block_symbol -> helper_variable_index; LoadLocal(variable_index, control.Object()); PutOp(OP_MONITOREXIT); method_stack -> HandlerRangeEnd(enclosing_level). Push(code_attribute -> CodeLength()); } else if (block -> Tag() == AstBlock::TRY_CLAUSE_WITH_CATCH) { method_stack -> HandlerRangeEnd(enclosing_level). Push(code_attribute -> CodeLength()); } } if (variable_index >= 0) LoadLocal(variable_index, return_type); for (unsigned j = method_stack -> Size() - 1; j > 0 && method_stack -> NestingLevel(j) != level; j--) { unsigned nesting_level = method_stack -> NestingLevel(j); unsigned enclosing_level = method_stack -> NestingLevel(j - 1); AstBlock* block = method_stack -> Block(nesting_level); if (block -> Tag() == AstBlock::SYNCHRONIZED || block -> Tag() == AstBlock::TRY_CLAUSE_WITH_CATCH || block -> Tag() == AstBlock::TRY_CLAUSE_WITH_FINALLY) { method_stack -> HandlerRangeStart(enclosing_level). Push(code_attribute -> CodeLength() + width); } else if (block -> Tag() == AstBlock::ABRUPT_TRY_FINALLY) { method_stack -> HandlerRangeStart(enclosing_level). Push(code_attribute -> CodeLength()); return false; } } return true; } void ByteCode::EmitBranch(Opcode opc, Label& lab, AstStatement* over) { // Use the number of tokens as a heuristic for the size of the statement // we're jumping over. If the statement is large enough, either change // to the 4-byte branch opcode or write out a branch around a goto_w for // branch opcodes that don't have a long form. int sizeHeuristic = over ? over -> RightToken() - over -> LeftToken() : 0; if (sizeHeuristic < TOKEN_WIDTH_REQUIRING_GOTOW) { PutOp(opc); UseLabel(lab, 2, 1); return; } if (opc == OP_GOTO) { PutOp(OP_GOTO_W); UseLabel(lab, 4, 1); return; } if (opc == OP_JSR) { PutOp(OP_JSR_W); UseLabel(lab, 4, 1); return; } // if op lab // => // if !op label2 // goto_w lab // label2: PutOp(InvertIfOpCode(opc)); Label label2; UseLabel(label2, 2, 1); PutOp(OP_GOTO_W); UseLabel(lab, 4, 1); DefineLabel(label2); CompleteLabel(label2); } // // java provides a variety of conditional branch instructions, so // that a number of operators merit special handling: // constant operand // negation (we eliminate it) // equality // ?: && and || (partial evaluation) // comparisons // Other expressions are just evaluated and the appropriate // branch emitted. // // TODO: return a bool that is true if the statement being branched over is // even needed (if statements and other places might have a constant false // expression, allowing the next block of code to be skipped entirely). // void ByteCode::EmitBranchIfExpression(AstExpression* p, bool cond, Label& lab, AstStatement* over) { p = StripNops(p); assert(p -> Type() == control.boolean_type); if (p -> IsConstant()) { if (IsZero(p) != cond) EmitBranch(OP_GOTO, lab, over); return; } AstPreUnaryExpression* pre = p -> PreUnaryExpressionCast(); if (pre) // must be ! { // // branch_if(!e,c,l) => branch_if(e,!c,l) // assert(pre -> Tag() == AstPreUnaryExpression::NOT); EmitBranchIfExpression(pre -> expression, ! cond, lab, over); return; } AstConditionalExpression* conditional = p -> ConditionalExpressionCast(); if (conditional) { if (conditional -> test_expression -> IsConstant()) { // // branch_if(true?a:b, cond, lab) => branch_if(a, cond, lab); // branch_if(false?a:b, cond, lab) => branch_if(b, cond, lab); // EmitBranchIfExpression((IsZero(conditional -> test_expression) ? conditional -> false_expression : conditional -> true_expression), cond, lab, over); } else if (IsOne(conditional -> true_expression)) { // // branch_if(expr?true:true, c, l) => expr, branch if c // branch_if(expr?true:false, c, l) => branch_if(expr, c, l); // branch_if(expr?true:b, c, l) => branch_if(expr || b, c, l); // if (IsOne(conditional -> false_expression)) { EmitExpression(conditional -> test_expression, false); if (cond) EmitBranch(OP_GOTO, lab, over); } else if (IsZero(conditional -> false_expression)) { EmitBranchIfExpression(conditional -> test_expression, cond, lab, over); } else if (cond) { EmitBranchIfExpression(conditional -> test_expression, true, lab, over); EmitBranchIfExpression(conditional -> false_expression, true, lab, over); } else { Label skip; EmitBranchIfExpression(conditional -> test_expression, true, skip, over); EmitBranchIfExpression(conditional -> false_expression, false, lab, over); DefineLabel(skip); CompleteLabel(skip); } } else if (IsZero(conditional -> true_expression)) { // // branch_if(expr?false:true, c, l) => branch_if(expr, ! c, l); // branch_if(expr?false:false, c, l) => expr, branch if ! c // branch_if(expr?false:b, c, l) => branch_if(!expr && b, c, l); // if (IsOne(conditional -> false_expression)) { EmitBranchIfExpression(conditional -> test_expression, ! cond, lab, over); } else if (IsZero(conditional -> false_expression)) { EmitExpression(conditional -> test_expression, false); if (! cond) EmitBranch(OP_GOTO, lab, over); } else if (! cond) { EmitBranchIfExpression(conditional -> test_expression, true, lab, over); EmitBranchIfExpression(conditional -> false_expression, false, lab, over); } else { Label skip; EmitBranchIfExpression(conditional -> test_expression, true, skip, over); EmitBranchIfExpression(conditional -> false_expression, true, lab, over); DefineLabel(skip); CompleteLabel(skip); } } else if (IsOne(conditional -> false_expression)) { // // branch_if(expr?a:true, c, l) => branch_if(!expr || a, c, l); // if (cond) { EmitBranchIfExpression(conditional -> test_expression, false, lab, over); EmitBranchIfExpression(conditional -> true_expression, true, lab, over); } else { Label skip; EmitBranchIfExpression(conditional -> test_expression, false, skip, over); EmitBranchIfExpression(conditional -> true_expression, false, lab, over); DefineLabel(skip); CompleteLabel(skip); } } else if (IsZero(conditional -> false_expression)) { // // branch_if(expr?a:false, c, l) => branch_if(expr && a, c, l); // if (! cond) { EmitBranchIfExpression(conditional -> test_expression, false, lab, over); EmitBranchIfExpression(conditional -> true_expression, false, lab, over); } else { Label skip; EmitBranchIfExpression(conditional -> test_expression, false, skip, over); EmitBranchIfExpression(conditional -> true_expression, true, lab, over); DefineLabel(skip); CompleteLabel(skip); } } else { // // branch_if(expr?a:b, c, l) => // branch_if(expr, false, lab1) // branch_if(a, c, l) // goto lab2 // lab1: branch_if(b, c, l) // lab2: // Label lab1, lab2; EmitBranchIfExpression(conditional -> test_expression, false, lab1, over); EmitBranchIfExpression(conditional -> true_expression, cond, lab, over); EmitBranch(OP_GOTO, lab2, over); DefineLabel(lab1); CompleteLabel(lab1); EmitBranchIfExpression(conditional -> false_expression, cond, lab, over); DefineLabel(lab2); CompleteLabel(lab2); } return; } AstInstanceofExpression* instanceof = p -> InstanceofExpressionCast(); if (instanceof) { AstExpression* expr = StripNops(instanceof -> expression); TypeSymbol* left_type = expr -> Type(); TypeSymbol* right_type = instanceof -> type -> symbol; if (right_type -> num_dimensions > 255) { semantic.ReportSemError(SemanticError::ARRAY_OVERFLOW, instanceof -> type); } if (left_type == control.null_type) { // // We know the result: false. But emit the left expression, // in case of side effects in (expr ? null : null). // EmitExpression(expr, false); if (! cond) EmitBranch(OP_GOTO, lab, over); } else if (expr -> IsConstant() || // a String constant expr -> BinaryExpressionCast()) // a String concat { // // We know the result: true, since the expression is non-null // and String is a final class. // assert(left_type == control.String()); EmitExpression(expr, false); if (cond) EmitBranch(OP_GOTO, lab, over); } else if ((expr -> ThisExpressionCast() || expr -> SuperExpressionCast() || expr -> ClassLiteralCast() || expr -> ClassCreationExpressionCast() || expr -> ArrayCreationExpressionCast()) && left_type -> IsSubtype(right_type)) { // // We know the result: true, since the expression is non-null. // EmitExpression(expr, false); if (cond) EmitBranch(OP_GOTO, lab, over); } else { EmitExpression(expr); PutOp(OP_INSTANCEOF); PutU2(RegisterClass(right_type)); EmitBranch((cond ? OP_IFNE : OP_IFEQ), lab, over); } return; } // // dispose of non-binary expression case by just evaluating // operand and emitting appropiate test. // AstBinaryExpression* bp = p -> BinaryExpressionCast(); if (! bp) { EmitExpression(p); EmitBranch((cond ? OP_IFNE : OP_IFEQ), lab, over); return; } // // Here if binary expression, so extract operands // AstExpression* left = StripNops(bp -> left_expression); AstExpression* right = StripNops(bp -> right_expression); TypeSymbol* left_type = left -> Type(); TypeSymbol* right_type = right -> Type(); switch (bp -> Tag()) { case AstBinaryExpression::AND_AND: // // branch_if(true&&b, cond, lab) => branch_if(b, cond, lab); // branch_if(false&&b, cond, lab) => branch_if(false, cond, lab); // if (left -> IsConstant()) { if (IsOne(left)) EmitBranchIfExpression(right, cond, lab, over); else if (! cond) EmitBranch(OP_GOTO, lab, over); } // // branch_if(a&&true, cond, lab) => branch_if(a, cond, lab); // branch_if(a&&false, cond, lab) => emit(a), pop; for side effects // else if (right -> IsConstant()) { if (IsOne(right)) EmitBranchIfExpression(left, cond, lab, over); else { EmitExpression(left, false); if (! cond) EmitBranch(OP_GOTO, lab, over); } } // // branch_if(a&&b, true, lab) => // branch_if(a,false,skip); // branch_if(b,true,lab); // skip: // branch_if(a&&b, false, lab) => // branch_if(a,false,lab); // branch_if(b,false,lab); // else if (cond) { Label skip; EmitBranchIfExpression(left, false, skip, over); EmitBranchIfExpression(right, true, lab, over); DefineLabel(skip); CompleteLabel(skip); } else { EmitBranchIfExpression(left, false, lab, over); EmitBranchIfExpression(right, false, lab, over); } return; case AstBinaryExpression::OR_OR: // // branch_if(false||b, cond, lab) => branch_if(b, cond, lab); // branch_if(true||b, cond, lab) => branch_if(true, cond, lab); // if (left -> IsConstant()) { if (IsZero(left)) EmitBranchIfExpression(right, cond, lab, over); else if (cond) EmitBranch(OP_GOTO, lab, over); } // // branch_if(a||false, cond, lab) => branch_if(a, cond, lab); // branch_if(a||true, cond, lab) => emit(a), pop; for side effects // else if (right -> IsConstant()) { if (IsZero(right)) EmitBranchIfExpression(left, cond, lab, over); else { EmitExpression(left, false); if (cond) EmitBranch(OP_GOTO, lab, over); } } // // branch_if(a||b,true,lab) => // branch_if(a,true,lab); // branch_if(b,true,lab); // branch_if(a||b,false,lab) => // branch_if(a,true,skip); // branch_if(b,false,lab); // skip: // else if (cond) { EmitBranchIfExpression(left, true, lab, over); EmitBranchIfExpression(right, true, lab, over); } else { Label skip; EmitBranchIfExpression(left, true, skip, over); EmitBranchIfExpression(right, false, lab, over); DefineLabel(skip); CompleteLabel(skip); } return; case AstBinaryExpression::XOR: // ^ on booleans is equavalent to != assert(left_type == control.boolean_type); // Fallthrough! case AstBinaryExpression::EQUAL_EQUAL: case AstBinaryExpression::NOT_EQUAL: // // One of the operands is null. We must evaluate both operands, to get // any side effects in (expr ? null : null). // if (left_type == control.null_type || right_type == control.null_type) { EmitExpression(left, left_type != control.null_type); EmitExpression(right, right_type != control.null_type); if (left_type == right_type) { if (cond == (bp -> Tag() == AstBinaryExpression::EQUAL_EQUAL)) { EmitBranch(OP_GOTO, lab, over); } } else { if (bp -> Tag() == AstBinaryExpression::EQUAL_EQUAL) EmitBranch(cond ? OP_IFNULL : OP_IFNONNULL, lab, over); else EmitBranch(cond ? OP_IFNONNULL : OP_IFNULL, lab, over); } return; } // // One of the operands is true. Branch on the other. // if (left_type == control.boolean_type && (IsOne(left) || IsOne(right))) { EmitBranchIfExpression(IsOne(left) ? right : left, cond == (bp -> Tag() == AstBinaryExpression::EQUAL_EQUAL), lab, over); return; } // // Both operands are integer. // if (control.IsSimpleIntegerValueType(left_type) || left_type == control.boolean_type) { assert(control.IsSimpleIntegerValueType(right_type) || right_type == control.boolean_type); if (IsZero(left) || IsZero(right)) { if (left_type == control.boolean_type) { // // One of the operands is false. Branch on the other. // EmitBranchIfExpression(IsZero(left) ? right : left, cond == (bp -> Tag() != AstBinaryExpression::EQUAL_EQUAL), lab, over); } else { // // One of the operands is zero. Only emit the other. // EmitExpression(IsZero(left) ? right : left); if (bp -> Tag() == AstBinaryExpression::EQUAL_EQUAL) EmitBranch((cond ? OP_IFEQ : OP_IFNE), lab, over); else EmitBranch((cond ? OP_IFNE : OP_IFEQ), lab, over); } } else { EmitExpression(left); EmitExpression(right); if (bp -> Tag() == AstBinaryExpression::EQUAL_EQUAL) EmitBranch((cond ? OP_IF_ICMPEQ : OP_IF_ICMPNE), lab, over); else EmitBranch((cond ? OP_IF_ICMPNE : OP_IF_ICMPEQ), lab, over); } return; } // // Both operands are reference types: just do the comparison. // if (IsReferenceType(left_type)) { assert(IsReferenceType(right_type)); EmitExpression(left); EmitExpression(right); if (bp -> Tag() == AstBinaryExpression::EQUAL_EQUAL) EmitBranch((cond ? OP_IF_ACMPEQ : OP_IF_ACMPNE), lab, over); else EmitBranch((cond ? OP_IF_ACMPNE : OP_IF_ACMPEQ), lab, over); return; } break; case AstBinaryExpression::IOR: // // One argument is false. Branch on other. // if (IsZero(left) || IsZero(right)) { EmitBranchIfExpression(IsZero(left) ? right : left, cond, lab, over); return; } // // One argument is true. Emit the other, and result is true. // if (IsOne(left) || IsOne(right)) { EmitExpression(IsOne(left) ? right : left, false); if (cond) EmitBranch(OP_GOTO, lab, over); return; } break; case AstBinaryExpression::AND: // // One argument is true. Branch on other. // if (IsOne(left) || IsOne(right)) { EmitBranchIfExpression(IsOne(left) ? right : left, cond, lab, over); return; } // // One argument is false. Emit the other, and result is false. // if (IsZero(left) || IsZero(right)) { EmitExpression(IsZero(left) ? right : left, false); if (! cond) EmitBranch(OP_GOTO, lab, over); return; } break; default: break; } // // here if not comparison, comparison for non-integral numeric types, or // integral comparison for which no special casing needed. // Begin by dealing with non-comparisons // switch (bp -> Tag()) { case AstBinaryExpression::LESS: case AstBinaryExpression::LESS_EQUAL: case AstBinaryExpression::GREATER: case AstBinaryExpression::GREATER_EQUAL: case AstBinaryExpression::EQUAL_EQUAL: case AstBinaryExpression::NOT_EQUAL: break; // break to continue comparison processing default: // // not a comparison, get the (necessarily boolean) value // of the expression and branch on the result // EmitExpression(p); EmitBranch(cond ? OP_IFNE : OP_IFEQ, lab, over); return; } // // // Opcode opcode = OP_NOP, op_true, op_false; assert(left_type != control.boolean_type); if (control.IsSimpleIntegerValueType(left_type)) { // // we have already dealt with EQUAL_EQUAL and NOT_EQUAL for the case // of two integers, but still need to look for comparisons for which // one operand may be zero. // if (IsZero(left)) { EmitExpression(right); switch (bp -> Tag()) { case AstBinaryExpression::LESS: // if (0 < x) same as if (x > 0) op_true = OP_IFGT; op_false = OP_IFLE; break; case AstBinaryExpression::LESS_EQUAL: // if (0 <= x) same as if (x >= 0) op_true = OP_IFGE; op_false = OP_IFLT; break; case AstBinaryExpression::GREATER: // if (0 > x) same as if (x < 0) op_true = OP_IFLT; op_false = OP_IFGE; break; case AstBinaryExpression::GREATER_EQUAL: // if (0 >= x) same as if (x <= 0) op_true = OP_IFLE; op_false = OP_IFGT; break; default: assert(false); break; } } else if (IsZero(right)) { EmitExpression(left); switch (bp -> Tag()) { case AstBinaryExpression::LESS: op_true = OP_IFLT; op_false = OP_IFGE; break; case AstBinaryExpression::LESS_EQUAL: op_true = OP_IFLE; op_false = OP_IFGT; break; case AstBinaryExpression::GREATER: op_true = OP_IFGT; op_false = OP_IFLE; break; case AstBinaryExpression::GREATER_EQUAL: op_true = OP_IFGE; op_false = OP_IFLT; break; default: assert(false); break; } } else { EmitExpression(left); EmitExpression(right); switch (bp -> Tag()) { case AstBinaryExpression::LESS: op_true = OP_IF_ICMPLT; op_false = OP_IF_ICMPGE; break; case AstBinaryExpression::LESS_EQUAL: op_true = OP_IF_ICMPLE; op_false = OP_IF_ICMPGT; break; case AstBinaryExpression::GREATER: op_true = OP_IF_ICMPGT; op_false = OP_IF_ICMPLE; break; case AstBinaryExpression::GREATER_EQUAL: op_true = OP_IF_ICMPGE; op_false = OP_IF_ICMPLT; break; default: assert(false); break; } } } else if (left_type == control.long_type) { EmitExpression(left); EmitExpression(right); opcode = OP_LCMP; // // branch according to result value on stack // switch (bp -> Tag()) { case AstBinaryExpression::EQUAL_EQUAL: op_true = OP_IFEQ; op_false = OP_IFNE; break; case AstBinaryExpression::NOT_EQUAL: op_true = OP_IFNE; op_false = OP_IFEQ; break; case AstBinaryExpression::LESS: op_true = OP_IFLT; op_false = OP_IFGE; break; case AstBinaryExpression::LESS_EQUAL: op_true = OP_IFLE; op_false = OP_IFGT; break; case AstBinaryExpression::GREATER: op_true = OP_IFGT; op_false = OP_IFLE; break; case AstBinaryExpression::GREATER_EQUAL: op_true = OP_IFGE; op_false = OP_IFLT; break; default: assert(false); break; } } else if (left_type == control.float_type) { EmitExpression(left); EmitExpression(right); switch (bp -> Tag()) { case AstBinaryExpression::EQUAL_EQUAL: opcode = OP_FCMPL; op_true = OP_IFEQ; op_false = OP_IFNE; break; case AstBinaryExpression::NOT_EQUAL: opcode = OP_FCMPL; op_true = OP_IFNE; op_false = OP_IFEQ; break; case AstBinaryExpression::LESS: opcode = OP_FCMPG; op_true = OP_IFLT; op_false = OP_IFGE; break; case AstBinaryExpression::LESS_EQUAL: opcode = OP_FCMPG; op_true = OP_IFLE; op_false = OP_IFGT; break; case AstBinaryExpression::GREATER: opcode = OP_FCMPL; op_true = OP_IFGT; op_false = OP_IFLE; break; case AstBinaryExpression::GREATER_EQUAL: opcode = OP_FCMPL; op_true = OP_IFGE; op_false = OP_IFLT; break; default: assert(false); break; } } else if (left_type == control.double_type) { EmitExpression(left); EmitExpression(right); switch (bp -> Tag()) { case AstBinaryExpression::EQUAL_EQUAL: opcode = OP_DCMPL; op_true = OP_IFEQ; op_false = OP_IFNE; break; case AstBinaryExpression::NOT_EQUAL: opcode = OP_DCMPL; op_true = OP_IFNE; op_false = OP_IFEQ; break; case AstBinaryExpression::LESS: opcode = OP_DCMPG; op_true = OP_IFLT; op_false = OP_IFGE; break; case AstBinaryExpression::LESS_EQUAL: opcode = OP_DCMPG; op_true = OP_IFLE; op_false = OP_IFGT; break; case AstBinaryExpression::GREATER: opcode = OP_DCMPL; op_true = OP_IFGT; op_false = OP_IFLE; break; case AstBinaryExpression::GREATER_EQUAL: opcode = OP_DCMPL; op_true = OP_IFGE; op_false = OP_IFLT; break; default: assert(false); break; } } else assert(false && "comparison of unsupported type"); if (opcode != OP_NOP) PutOp(opcode); // if need to emit comparison before branch EmitBranch (cond ? op_true : op_false, lab, over); } // // Emits a synchronized statement, including monitor cleanup. The return // value is true if the contained statement is abrupt. // bool ByteCode::EmitSynchronizedStatement(AstSynchronizedStatement* statement) { int variable_index = method_stack -> TopBlock() -> block_symbol -> helper_variable_index; Label start_label; // // This code must be careful of asynchronous exceptions. Even if the // synchronized block is empty, user code can use Thread.stop(Throwable), // so we must ensure the monitor exits. We make sure that all instructions // after the monitorenter are covered. By sticking the catchall code // before the synchronized block, we can even make abrupt exits inside the // statement be asynch-exception safe. Note that the user can cause // deadlock (ie. an infinite loop), by releasing the monitor (via JNI or // some other means) in the block statement, so that the monitorexit fails // synchronously with an IllegalMonitorStateException and tries again; but // JLS 17.13 states that the compiler need not worry about such user // stupidity. // EmitBranch(OP_GOTO, start_label, NULL); u2 handler_pc = code_attribute -> CodeLength(); assert(stack_depth == 0); stack_depth = 1; // account for the exception already on the stack LoadLocal(variable_index, control.Object()); // reload monitor PutOp(OP_MONITOREXIT); u2 throw_pc = code_attribute -> CodeLength(); PutOp(OP_ATHROW); code_attribute -> AddException(handler_pc, throw_pc, handler_pc, 0); // // Even if enclosed statement is a nop, we must enter the monitor, because // of memory flushing side effects of synchronization. // DefineLabel(start_label); CompleteLabel(start_label); EmitExpression(statement -> expression); PutOp(OP_DUP); // duplicate for saving, entering monitor StoreLocal(variable_index, control.Object()); // save address of object PutOp(OP_MONITORENTER); // enter monitor associated with object assert(method_stack -> TopHandlerRangeStart().Length() == 0 && method_stack -> TopHandlerRangeEnd().Length() == 0); method_stack -> TopHandlerRangeStart().Push(code_attribute -> CodeLength()); bool abrupt = EmitBlockStatement(statement -> block); if (! abrupt) { LoadLocal(variable_index, control.Object()); // reload monitor PutOp(OP_MONITOREXIT); } u2 end_pc = code_attribute -> CodeLength(); method_stack -> TopHandlerRangeEnd().Push(end_pc); unsigned count = method_stack -> TopHandlerRangeStart().Length(); assert(count == method_stack -> TopHandlerRangeEnd().Length()); while (count--) { code_attribute -> AddException(method_stack -> TopHandlerRangeStart().Pop(), method_stack -> TopHandlerRangeEnd().Pop(), handler_pc, 0); } return abrupt; } void ByteCode::EmitAssertStatement(AstAssertStatement* assertion) { // // When constant true, the assert statement is a no-op. // Otherwise, assert a : b; is syntactic sugar for: // // while (! ($noassert && (a))) // throw new java.lang.AssertionError(b); // if (semantic.IsConstantTrue(assertion -> condition) || control.option.noassert || control.option.target < JikesOption::SDK1_4) { return; } PutOp(OP_GETSTATIC); PutU2(RegisterFieldref(assertion -> assert_variable)); Label label; EmitBranch(OP_IFNE, label); EmitBranchIfExpression(assertion -> condition, true, label); PutOp(OP_NEW); PutU2(RegisterClass(control.AssertionError())); PutOp(OP_DUP); MethodSymbol* constructor = NULL; if (assertion -> message_opt) { EmitExpression(assertion -> message_opt); TypeSymbol* type = assertion -> message_opt -> Type(); if (! control.AssertionError() -> Bad()) { // We found the class, now can we find the method? if (type == control.char_type) constructor = control.AssertionError_InitWithCharMethod(); else if (type == control.boolean_type) constructor = control.AssertionError_InitWithBooleanMethod(); else if (type == control.int_type || type == control.short_type || type == control.byte_type) { constructor = control.AssertionError_InitWithIntMethod(); } else if (type == control.long_type) constructor = control.AssertionError_InitWithLongMethod(); else if (type == control.float_type) constructor = control.AssertionError_InitWithFloatMethod(); else if (type == control.double_type) constructor = control.AssertionError_InitWithDoubleMethod(); else if (type == control.null_type || IsReferenceType(type)) constructor = control.AssertionError_InitWithObjectMethod(); else assert (false && "Missing AssertionError constructor!"); if (! constructor) // We didn't find it; suckage.... // TODO: error ought to include what we were looking for semantic.ReportSemError(SemanticError::LIBRARY_METHOD_NOT_FOUND, assertion, unit_type -> ContainingPackageName(), unit_type -> ExternalName()); } else { // The type for AssertionError is BAD, that means it wasn't // found! but the calls to control.AssertionError() above will // file a semantic error for us, no need to here. } ChangeStack(- GetTypeWords(type)); } else constructor = control.AssertionError_InitMethod(); PutOp(OP_INVOKESPECIAL); PutU2(RegisterLibraryMethodref(constructor)); PutOp(OP_ATHROW); DefineLabel(label); CompleteLabel(label); } void ByteCode::EmitForeachStatement(AstForeachStatement* foreach) { int helper_index = method_stack -> TopBlock() -> block_symbol -> helper_variable_index; bool abrupt; EmitExpression(foreach -> expression); Label loop; Label& comp = method_stack -> TopContinueLabel(); Label end; TypeSymbol* expr_type = foreach -> expression -> Type(); VariableSymbol* var = foreach -> formal_parameter -> formal_declarator -> symbol; TypeSymbol* component_type = var -> Type(); if (expr_type -> IsArray()) { // // Turn 'l: for(a b : c) d' into // { expr_type #0 = c; // int #1 = #0.length; // l: for(int #2 = 0; #2 < #1; #2++) { // a b = #0[#2]; // d; }} // Or in bytecode: // eval c onto stack // dup // astore helper_index // arraylength // dup // istore helper_index+1 // ifeq end // iconst_0 // istore helper_index+2 // iconst_0 // loop: // aload helper_index // swap // xaload (for x = b, s, i, l, c, f, d, a) // assignment-conversion (if necessary) // xstore b (for x = i, l, f, d, a) // eval d (continue to comp, break to end) // comp: // iinc helper_index+2, 1 // iload helper_index+1 // iload helper_index+2 // dup_x1 // if_icmpgt loop // pop // end: // TypeSymbol* expr_subtype = expr_type -> ArraySubtype(); if (IsNop(foreach -> statement) && (! component_type -> Primitive() || expr_subtype -> Primitive())) { // // Optimization (arrays only): no need to increment loop counter // if nothing is done in the loop; and we simply check that the // array is non-null from arraylength. But beware of autounboxing, // which can cause NullPointerException. // PutOp(OP_ARRAYLENGTH); PutOp(OP_POP); return; } PutOp(OP_DUP); StoreLocal(helper_index, expr_type); PutOp(OP_ARRAYLENGTH); PutOp(OP_DUP); StoreLocal(helper_index + 1, control.int_type); EmitBranch(OP_IFEQ, end); PutOp(OP_ICONST_0); StoreLocal(helper_index + 2, control.int_type); PutOp(OP_ICONST_0); DefineLabel(loop); LoadLocal(helper_index, expr_type); PutOp(OP_SWAP); LoadArrayElement(expr_type -> ArraySubtype()); EmitCast(component_type, expr_type -> ArraySubtype()); u2 var_pc = code_attribute -> CodeLength(); StoreLocal(var -> LocalVariableIndex(), component_type); abrupt = EmitStatement(foreach -> statement); if (control.option.g & JikesOption::VARS) { local_variable_table_attribute -> AddLocalVariable(var_pc, code_attribute -> CodeLength(), RegisterName(var -> ExternalIdentity()), RegisterUtf8(component_type -> signature), var -> LocalVariableIndex()); } if (! abrupt || foreach -> statement -> can_complete_normally) { DefineLabel(comp); PutOpIINC(helper_index + 2, 1); LoadLocal(helper_index + 1, control.int_type); LoadLocal(helper_index + 2, control.int_type); PutOp(OP_DUP_X1); EmitBranch(OP_IF_ICMPGT, loop); PutOp(OP_POP); } } else { assert(foreach -> expression -> Type() -> IsSubtype(control.Iterable())); // // Turn 'l: for(a b : c) d' into // for(java.util.Iterator #0 = c.iterator(); #0.hasNext();) { // a b = (a) c.next(); // d; } // Or in bytecode: // eval c onto stack // invokeinterface java.lang.Iterable.iterator()Ljava/util/Iterator; // dup // invokeinterface java.util.Iterator.hasNext()Z // ifeq cleanup // dup // astore helper_index // loop: // invokeinterface java.util.Iterator.next()Ljava/lang/Object; // checkcast a // astore b // eval d (continue to comp, break to end) // comp: // aload helper_index // dup // invokeinterface java.util.Iterator.hasNext()Z // ifne loop // cleanup: // pop // end: // Label cleanup; PutOp(OP_INVOKEINTERFACE); PutU2(RegisterLibraryMethodref(control.Iterable_iteratorMethod())); PutU1(1); PutU1(0); ChangeStack(1); PutOp(OP_DUP); PutOp(OP_INVOKEINTERFACE); u2 hasNext_index = RegisterLibraryMethodref(control.Iterator_hasNextMethod()); PutU2(hasNext_index); PutU1(1); PutU1(0); ChangeStack(1); EmitBranch(OP_IFEQ, cleanup); PutOp(OP_DUP); StoreLocal(helper_index, control.Iterator()); DefineLabel(loop); PutOp(OP_INVOKEINTERFACE); PutU2(RegisterLibraryMethodref(control.Iterator_nextMethod())); PutU1(1); PutU1(0); ChangeStack(1); if (component_type != control.Object()) { PutOp(OP_CHECKCAST); PutU2(RegisterClass(component_type)); } u2 var_pc = code_attribute -> CodeLength(); StoreLocal(var -> LocalVariableIndex(), component_type); abrupt = EmitStatement(foreach -> statement); if (control.option.g & JikesOption::VARS) { local_variable_table_attribute -> AddLocalVariable(var_pc, code_attribute -> CodeLength(), RegisterName(var -> ExternalIdentity()), RegisterUtf8(component_type -> signature), var -> LocalVariableIndex()); } if (! abrupt || foreach -> statement -> can_complete_normally) { DefineLabel(comp); LoadLocal(helper_index, control.Iterator()); PutOp(OP_DUP); PutOp(OP_INVOKEINTERFACE); PutU2(hasNext_index); PutU1(1); PutU1(0); ChangeStack(1); EmitBranch(OP_IFNE, loop); } else ChangeStack(1); DefineLabel(cleanup); CompleteLabel(cleanup); PutOp(OP_POP); } DefineLabel(end); CompleteLabel(loop); CompleteLabel(comp); CompleteLabel(end); } // // JLS is Java Language Specification // JVM is Java Virtual Machine // // Expressions: Chapter 14 of JLS // int ByteCode::EmitExpression(AstExpression* expression, bool need_value) { expression = StripNops(expression); if (expression -> IsConstant()) { if (need_value) { LoadLiteral(expression -> value, expression -> Type()); return GetTypeWords(expression -> Type()); } return 0; } switch (expression -> kind) { case Ast::NAME: return EmitName((AstName*) expression, need_value); case Ast::THIS_EXPRESSION: { AstThisExpression* this_expr = (AstThisExpression*) expression; if (this_expr -> resolution_opt && need_value) return EmitExpression(this_expr -> resolution_opt, true); } if (need_value) { PutOp(OP_ALOAD_0); return 1; } return 0; case Ast::SUPER_EXPRESSION: { AstSuperExpression* super_expr = (AstSuperExpression*) expression; if (super_expr -> resolution_opt && need_value) return EmitExpression(super_expr -> resolution_opt, true); } if (need_value) { PutOp(OP_ALOAD_0); return 1; } return 0; case Ast::CLASS_CREATION: return EmitClassCreationExpression ((AstClassCreationExpression*) expression, need_value); case Ast::ARRAY_CREATION: return EmitArrayCreationExpression((AstArrayCreationExpression*) expression, need_value); case Ast::CLASS_LITERAL: { AstClassLiteral* class_lit = (AstClassLiteral*) expression; if (class_lit -> resolution_opt) return GenerateClassAccess(class_lit, need_value); TypeSymbol* type = expression -> symbol -> TypeCast(); if (type) { // Must load for side effect of class not found assert(type == control.Class()); LoadConstantAtIndex(RegisterClass(class_lit -> type -> symbol)); if (! need_value) PutOp(OP_POP); } else if (need_value) { // No side effects for Integer.TYPE and friends. assert(expression -> symbol -> VariableCast()); PutOp(OP_GETSTATIC); PutU2(RegisterFieldref((VariableSymbol*) expression -> symbol)); } return need_value ? 1 : 0; } case Ast::DOT: return EmitFieldAccess((AstFieldAccess*) expression, need_value); case Ast::CALL: return EmitMethodInvocation((AstMethodInvocation*) expression, need_value); case Ast::ARRAY_ACCESS: { // must evaluate, for potential Exception side effects int words = EmitArrayAccessRhs((AstArrayAccess*) expression); if (need_value) return words; PutOp(words == 1 ? OP_POP : OP_POP2); return 0; } case Ast::POST_UNARY: return EmitPostUnaryExpression((AstPostUnaryExpression*) expression, need_value); case Ast::PRE_UNARY: return EmitPreUnaryExpression((AstPreUnaryExpression*) expression, need_value); case Ast::CAST: return EmitCastExpression((AstCastExpression*) expression, need_value); case Ast::BINARY: return EmitBinaryExpression((AstBinaryExpression*) expression, need_value); case Ast::INSTANCEOF: return EmitInstanceofExpression((AstInstanceofExpression*) expression, need_value); case Ast::CONDITIONAL: return EmitConditionalExpression(((AstConditionalExpression*) expression), need_value); case Ast::ASSIGNMENT: return EmitAssignmentExpression((AstAssignmentExpression*) expression, need_value); case Ast::NULL_LITERAL: if (need_value) { PutOp(OP_ACONST_NULL); return 1; } return 0; default: assert(false && "unknown expression kind"); break; } return 0; // even though we will not reach here } AstExpression* ByteCode::VariableExpressionResolution(AstExpression* expression) { // // JLS2 added ability for parenthesized variable to remain a variable. // If the expression was resolved, get the resolution. // expression = StripNops(expression); AstFieldAccess* field = expression -> FieldAccessCast(); if (field && field -> resolution_opt) return field -> resolution_opt; AstName* name = expression -> NameCast(); if (name && name -> resolution_opt) return name -> resolution_opt; return expression; } TypeSymbol* ByteCode::VariableTypeResolution(AstExpression* expression, VariableSymbol* sym) { expression = VariableExpressionResolution(expression); AstFieldAccess* field = expression -> FieldAccessCast(); AstName* name = expression -> NameCast(); assert(field || name); // // JLS2 13.1 Use the type of the base expression for qualified reference // (this even works for super expressions), and the innermost type that // contains the (possibly inherited) field for simple name reference. // // Prior to JDK 1.4, VMs incorrectly complained if a field declared in an // interface is referenced by inheritance, even though the JVMS permits it // and JLS 13 requires it. // TypeSymbol* candidate = field ? field -> base -> Type() : name -> base_opt ? name -> base_opt -> Type() : unit_type; return (sym -> ContainingType() -> ACC_INTERFACE() && control.option.target < JikesOption::SDK1_4) ? sym -> ContainingType() : candidate; } TypeSymbol* ByteCode::MethodTypeResolution(AstExpression* base, MethodSymbol* msym) { // // JLS 13.1 If the method is declared in Object, use Object. Otherwise, // use the type of the base expression for qualified reference (this even // works for super expressions), and the innermost type that contains the // (possibly inherited) method for simple name reference. However, if // this is an accessor method, use the owner_type (since the base type // relates to the accessed expression, not the accessor method). // TypeSymbol* owner_type = msym -> containing_type; TypeSymbol* base_type = msym -> ACC_SYNTHETIC() ? owner_type : base ? base -> Type() : unit_type; return owner_type == control.Object() ? owner_type : base_type; } void ByteCode::EmitFieldAccessLhsBase(AstExpression* expression) { expression = VariableExpressionResolution(expression); AstFieldAccess* field = expression -> FieldAccessCast(); AstName* name = expression -> NameCast(); // // We now have the right expression. Check if it is qualified, in which // case we process the base. Otherwise, it must be a simple name. // if (field || (name && name -> base_opt)) EmitExpression(field ? field -> base : name -> base_opt); else PutOp(OP_ALOAD_0); // get address of "this" } void ByteCode::EmitFieldAccessLhs(AstExpression* expression) { EmitFieldAccessLhsBase(expression); PutOp(OP_DUP); // save base address of field for later store PutOp(OP_GETFIELD); if (control.IsDoubleWordType(expression -> Type())) ChangeStack(1); VariableSymbol* sym = (VariableSymbol*) expression -> symbol; PutU2(RegisterFieldref(VariableTypeResolution(expression, sym), sym)); } // // Generate code for access method used to set class literal fields, when // compiling for older VMs. // void ByteCode::GenerateClassAccessMethod() { assert(control.option.target < JikesOption::SDK1_5); // // Here, we add a line-number attribute entry for this method. // Even though this is a generated method, JPDA debuggers will // still fail setting breakpoints if methods don't have line numbers. // Sun's javac compiler generates a single line number entry // with start_pc set to zero and line number set to the first line of // code in the source. In testing, it appears that setting the start_pc // and line_number to zero as we do here, also works. // line_number_table_attribute -> AddLineNumber(0, 0); // // Since the VM does not have a nice way of finding a class without a // runtime object, we use this approach. Notice that forName can throw // a checked exception, but JLS semantics do not allow this, so we must // add a catch block to convert the problem to an unchecked Error. // Likewise, note that we must not initialize the class in question, // hence the use of forName on array types in all cases. // // The generated code is semantically equivalent to: // // /*synthetic*/ static java.lang.Class class$(java.lang.String name, // boolean array) { // try { // Class result = java.lang.Class.forName(name); // return array ? result : result.getComponentType(); // } catch (ClassNotFoundException e) { // throw new NoClassDefFoundError(((Throwable) e).getMessage()); // } // } // // When option.target >= SDK1_4, we use the new exception chaining, // and the catch clause becomes // throw (Error) ((Throwable) new NoClassDefFoundError()).initCause(e); // // Since ClassNotFoundException inherits, rather than declares, getMessage, // we link to Throwable, and use the cast to Throwable in the code above to // show that we are still obeying JLS 13.1, which requires that .class // files must link to the type of the qualifying expression. // // aload_0 load class name in array form // invokestatic java/lang/Class.forName(Ljava/lang/String;)Ljava/lang/Class; // iload_1 load array // ifne label // invokevirtual java/lang/Class.getComponentType()Ljava/lang/Class; // label: // areturn return Class object // // pre-SDK1_4 exception handler if forName fails (optimization: the // ClassNotFoundException will already be on the stack): // // invokevirtual java/lang/Throwable.getMessage()Ljava/lang/String; // new java/lang/NoClassDefFoundError // dup_x1 save copy to throw, but below string arg to constructor // swap swap string and new object to correct order // invokespecial java/lang/NoClassDefFoundError.(Ljava/lang/String;)V // athrow throw the correct exception // // post-SDK1_4 exception handler if forName fails (optimization: the // ClassNotFoundException will already be on the stack): // // new java/lang/NoClassDefFoundError // dup_x1 save copy, but below cause // invokespecial java/lang/NoClassDefFoundError.()V // invokevirtual java/lang/Throwable.initCause(Ljava/lang/Throwable;)Ljava/lang/Throwable; // athrow throw the correct exception // Label label; PutOp(OP_ALOAD_0); PutOp(OP_INVOKESTATIC); PutU2(RegisterLibraryMethodref(control.Class_forNameMethod())); PutOp(OP_ILOAD_1); EmitBranch(OP_IFNE, label); PutOp(OP_INVOKEVIRTUAL); PutU2(RegisterLibraryMethodref(control.Class_getComponentTypeMethod())); ChangeStack(1); // account for the return DefineLabel(label); CompleteLabel(label); PutOp(OP_ARETURN); code_attribute -> AddException(0, 12, 12, RegisterClass(control.ClassNotFoundException())); ChangeStack(1); // account for the exception on the stack if (control.option.target < JikesOption::SDK1_4) { PutOp(OP_INVOKEVIRTUAL); PutU2(RegisterLibraryMethodref(control.Throwable_getMessageMethod())); ChangeStack(1); // account for the returned string PutOp(OP_NEW); PutU2(RegisterClass(control.NoClassDefFoundError())); PutOp(OP_DUP_X1); PutOp(OP_SWAP); PutOp(OP_INVOKESPECIAL); PutU2(RegisterLibraryMethodref(control.NoClassDefFoundError_InitStringMethod())); ChangeStack(-1); // account for the argument to the constructor } else { PutOp(OP_NEW); PutU2(RegisterClass(control.NoClassDefFoundError())); PutOp(OP_DUP_X1); PutOp(OP_INVOKESPECIAL); PutU2(RegisterLibraryMethodref(control.NoClassDefFoundError_InitMethod())); PutOp(OP_INVOKEVIRTUAL); PutU2(RegisterLibraryMethodref(control.Throwable_initCauseMethod())); } PutOp(OP_ATHROW); } // // Generate code to dymanically initialize the field for a class literal, and // return its value. Only generated for older VMs (since newer ones support // ldc class). // int ByteCode::GenerateClassAccess(AstClassLiteral* class_lit, bool need_value) { assert(control.option.target < JikesOption::SDK1_5); // // Evaluate X.class literal. If X is a primitive type, this is a // predefined field, and we emitted it directly rather than relying on // this method. Otherwise, we have created a synthetic field to cache // the desired result, and we initialize it at runtime. Within a class, // this cannot be done in the static initializer, because it is possible // to access a class literal before a class is initialized. // // Foo.Bar.class becomes // (class$Foo$Bar == null ? class$Foo$Bar = class$("[LFoo.Bar;", false) // : class$Foo$Bar) // int[].class becomes // (array$I == null ? array$I = class$("[I", true) : array$I) // // getstatic class_field load class field // dup optimize: common case is non-null // ifnonnull label branch if it exists, otherwise initialize // pop pop the null we just duplicated // load class_constant get name of class // iconst_x true iff array // invokestatic invoke synthetic class$ method // dup save value so can return it // put class_field initialize the field // label: // Label label; assert(class_lit -> symbol -> VariableCast()); VariableSymbol* cache = (VariableSymbol*) class_lit -> symbol; u2 field_index = RegisterFieldref(cache); PutOp(OP_GETSTATIC); PutU2(field_index); if (need_value) PutOp(OP_DUP); EmitBranch(OP_IFNONNULL, label); if (need_value) PutOp(OP_POP); TypeSymbol* type = class_lit -> type -> symbol; if (type -> num_dimensions > 255) semantic.ReportSemError(SemanticError::ARRAY_OVERFLOW, class_lit); bool is_array = type -> IsArray(); if (! is_array) type = type -> GetArrayType(control.system_semantic, 1); LoadLiteral(type -> FindOrInsertClassLiteralName(control), control.String()); PutOp(is_array ? OP_ICONST_1 : OP_ICONST_0); PutOp(OP_INVOKESTATIC); CompleteCall(cache -> ContainingType() -> ClassLiteralMethod(), 2); if (need_value) PutOp(OP_DUP); PutOp(OP_PUTSTATIC); PutU2(field_index); DefineLabel(label); CompleteLabel(label); return need_value ? 1 : 0; } // // Generate code for initializing assert variable // void ByteCode::GenerateAssertVariableInitializer(TypeSymbol* tsym, VariableSymbol* vsym) { // // Create the field initializer. This approach avoids using a class // literal, for two reasons: // - we use fewer bytecodes if the rest of the class does not use class // literals (and we need no try-catch block) // - determining assertion status will not initialize an enclosing class. // // Unfortunately, until the VM supports easier determination of classes // from a static context, we must create an empty garbage array. // We initialize to the opposite of desiredAssertionStatus to obey the // semantics of assert - until class initialization starts, the default // value of false in this variable will enable asserts anywhere in the // class. // // private static final boolean $noassert // = ! Class.forName("[L;").getComponentType() // .desiredAssertionStatus(); // // ldc "L[;" // invokevirtual java/lang/Class.forName(Ljava/lang/String;)java/lang/Class // invokevirtual java/lang/Class.getComponentType()Ljava/lang/Class; // invokevirtual java/lang/Class.desiredAssertionStatus()Z // iconst_1 // ixor result ^ true <=> !result // putstatic .$noassert // assert(! control.option.noassert && control.option.target >= JikesOption::SDK1_4); tsym = tsym -> GetArrayType(control.system_semantic, 1); LoadLiteral(tsym -> FindOrInsertClassLiteralName(control), control.String()); PutOp(OP_INVOKESTATIC); PutU2(RegisterLibraryMethodref(control.Class_forNameMethod())); PutOp(OP_INVOKEVIRTUAL); ChangeStack(1); // for returned value PutU2(RegisterLibraryMethodref(control.Class_getComponentTypeMethod())); PutOp(OP_INVOKEVIRTUAL); ChangeStack(1); // for returned value PutU2(RegisterLibraryMethodref(control.Class_desiredAssertionStatusMethod())); PutOp(OP_ICONST_1); PutOp(OP_IXOR); PutOp(OP_PUTSTATIC); PutU2(RegisterFieldref(vsym)); } int ByteCode::EmitName(AstName* expression, bool need_value) { if (expression -> symbol -> TypeCast()) return 0; VariableSymbol* var = expression -> symbol -> VariableCast(); return LoadVariable((expression -> resolution_opt ? ACCESSED_VAR : var -> owner -> MethodCast() ? LOCAL_VAR : var -> ACC_STATIC() ? STATIC_VAR : FIELD_VAR), expression, need_value); } // // see also OP_MULTIANEWARRAY // int ByteCode::EmitArrayCreationExpression(AstArrayCreationExpression* expression, bool need_value) { unsigned num_dims = expression -> NumDimExprs(); if (expression -> Type() -> num_dimensions > 255) semantic.ReportSemError(SemanticError::ARRAY_OVERFLOW, expression); if (expression -> array_initializer_opt) { InitializeArray(expression -> Type(), expression -> array_initializer_opt, need_value); } else { // // Need to push value of dimension(s) and create array. This can be // skipped if we don't need a value, but only if we know that all // dimensions are non-negative. // bool create_array = need_value; for (unsigned i = 0; ! create_array && i < num_dims; i++) { AstExpression* expr = StripNops(expression -> DimExpr(i) -> expression); if (expr -> IsConstant()) { if (DYNAMIC_CAST (expr -> value) -> value < 0) { create_array = true; } } else if (expr -> Type() != control.char_type) create_array = true; } for (unsigned j = 0; j < num_dims; j++) EmitExpression(expression -> DimExpr(j) -> expression, create_array); if (create_array) { EmitNewArray(num_dims, expression -> Type()); if (! need_value) PutOp(OP_POP); } } return need_value ? 1 : 0; } // // ASSIGNMENT // int ByteCode::EmitAssignmentExpression(AstAssignmentExpression* assignment_expression, bool need_value) { // // JLS2 added ability for parenthesized variable to remain a variable. // AstCastExpression* casted_left_hand_side = assignment_expression -> left_hand_side -> CastExpressionCast(); AstExpression* left_hand_side = StripNops(casted_left_hand_side ? casted_left_hand_side -> expression : assignment_expression -> left_hand_side); TypeSymbol* left_type = left_hand_side -> Type(); VariableCategory kind = GetVariableKind(assignment_expression); VariableSymbol* accessed_member = assignment_expression -> write_method ? (assignment_expression -> write_method -> accessed_member -> VariableCast()) : (VariableSymbol*) NULL; if (assignment_expression -> SimpleAssignment()) { switch (kind) { case ARRAY_VAR: // lhs must be array access EmitArrayAccessLhs(left_hand_side -> ArrayAccessCast()); break; case FIELD_VAR: // load base for field access EmitFieldAccessLhsBase(left_hand_side); break; case STATIC_VAR: // // If the access is qualified by an arbitrary base // expression, evaluate it for side effects. // if (left_hand_side -> FieldAccessCast()) { AstExpression* base = ((AstFieldAccess*) left_hand_side) -> base; EmitExpression(base, false); } else if (left_hand_side -> NameCast()) { AstName* base = ((AstName*) left_hand_side) -> base_opt; if (base) EmitName(base, false); } break; case ACCESSED_VAR: // need to load address of object, obtained from resolution if (! accessed_member -> ACC_STATIC()) { AstExpression* resolve = left_hand_side -> FieldAccessCast() ? left_hand_side -> FieldAccessCast() -> resolution_opt : left_hand_side -> NameCast() -> resolution_opt; assert(resolve); AstExpression* base = resolve -> MethodInvocationCast() -> base_opt; assert(base); EmitExpression(base); } else if (left_hand_side -> FieldAccessCast()) // // If the access is qualified by an arbitrary base // expression, evaluate it for side effects. // EmitExpression(((AstFieldAccess*) left_hand_side) -> base, false); break; case LOCAL_VAR: break; default: assert(false && "bad kind in EmitAssignmentExpression"); } EmitExpression(assignment_expression -> expression); } // // Here for compound assignment. Get the left operand, saving any // information necessary to update its value on the stack below the value. // else { switch (kind) { case ARRAY_VAR: // lhs must be array access EmitArrayAccessLhs(left_hand_side -> ArrayAccessCast()); PutOp(OP_DUP2); // save base and index for later store // // load current value // LoadArrayElement(assignment_expression -> Type()); break; case FIELD_VAR: EmitFieldAccessLhs(left_hand_side); break; case LOCAL_VAR: if (! casted_left_hand_side && assignment_expression -> Type() == control.int_type && assignment_expression -> expression -> IsConstant() && ((assignment_expression -> Tag() == AstAssignmentExpression::PLUS_EQUAL) || (assignment_expression -> Tag() == AstAssignmentExpression::MINUS_EQUAL))) { IntLiteralValue* vp = DYNAMIC_CAST (assignment_expression -> expression -> value); int val = ((assignment_expression -> Tag() == AstAssignmentExpression::MINUS_EQUAL) ? -(vp -> value) // we treat "a -= x" as "a += (-x)" : vp -> value); if (val >= -32768 && val < 32768) // if value in range { VariableSymbol* sym = (VariableSymbol*) left_hand_side -> symbol; PutOpIINC(sym -> LocalVariableIndex(), val); LoadVariable(LOCAL_VAR, left_hand_side, need_value); return GetTypeWords(assignment_expression -> Type()); } } LoadVariable(kind, left_hand_side); break; case STATIC_VAR: LoadVariable(kind, left_hand_side); break; case ACCESSED_VAR: // // If we are accessing a static member, get value by invoking // appropriate resolution. Otherwise, in addition to getting // the value, we need to load address of the object, // obtained from the resolution, saving a copy on the stack. // if (accessed_member -> ACC_STATIC()) EmitExpression(left_hand_side); else ResolveAccess(left_hand_side); break; default: assert(false && "bad kind in EmitAssignmentExpression"); } // // Here for string concatenation. // if ((assignment_expression -> Tag() == AstAssignmentExpression::PLUS_EQUAL) && left_type == control.String()) { PutOp(OP_NEW); PutU2(RegisterClass(control.option.target >= JikesOption::SDK1_5 ? control.StringBuilder() : control.StringBuffer())); PutOp(OP_DUP_X1); PutOp(OP_INVOKESPECIAL); PutU2(RegisterLibraryMethodref (control.option.target >= JikesOption::SDK1_5 ? control.StringBuilder_InitMethod() : control.StringBuffer_InitMethod())); EmitStringAppendMethod(control.String()); AppendString(assignment_expression -> expression, true); PutOp(OP_INVOKEVIRTUAL); PutU2(RegisterLibraryMethodref (control.option.target >= JikesOption::SDK1_5 ? control.StringBuilder_toStringMethod() : control.StringBuffer_toStringMethod())); ChangeStack(1); // account for return value } // // Here for operation other than string concatenation. Determine the // opcode to use. // else { Opcode opc; TypeSymbol* op_type = (casted_left_hand_side ? casted_left_hand_side -> Type() : assignment_expression -> Type()); if (control.IsSimpleIntegerValueType(op_type) || op_type == control.boolean_type) { switch (assignment_expression -> Tag()) { case AstAssignmentExpression::STAR_EQUAL: opc = OP_IMUL; break; case AstAssignmentExpression::SLASH_EQUAL: opc = OP_IDIV; break; case AstAssignmentExpression::MOD_EQUAL: opc = OP_IREM; break; case AstAssignmentExpression::PLUS_EQUAL: opc = OP_IADD; break; case AstAssignmentExpression::MINUS_EQUAL: opc = OP_ISUB; break; case AstAssignmentExpression::LEFT_SHIFT_EQUAL: opc = OP_ISHL; break; case AstAssignmentExpression::RIGHT_SHIFT_EQUAL: opc = OP_ISHR; break; case AstAssignmentExpression::UNSIGNED_RIGHT_SHIFT_EQUAL: opc = OP_IUSHR; break; case AstAssignmentExpression::AND_EQUAL: opc = OP_IAND; break; case AstAssignmentExpression::IOR_EQUAL: opc = OP_IOR; break; case AstAssignmentExpression::XOR_EQUAL: opc = OP_IXOR; break; default: assert(false && "bad op_type in EmitAssignmentExpression"); } } else if (op_type == control.long_type) { switch (assignment_expression -> Tag()) { case AstAssignmentExpression::STAR_EQUAL: opc = OP_LMUL; break; case AstAssignmentExpression::SLASH_EQUAL: opc = OP_LDIV; break; case AstAssignmentExpression::MOD_EQUAL: opc = OP_LREM; break; case AstAssignmentExpression::PLUS_EQUAL: opc = OP_LADD; break; case AstAssignmentExpression::MINUS_EQUAL: opc = OP_LSUB; break; case AstAssignmentExpression::LEFT_SHIFT_EQUAL: opc = OP_LSHL; break; case AstAssignmentExpression::RIGHT_SHIFT_EQUAL: opc = OP_LSHR; break; case AstAssignmentExpression::UNSIGNED_RIGHT_SHIFT_EQUAL: opc = OP_LUSHR; break; case AstAssignmentExpression::AND_EQUAL: opc = OP_LAND; break; case AstAssignmentExpression::IOR_EQUAL: opc = OP_LOR; break; case AstAssignmentExpression::XOR_EQUAL: opc = OP_LXOR; break; default: assert(false && "bad op_type in EmitAssignmentExpression"); } } else if (op_type == control.float_type) { switch (assignment_expression -> Tag()) { case AstAssignmentExpression::STAR_EQUAL: opc = OP_FMUL; break; case AstAssignmentExpression::SLASH_EQUAL: opc = OP_FDIV; break; case AstAssignmentExpression::MOD_EQUAL: opc = OP_FREM; break; case AstAssignmentExpression::PLUS_EQUAL: opc = OP_FADD; break; case AstAssignmentExpression::MINUS_EQUAL: opc = OP_FSUB; break; default: assert(false && "bad op_type in EmitAssignmentExpression"); } } else if (op_type == control.double_type) { switch (assignment_expression -> Tag()) { case AstAssignmentExpression::STAR_EQUAL: opc = OP_DMUL; break; case AstAssignmentExpression::SLASH_EQUAL: opc = OP_DDIV; break; case AstAssignmentExpression::MOD_EQUAL: opc = OP_DREM; break; case AstAssignmentExpression::PLUS_EQUAL: opc = OP_DADD; break; case AstAssignmentExpression::MINUS_EQUAL: opc = OP_DSUB; break; default: assert(false && "bad op_type in EmitAssignmentExpression"); } } else { assert(false && "unrecognized op_type in EmitAssignmentExpression"); } // // convert value to desired type if necessary // if (casted_left_hand_side) EmitCast(casted_left_hand_side -> Type(), left_type); EmitExpression(assignment_expression -> expression); PutOp(opc); if (casted_left_hand_side) // now cast result back to type of result EmitCast(left_type, casted_left_hand_side -> Type()); } } // // Update left operand, saving value of right operand if it is needed. // switch (kind) { case ARRAY_VAR: if (need_value) PutOp(control.IsDoubleWordType(left_type) ? OP_DUP2_X2 : OP_DUP_X2); StoreArrayElement(assignment_expression -> Type()); break; case FIELD_VAR: if (need_value) PutOp(control.IsDoubleWordType(left_type) ? OP_DUP2_X1 : OP_DUP_X1); StoreField(left_hand_side); break; case ACCESSED_VAR: { if (need_value) { if (accessed_member -> ACC_STATIC()) PutOp(control.IsDoubleWordType(left_type) ? OP_DUP2 : OP_DUP); else PutOp(control.IsDoubleWordType(left_type) ? OP_DUP2_X1 : OP_DUP_X1); } int stack_words = (GetTypeWords(left_type) + (accessed_member -> ACC_STATIC() ? 0 : 1)); PutOp(OP_INVOKESTATIC); CompleteCall(assignment_expression -> write_method, stack_words); } break; case LOCAL_VAR: // // Prior to JDK 1.5, VMs incorrectly complained if assigning an array // type into an element of a null expression (in other words, null // was not being treated as compatible with a multi-dimensional array // on the aastore opcode). The workaround requires a checkcast any // time null might be assigned to a multi-dimensional local variable // or directly used as an array access base. // if (control.option.target < JikesOption::SDK1_5 && IsMultiDimensionalArray(left_type) && (StripNops(assignment_expression -> expression) -> Type() == control.null_type)) { assert(assignment_expression -> SimpleAssignment()); PutOp(OP_CHECKCAST); PutU2(RegisterClass(left_type)); } // fallthrough case STATIC_VAR: if (need_value) PutOp(control.IsDoubleWordType(left_type) ? OP_DUP2 : OP_DUP); StoreVariable(kind, left_hand_side); break; default: assert(false && "bad kind in EmitAssignmentExpression"); } return GetTypeWords(assignment_expression -> Type()); } // // BINARY: Similar code patterns are used for the ordered comparisons. This // method relies on the compiler having already inserted numeric promotion // casts, so that the type of the left and right expressions match. // int ByteCode::EmitBinaryExpression(AstBinaryExpression* expression, bool need_value) { TypeSymbol* type = expression -> Type(); // // First, special case string concatenation. // if (type == control.String()) { assert(expression -> Tag() == AstBinaryExpression::PLUS); ConcatenateString(expression, need_value); if (! need_value) { PutOp(OP_POP); return 0; } PutOp(OP_INVOKEVIRTUAL); PutU2(RegisterLibraryMethodref (control.option.target >= JikesOption::SDK1_5 ? control.StringBuilder_toStringMethod() : control.StringBuffer_toStringMethod())); ChangeStack(1); // account for return value return 1; } // // Next, simplify if no result is needed. Be careful of side-effects with // binary / and % on integral 0, as well as evaluation order of && and ||. // if (! need_value) { if ((expression -> Tag() == AstBinaryExpression::SLASH || expression -> Tag() == AstBinaryExpression::MOD) && control.IsIntegral(type) && (IsZero(expression -> right_expression) || ! expression -> right_expression -> IsConstant())) { if (IsZero(expression -> right_expression)) { // // Undo compiler-inserted numeric promotion. // AstExpression* left_expr = expression -> left_expression; if (left_expr -> CastExpressionCast() && left_expr -> generated) { left_expr = ((AstCastExpression*) left_expr) -> expression; } type = left_expr -> Type(); EmitExpression(left_expr); PutOp(type == control.long_type ? OP_LCONST_0 : OP_ICONST_0); } else { EmitExpression(expression -> left_expression); EmitExpression(expression -> right_expression); } if (type == control.long_type) { PutOp(expression -> Tag() == AstBinaryExpression::SLASH ? OP_LDIV : OP_LREM); PutOp(OP_POP2); } else { PutOp(expression -> Tag() == AstBinaryExpression::SLASH ? OP_IDIV : OP_IREM); PutOp(OP_POP); } } else if (expression -> Tag() == AstBinaryExpression::OR_OR) { // // if (cond || true); => cond; // if (cond || false); => cond; // if (expression -> right_expression -> IsConstant()) { EmitExpression(expression -> left_expression, false); } // // if (true || cond); => nop // if (a || b); => if (!a) b; // else if (! IsOne(expression -> left_expression)) { Label label; EmitBranchIfExpression(expression -> left_expression, true, label); EmitExpression(expression -> right_expression, false); DefineLabel(label); CompleteLabel(label); } } else if (expression -> Tag() == AstBinaryExpression::AND_AND) { // // if (cond && true); => cond; // if (cond && false); => cond; // if (expression -> right_expression -> IsConstant()) { EmitExpression(expression -> left_expression, false); } // // if (false && cond); => nop // if (a && b); => if (a) b; // else if (! IsZero(expression -> left_expression)) { Label label; EmitBranchIfExpression(expression -> left_expression, false, label); EmitExpression(expression -> right_expression, false); DefineLabel(label); CompleteLabel(label); } } else { EmitExpression(expression -> left_expression, false); EmitExpression(expression -> right_expression, false); } return 0; } // // Next, try to simplify if one operand known to be zero or one. // if (IsZero(expression -> left_expression)) { // // Undo compiler-inserted numeric promotion, as well as narrowing from // long to int in shifts, to avoid unnecessary type conversions. // AstExpression* right_expr = expression -> right_expression; if (right_expr -> CastExpressionCast() && right_expr -> generated) right_expr = ((AstCastExpression*) right_expr) -> expression; TypeSymbol* right_type = right_expr -> Type(); switch (expression -> Tag()) { case AstBinaryExpression::AND_AND: PutOp(OP_ICONST_0); return 1; case AstBinaryExpression::EQUAL_EQUAL: if (right_type != control.boolean_type) break; EmitExpression(right_expr); PutOp(OP_ICONST_1); PutOp(OP_IXOR); return 1; case AstBinaryExpression::NOT_EQUAL: if (right_type != control.boolean_type) break; // Fallthrough on boolean case! case AstBinaryExpression::PLUS: case AstBinaryExpression::IOR: case AstBinaryExpression::XOR: case AstBinaryExpression::OR_OR: // // Note that +0.0 + expr cannot be simplified if expr is floating // point, because of -0.0 rules. // if (control.IsFloatingPoint(right_type)) { if (expression -> left_expression -> Type() == control.float_type) { FloatLiteralValue* value = DYNAMIC_CAST (expression -> left_expression -> value); if (value -> value.IsPositiveZero()) break; } else if (expression -> left_expression -> Type() == control.double_type) { DoubleLiteralValue* value = DYNAMIC_CAST (expression -> left_expression -> value); if (value -> value.IsPositiveZero()) break; } } // Use promoted version, not the stripped right_expr. EmitExpression(expression -> right_expression); return GetTypeWords(type); case AstBinaryExpression::STAR: case AstBinaryExpression::AND: case AstBinaryExpression::LEFT_SHIFT: case AstBinaryExpression::RIGHT_SHIFT: case AstBinaryExpression::UNSIGNED_RIGHT_SHIFT: // // Floating point multiplication by 0 cannot be simplified, because // of NaN, infinity, and -0.0 rules. And in general, division // cannot be simplified because of divide by 0 for integers and // corner cases for floating point. // if (control.IsFloatingPoint(type)) break; EmitExpression(right_expr, false); PutOp(type == control.long_type ? OP_LCONST_0 : OP_ICONST_0); return GetTypeWords(type); case AstBinaryExpression::MINUS: // // 0 - x is negation, but note that +0.0 - expr cannot be // simplified if expr is floating point, because of -0.0 rules. // if (control.IsFloatingPoint(right_type)) { if (expression -> left_expression -> Type() == control.float_type) { FloatLiteralValue* value = DYNAMIC_CAST (expression -> left_expression -> value); if (value -> value.IsPositiveZero()) break; } else if (expression -> left_expression -> Type() == control.double_type) { DoubleLiteralValue* value = DYNAMIC_CAST (expression -> left_expression -> value); if (value -> value.IsPositiveZero()) break; } } // Use promoted version, not the stripped right_expr. EmitExpression(expression -> right_expression); PutOp(control.IsSimpleIntegerValueType(type) ? OP_INEG : type == control.long_type ? OP_LNEG : type == control.float_type ? OP_FNEG : OP_DNEG); // double_type return GetTypeWords(type); default: break; } } if (IsOne(expression -> left_expression)) { if (expression -> Tag() == AstBinaryExpression::STAR) { EmitExpression(expression -> right_expression); return GetTypeWords(type); } if (expression -> left_expression -> Type() == control.boolean_type) { switch (expression -> Tag()) { case AstBinaryExpression::EQUAL_EQUAL: case AstBinaryExpression::AND_AND: case AstBinaryExpression::AND: EmitExpression(expression -> right_expression); break; case AstBinaryExpression::IOR: EmitExpression(expression -> right_expression, false); // Fallthrough case AstBinaryExpression::OR_OR: PutOp(OP_ICONST_1); break; case AstBinaryExpression::NOT_EQUAL: case AstBinaryExpression::XOR: EmitExpression(expression -> right_expression); PutOp(OP_ICONST_1); PutOp(OP_IXOR); break; default: assert(false && "Invalid operator on boolean"); } return 1; } } if (IsZero(expression -> right_expression)) { // // Undo compiler-inserted numeric promotion to avoid unnecessary type // conversions. // AstExpression* left_expr = expression -> left_expression; if (left_expr -> CastExpressionCast() && left_expr -> generated) left_expr = ((AstCastExpression*) left_expr) -> expression; TypeSymbol* left_type = left_expr -> Type(); switch (expression -> Tag()) { case AstBinaryExpression::EQUAL_EQUAL: if (left_type != control.boolean_type) break; EmitExpression(left_expr); PutOp(OP_ICONST_1); PutOp(OP_IXOR); return 1; case AstBinaryExpression::NOT_EQUAL: if (left_type != control.boolean_type) break; // Fallthrough on boolean case! case AstBinaryExpression::PLUS: case AstBinaryExpression::MINUS: case AstBinaryExpression::IOR: case AstBinaryExpression::XOR: case AstBinaryExpression::OR_OR: case AstBinaryExpression::LEFT_SHIFT: case AstBinaryExpression::RIGHT_SHIFT: case AstBinaryExpression::UNSIGNED_RIGHT_SHIFT: // // Here for cases that simplify to the left operand. Note that // (expr + +0.0) and (expr - -0.0) cannot be simplified if expr // is floating point, because of -0.0 rules. // if (control.IsFloatingPoint(left_type)) { if (expression -> right_expression -> Type() == control.float_type) { FloatLiteralValue* value = DYNAMIC_CAST (expression -> right_expression -> value); if (value -> value.IsPositiveZero() == (expression -> Tag() == AstBinaryExpression::PLUS)) break; } else if (expression -> right_expression -> Type() == control.double_type) { DoubleLiteralValue* value = DYNAMIC_CAST (expression -> right_expression -> value); if (value -> value.IsPositiveZero() == (expression -> Tag() == AstBinaryExpression::PLUS)) break; } } // Use promoted version, not the stripped left_expr. EmitExpression(expression -> left_expression); return GetTypeWords(type); case AstBinaryExpression::STAR: case AstBinaryExpression::AND: case AstBinaryExpression::AND_AND: // // Floating point multiplication by 0 cannot be simplified, because // of NaN, infinity, and -0.0 rules. And in general, division // cannot be simplified because of divide by 0 for integers and // corner cases for floating point. // if (control.IsFloatingPoint(type)) break; EmitExpression(left_expr, false); PutOp(type == control.long_type ? OP_LCONST_0 : OP_ICONST_0); return GetTypeWords(type); default: break; } } if (IsOne(expression -> right_expression)) { if (expression -> Tag() == AstBinaryExpression::STAR || expression -> Tag() == AstBinaryExpression::SLASH) { EmitExpression(expression -> left_expression); return GetTypeWords(type); } if (expression -> right_expression -> Type() == control.boolean_type) { switch (expression -> Tag()) { case AstBinaryExpression::EQUAL_EQUAL: case AstBinaryExpression::AND_AND: case AstBinaryExpression::AND: EmitExpression(expression -> left_expression); break; case AstBinaryExpression::IOR: case AstBinaryExpression::OR_OR: EmitExpression(expression -> left_expression, false); PutOp(OP_ICONST_1); break; case AstBinaryExpression::NOT_EQUAL: case AstBinaryExpression::XOR: EmitExpression(expression -> left_expression); PutOp(OP_ICONST_1); PutOp(OP_IXOR); break; default: assert(false && "Invalid operator on boolean"); } return 1; } } // // Next, simplify all remaining boolean result expressions. // if (expression -> left_expression -> Type() == control.boolean_type && (expression -> Tag() == AstBinaryExpression::EQUAL_EQUAL || expression -> Tag() == AstBinaryExpression::NOT_EQUAL)) { EmitExpression(expression -> left_expression); EmitExpression(expression -> right_expression); PutOp(OP_IXOR); if (expression -> Tag() == AstBinaryExpression::EQUAL_EQUAL) { PutOp(OP_ICONST_1); PutOp(OP_IXOR); } return 1; } switch (expression -> Tag()) { case AstBinaryExpression::OR_OR: case AstBinaryExpression::AND_AND: case AstBinaryExpression::LESS: case AstBinaryExpression::LESS_EQUAL: case AstBinaryExpression::GREATER: case AstBinaryExpression::GREATER_EQUAL: case AstBinaryExpression::EQUAL_EQUAL: case AstBinaryExpression::NOT_EQUAL: { // Assume false, and update if true. Label label; PutOp(OP_ICONST_0); // push false EmitBranchIfExpression(expression, false, label); PutOp(OP_POP); // pop the false PutOp(OP_ICONST_1); // push true DefineLabel(label); CompleteLabel(label); } return 1; default: break; } // // Finally, if we get here, the expression cannot be optimized. // EmitExpression(expression -> left_expression); EmitExpression(expression -> right_expression); bool integer_type = type == control.boolean_type || control.IsSimpleIntegerValueType(type); switch (expression -> Tag()) { case AstBinaryExpression::STAR: PutOp(integer_type ? OP_IMUL : type == control.long_type ? OP_LMUL : type == control.float_type ? OP_FMUL : OP_DMUL); // double_type break; case AstBinaryExpression::SLASH: PutOp(integer_type ? OP_IDIV : type == control.long_type ? OP_LDIV : type == control.float_type ? OP_FDIV : OP_DDIV); // double_type break; case AstBinaryExpression::MOD: PutOp(integer_type ? OP_IREM : type == control.long_type ? OP_LREM : type == control.float_type ? OP_FREM : OP_DREM); // double_type break; case AstBinaryExpression::PLUS: PutOp(integer_type ? OP_IADD : type == control.long_type ? OP_LADD : type == control.float_type ? OP_FADD : OP_DADD); // double_type break; case AstBinaryExpression::MINUS: PutOp(integer_type ? OP_ISUB : type == control.long_type ? OP_LSUB : type == control.float_type ? OP_FSUB : OP_DSUB); // double_type break; case AstBinaryExpression::LEFT_SHIFT: PutOp(integer_type ? OP_ISHL : OP_LSHL); break; case AstBinaryExpression::RIGHT_SHIFT: PutOp(integer_type ? OP_ISHR : OP_LSHR); break; case AstBinaryExpression::UNSIGNED_RIGHT_SHIFT: PutOp(integer_type ? OP_IUSHR : OP_LUSHR); break; case AstBinaryExpression::AND: PutOp(integer_type ? OP_IAND : OP_LAND); break; case AstBinaryExpression::XOR: PutOp(integer_type ? OP_IXOR : OP_LXOR); break; case AstBinaryExpression::IOR: PutOp(integer_type ? OP_IOR : OP_LOR); break; default: assert(false && "binary unknown tag"); } return GetTypeWords(expression -> Type()); } int ByteCode::EmitInstanceofExpression(AstInstanceofExpression* expr, bool need_value) { TypeSymbol* left_type = expr -> expression -> Type(); TypeSymbol* right_type = expr -> type -> symbol; if (right_type -> num_dimensions > 255) semantic.ReportSemError(SemanticError::ARRAY_OVERFLOW, expr -> type); if (left_type == control.null_type) { // // We know the result: false. But emit the left expression, // in case of side effects in (expr ? null : null). // EmitExpression(expr -> expression, false); if (need_value) PutOp(OP_ICONST_0); } else if (expr -> expression -> IsConstant() || expr -> expression -> BinaryExpressionCast()) { // // We know the result: true, since the string literals and string // concats are non-null and String is a final class. // assert(left_type == control.String()); EmitExpression(expr -> expression, false); if (need_value) PutOp(OP_ICONST_1); } else if ((expr -> expression -> ThisExpressionCast() || expr -> expression -> SuperExpressionCast() || expr -> expression -> ClassLiteralCast() || expr -> expression -> ClassCreationExpressionCast() || expr -> expression -> ArrayCreationExpressionCast()) && left_type -> IsSubtype(right_type)) { // // We know the result: true, since the expression is non-null. // EmitExpression(expr -> expression, false); if (need_value) PutOp(OP_ICONST_1); } else { EmitExpression(expr -> expression, need_value); if (need_value) { PutOp(OP_INSTANCEOF); PutU2(RegisterClass(right_type)); } } return need_value ? 1 : 0; } int ByteCode::EmitCastExpression(AstCastExpression* expression, bool need_value) { TypeSymbol* dest_type = expression -> Type(); TypeSymbol* source_type = expression -> expression -> Type(); if (dest_type -> num_dimensions > 255 && expression -> type) { semantic.ReportSemError(SemanticError::ARRAY_OVERFLOW, expression -> type); } // // Object downcasts must be emitted, in case of a ClassCastException. // EmitExpression(expression -> expression, need_value || dest_type -> IsSubtype(source_type)); if (need_value || dest_type -> IsSubtype(source_type)) { EmitCast(dest_type, source_type); if (! need_value) { assert(source_type -> IsSubtype(control.Object())); PutOp(OP_POP); } } return need_value ? GetTypeWords(dest_type) : 0; } void ByteCode::EmitCast(TypeSymbol* dest_type, TypeSymbol* source_type) { if (source_type -> IsSubtype(dest_type) || source_type == control.null_type) { return; // done if nothing to do } if (control.IsSimpleIntegerValueType(source_type)) { if (dest_type == control.int_type || (source_type == control.byte_type && dest_type == control.short_type)) { return; // no conversion needed } Opcode op_kind = (dest_type == control.long_type ? OP_I2L : dest_type == control.float_type ? OP_I2F : dest_type == control.double_type ? OP_I2D : dest_type == control.char_type ? OP_I2C : dest_type == control.byte_type ? OP_I2B : OP_I2S); // short_type // If the type we wanted to cast to could not be matched then // the cast is invalid. For example, one might be trying // to cast an int to a Object. assert(op_kind != OP_I2S || dest_type == control.short_type); PutOp(op_kind); } else if (source_type == control.long_type) { Opcode op_kind = (dest_type == control.float_type ? OP_L2F : dest_type == control.double_type ? OP_L2D : OP_L2I); PutOp(op_kind); if (op_kind == OP_L2I && dest_type != control.int_type) { assert(control.IsSimpleIntegerValueType(dest_type) && "unsupported conversion"); PutOp(dest_type == control.char_type ? OP_I2C : dest_type == control.byte_type ? OP_I2B : OP_I2S); // short_type } } else if (source_type == control.float_type) { Opcode op_kind = (dest_type == control.long_type ? OP_F2L : dest_type == control.double_type ? OP_F2D : OP_F2I); PutOp(op_kind); if (op_kind == OP_F2I && dest_type != control.int_type) { assert(control.IsSimpleIntegerValueType(dest_type) && "unsupported conversion"); PutOp(dest_type == control.char_type ? OP_I2C : dest_type == control.byte_type ? OP_I2B : OP_I2S); // short_type } } else if (source_type == control.double_type) { Opcode op_kind = (dest_type == control.long_type ? OP_D2L : dest_type == control.float_type ? OP_D2F : OP_D2I); PutOp(op_kind); if (op_kind == OP_D2I && dest_type != control.int_type) { assert(control.IsSimpleIntegerValueType(dest_type) && "unsupported conversion"); PutOp(dest_type == control.char_type ? OP_I2C : dest_type == control.byte_type ? OP_I2B : OP_I2S); // short_type } } else { PutOp(OP_CHECKCAST); PutU2(RegisterClass(dest_type)); } } // // Emits the required check for null in a qualified instance creation, // super constructor call, or constant instance variable reference, if the // base expression can possibly be null. It also emits the base expression. // In the case of anonymous classes, we emit an alternate expression (the // constructor parameter), after performing the null check on the qualifier // of the anonymous class instance creation expression. // void ByteCode::EmitCheckForNull(AstExpression* expression, bool need_value) { expression = StripNops(expression); if (expression -> Type() == control.null_type) { // // It's guaranteed to be null, so cause any side effects, then throw // the null already on the stack (which will make the VM correctly // create and throw a NullPointerException). Adjust the stack if // necessary, since the calling context does not realize that this // will always complete abruptly. // EmitExpression(expression, true); PutOp(OP_ATHROW); if (need_value) ChangeStack(1); return; } VariableSymbol* variable = expression -> symbol -> VariableCast(); if (expression -> ClassCreationExpressionCast() || expression -> ThisExpressionCast() || expression -> SuperExpressionCast() || expression -> ClassLiteralCast() || (variable && variable -> ACC_SYNTHETIC() && variable -> Identity() == control.this_name_symbol)) { EmitExpression(expression, need_value); return; } // // We did not bother checking for other guaranteed non-null conditions: // IsConstant(), string concats, and ArrayCreationExpressionCast(), since // none of these can qualify a constructor invocation or a constant // instance field reference. If we get here, it is uncertain whether the // expression can be null, so check, using: // // ((Object) ref).getClass(); // // This discarded instance method call will cause the necessary // NullPointerException if invoked on null; and since it is final in // Object, we can be certain it has no side-effects. // EmitExpression(expression, true); if (need_value) PutOp(OP_DUP); PutOp(OP_INVOKEVIRTUAL); ChangeStack(1); // for returned value PutU2(RegisterLibraryMethodref(control.Object_getClassMethod())); PutOp(OP_POP); } int ByteCode::EmitClassCreationExpression(AstClassCreationExpression* expr, bool need_value) { if (expr -> resolution_opt) expr = expr -> resolution_opt; MethodSymbol* constructor = (MethodSymbol*) expr -> symbol; TypeSymbol* type = constructor -> containing_type; PutOp(OP_NEW); PutU2(RegisterClass(type)); if (need_value) // save address of new object for constructor PutOp(OP_DUP); // // Pass enclosing instance along, then real arguments, then shadow // variables, and finally an extra null argument, as needed. // int stack_words = 0; unsigned i = 0; if (expr -> base_opt) { stack_words++; EmitCheckForNull(expr -> base_opt); } if (type -> Anonymous() && type -> super -> EnclosingInstance()) { stack_words++; EmitCheckForNull(expr -> arguments -> Argument(i++)); } for ( ; i < expr -> arguments -> NumArguments(); i++) stack_words += EmitExpression(expr -> arguments -> Argument(i)); for (i = 0; i < expr -> arguments -> NumLocalArguments(); i++) stack_words += EmitExpression(expr -> arguments -> LocalArgument(i)); if (expr -> arguments -> NeedsExtraNullArgument()) { PutOp(OP_ACONST_NULL); stack_words++; } PutOp(OP_INVOKESPECIAL); ChangeStack(-stack_words); PutU2(RegisterMethodref(type, constructor)); return 1; } int ByteCode::EmitConditionalExpression(AstConditionalExpression* expression, bool need_value) { // // Optimize (true ? a : b) to (a). // Optimize (false ? a : b) (b). // if (expression -> test_expression -> IsConstant()) return EmitExpression((IsZero(expression -> test_expression) ? expression -> false_expression : expression -> true_expression), need_value); if (expression -> Type() == control.null_type) { // // The null literal has no side effects, but null_expr might. // Optimize (cond ? null : null) to (cond, null). // Optimize (cond ? null_expr : null) to (cond && null_expr, null). // Optimize (cond ? null : null_expr) to (cond || null_expr, null). // if (expression -> false_expression -> NullLiteralCast()) { if (expression -> true_expression -> NullLiteralCast()) EmitExpression(expression -> test_expression, false); else { Label lab; EmitBranchIfExpression(expression -> test_expression, false, lab); EmitExpression(expression -> true_expression, false); DefineLabel(lab); CompleteLabel(lab); } if (need_value) PutOp(OP_ACONST_NULL); return need_value ? 1 : 0; } if (expression -> true_expression -> NullLiteralCast()) { Label lab; EmitBranchIfExpression(expression -> test_expression, true, lab); EmitExpression(expression -> false_expression, false); DefineLabel(lab); CompleteLabel(lab); if (need_value) PutOp(OP_ACONST_NULL); return need_value ? 1 : 0; } } else if (expression -> true_expression -> IsConstant()) { if (expression -> false_expression -> IsConstant()) { if (! need_value) return EmitExpression(expression -> test_expression, false); if (expression -> true_expression -> value == expression -> false_expression -> value) { // // Optimize (cond ? expr : expr) to (cond, expr). // EmitExpression(expression -> test_expression, false); return EmitExpression(expression -> true_expression); } if (control.IsSimpleIntegerValueType(expression -> Type()) || expression -> Type() == control.boolean_type) { // // Optimize (expr ? 1 : 0) to (expr). // Optimize (expr ? value + 1 : value) to (expr + value). // Optimize (expr ? value - 1 : value) to (value - expr). // IntLiteralValue* left = DYNAMIC_CAST (expression -> true_expression -> value); IntLiteralValue* right = DYNAMIC_CAST (expression -> false_expression -> value); if (left -> value == 1 && right -> value == 0) return EmitExpression(expression -> test_expression); if (left -> value == right -> value + 1) { EmitExpression(expression -> test_expression); EmitExpression(expression -> false_expression); PutOp(OP_IADD); return 1; } if (left -> value == right -> value - 1) { EmitExpression(expression -> false_expression); EmitExpression(expression -> test_expression); PutOp(OP_ISUB); return 1; } } } else if ((control.IsSimpleIntegerValueType(expression -> Type()) || expression -> Type() == control.boolean_type) && (IsOne(expression -> true_expression) || IsZero(expression -> true_expression))) { // // Optimize (cond ? 1 : b) to (cond || b) // Optimize (cond ? 0 : b) to (!cond && b) // Label label; if (need_value) PutOp(IsZero(expression -> true_expression) ? OP_ICONST_0 : OP_ICONST_1); EmitBranchIfExpression(expression -> test_expression, true, label); if (need_value) PutOp(OP_POP); EmitExpression(expression -> false_expression, need_value); DefineLabel(label); CompleteLabel(label); return need_value ? 1 : 0; } } else if ((control.IsSimpleIntegerValueType(expression -> Type()) || expression -> Type() == control.boolean_type) && (IsOne(expression -> false_expression) || IsZero(expression -> false_expression))) { // // Optimize (cond ? a : 0) to (cond && a) // Optimize (cond ? a : 1) to (!cond || a) // Label label; if (need_value) PutOp(IsZero(expression -> false_expression) ? OP_ICONST_0 : OP_ICONST_1); EmitBranchIfExpression(expression -> test_expression, false, label); if (need_value) PutOp(OP_POP); EmitExpression(expression -> true_expression, need_value); DefineLabel(label); CompleteLabel(label); return need_value ? 1 : 0; } Label lab1, lab2; EmitBranchIfExpression(expression -> test_expression, false, lab1); EmitExpression(expression -> true_expression, need_value); EmitBranch(OP_GOTO, lab2); if (need_value) // restore the stack size ChangeStack(- GetTypeWords(expression -> Type())); DefineLabel(lab1); EmitExpression(expression -> false_expression, need_value); DefineLabel(lab2); CompleteLabel(lab2); CompleteLabel(lab1); return GetTypeWords(expression -> true_expression -> Type()); } int ByteCode::EmitFieldAccess(AstFieldAccess* expression, bool need_value) { if (expression -> resolution_opt) return LoadVariable(ACCESSED_VAR, expression, need_value); VariableSymbol* sym = expression -> symbol -> VariableCast(); assert(sym); return LoadVariable(sym -> ACC_STATIC() ? STATIC_VAR : FIELD_VAR, expression, need_value); } int ByteCode::EmitMethodInvocation(AstMethodInvocation* expression, bool need_value) { // // If the method call was resolved into a call to another method, use the // resolution expression. // AstMethodInvocation* method_call = expression -> resolution_opt ? expression -> resolution_opt -> MethodInvocationCast() : expression; assert(method_call); MethodSymbol* msym = (MethodSymbol*) method_call -> symbol; AstExpression* base = method_call -> base_opt; bool is_super = false; // set if super call if (msym -> ACC_STATIC()) { // // If the access is qualified by an arbitrary base // expression, evaluate it for side effects. // Notice that accessor methods, which are always static, might // access an instance method, in which case the base expression // will already be evaluated as the first parameter. // if (base && (! msym -> accessed_member || msym -> AccessesStaticMember())) { EmitExpression(base, false); } } else { if (base) { // // Note that field will be marked IsSuperAccess only in synthetic // accessor methods. Code that calls Foo.super.bar() in a nested // class creates an accessor method: // Foo.access$(Foo $1) { $1.bar(); } // but must use invokespecial instead of the regular invokevirtual. // is_super = base -> SuperExpressionCast() != NULL; EmitExpression(base); } else PutOp(OP_ALOAD_0); } int stack_words = 0; // words on stack needed for arguments for (unsigned i = 0; i < method_call -> arguments -> NumArguments(); i++) stack_words += EmitExpression(method_call -> arguments -> Argument(i)); TypeSymbol* type = MethodTypeResolution(method_call -> base_opt, msym); PutOp(msym -> ACC_STATIC() ? OP_INVOKESTATIC : (is_super || msym -> ACC_PRIVATE()) ? OP_INVOKESPECIAL : type -> ACC_INTERFACE() ? OP_INVOKEINTERFACE : OP_INVOKEVIRTUAL); return CompleteCall(msym, stack_words, need_value, type); } int ByteCode::CompleteCall(MethodSymbol* msym, int stack_words, bool need_value, TypeSymbol* base_type) { ChangeStack(- stack_words); TypeSymbol* type = (base_type ? base_type : msym -> containing_type); PutU2(RegisterMethodref(type, msym)); if (type -> ACC_INTERFACE()) { PutU1(stack_words + 1); PutU1(0); } // // Must account for value returned by method. // if (msym -> Type() == control.void_type) return 0; bool wide = control.IsDoubleWordType(msym -> Type()); ChangeStack(wide ? 2 : 1); if (! need_value) { PutOp(wide ? OP_POP2 : OP_POP); return 0; } return wide ? 2 : 1; } // // Called when expression has been parenthesized; remove parentheses and // widening casts to expose true structure. // AstExpression* ByteCode::StripNops(AstExpression* expr) { while (! expr -> IsConstant()) { if (expr -> ParenthesizedExpressionCast()) expr = ((AstParenthesizedExpression*) expr) -> expression; else if (expr -> CastExpressionCast()) { AstCastExpression* cast_expr = (AstCastExpression*) expr; TypeSymbol* cast_type = expr -> Type(); AstExpression* sub_expr = StripNops(cast_expr -> expression); TypeSymbol* sub_type = sub_expr -> Type(); if (sub_type -> IsSubtype(cast_type) || (sub_type == control.byte_type && (cast_type == control.short_type || cast_type == control.int_type)) || ((sub_type == control.short_type || sub_type == control.char_type) && cast_type == control.int_type) || (sub_type == control.null_type && cast_type -> num_dimensions <= 255)) { return sub_expr; } else return expr; } else return expr; } return expr; } bool ByteCode::IsNop(AstBlock* block) { for (int i = block -> NumStatements() - 1; i >= 0; i--) { Ast* statement = block -> Statement(i); if (statement -> EmptyStatementCast() || statement -> LocalClassStatementCast() || (statement -> BlockCast() && IsNop((AstBlock*) statement))) continue; if (statement -> kind == Ast::IF) { AstIfStatement* ifstat = (AstIfStatement*) statement; if ((IsOne(ifstat -> expression) && IsNop(ifstat -> true_statement)) || (IsZero(ifstat -> expression) && (! ifstat -> false_statement_opt || IsNop(ifstat -> false_statement_opt)))) { continue; } } // // TODO: Is it worth adding more checks for bypassed code? // return false; } return true; } void ByteCode::EmitNewArray(unsigned num_dims, const TypeSymbol* type) { assert(num_dims); if (num_dims == 1) { TypeSymbol* element_type = type -> ArraySubtype(); if (control.IsPrimitive(element_type)) { PutOp(OP_NEWARRAY); PutU1(element_type == control.boolean_type ? 4 : element_type == control.char_type ? 5 : element_type == control.float_type ? 6 : element_type == control.double_type ? 7 : element_type == control.byte_type ? 8 : element_type == control.short_type ? 9 : element_type == control.int_type ? 10 : 11); // control.long_type } else // must be reference type { PutOp(OP_ANEWARRAY); PutU2(RegisterClass(element_type)); } } else { PutOp(OP_MULTIANEWARRAY); PutU2(RegisterClass(type)); PutU1(num_dims); // load dims count ChangeStack(1 - num_dims); } } // // Initial part of array access: ready to either load or store after this. // void ByteCode::EmitArrayAccessLhs(AstArrayAccess* expression) { TypeSymbol* base_type = expression -> base -> Type(); AstExpression* base = StripNops(expression -> base); EmitExpression(base); if (control.option.target < JikesOption::SDK1_5 && IsMultiDimensionalArray(base_type) && base -> Type() == control.null_type) { // // Prior to JDK 1.5, VMs incorrectly complained if assigning an array // type into an element of a null expression (in other words, null // was not being treated as compatible with a multi-dimensional array // on the aastore opcode). The workaround requires a checkcast any // time null might be assigned to a multi-dimensional local variable // or directly used as an array access base. // PutOp(OP_CHECKCAST); PutU2(RegisterClass(base_type)); } EmitExpression(expression -> expression); } // // POST_UNARY // int ByteCode::EmitPostUnaryExpression(AstPostUnaryExpression* expression, bool need_value) { VariableCategory kind = GetVariableKind(expression); switch (kind) { case LOCAL_VAR: case STATIC_VAR: EmitPostUnaryExpressionSimple(kind, expression, need_value); break; case ARRAY_VAR: EmitPostUnaryExpressionArray(expression, need_value); break; case FIELD_VAR: EmitPostUnaryExpressionField(kind, expression, need_value); break; case ACCESSED_VAR: { VariableSymbol* accessed_member = expression -> write_method -> accessed_member -> VariableCast(); if (accessed_member -> ACC_STATIC()) EmitPostUnaryExpressionSimple(kind, expression, need_value); else EmitPostUnaryExpressionField(kind, expression, need_value); } break; default: assert(false && "unknown lhs kind for assignment"); } return GetTypeWords(expression -> Type()); } // // AstExpression* expression; // POST_UNARY on instance variable // load value of field, duplicate, do increment or decrement, then store // back, leaving original value on top of stack. // void ByteCode::EmitPostUnaryExpressionField(VariableCategory kind, AstPostUnaryExpression* expression, bool need_value) { if (kind == ACCESSED_VAR) ResolveAccess(expression -> expression); // get address and value else EmitFieldAccessLhs(expression -> expression); TypeSymbol* expression_type = expression -> Type(); if (need_value) PutOp(control.IsDoubleWordType(expression_type) ? OP_DUP2_X1 : OP_DUP_X1); if (control.IsSimpleIntegerValueType(expression_type)) { PutOp(OP_ICONST_1); PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_IADD : OP_ISUB); EmitCast(expression_type, control.int_type); } else if (expression_type == control.long_type) { PutOp(OP_LCONST_1); PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_LADD : OP_LSUB); } else if (expression_type == control.float_type) { PutOp(OP_FCONST_1); PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_FADD : OP_FSUB); } else if (expression_type == control.double_type) { PutOp(OP_DCONST_1); // load 1.0 PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_DADD : OP_DSUB); } if (kind == ACCESSED_VAR) { int stack_words = GetTypeWords(expression_type) + 1; PutOp(OP_INVOKESTATIC); CompleteCall(expression -> write_method, stack_words); } else // assert(kind == FIELD_VAR) { PutOp(OP_PUTFIELD); if (control.IsDoubleWordType(expression_type)) ChangeStack(-1); VariableSymbol* sym = (VariableSymbol*) expression -> symbol; PutU2(RegisterFieldref(VariableTypeResolution(expression -> expression, sym), sym)); } } // // AstExpression* expression; // POST_UNARY on local variable // load value of variable, duplicate, do increment or decrement, then store // back, leaving original value on top of stack. // void ByteCode::EmitPostUnaryExpressionSimple(VariableCategory kind, AstPostUnaryExpression* expression, bool need_value) { TypeSymbol* expression_type = expression -> Type(); if (kind == LOCAL_VAR && expression_type == control.int_type) { // can we use IINC ?? LoadVariable(kind, StripNops(expression -> expression), need_value); PutOpIINC(expression -> symbol -> VariableCast() -> LocalVariableIndex(), expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? 1 : -1); return; } // this will also load value needing resolution LoadVariable(kind, StripNops(expression -> expression)); if (need_value) PutOp(control.IsDoubleWordType(expression_type) ? OP_DUP2 : OP_DUP); if (control.IsSimpleIntegerValueType(expression_type)) { PutOp(OP_ICONST_1); PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_IADD : OP_ISUB); EmitCast(expression_type, control.int_type); } else if (expression_type == control.long_type) { PutOp(OP_LCONST_1); PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_LADD : OP_LSUB); } else if (expression_type == control.float_type) { PutOp(OP_FCONST_1); PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_FADD : OP_FSUB); } else if (expression_type == control.double_type) { PutOp(OP_DCONST_1); // load 1.0 PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_DADD : OP_DSUB); } if (kind == ACCESSED_VAR) { int stack_words = GetTypeWords(expression_type); PutOp(OP_INVOKESTATIC); CompleteCall(expression -> write_method, stack_words); } else StoreVariable(kind, expression -> expression); } // // Post Unary for which operand is array element // assignment for which lhs is array element // AstExpression* expression; // void ByteCode::EmitPostUnaryExpressionArray(AstPostUnaryExpression* expression, bool need_value) { // // JLS2 added ability for parenthesized variable to remain a variable. // EmitArrayAccessLhs((AstArrayAccess*) StripNops(expression -> expression)); // lhs must be array access PutOp(OP_DUP2); // save array base and index for later store TypeSymbol* expression_type = expression -> Type(); if (expression_type == control.int_type) { PutOp(OP_IALOAD); if (need_value) // save value below saved array base and index PutOp(OP_DUP_X2); PutOp(OP_ICONST_1); PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_IADD : OP_ISUB); PutOp(OP_IASTORE); } else if (expression_type == control.byte_type ) { PutOp(OP_BALOAD); if (need_value) // save value below saved array base and index PutOp(OP_DUP_X2); PutOp(OP_ICONST_1); PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_IADD : OP_ISUB); PutOp(OP_I2B); PutOp(OP_BASTORE); } else if (expression_type == control.char_type ) { PutOp(OP_CALOAD); if (need_value) // save value below saved array base and index PutOp(OP_DUP_X2); PutOp(OP_ICONST_1); PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_IADD : OP_ISUB); PutOp(OP_I2C); PutOp(OP_CASTORE); } else if (expression_type == control.short_type) { PutOp(OP_SALOAD); if (need_value) // save value below saved array base and index PutOp(OP_DUP_X2); PutOp(OP_ICONST_1); PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_IADD : OP_ISUB); PutOp(OP_I2S); PutOp(OP_SASTORE); } else if (expression_type == control.long_type) { PutOp(OP_LALOAD); if (need_value) // save value below saved array base and index PutOp(OP_DUP2_X2); PutOp(OP_LCONST_1); PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_LADD : OP_LSUB); PutOp(OP_LASTORE); } else if (expression_type == control.float_type) { PutOp(OP_FALOAD); if (need_value) // save value below saved array base and index PutOp(OP_DUP_X2); PutOp(OP_FCONST_1); PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_FADD : OP_FSUB); PutOp(OP_FASTORE); } else if (expression_type == control.double_type) { PutOp(OP_DALOAD); if (need_value) // save value below saved array base and index PutOp(OP_DUP2_X2); PutOp(OP_DCONST_1); PutOp(expression -> Tag() == AstPostUnaryExpression::PLUSPLUS ? OP_DADD : OP_DSUB); PutOp(OP_DASTORE); } else assert(false && "unsupported postunary type"); } // // PRE_UNARY // int ByteCode::EmitPreUnaryExpression(AstPreUnaryExpression* expression, bool need_value) { TypeSymbol* type = expression -> Type(); if (expression -> Tag() == AstPreUnaryExpression::PLUSPLUS || expression -> Tag() == AstPreUnaryExpression::MINUSMINUS) { EmitPreUnaryIncrementExpression(expression, need_value); } else // here for ordinary unary operator without side effects. { EmitExpression(expression -> expression, need_value); if (! need_value) return 0; switch (expression -> Tag()) { case AstPreUnaryExpression::PLUS: // Nothing else to do. break; case AstPreUnaryExpression::MINUS: assert(control.IsNumeric(type) && "unary minus on bad type"); PutOp(control.IsSimpleIntegerValueType(type) ? OP_INEG : type == control.long_type ? OP_LNEG : type == control.float_type ? OP_FNEG : OP_DNEG); // double_type break; case AstPreUnaryExpression::TWIDDLE: if (control.IsSimpleIntegerValueType(type)) { PutOp(OP_ICONST_M1); // -1 PutOp(OP_IXOR); // exclusive or to get result } else if (type == control.long_type) { PutOp(OP_LCONST_1); // make -1 PutOp(OP_LNEG); PutOp(OP_LXOR); // exclusive or to get result } else assert(false && "unary ~ on unsupported type"); break; case AstPreUnaryExpression::NOT: assert(type == control.boolean_type); PutOp(OP_ICONST_1); PutOp(OP_IXOR); // !(e) <=> (e)^true break; default: assert(false && "unknown preunary tag"); } } return GetTypeWords(type); } // // PRE_UNARY with side effects (++X or --X) // void ByteCode::EmitPreUnaryIncrementExpression(AstPreUnaryExpression* expression, bool need_value) { VariableCategory kind = GetVariableKind(expression); switch (kind) { case LOCAL_VAR: case STATIC_VAR: EmitPreUnaryIncrementExpressionSimple(kind, expression, need_value); break; case ARRAY_VAR: EmitPreUnaryIncrementExpressionArray(expression, need_value); break; case FIELD_VAR: EmitPreUnaryIncrementExpressionField(kind, expression, need_value); break; case ACCESSED_VAR: { VariableSymbol* accessed_member = expression -> write_method -> accessed_member -> VariableCast(); if (accessed_member -> ACC_STATIC()) EmitPreUnaryIncrementExpressionSimple(kind, expression, need_value); else EmitPreUnaryIncrementExpressionField(kind, expression, need_value); } break; default: assert(false && "unknown lhs kind for assignment"); } } // // AstExpression* expression; // PRE_UNARY on name // load value of variable, do increment or decrement, duplicate, then store // back, leaving new value on top of stack. // void ByteCode::EmitPreUnaryIncrementExpressionSimple(VariableCategory kind, AstPreUnaryExpression* expression, bool need_value) { TypeSymbol* type = expression -> Type(); if (kind == LOCAL_VAR && type == control.int_type) { PutOpIINC(expression -> symbol -> VariableCast() -> LocalVariableIndex(), expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? 1 : -1); LoadVariable(kind, StripNops(expression -> expression), need_value); return; } // will also load value if resolution needed LoadVariable(kind, StripNops(expression -> expression)); if (control.IsSimpleIntegerValueType(type)) { PutOp(OP_ICONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_IADD : OP_ISUB); EmitCast(type, control.int_type); if (need_value) PutOp(OP_DUP); } else if (type == control.long_type) { PutOp(OP_LCONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_LADD : OP_LSUB); if (need_value) PutOp(OP_DUP2); } else if (type == control.float_type) { PutOp(OP_FCONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_FADD : OP_FSUB); if (need_value) PutOp(OP_DUP); } else if (type == control.double_type) { PutOp(OP_DCONST_1); // load 1.0 PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_DADD : OP_DSUB); if (need_value) PutOp(OP_DUP2); } if (kind == ACCESSED_VAR) { int stack_words = GetTypeWords(type); PutOp(OP_INVOKESTATIC); CompleteCall(expression -> write_method, stack_words); } else StoreVariable(kind, expression -> expression); } // // Post Unary for which operand is array element // assignment for which lhs is array element // AstExpression* expression; // void ByteCode::EmitPreUnaryIncrementExpressionArray(AstPreUnaryExpression* expression, bool need_value) { // // JLS2 added ability for parenthesized variable to remain a variable. // // lhs must be array access EmitArrayAccessLhs((AstArrayAccess*) StripNops(expression -> expression)); PutOp(OP_DUP2); // save array base and index for later store TypeSymbol* type = expression -> Type(); if (type == control.int_type) { PutOp(OP_IALOAD); PutOp(OP_ICONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_IADD : OP_ISUB); if (need_value) PutOp(OP_DUP_X2); PutOp(OP_IASTORE); } else if (type == control.byte_type) { PutOp(OP_BALOAD); PutOp(OP_ICONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_IADD : OP_ISUB); PutOp(OP_I2B); if (need_value) PutOp(OP_DUP_X2); PutOp(OP_BASTORE); } else if (type == control.char_type) { PutOp(OP_CALOAD); PutOp(OP_ICONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_IADD : OP_ISUB); PutOp(OP_I2C); if (need_value) PutOp(OP_DUP_X2); PutOp(OP_CASTORE); } else if (type == control.short_type) { PutOp(OP_SALOAD); PutOp(OP_ICONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_IADD : OP_ISUB); PutOp(OP_I2S); if (need_value) PutOp(OP_DUP_X2); PutOp(OP_SASTORE); } else if (type == control.long_type) { PutOp(OP_LALOAD); PutOp(OP_LCONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_LADD : OP_LSUB); if (need_value) PutOp(OP_DUP2_X2); PutOp(OP_LASTORE); } else if (type == control.float_type) { PutOp(OP_FALOAD); PutOp(OP_FCONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_FADD : OP_FSUB); if (need_value) PutOp(OP_DUP_X2); PutOp(OP_FASTORE); } else if (type == control.double_type) { PutOp(OP_DALOAD); PutOp(OP_DCONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_DADD : OP_DSUB); if (need_value) PutOp(OP_DUP2_X2); PutOp(OP_DASTORE); } else assert(false && "unsupported PreUnary type"); } // // Pre Unary for which operand is field (instance variable) // AstExpression* expression; // void ByteCode::EmitPreUnaryIncrementExpressionField(VariableCategory kind, AstPreUnaryExpression* expression, bool need_value) { if (kind == ACCESSED_VAR) ResolveAccess(expression -> expression); // get address and value else // need to load address of object, obtained from resolution, saving // a copy on the stack EmitFieldAccessLhs(expression -> expression); TypeSymbol* expression_type = expression -> Type(); if (control.IsSimpleIntegerValueType(expression_type)) { PutOp(OP_ICONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_IADD : OP_ISUB); EmitCast(expression_type, control.int_type); if (need_value) PutOp(OP_DUP_X1); } else if (expression_type == control.long_type) { PutOp(OP_LCONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_LADD : OP_LSUB); if (need_value) PutOp(OP_DUP2_X1); } else if (expression_type == control.float_type) { PutOp(OP_FCONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_FADD : OP_FSUB); if (need_value) PutOp(OP_DUP_X1); } else if (expression_type == control.double_type) { PutOp(OP_DCONST_1); PutOp(expression -> Tag() == AstPreUnaryExpression::PLUSPLUS ? OP_DADD : OP_DSUB); if (need_value) PutOp(OP_DUP2_X1); } else assert(false && "unsupported PreUnary type"); if (kind == ACCESSED_VAR) { int stack_words = GetTypeWords(expression_type) + 1; PutOp(OP_INVOKESTATIC); CompleteCall(expression -> write_method, stack_words); } else { PutOp(OP_PUTFIELD); if (control.IsDoubleWordType(expression_type)) ChangeStack(-1); VariableSymbol* sym = (VariableSymbol*) expression -> symbol; PutU2(RegisterFieldref(VariableTypeResolution(expression -> expression, sym), sym)); } } void ByteCode::EmitThisInvocation(AstThisCall* this_call) { // // Pass enclosing instance along, then real arguments. // PutOp(OP_ALOAD_0); // load 'this' int stack_words = 0; // words on stack needed for arguments if (unit_type -> EnclosingType()) LoadLocal(++stack_words, unit_type -> EnclosingType()); for (unsigned k = 0; k < this_call -> arguments -> NumArguments(); k++) stack_words += EmitExpression(this_call -> arguments -> Argument(k)); // // Now do a transfer of the shadow variables. We do not need to worry // about an extra null argument, as there are no accessibility issues // when invoking this(). // if (shadow_parameter_offset) { int offset = shadow_parameter_offset; for (unsigned i = 0; i < unit_type -> NumConstructorParameters(); i++) { VariableSymbol* shadow = unit_type -> ConstructorParameter(i); LoadLocal(offset, shadow -> Type()); int words = GetTypeWords(shadow -> Type()); offset += words; stack_words += words; } } PutOp(OP_INVOKESPECIAL); ChangeStack(-stack_words); PutU2(RegisterMethodref(unit_type, this_call -> symbol)); } void ByteCode::EmitSuperInvocation(AstSuperCall* super_call) { // // Pass enclosing instance along, then real arguments, then shadow // variables, and finally any extra null argument for accessibility // issues. // PutOp(OP_ALOAD_0); // load 'this' int stack_words = 0; // words on stack needed for arguments unsigned i; if (super_call -> base_opt) { stack_words++; if (unit_type -> Anonymous()) { // // Special case - the null check was done during the class instance // creation, so we skip it here. // EmitExpression(super_call -> base_opt); } else EmitCheckForNull(super_call -> base_opt); } for (i = 0; i < super_call -> arguments -> NumArguments(); i++) stack_words += EmitExpression(super_call -> arguments -> Argument(i)); for (i = 0; i < super_call -> arguments -> NumLocalArguments(); i++) stack_words += EmitExpression(super_call -> arguments -> LocalArgument(i)); if (super_call -> arguments -> NeedsExtraNullArgument()) { PutOp(OP_ACONST_NULL); stack_words++; } PutOp(OP_INVOKESPECIAL); ChangeStack(-stack_words); PutU2(RegisterMethodref(unit_type -> super, super_call -> symbol)); } // // Methods for string concatenation // void ByteCode::ConcatenateString(AstBinaryExpression* expression, bool need_value) { // // Generate code to concatenate strings, by generating a string buffer // and appending the arguments before calling toString, i.e., // s1+s2 // compiles to // new StringBuffer().append(s1).append(s2).toString(); // Use recursion to share a single buffer where possible. // If concatenated string is not needed, we must still perform string // conversion on all objects, as well as perform side effects of terms. // In 1.5 and later, StringBuilder was added with better performance. // AstExpression* left_expr = StripNops(expression -> left_expression); if (left_expr -> Type() == control.String() && left_expr -> BinaryExpressionCast() && ! left_expr -> IsConstant()) { ConcatenateString((AstBinaryExpression*) left_expr, need_value); } else { PutOp(OP_NEW); PutU2(RegisterClass(control.option.target >= JikesOption::SDK1_5 ? control.StringBuilder() : control.StringBuffer())); PutOp(OP_DUP); if (left_expr -> IsConstant()) { // // Optimizations: if the left term is "", just append the right // term to an empty StringBuffer. If the left term is not "", // use new StringBuffer(String) to create a StringBuffer // that includes the left term. No need to worry about // new StringBuffer(null) raising a NullPointerException // since string constants are never null. // Utf8LiteralValue* value = DYNAMIC_CAST (left_expr -> value); if (value -> length == 0 || ! need_value) { PutOp(OP_INVOKESPECIAL); PutU2(RegisterLibraryMethodref (control.option.target >= JikesOption::SDK1_5 ? control.StringBuilder_InitMethod() : control.StringBuffer_InitMethod())); } else { LoadConstantAtIndex(RegisterString(value)); PutOp(OP_INVOKESPECIAL); PutU2(RegisterLibraryMethodref (control.option.target >= JikesOption::SDK1_5 ? control.StringBuilder_InitWithStringMethod() : control.StringBuffer_InitWithStringMethod())); ChangeStack(-1); // account for the argument } } else { PutOp(OP_INVOKESPECIAL); PutU2(RegisterLibraryMethodref (control.option.target >= JikesOption::SDK1_5 ? control.StringBuilder_InitMethod() : control.StringBuffer_InitMethod())); // // Don't pass stripped left_expr, or ((int)char)+"" would be // treated as a char append rather than int append. // AppendString(expression -> left_expression, need_value); } } AppendString(expression -> right_expression, need_value); } void ByteCode::AppendString(AstExpression* expression, bool need_value) { // // Grab the type before reducing no-ops, in the case of ""+(int)char. // TypeSymbol* type = expression -> Type(); expression = StripNops(expression); if (expression -> IsConstant()) { Utf8LiteralValue* value = DYNAMIC_CAST (expression -> value); assert(value != NULL); assert(! control.IsPrimitive(type)); // Bug 2919. // Optimization: do nothing when appending "", or for unused result. if (value -> length == 0 || ! need_value) return; if (value -> length == 1) { // Optimization: append(char) more efficient than append(String) LoadImmediateInteger(value -> value[0]); type = control.char_type; } else if (value -> length == 2 && (value -> value[0] & 0x00E0) == 0x00C0) { // 2-byte string in UTF-8, but still single character. LoadImmediateInteger(((value -> value[0] & 0x001F) << 6) | (value -> value[1] & 0x003F)); type = control.char_type; } else if (value -> length == 3 && (value -> value[0] & 0x00E0) == 0x00E0) { // 3-byte string in UTF-8, but still single character. LoadImmediateInteger(((value -> value[0] & 0x000F) << 12) | ((value -> value[1] & 0x003F) << 6) | (value -> value[2] & 0x003F)); type = control.char_type; } else LoadConstantAtIndex(RegisterString(value)); } else { AstBinaryExpression* binary_expression = expression -> BinaryExpressionCast(); if (binary_expression && type == control.String()) { assert(binary_expression -> Tag() == AstBinaryExpression::PLUS); AppendString(binary_expression -> left_expression, need_value); AppendString(binary_expression -> right_expression, need_value); return; } if (! need_value && control.IsPrimitive(type)) { // Optimization: appending non-Object is no-op if result is unused. EmitExpression(expression, false); return; } EmitExpression(expression); } EmitStringAppendMethod(type); } void ByteCode::EmitStringAppendMethod(TypeSymbol* type) { // // Find appropriate append routine to add to string buffer. Do not use // append(char[]), because that inserts the contents instead of the // correct char[].toString(). Treating null as a String is slightly more // efficient than as an Object. // MethodSymbol* append_method; if (control.option.target >= JikesOption::SDK1_5) { append_method = (type == control.char_type ? control.StringBuilder_append_charMethod() : type == control.boolean_type ? control.StringBuilder_append_booleanMethod() : (type == control.int_type || type == control.short_type || type == control.byte_type) ? control.StringBuilder_append_intMethod() : type == control.long_type ? control.StringBuilder_append_longMethod() : type == control.float_type ? control.StringBuilder_append_floatMethod() : type == control.double_type ? control.StringBuilder_append_doubleMethod() : (type == control.String() || type == control.null_type) ? control.StringBuilder_append_stringMethod() : IsReferenceType(type) ? control.StringBuilder_append_objectMethod() : (MethodSymbol*) NULL); // for assertion } else { append_method = (type == control.char_type ? control.StringBuffer_append_charMethod() : type == control.boolean_type ? control.StringBuffer_append_booleanMethod() : (type == control.int_type || type == control.short_type || type == control.byte_type) ? control.StringBuffer_append_intMethod() : type == control.long_type ? control.StringBuffer_append_longMethod() : type == control.float_type ? control.StringBuffer_append_floatMethod() : type == control.double_type ? control.StringBuffer_append_doubleMethod() : (type == control.String() || type == control.null_type) ? control.StringBuffer_append_stringMethod() : IsReferenceType(type) ? control.StringBuffer_append_objectMethod() : (MethodSymbol*) NULL); // for assertion } assert(append_method && "unable to find method for string buffer concatenation"); PutOp(OP_INVOKEVIRTUAL); if (control.IsDoubleWordType(type)) ChangeStack(-1); PutU2(RegisterLibraryMethodref(append_method)); } #ifdef JIKES_DEBUG static void op_trap() { int i = 0; // used for debugger trap i++; // avoid compiler warnings about unused variable } #endif // JIKES_DEBUG ByteCode::ByteCode(TypeSymbol* type) : ClassFile() , control(type -> semantic_environment -> sem -> control) , semantic(*type -> semantic_environment -> sem) , unit_type(type) , string_overflow(false) , library_method_not_found(false) , last_op_goto(false) , shadow_parameter_offset(0) , code_attribute(NULL) , line_number_table_attribute(NULL) , local_variable_table_attribute(NULL) , inner_classes_attribute(NULL) , double_constant_pool_index(NULL) , integer_constant_pool_index(NULL) , long_constant_pool_index(NULL) , float_constant_pool_index(NULL) , string_constant_pool_index(NULL) , utf8_constant_pool_index(segment_pool, control.Utf8_pool.symbol_pool.Length()) , class_constant_pool_index(segment_pool, control.Utf8_pool.symbol_pool.Length()) , name_and_type_constant_pool_index(NULL) , fieldref_constant_pool_index(NULL) , methodref_constant_pool_index(NULL) { #ifdef JIKES_DEBUG if (! control.option.nowrite) control.class_files_written++; #endif // JIKES_DEBUG // // For compatibility reasons, protected classes are marked public, and // private classes are marked default; and no class may be static or // strictfp. Also, a non-access flag, the super bit, must be set for // classes but not interfaces. For top-level types, this changes nothing // except adding the super bit. For nested types, the correct access bits // are emitted later as part of the InnerClasses attribute. Also, no class // is marked strictfp. // SetFlags(unit_type -> Flags()); if (ACC_PROTECTED()) { ResetACC_PROTECTED(); SetACC_PUBLIC(); } else if (ACC_PRIVATE()) ResetACC_PRIVATE(); ResetACC_STATIC(); ResetACC_STRICTFP(); if (! unit_type -> ACC_INTERFACE()) SetACC_SUPER(); switch (control.option.target) { case JikesOption::SDK1_1: major_version = 45; minor_version = 3; break; case JikesOption::SDK1_2: major_version = 46; minor_version = 0; break; case JikesOption::SDK1_3: major_version = 47; minor_version = 0; break; case JikesOption::SDK1_4: case JikesOption::SDK1_4_2: major_version = 48; minor_version = 0; break; case JikesOption::SDK1_5: major_version = 49; minor_version = 0; break; default: assert(false && "unknown version for target"); } #ifdef JIKES_DEBUG if (control.option.verbose) Coutput << "[generating code for class " << unit_type -> fully_qualified_name -> value << " as version " << major_version << '.' << minor_version << ']' << endl; #endif // JIKES_DEBUG this_class = RegisterClass(unit_type); super_class = (unit_type -> super ? RegisterClass(unit_type -> super) : 0); for (unsigned k = 0; k < unit_type -> NumInterfaces(); k++) interfaces.Next() = RegisterClass(unit_type -> Interface(k)); } // // Methods for manipulating labels // void ByteCode::DefineLabel(Label& lab) { assert(! lab.defined && "duplicate label definition"); // // Optimize if previous instruction was unconditional jump to this label. // However, we cannot perform the optimization if another label was also // defined at this location. Likewise, if local symbol tables are being // emitted, this optimization would screw up the symbol table. // // TODO: It would be nice to redo the bytecode emitter, to make it a // two-pass algorithm with straight-forward emission the first time, and // peephole optimizations the second time. This would be a better way to // cleanly collapse useless jumps, and could catch several other cases // that are missed or difficult to detect currently. This would require // creating labels at the compiled method level, rather than on the // invocation stack at the compiled statement level; as well as other code // changes. However, it might also improve inlining (such as in // try-finally, or in private methods); and might allow us to finally // implement the -O option as more than a no-op. // int index = lab.uses.Length() - 1; if (last_op_goto && index >= 0 && ! (control.option.g & JikesOption::VARS)) { unsigned int luse = lab.uses[index].use_offset; int start = luse - lab.uses[index].op_offset; if (start == last_op_pc && code_attribute -> CodeLength() != last_label_pc) { #ifdef JIKES_DEBUG if (control.option.debug_trace_stack_change) Coutput << "removing dead jump: pc " << start << endl; #endif code_attribute -> DeleteCode(lab.uses[index].op_offset + lab.uses[index].use_length); lab.uses.Reset(index); line_number_table_attribute -> SetMax(start); last_op_goto = false; } } lab.defined = true; lab.definition = code_attribute -> CodeLength(); if (lab.uses.Length()) last_label_pc = lab.definition; } // // patch all uses to have proper value. This requires that // all labels be freed at some time. // void ByteCode::CompleteLabel(Label& lab) { if (lab.uses.Length()) { assert(lab.defined && "label used but with no definition"); // // Sanity check - when completing method, make sure nothing jumps out // of the method. This also collapses two labels that begin on // the same location, before one is optimized away, as in // "if (b) else {}". // if (lab.definition > code_attribute -> CodeLength()) lab.definition = code_attribute -> CodeLength(); // // patch byte code reference to label to reflect its definition // as 16-bit signed offset. // for (unsigned i = 0; i < lab.uses.Length(); i++) { unsigned int luse = lab.uses[i].use_offset; int start = luse - lab.uses[i].op_offset, offset = lab.definition - start; if (lab.uses[i].use_length == 2) // here if short offset { assert(offset < 32768 && offset >= -32768 && "needed longer branch offset"); code_attribute -> ResetCode(luse, (offset >> 8) & 0xFF); code_attribute -> ResetCode(luse + 1, offset & 0xFF); } else if (lab.uses[i].use_length == 4) // here if 4 byte use { code_attribute -> ResetCode(luse, (offset >> 24) & 0xFF); code_attribute -> ResetCode(luse + 1, (offset >> 16) & 0xFF); code_attribute -> ResetCode(luse + 2, (offset >> 8) & 0xFF); code_attribute -> ResetCode(luse + 3, offset & 0xFF); } else assert(false && "label use length not 2 or 4"); } } // // reset in case label is used again. // lab.Reset(); } void ByteCode::UseLabel(Label& lab, int _length, int _op_offset) { int lab_index = lab.uses.NextIndex(); lab.uses[lab_index].use_length = _length; lab.uses[lab_index].op_offset = _op_offset; lab.uses[lab_index].use_offset = code_attribute -> CodeLength(); // // fill next length bytes with zero; will be filled in with proper value // when label completed // for (int i = 0; i < lab.uses[lab_index].use_length; i++) code_attribute -> AddCode(0); } void ByteCode::LoadLocal(int varno, const TypeSymbol* type) { if (control.IsSimpleIntegerValueType(type) || type == control.boolean_type) { if (varno <= 3) PutOp((Opcode) (OP_ILOAD_0 + varno)); // Exploit opcode encodings else PutOpWide(OP_ILOAD, varno); } else if (type == control.long_type) { if (varno <= 3) PutOp((Opcode) (OP_LLOAD_0 + varno)); // Exploit opcode encodings else PutOpWide(OP_LLOAD, varno); } else if (type == control.float_type) { if (varno <= 3) PutOp((Opcode) (OP_FLOAD_0 + varno)); // Exploit opcode encodings else PutOpWide(OP_FLOAD, varno); } else if (type == control.double_type) { if (varno <= 3) PutOp((Opcode) (OP_DLOAD_0 + varno)); // Exploit opcode encodings else PutOpWide(OP_DLOAD, varno); } else // assume reference { if (varno <= 3) PutOp((Opcode) (OP_ALOAD_0 + varno)); // Exploit opcode encodings else PutOpWide(OP_ALOAD, varno); } } // // See if we can load without using LDC; otherwise generate constant pool // entry if one has not yet been generated. // void ByteCode::LoadLiteral(LiteralValue* litp, const TypeSymbol* type) { if (control.IsSimpleIntegerValueType(type) || type == control.boolean_type) { // load literal using literal value IntLiteralValue* vp = DYNAMIC_CAST (litp); LoadImmediateInteger(vp -> value); } else if (type == control.String() || type == control.null_type) { // register index as string if this has not yet been done Utf8LiteralValue* vp = DYNAMIC_CAST (litp); LoadConstantAtIndex(RegisterString(vp)); } else if (type == control.long_type) { LongLiteralValue* vp = DYNAMIC_CAST (litp); if (vp -> value == 0) PutOp(OP_LCONST_0); else if (vp -> value == 1) PutOp(OP_LCONST_1); else if (vp -> value >= -1 && vp -> value <= 5) { LoadImmediateInteger(vp -> value.LowWord()); PutOp(OP_I2L); } else { PutOp(OP_LDC2_W); PutU2(RegisterLong(vp)); } } else if (type == control.float_type) { FloatLiteralValue* vp = DYNAMIC_CAST (litp); IEEEfloat val = vp -> value; if (val.IsZero()) { PutOp(OP_FCONST_0); if (val.IsNegative()) PutOp(OP_FNEG); } else if (val == 1.0f) PutOp(OP_FCONST_1); else if (val == 2.0f) PutOp(OP_FCONST_2); else if (val == -1.0f) { PutOp(OP_FCONST_1); PutOp(OP_FNEG); } else if (val == 3.0f || val == 4.0f || val == 5.0f) { LoadImmediateInteger(val.IntValue()); PutOp(OP_I2F); } else LoadConstantAtIndex(RegisterFloat(vp)); } else if (type == control.double_type) { DoubleLiteralValue* vp = DYNAMIC_CAST (litp); IEEEdouble val = vp -> value; if (val.IsZero()) { PutOp(OP_DCONST_0); if (val.IsNegative()) PutOp(OP_DNEG); } else if (val == 1.0) PutOp(OP_DCONST_1); else if (val == -1.0) { PutOp(OP_DCONST_1); PutOp(OP_DNEG); } else if (val == 2.0 || val == 3.0 || val == 4.0 || val == 5.0) { LoadImmediateInteger(val.IntValue()); PutOp(OP_I2D); } else { PutOp(OP_LDC2_W); PutU2(RegisterDouble(vp)); } } else assert(false && "unsupported constant kind"); } void ByteCode::LoadImmediateInteger(i4 val) { if (val >= -1 && val <= 5) PutOp((Opcode) (OP_ICONST_0 + val)); // exploit opcode encoding else if (val >= -128 && val < 128) { PutOp(OP_BIPUSH); PutU1(val); } else if (val >= -32768 && val < 32768) { // // For a short value, look to see if it is already in the constant // pool. In such a case, ldc is two bytes, while sipush is three, so // we emit a smaller classfile with no penalty to a good JIT. Note // that ldc_w does not buy us anything, however. // u2 index = FindInteger(control.int_pool.Find(val)); if (index == 0 || index > 255) { PutOp(OP_SIPUSH); PutU2(val); } else LoadConstantAtIndex(index); } else if (val == 65535) { PutOp(OP_ICONST_M1); PutOp(OP_I2C); } // Outside the range of sipush, we must use the constant pool. else LoadConstantAtIndex(RegisterInteger(control.int_pool.FindOrInsert(val))); } // // Call to an access method for a compound operator such as ++, --, // or "op=". // void ByteCode::ResolveAccess(AstExpression* p) { // // JLS2 added ability for parenthesized variable to remain a variable. // p = StripNops(p); AstFieldAccess* field = p -> FieldAccessCast(); AstExpression* resolve_expression = field ? field -> resolution_opt : p -> NameCast() -> resolution_opt; AstMethodInvocation* read_method = resolve_expression -> MethodInvocationCast(); // a read method has exactly one argument: the object in question. assert(read_method && read_method -> arguments -> NumArguments() == 1); int stack_words = EmitExpression(read_method -> arguments -> Argument(0)); PutOp(OP_DUP); PutOp(OP_INVOKESTATIC); CompleteCall(read_method -> symbol -> MethodCast(), stack_words); } int ByteCode::LoadVariable(VariableCategory kind, AstExpression* expr, bool need_value) { VariableSymbol* sym = (VariableSymbol*) expr -> symbol; TypeSymbol* expression_type = expr -> Type(); AstFieldAccess* field_access = expr -> FieldAccessCast(); AstName* name = expr -> NameCast(); AstExpression* base = name ? name -> base_opt : field_access -> base; assert(field_access || name); switch (kind) { case LOCAL_VAR: assert(name && ! base); if (! need_value) return 0; if (expr -> IsConstant()) LoadLiteral(expr -> value, expression_type); else LoadLocal(sym -> LocalVariableIndex(), expression_type); return GetTypeWords(expression_type); case ACCESSED_VAR: { // // A resolution is related to either this$0.field or // this$0.access$(). If need_value is false, and the access is // static, field access is smart enough to optimize away, but // method access requires some help. // MethodSymbol* method = expr -> symbol -> MethodCast(); if (! need_value && method && method -> AccessesStaticMember()) return base ? EmitExpression(base, false) : 0; return EmitExpression((name ? name -> resolution_opt : field_access -> resolution_opt), need_value); } case FIELD_VAR: assert(sym -> IsInitialized() || ! sym -> ACC_FINAL()); if (shadow_parameter_offset && sym -> owner == unit_type && (sym -> accessed_local || sym -> Identity() == control.this_name_symbol)) { // // In a constructor, use the parameter that was passed to the // constructor rather than the val$ or this$0 field, because the // field is not yet initialized. // if (! sym -> accessed_local) { PutOp(OP_ALOAD_1); return 1; } int offset = shadow_parameter_offset; for (unsigned i = 0; i < unit_type -> NumConstructorParameters(); i++) { VariableSymbol* shadow = unit_type -> ConstructorParameter(i); if (sym == shadow) { LoadLocal(offset, expression_type); return GetTypeWords(expression_type); } offset += GetTypeWords(shadow -> Type()); } assert(false && "local variable shadowing is messed up"); } if (base && base -> Type() -> IsArray()) { assert(sym -> name_symbol == control.length_name_symbol); if (base -> ArrayCreationExpressionCast() && ! need_value) { EmitExpression(base, false); return 0; } EmitExpression(base); PutOp(OP_ARRAYLENGTH); if (need_value) return 1; PutOp(OP_POP); return 0; } if (sym -> initial_value) { // // Inline constants without referring to the field. However, we // must still check for null. // if (base) EmitCheckForNull(base, false); if (need_value) { LoadLiteral(sym -> initial_value, expression_type); return GetTypeWords(expression_type); } return 0; } if (base) EmitExpression(base); else PutOp(OP_ALOAD_0); PutOp(OP_GETFIELD); break; case STATIC_VAR: // // If the access is qualified by an arbitrary base expression, // evaluate it for side effects. Likewise, volatile fields must be // loaded because of the memory barrier side effect. // if (base) EmitExpression(base, false); if (need_value || sym -> ACC_VOLATILE()) { if (sym -> initial_value) { // // Inline any constant. Note that volatile variables can't // be final, so they are not constant. // LoadLiteral(sym -> initial_value, expression_type); return GetTypeWords(expression_type); } PutOp(OP_GETSTATIC); break; } else return 0; default: assert(false && "LoadVariable bad kind"); } if (control.IsDoubleWordType(expression_type)) ChangeStack(1); PutU2(RegisterFieldref(VariableTypeResolution(expr, sym), sym)); if (need_value) { return GetTypeWords(expression_type); } PutOp(control.IsDoubleWordType(expression_type) ? OP_POP2 : OP_POP); return 0; } int ByteCode::LoadArrayElement(const TypeSymbol* type) { PutOp((type == control.byte_type || type == control.boolean_type) ? OP_BALOAD : type == control.short_type ? OP_SALOAD : type == control.int_type ? OP_IALOAD : type == control.long_type ? OP_LALOAD : type == control.char_type ? OP_CALOAD : type == control.float_type ? OP_FALOAD : type == control.double_type ? OP_DALOAD : OP_AALOAD); // assume reference return GetTypeWords(type); } void ByteCode::StoreArrayElement(const TypeSymbol* type) { PutOp((type == control.byte_type || type == control.boolean_type) ? OP_BASTORE : type == control.short_type ? OP_SASTORE : type == control.int_type ? OP_IASTORE : type == control.long_type ? OP_LASTORE : type == control.char_type ? OP_CASTORE : type == control.float_type ? OP_FASTORE : type == control.double_type ? OP_DASTORE : OP_AASTORE); // assume reference } // // Method to generate field reference // void ByteCode::StoreField(AstExpression* expression) { VariableSymbol* sym = (VariableSymbol*) expression -> symbol; TypeSymbol* expression_type = expression -> Type(); if (sym -> ACC_STATIC()) { PutOp(OP_PUTSTATIC); ChangeStack(1 - GetTypeWords(expression_type)); } else { PutOp(OP_PUTFIELD); ChangeStack(1 - GetTypeWords(expression_type)); } PutU2(RegisterFieldref(VariableTypeResolution(expression, sym), sym)); } void ByteCode::StoreLocal(int varno, const TypeSymbol* type) { if (control.IsSimpleIntegerValueType(type) || type == control.boolean_type) { if (varno <= 3) PutOp((Opcode) (OP_ISTORE_0 + varno)); // Exploit opcode encodings else PutOpWide(OP_ISTORE, varno); } else if (type == control.long_type) { if (varno <= 3) PutOp((Opcode) (OP_LSTORE_0 + varno)); // Exploit opcode encodings else PutOpWide(OP_LSTORE, varno); } else if (type == control.float_type) { if (varno <= 3) PutOp((Opcode) (OP_FSTORE_0 + varno)); // Exploit opcode encodings else PutOpWide(OP_FSTORE, varno); } else if (type == control.double_type) { if (varno <= 3) PutOp((Opcode) (OP_DSTORE_0 + varno)); // Exploit opcode encodings else PutOpWide(OP_DSTORE, varno); } else // assume reference { if (varno <= 3) PutOp((Opcode) (OP_ASTORE_0 + varno)); // Exploit opcode encodings else PutOpWide(OP_ASTORE, varno); } } void ByteCode::StoreVariable(VariableCategory kind, AstExpression* expr) { VariableSymbol* sym = (VariableSymbol*) expr -> symbol; switch (kind) { case LOCAL_VAR: StoreLocal(sym -> LocalVariableIndex(), sym -> Type()); break; case FIELD_VAR: case STATIC_VAR: { if (sym -> ACC_STATIC()) { PutOp(OP_PUTSTATIC); ChangeStack(1 - GetTypeWords(expr -> Type())); } else { PutOp(OP_ALOAD_0); // get address of "this" PutOp(OP_PUTFIELD); ChangeStack(1 - GetTypeWords(expr -> Type())); } PutU2(RegisterFieldref(VariableTypeResolution(expr, sym), sym)); } break; default: assert(false && "StoreVariable bad kind"); } } // // Finish off code by writing remaining type-level attributes. // void ByteCode::FinishCode() { // // Only output SourceFile attribute if -g:source is enabled. // if (control.option.g & JikesOption::SOURCE) AddAttribute(new SourceFileAttribute (RegisterUtf8(control.SourceFile_literal), RegisterUtf8(unit_type -> file_symbol -> FileNameLiteral()))); if (unit_type -> IsDeprecated()) AddAttribute(CreateDeprecatedAttribute()); if (unit_type -> ACC_SYNTHETIC() && control.option.target < JikesOption::SDK1_5) { AddAttribute(CreateSyntheticAttribute()); } if (unit_type -> owner -> MethodCast()) { MethodSymbol* enclosing = (MethodSymbol*) unit_type -> owner; AddAttribute(CreateEnclosingMethodAttribute(enclosing)); } // // In case they weren't referenced elsewhere, make sure all nested types // of this class are listed in the constant pool. A side effect of // registering the class is updating the InnerClasses attribute. // unsigned i = unit_type -> NumNestedTypes(); while (i--) RegisterClass(unit_type -> NestedType(i)); } void ByteCode::PutOp(Opcode opc) { #ifdef JIKES_DEBUG if (control.option.debug_trap_op && code_attribute -> CodeLength() == (u2) control.option.debug_trap_op) { op_trap(); } if (control.option.debug_trace_stack_change) { const char* opname; OpDesc(opc, &opname, NULL); Coutput << "opcode: " << opname << endl; } #endif // JIKES_DEBUG // save pc at start of operation last_op_pc = code_attribute -> CodeLength(); code_attribute -> AddCode(opc); ChangeStack(stack_effect[opc]); last_op_goto = (opc == OP_GOTO || opc == OP_GOTO_W); } void ByteCode::PutOpWide(Opcode opc, u2 var) { if (var <= 255) // if can use standard form { PutOp(opc); PutU1(var); } else // need wide form { PutOp(OP_WIDE); PutOp(opc); PutU2(var); } } void ByteCode::PutOpIINC(u2 var, int val) { if (var <= 255 && (val >= -128 && val <= 127)) // if can use standard form { PutOp(OP_IINC); PutU1(var); PutU1(val); } else // else need wide form { PutOp(OP_WIDE); PutOp(OP_IINC); PutU2(var); PutU2(val); } } void ByteCode::ChangeStack(int i) { stack_depth += i; assert(stack_depth >= 0); if (i > 0 && stack_depth > max_stack) max_stack = stack_depth; #ifdef JIKES_DEBUG if (control.option.debug_trace_stack_change) Coutput << "stack change: pc " << last_op_pc << " change " << i << " stack_depth " << stack_depth << " max_stack: " << max_stack << endl; #endif // JIKES_DEBUG } #ifdef HAVE_JIKES_NAMESPACE } // Close namespace Jikes block #endif