# HG changeset patch # User goetz # Date 1384485899 28800 # Node ID 044b28168e20dc5a9239bf5f6e00e1eff98b6a1b # Parent eb178e97560c4efc844dfe695e18cc8777b7c433 8003854: PPC64 (part 115): Introduce PostallocExpand that expands nodes after register allocation Summary: added ability in C2 to expand mach nodes to several mach nodes after register allocation Reviewed-by: kvn diff -r eb178e97560c -r 044b28168e20 src/cpu/sparc/vm/sparc.ad --- a/src/cpu/sparc/vm/sparc.ad Thu Nov 07 11:47:11 2013 +0100 +++ b/src/cpu/sparc/vm/sparc.ad Thu Nov 14 19:24:59 2013 -0800 @@ -1034,6 +1034,11 @@ } } +bool MachConstantBaseNode::requires_postalloc_expand() const { return false; } +void MachConstantBaseNode::postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_) { + ShouldNotReachHere(); +} + void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { Compile* C = ra_->C; Compile::ConstantTable& constant_table = C->constant_table(); @@ -1884,6 +1889,9 @@ return (VM_Version::is_T4() || VM_Version::is_sparc64()) ? ConditionalMoveLimit : 0; } +// Does the CPU require late expand (see block.cpp for description of late expand)? +const bool Matcher::require_postalloc_expand = false; + // Should the Matcher clone shifts on addressing modes, expecting them to // be subsumed into complex addressing expressions or compute them into // registers? True for Intel but false for most RISCs diff -r eb178e97560c -r 044b28168e20 src/cpu/x86/vm/x86_32.ad --- a/src/cpu/x86/vm/x86_32.ad Thu Nov 07 11:47:11 2013 +0100 +++ b/src/cpu/x86/vm/x86_32.ad Thu Nov 14 19:24:59 2013 -0800 @@ -487,6 +487,11 @@ return 0; // absolute addressing, no offset } +bool MachConstantBaseNode::requires_postalloc_expand() const { return false; } +void MachConstantBaseNode::postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_) { + ShouldNotReachHere(); +} + void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { // Empty encoding } @@ -1389,6 +1394,9 @@ // No CMOVF/CMOVD with SSE/SSE2 const int Matcher::float_cmove_cost() { return (UseSSE>=1) ? ConditionalMoveLimit : 0; } +// Does the CPU require late expand (see block.cpp for description of late expand)? +const bool Matcher::require_postalloc_expand = false; + // Should the Matcher clone shifts on addressing modes, expecting them to // be subsumed into complex addressing expressions or compute them into // registers? True for Intel but false for most RISCs diff -r eb178e97560c -r 044b28168e20 src/cpu/x86/vm/x86_64.ad --- a/src/cpu/x86/vm/x86_64.ad Thu Nov 07 11:47:11 2013 +0100 +++ b/src/cpu/x86/vm/x86_64.ad Thu Nov 14 19:24:59 2013 -0800 @@ -688,6 +688,11 @@ return 0; // absolute addressing, no offset } +bool MachConstantBaseNode::requires_postalloc_expand() const { return false; } +void MachConstantBaseNode::postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_) { + ShouldNotReachHere(); +} + void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const { // Empty encoding } @@ -1542,6 +1547,9 @@ // No CMOVF/CMOVD with SSE2 const int Matcher::float_cmove_cost() { return ConditionalMoveLimit; } +// Does the CPU require late expand (see block.cpp for description of late expand)? +const bool Matcher::require_postalloc_expand = false; + // Should the Matcher clone shifts on addressing modes, expecting them // to be subsumed into complex addressing expressions or compute them // into registers? True for Intel but false for most RISCs diff -r eb178e97560c -r 044b28168e20 src/share/vm/adlc/adlparse.cpp --- a/src/share/vm/adlc/adlparse.cpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/adlc/adlparse.cpp Thu Nov 14 19:24:59 2013 -0800 @@ -219,19 +219,21 @@ else if (!strcmp(ident, "encode")) { parse_err(SYNERR, "Instructions specify ins_encode, not encode\n"); } - else if (!strcmp(ident, "ins_encode")) ins_encode_parse(*instr); - else if (!strcmp(ident, "opcode")) instr->_opcode = opcode_parse(instr); - else if (!strcmp(ident, "size")) instr->_size = size_parse(instr); - else if (!strcmp(ident, "effect")) effect_parse(instr); - else if (!strcmp(ident, "expand")) instr->_exprule = expand_parse(instr); - else if (!strcmp(ident, "rewrite")) instr->_rewrule = rewrite_parse(); + else if (!strcmp(ident, "ins_encode")) ins_encode_parse(*instr); + // Parse late expand keyword. + else if (!strcmp(ident, "postalloc_expand")) postalloc_expand_parse(*instr); + else if (!strcmp(ident, "opcode")) instr->_opcode = opcode_parse(instr); + else if (!strcmp(ident, "size")) instr->_size = size_parse(instr); + else if (!strcmp(ident, "effect")) effect_parse(instr); + else if (!strcmp(ident, "expand")) instr->_exprule = expand_parse(instr); + else if (!strcmp(ident, "rewrite")) instr->_rewrule = rewrite_parse(); else if (!strcmp(ident, "constraint")) { parse_err(SYNERR, "Instructions do not specify a constraint\n"); } else if (!strcmp(ident, "construct")) { parse_err(SYNERR, "Instructions do not specify a construct\n"); } - else if (!strcmp(ident, "format")) instr->_format = format_parse(); + else if (!strcmp(ident, "format")) instr->_format = format_parse(); else if (!strcmp(ident, "interface")) { parse_err(SYNERR, "Instructions do not specify an interface\n"); } @@ -240,13 +242,14 @@ // Check identifier to see if it is the name of an attribute const Form *form = _globalNames[ident]; AttributeForm *attr = form ? form->is_attribute() : NULL; - if( attr && (attr->_atype == INS_ATTR) ) { + if (attr && (attr->_atype == INS_ATTR)) { // Insert the new attribute into the linked list. Attribute *temp = attr_parse(ident); temp->_next = instr->_attribs; instr->_attribs = temp; } else { - parse_err(SYNERR, "expected one of:\n predicate, match, encode, or the name of an instruction attribute at %s\n", ident); + parse_err(SYNERR, "expected one of:\n predicate, match, encode, or the name of" + " an instruction attribute at %s\n", ident); } } skipws(); @@ -258,13 +261,17 @@ } // Check for "Set" form of chain rule adjust_set_rule(instr); - if (_AD._pipeline ) { - if( instr->expands() ) { - if( instr->_ins_pipe ) - parse_err(WARN, "ins_pipe and expand rule both specified for instruction \"%s\"; ins_pipe will be unused\n", instr->_ident); + if (_AD._pipeline) { + // No pipe required for late expand. + if (instr->expands() || instr->postalloc_expands()) { + if (instr->_ins_pipe) { + parse_err(WARN, "ins_pipe and expand rule both specified for instruction \"%s\";" + " ins_pipe will be unused\n", instr->_ident); + } } else { - if( !instr->_ins_pipe ) + if (!instr->_ins_pipe) { parse_err(WARN, "No ins_pipe specified for instruction \"%s\"\n", instr->_ident); + } } } // Add instruction to tail of instruction list @@ -2779,11 +2786,13 @@ encoding->add_parameter(opForm->_ident, param); } - // Define a MacroAssembler instance for use by the encoding. The - // name is chosen to match the __ idiom used for assembly in other - // parts of hotspot and assumes the existence of the standard - // #define __ _masm. - encoding->add_code(" MacroAssembler _masm(&cbuf);\n"); + if (!inst._is_postalloc_expand) { + // Define a MacroAssembler instance for use by the encoding. The + // name is chosen to match the __ idiom used for assembly in other + // parts of hotspot and assumes the existence of the standard + // #define __ _masm. + encoding->add_code(" MacroAssembler _masm(&cbuf);\n"); + } // Parse the following %{ }% block ins_encode_parse_block_impl(inst, encoding, ec_name); @@ -2857,7 +2866,8 @@ inst.set_is_mach_constant(true); if (_curchar == '(') { - parse_err(SYNERR, "constanttablebase in instruct %s cannot have an argument (only constantaddress and constantoffset)", ec_name); + parse_err(SYNERR, "constanttablebase in instruct %s cannot have an argument " + "(only constantaddress and constantoffset)", ec_name); return; } } @@ -3050,6 +3060,157 @@ inst._insencode = encrule; } +//------------------------------postalloc_expand_parse--------------------------- +// Encode rules have the form +// postalloc_expand( encode_class_name(parameter_list) ); +// +// The "encode_class_name" must be defined in the encode section. +// The parameter list contains $names that are locals. +// +// This is just a copy of ins_encode_parse without the loop. +void ADLParser::postalloc_expand_parse(InstructForm& inst) { + inst._is_postalloc_expand = true; + + // Parse encode class name. + skipws(); // Skip whitespace. + if (_curchar != '(') { + // Check for postalloc_expand %{ form + if ((_curchar == '%') && (*(_ptr+1) == '{')) { + next_char(); // Skip '%' + next_char(); // Skip '{' + + // Parse the block form of postalloc_expand + ins_encode_parse_block(inst); + return; + } + + parse_err(SYNERR, "missing '(' in postalloc_expand definition\n"); + return; + } + next_char(); // Move past '('. + skipws(); + + InsEncode *encrule = new InsEncode(); // Encode class for instruction. + encrule->_linenum = linenum(); + char *ec_name = NULL; // String representation of encode rule. + // identifier is optional. + if (_curchar != ')') { + ec_name = get_ident(); + if (ec_name == NULL) { + parse_err(SYNERR, "Invalid postalloc_expand class name after 'postalloc_expand('.\n"); + return; + } + // Check that encoding is defined in the encode section. + EncClass *encode_class = _AD._encode->encClass(ec_name); + + // Get list for encode method's parameters + NameAndList *params = encrule->add_encode(ec_name); + + // Parse the parameters to this encode method. + skipws(); + if (_curchar == '(') { + next_char(); // Move past '(' for parameters. + + // Parse the encode method's parameters. + while (_curchar != ')') { + char *param = get_ident_or_literal_constant("encoding operand"); + if (param != NULL) { + // Found a parameter: + + // First check for constant table support. + + // Check if this instruct is a MachConstantNode. + if (strcmp(param, "constanttablebase") == 0) { + // This instruct is a MachConstantNode. + inst.set_is_mach_constant(true); + + if (_curchar == '(') { + parse_err(SYNERR, "constanttablebase in instruct %s cannot have an argument " + "(only constantaddress and constantoffset)", ec_name); + return; + } + } + else if ((strcmp(param, "constantaddress") == 0) || + (strcmp(param, "constantoffset") == 0)) { + // This instruct is a MachConstantNode. + inst.set_is_mach_constant(true); + + // If the constant keyword has an argument, parse it. + if (_curchar == '(') constant_parse(inst); + } + + // Else check it is a local name, add it to the list, then check for more. + // New: allow hex constants as parameters to an encode method. + // New: allow parenthesized expressions as parameters. + // New: allow "primary", "secondary", "tertiary" as parameters. + // New: allow user-defined register name as parameter. + else if ((inst._localNames[param] == NULL) && + !ADLParser::is_literal_constant(param) && + (Opcode::as_opcode_type(param) == Opcode::NOT_AN_OPCODE) && + ((_AD._register == NULL) || (_AD._register->getRegDef(param) == NULL))) { + parse_err(SYNERR, "Using non-locally defined parameter %s for encoding %s.\n", param, ec_name); + return; + } + params->add_entry(param); + + skipws(); + if (_curchar == ',') { + // More parameters to come. + next_char(); // Move past ',' between parameters. + skipws(); // Skip to next parameter. + } else if (_curchar == ')') { + // Done with parameter list + } else { + // Only ',' or ')' are valid after a parameter name. + parse_err(SYNERR, "expected ',' or ')' after parameter %s.\n", ec_name); + return; + } + + } else { + skipws(); + // Did not find a parameter. + if (_curchar == ',') { + parse_err(SYNERR, "Expected encode parameter before ',' in postalloc_expand %s.\n", ec_name); + return; + } + if (_curchar != ')') { + parse_err(SYNERR, "Expected ')' after postalloc_expand parameters.\n"); + return; + } + } + } // WHILE loop collecting parameters. + next_char(); // Move past ')' at end of parameters. + } // Done with parameter list for encoding. + + // Check for ',' or ')' after encoding. + skipws(); // Move to character after parameters. + if (_curchar != ')') { + // Only a ')' is allowed. + parse_err(SYNERR, "Expected ')' after postalloc_expand %s.\n", ec_name); + return; + } + } // Done parsing postalloc_expand method and their parameters. + if (_curchar != ')') { + parse_err(SYNERR, "Missing ')' at end of postalloc_expand description.\n"); + return; + } + next_char(); // Move past ')'. + skipws(); // Skip leading whitespace. + + if (_curchar != ';') { + parse_err(SYNERR, "Missing ';' at end of postalloc_expand.\n"); + return; + } + next_char(); // Move past ';'. + skipws(); // Be friendly to oper_parse(). + + // Debug Stuff. + if (_AD._adl_debug > 1) fprintf(stderr, "Instruction postalloc_expand: %s\n", ec_name); + + // Set encode class of this instruction. + inst._insencode = encrule; +} + //------------------------------constant_parse--------------------------------- // Parse a constant expression. diff -r eb178e97560c -r 044b28168e20 src/share/vm/adlc/adlparse.hpp --- a/src/share/vm/adlc/adlparse.hpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/adlc/adlparse.hpp Thu Nov 14 19:24:59 2013 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -159,6 +159,8 @@ void ins_encode_parse(InstructForm &inst); void ins_encode_parse_block(InstructForm &inst); void ins_encode_parse_block_impl(InstructForm& inst, EncClass* encoding, char* ec_name); + // Parse instruction postalloc expand rule. + void postalloc_expand_parse(InstructForm &inst); void constant_parse(InstructForm& inst); void constant_parse_expression(EncClass* encoding, char* ec_name); diff -r eb178e97560c -r 044b28168e20 src/share/vm/adlc/archDesc.hpp --- a/src/share/vm/adlc/archDesc.hpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/adlc/archDesc.hpp Thu Nov 14 19:24:59 2013 -0800 @@ -311,6 +311,8 @@ void defineEvalConstant(FILE *fp, InstructForm &node); // Generator for Emit methods for instructions void defineEmit (FILE *fp, InstructForm &node); + // Generator for postalloc_expand methods for instructions. + void define_postalloc_expand(FILE *fp, InstructForm &node); // Define a MachOper encode method void define_oper_interface(FILE *fp, OperandForm &oper, FormDict &globals, diff -r eb178e97560c -r 044b28168e20 src/share/vm/adlc/formssel.cpp --- a/src/share/vm/adlc/formssel.cpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/adlc/formssel.cpp Thu Nov 14 19:24:59 2013 -0800 @@ -36,27 +36,28 @@ { _ftype = Form::INS; - _matrule = NULL; - _insencode = NULL; - _constant = NULL; - _opcode = NULL; - _size = NULL; - _attribs = NULL; - _predicate = NULL; - _exprule = NULL; - _rewrule = NULL; - _format = NULL; - _peephole = NULL; - _ins_pipe = NULL; - _uniq_idx = NULL; - _num_uniq = 0; - _cisc_spill_operand = Not_cisc_spillable;// Which operand may cisc-spill + _matrule = NULL; + _insencode = NULL; + _constant = NULL; + _is_postalloc_expand = false; + _opcode = NULL; + _size = NULL; + _attribs = NULL; + _predicate = NULL; + _exprule = NULL; + _rewrule = NULL; + _format = NULL; + _peephole = NULL; + _ins_pipe = NULL; + _uniq_idx = NULL; + _num_uniq = 0; + _cisc_spill_operand = Not_cisc_spillable;// Which operand may cisc-spill _cisc_spill_alternate = NULL; // possible cisc replacement - _cisc_reg_mask_name = NULL; - _is_cisc_alternate = false; - _is_short_branch = false; - _short_branch_form = NULL; - _alignment = 1; + _cisc_reg_mask_name = NULL; + _is_cisc_alternate = false; + _is_short_branch = false; + _short_branch_form = NULL; + _alignment = 1; } InstructForm::InstructForm(const char *id, InstructForm *instr, MatchRule *rule) @@ -68,27 +69,28 @@ { _ftype = Form::INS; - _matrule = rule; - _insencode = instr->_insencode; - _constant = instr->_constant; - _opcode = instr->_opcode; - _size = instr->_size; - _attribs = instr->_attribs; - _predicate = instr->_predicate; - _exprule = instr->_exprule; - _rewrule = instr->_rewrule; - _format = instr->_format; - _peephole = instr->_peephole; - _ins_pipe = instr->_ins_pipe; - _uniq_idx = instr->_uniq_idx; - _num_uniq = instr->_num_uniq; - _cisc_spill_operand = Not_cisc_spillable;// Which operand may cisc-spill - _cisc_spill_alternate = NULL; // possible cisc replacement - _cisc_reg_mask_name = NULL; - _is_cisc_alternate = false; - _is_short_branch = false; - _short_branch_form = NULL; - _alignment = 1; + _matrule = rule; + _insencode = instr->_insencode; + _constant = instr->_constant; + _is_postalloc_expand = instr->_is_postalloc_expand; + _opcode = instr->_opcode; + _size = instr->_size; + _attribs = instr->_attribs; + _predicate = instr->_predicate; + _exprule = instr->_exprule; + _rewrule = instr->_rewrule; + _format = instr->_format; + _peephole = instr->_peephole; + _ins_pipe = instr->_ins_pipe; + _uniq_idx = instr->_uniq_idx; + _num_uniq = instr->_num_uniq; + _cisc_spill_operand = Not_cisc_spillable; // Which operand may cisc-spill + _cisc_spill_alternate = NULL; // possible cisc replacement + _cisc_reg_mask_name = NULL; + _is_cisc_alternate = false; + _is_short_branch = false; + _short_branch_form = NULL; + _alignment = 1; // Copy parameters const char *name; instr->_parameters.reset(); @@ -157,6 +159,11 @@ return ( _exprule != NULL ); } +// This instruction has a late expand rule? +bool InstructForm::postalloc_expands() const { + return _is_postalloc_expand; +} + // This instruction has a peephole rule? Peephole *InstructForm::peepholes() const { return _peephole; diff -r eb178e97560c -r 044b28168e20 src/share/vm/adlc/formssel.hpp --- a/src/share/vm/adlc/formssel.hpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/adlc/formssel.hpp Thu Nov 14 19:24:59 2013 -0800 @@ -88,30 +88,31 @@ public: // Public Data - const char *_ident; // Name of this instruction - NameList _parameters; // Locally defined names - FormDict _localNames; // Table of operands & their types - MatchRule *_matrule; // Matching rule for this instruction - Opcode *_opcode; // Encoding of the opcode for instruction - char *_size; // Size of instruction - InsEncode *_insencode; // Encoding class instruction belongs to - InsEncode *_constant; // Encoding class constant value belongs to - Attribute *_attribs; // List of Attribute rules - Predicate *_predicate; // Predicate test for this instruction - FormDict _effects; // Dictionary of effect rules - ExpandRule *_exprule; // Expand rule for this instruction - RewriteRule *_rewrule; // Rewrite rule for this instruction - FormatRule *_format; // Format for assembly generation - Peephole *_peephole; // List of peephole rules for instruction - const char *_ins_pipe; // Instruction Scheduling description class + const char *_ident; // Name of this instruction + NameList _parameters; // Locally defined names + FormDict _localNames; // Table of operands & their types + MatchRule *_matrule; // Matching rule for this instruction + Opcode *_opcode; // Encoding of the opcode for instruction + char *_size; // Size of instruction + InsEncode *_insencode; // Encoding class instruction belongs to + InsEncode *_constant; // Encoding class constant value belongs to + bool _is_postalloc_expand; // Indicates that encoding just does a lateExpand. + Attribute *_attribs; // List of Attribute rules + Predicate *_predicate; // Predicate test for this instruction + FormDict _effects; // Dictionary of effect rules + ExpandRule *_exprule; // Expand rule for this instruction + RewriteRule *_rewrule; // Rewrite rule for this instruction + FormatRule *_format; // Format for assembly generation + Peephole *_peephole; // List of peephole rules for instruction + const char *_ins_pipe; // Instruction Scheduling description class - uint *_uniq_idx; // Indexes of unique operands - uint _uniq_idx_length; // Length of _uniq_idx array - uint _num_uniq; // Number of unique operands - ComponentList _components; // List of Components matches MachNode's - // operand structure + uint *_uniq_idx; // Indexes of unique operands + uint _uniq_idx_length; // Length of _uniq_idx array + uint _num_uniq; // Number of unique operands + ComponentList _components; // List of Components matches MachNode's + // operand structure - bool _has_call; // contain a call and caller save registers should be saved? + bool _has_call; // contain a call and caller save registers should be saved? // Public Methods InstructForm(const char *id, bool ideal_only = false); @@ -133,6 +134,8 @@ virtual uint num_defs_or_kills(); // This instruction has an expand rule? virtual bool expands() const ; + // This instruction has a late expand rule? + virtual bool postalloc_expands() const; // Return this instruction's first peephole rule, or NULL virtual Peephole *peepholes() const; // Add a peephole rule to this instruction diff -r eb178e97560c -r 044b28168e20 src/share/vm/adlc/output_c.cpp --- a/src/share/vm/adlc/output_c.cpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/adlc/output_c.cpp Thu Nov 14 19:24:59 2013 -0800 @@ -2488,7 +2488,113 @@ fprintf(fp, " return (VerifyOops ? MachNode::size(ra_) : %s);\n", inst._size); // (3) and (4) - fprintf(fp,"}\n"); + fprintf(fp,"}\n\n"); +} + +// Emit late expand function. +void ArchDesc::define_postalloc_expand(FILE *fp, InstructForm &inst) { + InsEncode *ins_encode = inst._insencode; + + // Output instruction's postalloc_expand prototype. + fprintf(fp, "void %sNode::postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_) {\n", + inst._ident); + + assert((_encode != NULL) && (ins_encode != NULL), "You must define an encode section."); + + // Output each operand's offset into the array of registers. + inst.index_temps(fp, _globalNames); + + // Output variables "unsigned idx_", Node *n_ and "MachOpnd *op_" + // for each parameter specified in the encoding. + ins_encode->reset(); + const char *ec_name = ins_encode->encode_class_iter(); + assert(ec_name != NULL, "late expand must specify an encoding"); + + EncClass *encoding = _encode->encClass(ec_name); + if (encoding == NULL) { + fprintf(stderr, "User did not define contents of this encode_class: %s\n", ec_name); + abort(); + } + if (ins_encode->current_encoding_num_args() != encoding->num_args()) { + globalAD->syntax_err(ins_encode->_linenum, "In %s: passing %d arguments to %s but expecting %d", + inst._ident, ins_encode->current_encoding_num_args(), + ec_name, encoding->num_args()); + } + + fprintf(fp, " // Access to ins and operands for late expand.\n"); + const int buflen = 2000; + char idxbuf[buflen]; char *ib = idxbuf; sprintf(ib, ""); + char nbuf [buflen]; char *nb = nbuf; sprintf(nb, ""); + char opbuf [buflen]; char *ob = opbuf; sprintf(ob, ""); + + encoding->_parameter_type.reset(); + encoding->_parameter_name.reset(); + const char *type = encoding->_parameter_type.iter(); + const char *name = encoding->_parameter_name.iter(); + int param_no = 0; + for (; (type != NULL) && (name != NULL); + (type = encoding->_parameter_type.iter()), (name = encoding->_parameter_name.iter())) { + const char* arg_name = ins_encode->rep_var_name(inst, param_no); + int idx = inst.operand_position_format(arg_name); + if (strcmp(arg_name, "constanttablebase") == 0) { + ib += sprintf(ib, " unsigned idx_%-5s = mach_constant_base_node_input(); \t// %s, \t%s\n", + name, type, arg_name); + nb += sprintf(nb, " Node *n_%-7s = lookup(idx_%s);\n", name, name); + // There is no operand for the constanttablebase. + } else if (inst.is_noninput_operand(idx)) { + globalAD->syntax_err(inst._linenum, + "In %s: you can not pass the non-input %s to a late expand encoding.\n", + inst._ident, arg_name); + } else { + ib += sprintf(ib, " unsigned idx_%-5s = idx%d; \t// %s, \t%s\n", + name, idx, type, arg_name); + nb += sprintf(nb, " Node *n_%-7s = lookup(idx_%s);\n", name, name); + ob += sprintf(ob, " %sOper *op_%s = (%sOper *)opnd_array(%d);\n", type, name, type, idx); + } + param_no++; + } + assert(ib < &idxbuf[buflen-1] && nb < &nbuf[buflen-1] && ob < &opbuf[buflen-1], "buffer overflow"); + + fprintf(fp, "%s", idxbuf); + fprintf(fp, " Node *n_region = lookup(0);\n"); + fprintf(fp, "%s%s", nbuf, opbuf); + fprintf(fp, " Compile *C = ra_->C;\n"); + + // Output this instruction's encodings. + fprintf(fp, " {"); + const char *ec_code = NULL; + const char *ec_rep_var = NULL; + assert(encoding == _encode->encClass(ec_name), ""); + + DefineEmitState pending(fp, *this, *encoding, *ins_encode, inst); + encoding->_code.reset(); + encoding->_rep_vars.reset(); + // Process list of user-defined strings, + // and occurrences of replacement variables. + // Replacement Vars are pushed into a list and then output. + while ((ec_code = encoding->_code.iter()) != NULL) { + if (! encoding->_code.is_signal(ec_code)) { + // Emit pending code. + pending.emit(); + pending.clear(); + // Emit this code section. + fprintf(fp, "%s", ec_code); + } else { + // A replacement variable or one of its subfields. + // Obtain replacement variable from list. + ec_rep_var = encoding->_rep_vars.iter(); + pending.add_rep_var(ec_rep_var); + } + } + // Emit pending code. + pending.emit(); + pending.clear(); + fprintf(fp, " }\n"); + + fprintf(fp, "}\n\n"); + + ec_name = ins_encode->encode_class_iter(); + assert(ec_name == NULL, "Late expand may only have one encoding."); } // defineEmit ----------------------------------------------------------------- @@ -2841,7 +2947,7 @@ } else if ( (strcmp(name,"disp") == 0) ) { fprintf(fp,"(PhaseRegAlloc *ra_, const Node *node, int idx) const { \n"); } else { - fprintf(fp,"() const { \n"); + fprintf(fp, "() const {\n"); } // Check for hexadecimal value OR replacement variable @@ -2891,6 +2997,8 @@ // Hex value fprintf(fp," return %s;\n", encoding); } else { + globalAD->syntax_err(oper._linenum, "In operand %s: Do not support this encode constant: '%s' for %s.", + oper._ident, encoding, name); assert( false, "Do not support octal or decimal encode constants"); } fprintf(fp," }\n"); @@ -3142,7 +3250,15 @@ // Ensure this is a machine-world instruction if ( instr->ideal_only() ) continue; - if (instr->_insencode) defineEmit (fp, *instr); + if (instr->_insencode) { + if (instr->postalloc_expands()) { + // Don't write this to _CPP_EXPAND_file, as the code generated calls C-code + // from code sections in ad file that is dumped to fp. + define_postalloc_expand(fp, *instr); + } else { + defineEmit(fp, *instr); + } + } if (instr->is_mach_constant()) defineEvalConstant(fp, *instr); if (instr->_size) defineSize (fp, *instr); diff -r eb178e97560c -r 044b28168e20 src/share/vm/adlc/output_h.cpp --- a/src/share/vm/adlc/output_h.cpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/adlc/output_h.cpp Thu Nov 14 19:24:59 2013 -0800 @@ -1633,7 +1633,12 @@ // Output the opcode function and the encode function here using the // encoding class information in the _insencode slot. if ( instr->_insencode ) { - fprintf(fp," virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const;\n"); + if (instr->postalloc_expands()) { + fprintf(fp," virtual bool requires_postalloc_expand() const { return true; }\n"); + fprintf(fp," virtual void postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_);\n"); + } else { + fprintf(fp," virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const;\n"); + } } // virtual function for getting the size of an instruction diff -r eb178e97560c -r 044b28168e20 src/share/vm/opto/block.cpp --- a/src/share/vm/opto/block.cpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/opto/block.cpp Thu Nov 14 19:24:59 2013 -0800 @@ -144,6 +144,10 @@ remove_node(find_node(n)); } +bool Block::contains(const Node *n) const { + return _nodes.contains(n); +} + // Return empty status of a block. Empty blocks contain only the head, other // ideal nodes, and an optional trailing goto. int Block::is_Empty() const { @@ -699,7 +703,7 @@ // Fix up the final control flow for basic blocks. void PhaseCFG::fixup_flow() { // Fixup final control flow for the blocks. Remove jump-to-next - // block. If neither arm of a IF follows the conditional branch, we + // block. If neither arm of an IF follows the conditional branch, we // have to add a second jump after the conditional. We place the // TRUE branch target in succs[0] for both GOTOs and IFs. for (uint i = 0; i < number_of_blocks(); i++) { @@ -844,6 +848,228 @@ } +// postalloc_expand: Expand nodes after register allocation. +// +// postalloc_expand has to be called after register allocation, just +// before output (i.e. scheduling). It only gets called if +// Matcher::require_postalloc_expand is true. +// +// Background: +// +// Nodes that are expandend (one compound node requiring several +// assembler instructions to be implemented split into two or more +// non-compound nodes) after register allocation are not as nice as +// the ones expanded before register allocation - they don't +// participate in optimizations as global code motion. But after +// register allocation we can expand nodes that use registers which +// are not spillable or registers that are not allocated, because the +// old compound node is simply replaced (in its location in the basic +// block) by a new subgraph which does not contain compound nodes any +// more. The scheduler called during output can later on process these +// non-compound nodes. +// +// Implementation: +// +// Nodes requiring postalloc expand are specified in the ad file by using +// a postalloc_expand statement instead of ins_encode. A postalloc_expand +// contains a single call to an encoding, as does an ins_encode +// statement. Instead of an emit() function a postalloc_expand() function +// is generated that doesn't emit assembler but creates a new +// subgraph. The code below calls this postalloc_expand function for each +// node with the appropriate attribute. This function returns the new +// nodes generated in an array passed in the call. The old node, +// potential MachTemps before and potential Projs after it then get +// disconnected and replaced by the new nodes. The instruction +// generating the result has to be the last one in the array. In +// general it is assumed that Projs after the node expanded are +// kills. These kills are not required any more after expanding as +// there are now explicitly visible def-use chains and the Projs are +// removed. This does not hold for calls: They do not only have +// kill-Projs but also Projs defining values. Therefore Projs after +// the node expanded are removed for all but for calls. If a node is +// to be reused, it must be added to the nodes list returned, and it +// will be added again. +// +// Implementing the postalloc_expand function for a node in an enc_class +// is rather tedious. It requires knowledge about many node details, as +// the nodes and the subgraph must be hand crafted. To simplify this, +// adlc generates some utility variables into the postalloc_expand function, +// e.g., holding the operands as specified by the postalloc_expand encoding +// specification, e.g.: +// * unsigned idx_ holding the index of the node in the ins +// * Node *n_ holding the node loaded from the ins +// * MachOpnd *op_ holding the corresponding operand +// +// The ordering of operands can not be determined by looking at a +// rule. Especially if a match rule matches several different trees, +// several nodes are generated from one instruct specification with +// different operand orderings. In this case the adlc generated +// variables are the only way to access the ins and operands +// deterministically. +// +// If assigning a register to a node that contains an oop, don't +// forget to call ra_->set_oop() for the node. +void PhaseCFG::postalloc_expand(PhaseRegAlloc* _ra) { + GrowableArray new_nodes(32); // Array with new nodes filled by postalloc_expand function of node. + GrowableArray remove(32); + GrowableArray succs(32); + unsigned int max_idx = C->unique(); // Remember to distinguish new from old nodes. + DEBUG_ONLY(bool foundNode = false); + + // for all blocks + for (uint i = 0; i < number_of_blocks(); i++) { + Block *b = _blocks[i]; + // For all instructions in the current block. + for (uint j = 0; j < b->number_of_nodes(); j++) { + Node *n = b->get_node(j); + if (n->is_Mach() && n->as_Mach()->requires_postalloc_expand()) { +#ifdef ASSERT + if (TracePostallocExpand) { + if (!foundNode) { + foundNode = true; + tty->print("POSTALLOC EXPANDING %d %s\n", C->compile_id(), + C->method() ? C->method()->name()->as_utf8() : C->stub_name()); + } + tty->print(" postalloc expanding "); n->dump(); + if (Verbose) { + tty->print(" with ins:\n"); + for (uint k = 0; k < n->len(); ++k) { + if (n->in(k)) { tty->print(" "); n->in(k)->dump(); } + } + } + } +#endif + new_nodes.clear(); + // Collect nodes that have to be removed from the block later on. + uint req = n->req(); + remove.clear(); + for (uint k = 0; k < req; ++k) { + if (n->in(k) && n->in(k)->is_MachTemp()) { + remove.push(n->in(k)); // MachTemps which are inputs to the old node have to be removed. + n->in(k)->del_req(0); + j--; + } + } + + // Check whether we can allocate enough nodes. We set a fix limit for + // the size of postalloc expands with this. + uint unique_limit = C->unique() + 40; + if (unique_limit >= _ra->node_regs_max_index()) { + Compile::current()->record_failure("out of nodes in postalloc expand"); + return; + } + + // Emit (i.e. generate new nodes). + n->as_Mach()->postalloc_expand(&new_nodes, _ra); + + assert(C->unique() < unique_limit, "You allocated too many nodes in your postalloc expand."); + + // Disconnect the inputs of the old node. + // + // We reuse MachSpillCopy nodes. If we need to expand them, there + // are many, so reusing pays off. If reused, the node already + // has the new ins. n must be the last node on new_nodes list. + if (!n->is_MachSpillCopy()) { + for (int k = req - 1; k >= 0; --k) { + n->del_req(k); + } + } + +#ifdef ASSERT + // Check that all nodes have proper operands. + for (int k = 0; k < new_nodes.length(); ++k) { + if (new_nodes.at(k)->_idx < max_idx || !new_nodes.at(k)->is_Mach()) continue; // old node, Proj ... + MachNode *m = new_nodes.at(k)->as_Mach(); + for (unsigned int l = 0; l < m->num_opnds(); ++l) { + if (MachOper::notAnOper(m->_opnds[l])) { + outputStream *os = tty; + os->print("Node %s ", m->Name()); + os->print("has invalid opnd %d: %p\n", l, m->_opnds[l]); + assert(0, "Invalid operands, see inline trace in hs_err_pid file."); + } + } + } +#endif + + // Collect succs of old node in remove (for projections) and in succs (for + // all other nodes) do _not_ collect projections in remove (but in succs) + // in case the node is a call. We need the projections for calls as they are + // associated with registes (i.e. they are defs). + succs.clear(); + for (DUIterator k = n->outs(); n->has_out(k); k++) { + if (n->out(k)->is_Proj() && !n->is_MachCall() && !n->is_MachBranch()) { + remove.push(n->out(k)); + } else { + succs.push(n->out(k)); + } + } + // Replace old node n as input of its succs by last of the new nodes. + for (int k = 0; k < succs.length(); ++k) { + Node *succ = succs.at(k); + for (uint l = 0; l < succ->req(); ++l) { + if (succ->in(l) == n) { + succ->set_req(l, new_nodes.at(new_nodes.length() - 1)); + } + } + for (uint l = succ->req(); l < succ->len(); ++l) { + if (succ->in(l) == n) { + succ->set_prec(l, new_nodes.at(new_nodes.length() - 1)); + } + } + } + + // Index of old node in block. + uint index = b->find_node(n); + // Insert new nodes into block and map them in nodes->blocks array + // and remember last node in n2. + Node *n2 = NULL; + for (int k = 0; k < new_nodes.length(); ++k) { + n2 = new_nodes.at(k); + b->insert_node(n2, ++index); + map_node_to_block(n2, b); + } + + // Add old node n to remove and remove them all from block. + remove.push(n); + j--; +#ifdef ASSERT + if (TracePostallocExpand && Verbose) { + tty->print(" removing:\n"); + for (int k = 0; k < remove.length(); ++k) { + tty->print(" "); remove.at(k)->dump(); + } + tty->print(" inserting:\n"); + for (int k = 0; k < new_nodes.length(); ++k) { + tty->print(" "); new_nodes.at(k)->dump(); + } + } +#endif + for (int k = 0; k < remove.length(); ++k) { + if (b->contains(remove.at(k))) { + b->find_remove(remove.at(k)); + } else { + assert(remove.at(k)->is_Proj() && (remove.at(k)->in(0)->is_MachBranch()), ""); + } + } + // If anything has been inserted (n2 != NULL), continue after last node inserted. + // This does not always work. Some postalloc expands don't insert any nodes, if they + // do optimizations (e.g., max(x,x)). In this case we decrement j accordingly. + j = n2 ? b->find_node(n2) : j; + } + } + } + +#ifdef ASSERT + if (foundNode) { + tty->print("FINISHED %d %s\n", C->compile_id(), + C->method() ? C->method()->name()->as_utf8() : C->stub_name()); + tty->flush(); + } +#endif +} + + +//------------------------------dump------------------------------------------- #ifndef PRODUCT void PhaseCFG::_dump_cfg( const Node *end, VectorSet &visited ) const { const Node *x = end->is_block_proj(); diff -r eb178e97560c -r 044b28168e20 src/share/vm/opto/block.hpp --- a/src/share/vm/opto/block.hpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/opto/block.hpp Thu Nov 14 19:24:59 2013 -0800 @@ -313,10 +313,12 @@ // Add an instruction to an existing block. It must go after the head // instruction and before the end instruction. void add_inst( Node *n ) { insert_node(n, end_idx()); } - // Find node in block + // Find node in block. Fails if node not in block. uint find_node( const Node *n ) const; // Find and remove n from block list void find_remove( const Node *n ); + // Check wether the node is in the block. + bool contains (const Node *n) const; // Return the empty status of a block enum { not_empty, empty_with_goto, completely_empty }; @@ -596,6 +598,9 @@ map_node_to_block(n, b); } + // Check all nodes and postalloc_expand them if necessary. + void postalloc_expand(PhaseRegAlloc* _ra); + #ifndef PRODUCT bool trace_opto_pipelining() const { return _trace_opto_pipelining; } diff -r eb178e97560c -r 044b28168e20 src/share/vm/opto/c2_globals.hpp --- a/src/share/vm/opto/c2_globals.hpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/opto/c2_globals.hpp Thu Nov 14 19:24:59 2013 -0800 @@ -463,6 +463,9 @@ experimental(bool, AggressiveUnboxing, false, \ "Control optimizations for aggressive boxing elimination") \ \ + develop(bool, TracePostallocExpand, false, "Trace expanding nodes after" \ + " register allocation.") \ + \ product(bool, DoEscapeAnalysis, true, \ "Perform escape analysis") \ \ diff -r eb178e97560c -r 044b28168e20 src/share/vm/opto/compile.cpp --- a/src/share/vm/opto/compile.cpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/opto/compile.cpp Thu Nov 14 19:24:59 2013 -0800 @@ -2250,6 +2250,12 @@ peep.do_transform(); } + // Do late expand if CPU requires this. + if (Matcher::require_postalloc_expand) { + NOT_PRODUCT(TracePhase t2c("postalloc_expand", &_t_postalloc_expand, true)); + cfg.postalloc_expand(_regalloc); + } + // Convert Nodes to instruction bits in a buffer { // %%%% workspace merge brought two timers together for one job diff -r eb178e97560c -r 044b28168e20 src/share/vm/opto/machnode.cpp --- a/src/share/vm/opto/machnode.cpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/opto/machnode.cpp Thu Nov 14 19:24:59 2013 -0800 @@ -134,6 +134,10 @@ ShouldNotCallThis(); } +//---------------------------postalloc_expand---------------------------------- +// Expand node after register allocation. +void MachNode::postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_) {} + //------------------------------size------------------------------------------- // Size of instruction in bytes uint MachNode::size(PhaseRegAlloc *ra_) const { diff -r eb178e97560c -r 044b28168e20 src/share/vm/opto/machnode.hpp --- a/src/share/vm/opto/machnode.hpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/opto/machnode.hpp Thu Nov 14 19:24:59 2013 -0800 @@ -155,7 +155,15 @@ virtual void ext_format(PhaseRegAlloc *,const MachNode *node,int idx, outputStream *st) const=0; virtual void dump_spec(outputStream *st) const; // Print per-operand info -#endif + + // Check whether o is a valid oper. + static bool notAnOper(const MachOper *o) { + if (o == NULL) return true; + if (((intptr_t)o & 1) != 0) return true; + if (*(address*)o == badAddress) return true; // kill by Node::destruct + return false; + } +#endif // !PRODUCT }; //------------------------------MachNode--------------------------------------- @@ -220,6 +228,12 @@ // Emit bytes into cbuf virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const; + // Expand node after register allocation. + // Node is replaced by several nodes in the postalloc expand phase. + // Corresponding methods are generated for nodes if they specify + // postalloc_expand. See block.cpp for more documentation. + virtual bool requires_postalloc_expand() const { return false; } + virtual void postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_); // Size of instruction in bytes virtual uint size(PhaseRegAlloc *ra_) const; // Helper function that computes size by emitting code @@ -356,6 +370,9 @@ virtual uint ideal_reg() const { return Op_RegP; } virtual uint oper_input_base() const { return 1; } + virtual bool requires_postalloc_expand() const; + virtual void postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_); + virtual void emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const; virtual uint size(PhaseRegAlloc* ra_) const; virtual bool pinned() const { return UseRDPCForConstantTableBase; } diff -r eb178e97560c -r 044b28168e20 src/share/vm/opto/matcher.hpp --- a/src/share/vm/opto/matcher.hpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/opto/matcher.hpp Thu Nov 14 19:24:59 2013 -0800 @@ -449,6 +449,10 @@ // aligned. static const bool misaligned_doubles_ok; + // Does the CPU require postalloc expand (see block.cpp for description of + // postalloc expand)? + static const bool require_postalloc_expand; + // Perform a platform dependent implicit null fixup. This is needed // on windows95 to take care of some unusual register constraints. void pd_implicit_null_fixup(MachNode *load, uint idx); diff -r eb178e97560c -r 044b28168e20 src/share/vm/opto/node.hpp --- a/src/share/vm/opto/node.hpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/opto/node.hpp Thu Nov 14 19:24:59 2013 -0800 @@ -357,6 +357,8 @@ // Reference to the i'th input Node. Error if out of bounds. Node* in(uint i) const { assert(i < _max, err_msg_res("oob: i=%d, _max=%d", i, _max)); return _in[i]; } + // Reference to the i'th input Node. NULL if out of bounds. + Node* lookup(uint i) const { return ((i < _max) ? _in[i] : NULL); } // Reference to the i'th output Node. Error if out of bounds. // Use this accessor sparingly. We are going trying to use iterators instead. Node* raw_out(uint i) const { assert(i < _outcnt,"oob"); return _out[i]; } @@ -384,6 +386,10 @@ // Set a required input edge, also updates corresponding output edge void add_req( Node *n ); // Append a NEW required input + void add_req( Node *n0, Node *n1 ) { + add_req(n0); add_req(n1); } + void add_req( Node *n0, Node *n1, Node *n2 ) { + add_req(n0); add_req(n1); add_req(n2); } void add_req_batch( Node* n, uint m ); // Append m NEW required inputs (all n). void del_req( uint idx ); // Delete required edge & compact void del_req_ordered( uint idx ); // Delete required edge & compact with preserved order @@ -1350,7 +1356,7 @@ public: Node_List() : Node_Array(Thread::current()->resource_area()), _cnt(0) {} Node_List(Arena *a) : Node_Array(a), _cnt(0) {} - bool contains(Node* n) { + bool contains(const Node* n) const { for (uint e = 0; e < size(); e++) { if (at(e) == n) return true; } diff -r eb178e97560c -r 044b28168e20 src/share/vm/opto/phase.cpp --- a/src/share/vm/opto/phase.cpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/opto/phase.cpp Thu Nov 14 19:24:59 2013 -0800 @@ -26,6 +26,7 @@ #include "code/nmethod.hpp" #include "compiler/compileBroker.hpp" #include "opto/compile.hpp" +#include "opto/matcher.hpp" #include "opto/node.hpp" #include "opto/phase.hpp" @@ -55,6 +56,7 @@ elapsedTimer Phase::_t_macroEliminate; elapsedTimer Phase::_t_macroExpand; elapsedTimer Phase::_t_peephole; +elapsedTimer Phase::_t_postalloc_expand; elapsedTimer Phase::_t_codeGeneration; elapsedTimer Phase::_t_registerMethod; elapsedTimer Phase::_t_temporaryTimer1; @@ -144,6 +146,9 @@ } tty->print_cr (" blockOrdering : %3.3f sec", Phase::_t_blockOrdering.seconds()); tty->print_cr (" peephole : %3.3f sec", Phase::_t_peephole.seconds()); + if (Matcher::require_postalloc_expand) { + tty->print_cr (" postalloc_expand: %3.3f sec", Phase::_t_postalloc_expand.seconds()); + } tty->print_cr (" codeGen : %3.3f sec", Phase::_t_codeGeneration.seconds()); tty->print_cr (" install_code : %3.3f sec", Phase::_t_registerMethod.seconds()); tty->print_cr (" -------------- : ----------"); diff -r eb178e97560c -r 044b28168e20 src/share/vm/opto/phase.hpp --- a/src/share/vm/opto/phase.hpp Thu Nov 07 11:47:11 2013 +0100 +++ b/src/share/vm/opto/phase.hpp Thu Nov 14 19:24:59 2013 -0800 @@ -91,6 +91,7 @@ static elapsedTimer _t_macroEliminate; static elapsedTimer _t_macroExpand; static elapsedTimer _t_peephole; + static elapsedTimer _t_postalloc_expand; static elapsedTimer _t_codeGeneration; static elapsedTimer _t_registerMethod; static elapsedTimer _t_temporaryTimer1;