/*
* Copyright (c) 1998, 2022, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
// FORMS.CPP - Definitions for ADL Parser Forms Classes
#include "adlc.hpp"
//==============================Instructions===================================
//------------------------------InstructForm-----------------------------------
InstructForm::InstructForm(const char *id, bool ideal_only)
: _ident(id), _ideal_only(ideal_only),
_localNames(cmpstr, hashstr, Form::arena),
_effects(cmpstr, hashstr, Form::arena),
_is_mach_constant(false),
_needs_constant_base(false),
_has_call(false)
{
_ftype = Form::INS;
_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;
}
InstructForm::InstructForm(const char *id, InstructForm *instr, MatchRule *rule)
: _ident(id), _ideal_only(false),
_localNames(instr->_localNames),
_effects(instr->_effects),
_is_mach_constant(instr->_is_mach_constant),
_needs_constant_base(false),
_has_call(false)
{
_ftype = Form::INS;
_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();
for (; (name = instr->_parameters.iter()) != NULL;)
_parameters.addName(name);
}
InstructForm::~InstructForm() {
}
InstructForm *InstructForm::is_instruction() const {
return (InstructForm*)this;
}
bool InstructForm::ideal_only() const {
return _ideal_only;
}
bool InstructForm::sets_result() const {
return (_matrule != NULL && _matrule->sets_result());
}
bool InstructForm::needs_projections() {
_components.reset();
for( Component *comp; (comp = _components.iter()) != NULL; ) {
if (comp->isa(Component::KILL)) {
return true;
}
}
return false;
}
bool InstructForm::has_temps() {
if (_matrule) {
// Examine each component to see if it is a TEMP
_components.reset();
// Skip the first component, if already handled as (SET dst (...))
Component *comp = NULL;
if (sets_result()) comp = _components.iter();
while ((comp = _components.iter()) != NULL) {
if (comp->isa(Component::TEMP)) {
return true;
}
}
}
return false;
}
uint InstructForm::num_defs_or_kills() {
uint defs_or_kills = 0;
_components.reset();
for( Component *comp; (comp = _components.iter()) != NULL; ) {
if( comp->isa(Component::DEF) || comp->isa(Component::KILL) ) {
++defs_or_kills;
}
}
return defs_or_kills;
}
// This instruction has an expand rule?
bool InstructForm::expands() const {
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;
}
// This instruction has a peephole rule?
void InstructForm::append_peephole(Peephole *peephole) {
if( _peephole == NULL ) {
_peephole = peephole;
} else {
_peephole->append_peephole(peephole);
}
}
// ideal opcode enumeration
const char *InstructForm::ideal_Opcode( FormDict &globalNames ) const {
if( !_matrule ) return "Node"; // Something weird
// Chain rules do not really have ideal Opcodes; use their source
// operand ideal Opcode instead.
if( is_simple_chain_rule(globalNames) ) {
const char *src = _matrule->_rChild->_opType;
OperandForm *src_op = globalNames[src]->is_operand();
assert( src_op, "Not operand class of chain rule" );
if( !src_op->_matrule ) return "Node";
return src_op->_matrule->_opType;
}
// Operand chain rules do not really have ideal Opcodes
if( _matrule->is_chain_rule(globalNames) )
return "Node";
return strcmp(_matrule->_opType,"Set")
? _matrule->_opType
: _matrule->_rChild->_opType;
}
// Recursive check on all operands' match rules in my match rule
bool InstructForm::is_pinned(FormDict &globals) {
if ( ! _matrule) return false;
int index = 0;
if (_matrule->find_type("Goto", index)) return true;
if (_matrule->find_type("If", index)) return true;
if (_matrule->find_type("CountedLoopEnd",index)) return true;
if (_matrule->find_type("Return", index)) return true;
if (_matrule->find_type("Rethrow", index)) return true;
if (_matrule->find_type("TailCall", index)) return true;
if (_matrule->find_type("TailJump", index)) return true;
if (_matrule->find_type("Halt", index)) return true;
if (_matrule->find_type("Jump", index)) return true;
return is_parm(globals);
}
// Recursive check on all operands' match rules in my match rule
bool InstructForm::is_projection(FormDict &globals) {
if ( ! _matrule) return false;
int index = 0;
if (_matrule->find_type("Goto", index)) return true;
if (_matrule->find_type("Return", index)) return true;
if (_matrule->find_type("Rethrow", index)) return true;
if (_matrule->find_type("TailCall",index)) return true;
if (_matrule->find_type("TailJump",index)) return true;
if (_matrule->find_type("Halt", index)) return true;
return false;
}
// Recursive check on all operands' match rules in my match rule
bool InstructForm::is_parm(FormDict &globals) {
if ( ! _matrule) return false;
int index = 0;
if (_matrule->find_type("Parm",index)) return true;
return false;
}
bool InstructForm::is_ideal_negD() const {
return (_matrule && _matrule->_rChild && strcmp(_matrule->_rChild->_opType, "NegD") == 0);
}
// Return 'true' if this instruction matches an ideal 'Copy*' node
int InstructForm::is_ideal_copy() const {
return _matrule ? _matrule->is_ideal_copy() : 0;
}
// Return 'true' if this instruction is too complex to rematerialize.
int InstructForm::is_expensive() const {
// We can prove it is cheap if it has an empty encoding.
// This helps with platform-specific nops like ThreadLocal and RoundFloat.
if (is_empty_encoding())
return 0;
if (is_tls_instruction())
return 1;
if (_matrule == NULL) return 0;
return _matrule->is_expensive();
}
// Has an empty encoding if _size is a constant zero or there
// are no ins_encode tokens.
int InstructForm::is_empty_encoding() const {
if (_insencode != NULL) {
_insencode->reset();
if (_insencode->encode_class_iter() == NULL) {
return 1;
}
}
if (_size != NULL && strcmp(_size, "0") == 0) {
return 1;
}
return 0;
}
int InstructForm::is_tls_instruction() const {
if (_ident != NULL &&
( ! strcmp( _ident,"tlsLoadP") ||
! strncmp(_ident,"tlsLoadP_",9)) ) {
return 1;
}
if (_matrule != NULL && _insencode != NULL) {
const char* opType = _matrule->_opType;
if (strcmp(opType, "Set")==0)
opType = _matrule->_rChild->_opType;
if (strcmp(opType,"ThreadLocal")==0) {
fprintf(stderr, "Warning: ThreadLocal instruction %s should be named 'tlsLoadP_*'\n",
(_ident == NULL ? "NULL" : _ident));
return 1;
}
}
return 0;
}
// Return 'true' if this instruction matches an ideal 'If' node
bool InstructForm::is_ideal_if() const {
if( _matrule == NULL ) return false;
return _matrule->is_ideal_if();
}
// Return 'true' if this instruction matches an ideal 'FastLock' node
bool InstructForm::is_ideal_fastlock() const {
if( _matrule == NULL ) return false;
return _matrule->is_ideal_fastlock();
}
// Return 'true' if this instruction matches an ideal 'MemBarXXX' node
bool InstructForm::is_ideal_membar() const {
if( _matrule == NULL ) return false;
return _matrule->is_ideal_membar();
}
// Return 'true' if this instruction matches an ideal 'LoadPC' node
bool InstructForm::is_ideal_loadPC() const {
if( _matrule == NULL ) return false;
return _matrule->is_ideal_loadPC();
}
// Return 'true' if this instruction matches an ideal 'Box' node
bool InstructForm::is_ideal_box() const {
if( _matrule == NULL ) return false;
return _matrule->is_ideal_box();
}
// Return 'true' if this instruction matches an ideal 'Goto' node
bool InstructForm::is_ideal_goto() const {
if( _matrule == NULL ) return false;
return _matrule->is_ideal_goto();
}
// Return 'true' if this instruction matches an ideal 'Jump' node
bool InstructForm::is_ideal_jump() const {
if( _matrule == NULL ) return false;
return _matrule->is_ideal_jump();
}
// Return 'true' if instruction matches ideal 'If' | 'Goto' | 'CountedLoopEnd'
bool InstructForm::is_ideal_branch() const {
if( _matrule == NULL ) return false;
return _matrule->is_ideal_if() || _matrule->is_ideal_goto();
}
// Return 'true' if this instruction matches an ideal 'Return' node
bool InstructForm::is_ideal_return() const {
if( _matrule == NULL ) return false;
// Check MatchRule to see if the first entry is the ideal "Return" node
int index = 0;
if (_matrule->find_type("Return",index)) return true;
if (_matrule->find_type("Rethrow",index)) return true;
if (_matrule->find_type("TailCall",index)) return true;
if (_matrule->find_type("TailJump",index)) return true;
return false;
}
// Return 'true' if this instruction matches an ideal 'Halt' node
bool InstructForm::is_ideal_halt() const {
int index = 0;
return _matrule && _matrule->find_type("Halt",index);
}
// Return 'true' if this instruction matches an ideal 'SafePoint' node
bool InstructForm::is_ideal_safepoint() const {
int index = 0;
return _matrule && _matrule->find_type("SafePoint",index);
}
// Return 'true' if this instruction matches an ideal 'Nop' node
bool InstructForm::is_ideal_nop() const {
return _ident && _ident[0] == 'N' && _ident[1] == 'o' && _ident[2] == 'p' && _ident[3] == '_';
}
bool InstructForm::is_ideal_control() const {
if ( ! _matrule) return false;
return is_ideal_return() || is_ideal_branch() || _matrule->is_ideal_jump() || is_ideal_halt();
}
// Return 'true' if this instruction matches an ideal 'Call' node
Form::CallType InstructForm::is_ideal_call() const {
if( _matrule == NULL ) return Form::invalid_type;
// Check MatchRule to see if the first entry is the ideal "Call" node
int idx = 0;
if(_matrule->find_type("CallStaticJava",idx)) return Form::JAVA_STATIC;
idx = 0;
if(_matrule->find_type("Lock",idx)) return Form::JAVA_STATIC;
idx = 0;
if(_matrule->find_type("Unlock",idx)) return Form::JAVA_STATIC;
idx = 0;
if(_matrule->find_type("CallDynamicJava",idx)) return Form::JAVA_DYNAMIC;
idx = 0;
if(_matrule->find_type("CallRuntime",idx)) return Form::JAVA_RUNTIME;
idx = 0;
if(_matrule->find_type("CallLeaf",idx)) return Form::JAVA_LEAF;
idx = 0;
if(_matrule->find_type("CallLeafNoFP",idx)) return Form::JAVA_LEAF;
idx = 0;
if(_matrule->find_type("CallLeafVector",idx)) return Form::JAVA_LEAF;
idx = 0;
return Form::invalid_type;
}
// Return 'true' if this instruction matches an ideal 'Load?' node
Form::DataType InstructForm::is_ideal_load() const {
if( _matrule == NULL ) return Form::none;
return _matrule->is_ideal_load();
}
// Return 'true' if this instruction matches an ideal 'LoadKlass' node
bool InstructForm::skip_antidep_check() const {
if( _matrule == NULL ) return false;
return _matrule->skip_antidep_check();
}
// Return 'true' if this instruction matches an ideal 'Load?' node
Form::DataType InstructForm::is_ideal_store() const {
if( _matrule == NULL ) return Form::none;
return _matrule->is_ideal_store();
}
// Return 'true' if this instruction matches an ideal vector node
bool InstructForm::is_vector() const {
if( _matrule == NULL ) return false;
return _matrule->is_vector();
}
// Return the input register that must match the output register
// If this is not required, return 0
uint InstructForm::two_address(FormDict &globals) {
uint matching_input = 0;
if(_components.count() == 0) return 0;
_components.reset();
Component *comp = _components.iter();
// Check if there is a DEF
if( comp->isa(Component::DEF) ) {
// Check that this is a register
const char *def_type = comp->_type;
const Form *form = globals[def_type];
OperandForm *op = form->is_operand();
if( op ) {
if( op->constrained_reg_class() != NULL &&
op->interface_type(globals) == Form::register_interface ) {
// Remember the local name for equality test later
const char *def_name = comp->_name;
// Check if a component has the same name and is a USE
do {
if( comp->isa(Component::USE) && strcmp(comp->_name,def_name)==0 ) {
return operand_position_format(def_name);
}
} while( (comp = _components.iter()) != NULL);
}
}
}
return 0;
}
// when chaining a constant to an instruction, returns 'true' and sets opType
Form::DataType InstructForm::is_chain_of_constant(FormDict &globals) {
const char *dummy = NULL;
const char *dummy2 = NULL;
return is_chain_of_constant(globals, dummy, dummy2);
}
Form::DataType InstructForm::is_chain_of_constant(FormDict &globals,
const char * &opTypeParam) {
const char *result = NULL;
return is_chain_of_constant(globals, opTypeParam, result);
}
Form::DataType InstructForm::is_chain_of_constant(FormDict &globals,
const char * &opTypeParam, const char * &resultParam) {
Form::DataType data_type = Form::none;
if ( ! _matrule) return data_type;
// !!!!!
// The source of the chain rule is 'position = 1'
uint position = 1;
const char *result = NULL;
const char *name = NULL;
const char *opType = NULL;
// Here base_operand is looking for an ideal type to be returned (opType).
if ( _matrule->is_chain_rule(globals)
&& _matrule->base_operand(position, globals, result, name, opType) ) {
data_type = ideal_to_const_type(opType);
// if it isn't an ideal constant type, just return
if ( data_type == Form::none ) return data_type;
// Ideal constant types also adjust the opType parameter.
resultParam = result;
opTypeParam = opType;
return data_type;
}
return data_type;
}
// Check if a simple chain rule
bool InstructForm::is_simple_chain_rule(FormDict &globals) const {
if( _matrule && _matrule->sets_result()
&& _matrule->_rChild->_lChild == NULL
&& globals[_matrule->_rChild->_opType]
&& globals[_matrule->_rChild->_opType]->is_opclass() ) {
return true;
}
return false;
}
// check for structural rematerialization
bool InstructForm::rematerialize(FormDict &globals, RegisterForm *registers ) {
bool rematerialize = false;
Form::DataType data_type = is_chain_of_constant(globals);
if( data_type != Form::none )
rematerialize = true;
// Constants
if( _components.count() == 1 && _components[0]->is(Component::USE_DEF) )
rematerialize = true;
// Pseudo-constants (values easily available to the runtime)
if (is_empty_encoding() && is_tls_instruction())
rematerialize = true;
// 1-input, 1-output, such as copies or increments.
if( _components.count() == 2 &&
_components[0]->is(Component::DEF) &&
_components[1]->isa(Component::USE) )
rematerialize = true;
// Check for an ideal 'Load?' and eliminate rematerialize option
if ( is_ideal_load() != Form::none || // Ideal load? Do not rematerialize
is_ideal_copy() != Form::none || // Ideal copy? Do not rematerialize
is_expensive() != Form::none) { // Expensive? Do not rematerialize
rematerialize = false;
}
// Always rematerialize the flags. They are more expensive to save &
// restore than to recompute (and possibly spill the compare's inputs).
if( _components.count() >= 1 ) {
Component *c = _components[0];
const Form *form = globals[c->_type];
OperandForm *opform = form->is_operand();
if( opform ) {
// Avoid the special stack_slots register classes
const char *rc_name = opform->constrained_reg_class();
if( rc_name ) {
if( strcmp(rc_name,"stack_slots") ) {
// Check for ideal_type of RegFlags
const char *type = opform->ideal_type( globals, registers );
if( (type != NULL) && !strcmp(type, "RegFlags") )
rematerialize = true;
} else
rematerialize = false; // Do not rematerialize things target stk
}
}
}
return rematerialize;
}
// loads from memory, so must check for anti-dependence
bool InstructForm::needs_anti_dependence_check(FormDict &globals) const {
if ( skip_antidep_check() ) return false;
// Machine independent loads must be checked for anti-dependences
if( is_ideal_load() != Form::none ) return true;
// !!!!! !!!!! !!!!!
// TEMPORARY
// if( is_simple_chain_rule(globals) ) return false;
// String.(compareTo/equals/indexOf) and Arrays.equals use many memorys edges,
// but writes none
if( _matrule && _matrule->_rChild &&
( strcmp(_matrule->_rChild->_opType,"StrComp" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrEquals" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrIndexOf" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrIndexOfChar" )==0 ||
strcmp(_matrule->_rChild->_opType,"CountPositives" )==0 ||
strcmp(_matrule->_rChild->_opType,"AryEq" )==0 ))
return true;
// Check if instruction has a USE of a memory operand class, but no defs
bool USE_of_memory = false;
bool DEF_of_memory = false;
Component *comp = NULL;
ComponentList &components = (ComponentList &)_components;
components.reset();
while( (comp = components.iter()) != NULL ) {
const Form *form = globals[comp->_type];
if( !form ) continue;
OpClassForm *op = form->is_opclass();
if( !op ) continue;
if( form->interface_type(globals) == Form::memory_interface ) {
if( comp->isa(Component::USE) ) USE_of_memory = true;
if( comp->isa(Component::DEF) ) {
OperandForm *oper = form->is_operand();
if( oper && oper->is_user_name_for_sReg() ) {
// Stack slots are unaliased memory handled by allocator
oper = oper; // debug stopping point !!!!!
} else {
DEF_of_memory = true;
}
}
}
}
return (USE_of_memory && !DEF_of_memory);
}
int InstructForm::memory_operand(FormDict &globals) const {
// Machine independent loads must be checked for anti-dependences
// Check if instruction has a USE of a memory operand class, or a def.
int USE_of_memory = 0;
int DEF_of_memory = 0;
const char* last_memory_DEF = NULL; // to test DEF/USE pairing in asserts
const char* last_memory_USE = NULL;
Component *unique = NULL;
Component *comp = NULL;
ComponentList &components = (ComponentList &)_components;
components.reset();
while( (comp = components.iter()) != NULL ) {
const Form *form = globals[comp->_type];
if( !form ) continue;
OpClassForm *op = form->is_opclass();
if( !op ) continue;
if( op->stack_slots_only(globals) ) continue;
if( form->interface_type(globals) == Form::memory_interface ) {
if( comp->isa(Component::DEF) ) {
last_memory_DEF = comp->_name;
DEF_of_memory++;
unique = comp;
} else if( comp->isa(Component::USE) ) {
if( last_memory_DEF != NULL ) {
assert(0 == strcmp(last_memory_DEF, comp->_name), "every memory DEF is followed by a USE of the same name");
last_memory_DEF = NULL;
}
// Handles same memory being used multiple times in the case of BMI1 instructions.
if (last_memory_USE != NULL) {
if (strcmp(comp->_name, last_memory_USE) != 0) {
USE_of_memory++;
}
} else {
USE_of_memory++;
}
last_memory_USE = comp->_name;
if (DEF_of_memory == 0) // defs take precedence
unique = comp;
} else {
assert(last_memory_DEF == NULL, "unpaired memory DEF");
}
}
}
assert(last_memory_DEF == NULL, "unpaired memory DEF");
assert(USE_of_memory >= DEF_of_memory, "unpaired memory DEF");
USE_of_memory -= DEF_of_memory; // treat paired DEF/USE as one occurrence
if( (USE_of_memory + DEF_of_memory) > 0 ) {
if( is_simple_chain_rule(globals) ) {
//fprintf(stderr, "Warning: chain rule is not really a memory user.\n");
//((InstructForm*)this)->dump();
// Preceding code prints nothing on sparc and these insns on intel:
// leaP8 leaP32 leaPIdxOff leaPIdxScale leaPIdxScaleOff leaP8 leaP32
// leaPIdxOff leaPIdxScale leaPIdxScaleOff
return NO_MEMORY_OPERAND;
}
if( DEF_of_memory == 1 ) {
assert(unique != NULL, "");
if( USE_of_memory == 0 ) {
// unique def, no uses
} else {
// // unique def, some uses
// // must return bottom unless all uses match def
// unique = NULL;
#ifdef S390
// This case is important for move instructions on s390x.
// On other platforms (e.g. x86), all uses always match the def.
unique = NULL;
#endif
}
} else if( DEF_of_memory > 0 ) {
// multiple defs, don't care about uses
unique = NULL;
} else if( USE_of_memory == 1) {
// unique use, no defs
assert(unique != NULL, "");
} else if( USE_of_memory > 0 ) {
// multiple uses, no defs
unique = NULL;
} else {
assert(false, "bad case analysis");
}
// process the unique DEF or USE, if there is one
if( unique == NULL ) {
return MANY_MEMORY_OPERANDS;
} else {
int pos = components.operand_position(unique->_name);
if( unique->isa(Component::DEF) ) {
pos += 1; // get corresponding USE from DEF
}
assert(pos >= 1, "I was just looking at it!");
return pos;
}
}
// missed the memory op??
if( true ) { // %%% should not be necessary
if( is_ideal_store() != Form::none ) {
fprintf(stderr, "Warning: cannot find memory opnd in instr.\n");
((InstructForm*)this)->dump();
// pretend it has multiple defs and uses
return MANY_MEMORY_OPERANDS;
}
if( is_ideal_load() != Form::none ) {
fprintf(stderr, "Warning: cannot find memory opnd in instr.\n");
((InstructForm*)this)->dump();
// pretend it has multiple uses and no defs
return MANY_MEMORY_OPERANDS;
}
}
return NO_MEMORY_OPERAND;
}
// This instruction captures the machine-independent bottom_type
// Expected use is for pointer vs oop determination for LoadP
bool InstructForm::captures_bottom_type(FormDict &globals) const {
if (_matrule && _matrule->_rChild &&
(!strcmp(_matrule->_rChild->_opType,"CastPP") || // new result type
!strcmp(_matrule->_rChild->_opType,"CastDD") ||
!strcmp(_matrule->_rChild->_opType,"CastFF") ||
!strcmp(_matrule->_rChild->_opType,"CastII") ||
!strcmp(_matrule->_rChild->_opType,"CastLL") ||
!strcmp(_matrule->_rChild->_opType,"CastVV") ||
!strcmp(_matrule->_rChild->_opType,"CastX2P") || // new result type
!strcmp(_matrule->_rChild->_opType,"DecodeN") ||
!strcmp(_matrule->_rChild->_opType,"EncodeP") ||
!strcmp(_matrule->_rChild->_opType,"DecodeNKlass") ||
!strcmp(_matrule->_rChild->_opType,"EncodePKlass") ||
!strcmp(_matrule->_rChild->_opType,"LoadN") ||
!strcmp(_matrule->_rChild->_opType,"LoadNKlass") ||
!strcmp(_matrule->_rChild->_opType,"CreateEx") || // type of exception
!strcmp(_matrule->_rChild->_opType,"CheckCastPP") ||
!strcmp(_matrule->_rChild->_opType,"GetAndSetP") ||
!strcmp(_matrule->_rChild->_opType,"GetAndSetN") ||
!strcmp(_matrule->_rChild->_opType,"RotateLeft") ||
!strcmp(_matrule->_rChild->_opType,"RotateRight") ||
#if INCLUDE_SHENANDOAHGC
!strcmp(_matrule->_rChild->_opType,"ShenandoahCompareAndExchangeP") ||
!strcmp(_matrule->_rChild->_opType,"ShenandoahCompareAndExchangeN") ||
#endif
!strcmp(_matrule->_rChild->_opType,"StrInflatedCopy") ||
!strcmp(_matrule->_rChild->_opType,"VectorCmpMasked")||
!strcmp(_matrule->_rChild->_opType,"VectorMaskGen")||
!strcmp(_matrule->_rChild->_opType,"CompareAndExchangeP") ||
!strcmp(_matrule->_rChild->_opType,"CompareAndExchangeN"))) return true;
else if ( is_ideal_load() == Form::idealP ) return true;
else if ( is_ideal_store() != Form::none ) return true;
if (needs_base_oop_edge(globals)) return true;
if (is_vector()) return true;
if (is_mach_constant()) return true;
return false;
}
// Access instr_cost attribute or return NULL.
const char* InstructForm::cost() {
for (Attribute* cur = _attribs; cur != NULL; cur = (Attribute*)cur->_next) {
if( strcmp(cur->_ident,AttributeForm::_ins_cost) == 0 ) {
return cur->_val;
}
}
return NULL;
}
// Return count of top-level operands.
uint InstructForm::num_opnds() {
int num_opnds = _components.num_operands();
// Need special handling for matching some ideal nodes
// i.e. Matching a return node
/*
if( _matrule ) {
if( strcmp(_matrule->_opType,"Return" )==0 ||
strcmp(_matrule->_opType,"Halt" )==0 )
return 3;
}
*/
return num_opnds;
}
const char* InstructForm::opnd_ident(int idx) {
return _components.at(idx)->_name;
}
const char* InstructForm::unique_opnd_ident(uint idx) {
uint i;
for (i = 1; i < num_opnds(); ++i) {
if (unique_opnds_idx(i) == idx) {
break;
}
}
return (_components.at(i) != NULL) ? _components.at(i)->_name : "";
}
// Return count of unmatched operands.
uint InstructForm::num_post_match_opnds() {
uint num_post_match_opnds = _components.count();
uint num_match_opnds = _components.match_count();
num_post_match_opnds = num_post_match_opnds - num_match_opnds;
return num_post_match_opnds;
}
// Return the number of leaves below this complex operand
uint InstructForm::num_consts(FormDict &globals) const {
if ( ! _matrule) return 0;
// This is a recursive invocation on all operands in the matchrule
return _matrule->num_consts(globals);
}
// Constants in match rule with specified type
uint InstructForm::num_consts(FormDict &globals, Form::DataType type) const {
if ( ! _matrule) return 0;
// This is a recursive invocation on all operands in the matchrule
return _matrule->num_consts(globals, type);
}
// Return the register class associated with 'leaf'.
const char *InstructForm::out_reg_class(FormDict &globals) {
assert( false, "InstructForm::out_reg_class(FormDict &globals); Not Implemented");
return NULL;
}
// Lookup the starting position of inputs we are interested in wrt. ideal nodes
uint InstructForm::oper_input_base(FormDict &globals) {
if( !_matrule ) return 1; // Skip control for most nodes
// Need special handling for matching some ideal nodes
// i.e. Matching a return node
if( strcmp(_matrule->_opType,"Return" )==0 ||
strcmp(_matrule->_opType,"Rethrow" )==0 ||
strcmp(_matrule->_opType,"TailCall" )==0 ||
strcmp(_matrule->_opType,"TailJump" )==0 ||
strcmp(_matrule->_opType,"SafePoint" )==0 ||
strcmp(_matrule->_opType,"Halt" )==0 )
return AdlcVMDeps::Parms; // Skip the machine-state edges
if( _matrule->_rChild &&
( strcmp(_matrule->_rChild->_opType,"AryEq" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrComp" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrEquals" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrInflatedCopy" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrCompressedCopy" )==0 ||
strcmp(_matrule->_rChild->_opType,"StrIndexOf")==0 ||
strcmp(_matrule->_rChild->_opType,"StrIndexOfChar")==0 ||
strcmp(_matrule->_rChild->_opType,"CountPositives")==0 ||
strcmp(_matrule->_rChild->_opType,"EncodeISOArray")==0)) {
// String.(compareTo/equals/indexOf) and Arrays.equals
// and sun.nio.cs.iso8859_1$Encoder.EncodeISOArray
// take 1 control and 1 memory edges.
// Also String.(compressedCopy/inflatedCopy).
return 2;
}
// Check for handling of 'Memory' input/edge in the ideal world.
// The AD file writer is shielded from knowledge of these edges.
int base = 1; // Skip control
base += _matrule->needs_ideal_memory_edge(globals);
// Also skip the base-oop value for uses of derived oops.
// The AD file writer is shielded from knowledge of these edges.
base += needs_base_oop_edge(globals);
return base;
}
// This function determines the order of the MachOper in _opnds[]
// by writing the operand names into the _components list.
//
// Implementation does not modify state of internal structures
void InstructForm::build_components() {
// Add top-level operands to the components
if (_matrule) _matrule->append_components(_localNames, _components);
// Add parameters that "do not appear in match rule".
bool has_temp = false;
const char *name;
const char *kill_name = NULL;
for (_parameters.reset(); (name = _parameters.iter()) != NULL;) {
OpClassForm *opForm = _localNames[name]->is_opclass();
assert(opForm != NULL, "sanity");
Effect* e = NULL;
{
const Form* form = _effects[name];
e = form ? form->is_effect() : NULL;
}
if (e != NULL) {
has_temp |= e->is(Component::TEMP);
// KILLs must be declared after any TEMPs because TEMPs are real
// uses so their operand numbering must directly follow the real
// inputs from the match rule. Fixing the numbering seems
// complex so simply enforce the restriction during parse.
if (kill_name != NULL &&
e->isa(Component::TEMP) && !e->isa(Component::DEF)) {
OpClassForm* kill = _localNames[kill_name]->is_opclass();
assert(kill != NULL, "sanity");
globalAD->syntax_err(_linenum, "%s: %s %s must be at the end of the argument list\n",
_ident, kill->_ident, kill_name);
} else if (e->isa(Component::KILL) && !e->isa(Component::USE)) {
kill_name = name;
}
}
const Component *component = _components.search(name);
if ( component == NULL ) {
if (e) {
_components.insert(name, opForm->_ident, e->_use_def, false);
component = _components.search(name);
if (component->isa(Component::USE) && !component->isa(Component::TEMP) && _matrule) {
const Form *form = globalAD->globalNames()[component->_type];
assert( form, "component type must be a defined form");
OperandForm *op = form->is_operand();
if (op->_interface && op->_interface->is_RegInterface()) {
globalAD->syntax_err(_linenum, "%s: illegal USE of non-input: %s %s\n",
_ident, opForm->_ident, name);
}
}
} else {
// This would be a nice warning but it triggers in a few places in a benign way
// if (_matrule != NULL && !expands()) {
// globalAD->syntax_err(_linenum, "%s: %s %s not mentioned in effect or match rule\n",
// _ident, opForm->_ident, name);
// }
_components.insert(name, opForm->_ident, Component::INVALID, false);
}
}
else if (e) {
// Component was found in the list
// Check if there is a new effect that requires an extra component.
// This happens when adding 'USE' to a component that is not yet one.
if ((!component->isa( Component::USE) && ((e->_use_def & Component::USE) != 0))) {
if (component->isa(Component::USE) && _matrule) {
const Form *form = globalAD->globalNames()[component->_type];
assert( form, "component type must be a defined form");
OperandForm *op = form->is_operand();
if (op->_interface && op->_interface->is_RegInterface()) {
globalAD->syntax_err(_linenum, "%s: illegal USE of non-input: %s %s\n",
_ident, opForm->_ident, name);
}
}
_components.insert(name, opForm->_ident, e->_use_def, false);
} else {
Component *comp = (Component*)component;
comp->promote_use_def_info(e->_use_def);
}
// Component positions are zero based.
int pos = _components.operand_position(name);
assert( ! (component->isa(Component::DEF) && (pos >= 1)),
"Component::DEF can only occur in the first position");
}
}
// Resolving the interactions between expand rules and TEMPs would
// be complex so simply disallow it.
if (_matrule == NULL && has_temp) {
globalAD->syntax_err(_linenum, "%s: TEMPs without match rule isn't supported\n", _ident);
}
return;
}
// Return zero-based position in component list; -1 if not in list.
int InstructForm::operand_position(const char *name, int usedef) {
return unique_opnds_idx(_components.operand_position(name, usedef, this));
}
int InstructForm::operand_position_format(const char *name) {
return unique_opnds_idx(_components.operand_position_format(name, this));
}
// Return zero-based position in component list; -1 if not in list.
int InstructForm::label_position() {
return unique_opnds_idx(_components.label_position());
}
int InstructForm::method_position() {
return unique_opnds_idx(_components.method_position());
}
// Return number of relocation entries needed for this instruction.
uint InstructForm::reloc(FormDict &globals) {
uint reloc_entries = 0;
// Check for "Call" nodes
if ( is_ideal_call() ) ++reloc_entries;
if ( is_ideal_return() ) ++reloc_entries;
if ( is_ideal_safepoint() ) ++reloc_entries;
// Check if operands MAYBE oop pointers, by checking for ConP elements
// Proceed through the leaves of the match-tree and check for ConPs
if ( _matrule != NULL ) {
uint position = 0;
const char *result = NULL;
const char *name = NULL;
const char *opType = NULL;
while (_matrule->base_operand(position, globals, result, name, opType)) {
if ( strcmp(opType,"ConP") == 0 ) {
++reloc_entries;
}
++position;
}
}
// Above is only a conservative estimate
// because it did not check contents of operand classes.
// !!!!! !!!!!
// Add 1 to reloc info for each operand class in the component list.
Component *comp;
_components.reset();
while ( (comp = _components.iter()) != NULL ) {
const Form *form = globals[comp->_type];
assert( form, "Did not find component's type in global names");
const OpClassForm *opc = form->is_opclass();
const OperandForm *oper = form->is_operand();
if ( opc && (oper == NULL) ) {
++reloc_entries;
} else if ( oper ) {
// floats and doubles loaded out of method's constant pool require reloc info
Form::DataType type = oper->is_base_constant(globals);
if ( (type == Form::idealF) || (type == Form::idealD) ) {
++reloc_entries;
}
}
}
// Float and Double constants may come from the CodeBuffer table
// and require relocatable addresses for access
// !!!!!
// Check for any component being an immediate float or double.
Form::DataType data_type = is_chain_of_constant(globals);
if( data_type==idealD || data_type==idealF ) {
reloc_entries++;
}
return reloc_entries;
}
// Utility function defined in archDesc.cpp
extern bool is_def(int usedef);
// Return the result of reducing an instruction
const char *InstructForm::reduce_result() {
const char* result = "Universe"; // default
_components.reset();
Component *comp = _components.iter();
if (comp != NULL && comp->isa(Component::DEF)) {
result = comp->_type;
// Override this if the rule is a store operation:
if (_matrule && _matrule->_rChild &&
is_store_to_memory(_matrule->_rChild->_opType))
result = "Universe";
}
return result;
}
// Return the name of the operand on the right hand side of the binary match
// Return NULL if there is no right hand side
const char *InstructForm::reduce_right(FormDict &globals) const {
if( _matrule == NULL ) return NULL;
return _matrule->reduce_right(globals);
}
// Similar for left
const char *InstructForm::reduce_left(FormDict &globals) const {
if( _matrule == NULL ) return NULL;
return _matrule->reduce_left(globals);
}
// Base class for this instruction, MachNode except for calls
const char *InstructForm::mach_base_class(FormDict &globals) const {
if( is_ideal_call() == Form::JAVA_STATIC ) {
return "MachCallStaticJavaNode";
}
else if( is_ideal_call() == Form::JAVA_DYNAMIC ) {
return "MachCallDynamicJavaNode";
}
else if( is_ideal_call() == Form::JAVA_RUNTIME ) {
return "MachCallRuntimeNode";
}
else if( is_ideal_call() == Form::JAVA_LEAF ) {
return "MachCallLeafNode";
}
else if (is_ideal_return()) {
return "MachReturnNode";
}
else if (is_ideal_halt()) {
return "MachHaltNode";
}
else if (is_ideal_safepoint()) {
return "MachSafePointNode";
}
else if (is_ideal_if()) {
return "MachIfNode";
}
else if (is_ideal_goto()) {
return "MachGotoNode";
}
else if (is_ideal_fastlock()) {
return "MachFastLockNode";
}
else if (is_ideal_nop()) {
return "MachNopNode";
}
else if( is_ideal_membar()) {
return "MachMemBarNode";
}
else if (is_ideal_jump()) {
return "MachJumpNode";
}
else if (is_mach_constant()) {
return "MachConstantNode";
}
else if (captures_bottom_type(globals)) {
return "MachTypeNode";
} else {
return "MachNode";
}
assert( false, "ShouldNotReachHere()");
return NULL;
}
// Compare the instruction predicates for textual equality
bool equivalent_predicates( const InstructForm *instr1, const InstructForm *instr2 ) {
const Predicate *pred1 = instr1->_predicate;
const Predicate *pred2 = instr2->_predicate;
if( pred1 == NULL && pred2 == NULL ) {
// no predicates means they are identical
return true;
}
if( pred1 != NULL && pred2 != NULL ) {
// compare the predicates
if (ADLParser::equivalent_expressions(pred1->_pred, pred2->_pred)) {
return true;
}
}
return false;
}
// Check if this instruction can cisc-spill to 'alternate'
bool InstructForm::cisc_spills_to(ArchDesc &AD, InstructForm *instr) {
assert( _matrule != NULL && instr->_matrule != NULL, "must have match rules");
// Do not replace if a cisc-version has been found.
if( cisc_spill_operand() != Not_cisc_spillable ) return false;
int cisc_spill_operand = Maybe_cisc_spillable;
char *result = NULL;
char *result2 = NULL;
const char *op_name = NULL;
const char *reg_type = NULL;
FormDict &globals = AD.globalNames();
cisc_spill_operand = _matrule->matchrule_cisc_spill_match(globals, AD.get_registers(), instr->_matrule, op_name, reg_type);
if( (cisc_spill_operand != Not_cisc_spillable) && (op_name != NULL) && equivalent_predicates(this, instr) ) {
cisc_spill_operand = operand_position(op_name, Component::USE);
int def_oper = operand_position(op_name, Component::DEF);
if( def_oper == NameList::Not_in_list && instr->num_opnds() == num_opnds()) {
// Do not support cisc-spilling for destination operands and
// make sure they have the same number of operands.
_cisc_spill_alternate = instr;
instr->set_cisc_alternate(true);
if( AD._cisc_spill_debug ) {
fprintf(stderr, "Instruction %s cisc-spills-to %s\n", _ident, instr->_ident);
fprintf(stderr, " using operand %s %s at index %d\n", reg_type, op_name, cisc_spill_operand);
}
// Record that a stack-version of the reg_mask is needed
// !!!!!
OperandForm *oper = (OperandForm*)(globals[reg_type]->is_operand());
assert( oper != NULL, "cisc-spilling non operand");
const char *reg_class_name = oper->constrained_reg_class();
AD.set_stack_or_reg(reg_class_name);
const char *reg_mask_name = AD.reg_mask(*oper);
set_cisc_reg_mask_name(reg_mask_name);
const char *stack_or_reg_mask_name = AD.stack_or_reg_mask(*oper);
} else {
cisc_spill_operand = Not_cisc_spillable;
}
} else {
cisc_spill_operand = Not_cisc_spillable;
}
set_cisc_spill_operand(cisc_spill_operand);
return (cisc_spill_operand != Not_cisc_spillable);
}
// Check to see if this instruction can be replaced with the short branch
// instruction `short-branch'
bool InstructForm::check_branch_variant(ArchDesc &AD, InstructForm *short_branch) {
if (_matrule != NULL &&
this != short_branch && // Don't match myself
!is_short_branch() && // Don't match another short branch variant
reduce_result() != NULL &&
strstr(_ident, "restoreMask") == NULL && // Don't match side effects
strcmp(reduce_result(), short_branch->reduce_result()) == 0 &&
_matrule->equivalent(AD.globalNames(), short_branch->_matrule)) {
// The instructions are equivalent.
// Now verify that both instructions have the same parameters and
// the same effects. Both branch forms should have the same inputs
// and resulting projections to correctly replace a long branch node
// with corresponding short branch node during code generation.
bool different = false;
if (short_branch->_components.count() != _components.count()) {
different = true;
} else if (_components.count() > 0) {
short_branch->_components.reset();
_components.reset();
Component *comp;
while ((comp = _components.iter()) != NULL) {
Component *short_comp = short_branch->_components.iter();
if (short_comp == NULL ||
short_comp->_type != comp->_type ||
short_comp->_usedef != comp->_usedef) {
different = true;
break;
}
}
if (short_branch->_components.iter() != NULL)
different = true;
}
if (different) {
globalAD->syntax_err(short_branch->_linenum, "Instruction %s and its short form %s have different parameters\n", _ident, short_branch->_ident);
}
if (AD._adl_debug > 1 || AD._short_branch_debug) {
fprintf(stderr, "Instruction %s has short form %s\n", _ident, short_branch->_ident);
}
_short_branch_form = short_branch;
return true;
}
return false;
}
// --------------------------- FILE *output_routines
//
// Generate the format call for the replacement variable
void InstructForm::rep_var_format(FILE *fp, const char *rep_var) {
// Handle special constant table variables.
if (strcmp(rep_var, "constanttablebase") == 0) {
fprintf(fp, "char reg[128]; ra->dump_register(in(mach_constant_base_node_input()), reg);\n");
fprintf(fp, " st->print(\"%%s\", reg);\n");
return;
}
if (strcmp(rep_var, "constantoffset") == 0) {
fprintf(fp, "st->print(\"#%%d\", constant_offset_unchecked());\n");
return;
}
if (strcmp(rep_var, "constantaddress") == 0) {
fprintf(fp, "st->print(\"constant table base + #%%d\", constant_offset_unchecked());\n");
return;
}
// Find replacement variable's type
const Form *form = _localNames[rep_var];
if (form == NULL) {
globalAD->syntax_err(_linenum, "Unknown replacement variable %s in format statement of %s.",
rep_var, _ident);
return;
}
OpClassForm *opc = form->is_opclass();
assert( opc, "replacement variable was not found in local names");
// Lookup the index position of the replacement variable
int idx = operand_position_format(rep_var);
if ( idx == -1 ) {
globalAD->syntax_err(_linenum, "Could not find replacement variable %s in format statement of %s.\n",
rep_var, _ident);
assert(strcmp(opc->_ident, "label") == 0, "Unimplemented");
return;
}
if (is_noninput_operand(idx)) {
// This component isn't in the input array. Print out the static
// name of the register.
OperandForm* oper = form->is_operand();
if (oper != NULL && oper->is_bound_register()) {
const RegDef* first = oper->get_RegClass()->find_first_elem();
fprintf(fp, " st->print_raw(\"%s\");\n", first->_regname);
} else {
globalAD->syntax_err(_linenum, "In %s can't find format for %s %s", _ident, opc->_ident, rep_var);
}
} else {
// Output the format call for this operand
fprintf(fp,"opnd_array(%d)->",idx);
if (idx == 0)
fprintf(fp,"int_format(ra, this, st); // %s\n", rep_var);
else
fprintf(fp,"ext_format(ra, this,idx%d, st); // %s\n", idx, rep_var );
}
}
// Search through operands to determine parameters unique positions.
void InstructForm::set_unique_opnds() {
uint* uniq_idx = NULL;
uint nopnds = num_opnds();
uint num_uniq = nopnds;
uint i;
_uniq_idx_length = 0;
if (nopnds > 0) {
// Allocate index array. Worst case we're mapping from each
// component back to an index and any DEF always goes at 0 so the
// length of the array has to be the number of components + 1.
_uniq_idx_length = _components.count() + 1;
uniq_idx = (uint*) AdlAllocateHeap(sizeof(uint) * _uniq_idx_length);
for (i = 0; i < _uniq_idx_length; i++) {
uniq_idx[i] = i;
}
}
// Do it only if there is a match rule and no expand rule. With an
// expand rule it is done by creating new mach node in Expand()
// method.
if (nopnds > 0 && _matrule != NULL && _exprule == NULL) {
const char *name;
uint count;
bool has_dupl_use = false;
_parameters.reset();
while ((name = _parameters.iter()) != NULL) {
count = 0;
uint position = 0;
uint uniq_position = 0;
_components.reset();
Component *comp = NULL;
if (sets_result()) {
comp = _components.iter();
position++;
}
// The next code is copied from the method operand_position().
for (; (comp = _components.iter()) != NULL; ++position) {
// When the first component is not a DEF,
// leave space for the result operand!
if (position==0 && (!comp->isa(Component::DEF))) {
++position;
}
if (strcmp(name, comp->_name) == 0) {
if (++count > 1) {
assert(position < _uniq_idx_length, "out of bounds");
uniq_idx[position] = uniq_position;
has_dupl_use = true;
} else {
uniq_position = position;
}
}
if (comp->isa(Component::DEF) && comp->isa(Component::USE)) {
++position;
if (position != 1)
--position; // only use two slots for the 1st USE_DEF
}
}
}
if (has_dupl_use) {
for (i = 1; i < nopnds; i++) {
if (i != uniq_idx[i]) {
break;
}
}
uint j = i;
for (; i < nopnds; i++) {
if (i == uniq_idx[i]) {
uniq_idx[i] = j++;
}
}
num_uniq = j;
}
}
_uniq_idx = uniq_idx;
_num_uniq = num_uniq;
}
// Generate index values needed for determining the operand position
void InstructForm::index_temps(FILE *fp, FormDict &globals, const char *prefix, const char *receiver) {
uint idx = 0; // position of operand in match rule
int cur_num_opnds = num_opnds();
// Compute the index into vector of operand pointers:
// idx0=0 is used to indicate that info comes from this same node, not from input edge.
// idx1 starts at oper_input_base()
if ( cur_num_opnds >= 1 ) {
fprintf(fp," // Start at oper_input_base() and count operands\n");
fprintf(fp," unsigned %sidx0 = %d;\n", prefix, oper_input_base(globals));
fprintf(fp," unsigned %sidx1 = %d;", prefix, oper_input_base(globals));
fprintf(fp," \t// %s\n", unique_opnd_ident(1));
// Generate starting points for other unique operands if they exist
for ( idx = 2; idx < num_unique_opnds(); ++idx ) {
if( *receiver == 0 ) {
fprintf(fp," unsigned %sidx%d = %sidx%d + opnd_array(%d)->num_edges();",
prefix, idx, prefix, idx-1, idx-1 );
} else {
fprintf(fp," unsigned %sidx%d = %sidx%d + %s_opnds[%d]->num_edges();",
prefix, idx, prefix, idx-1, receiver, idx-1 );
}
fprintf(fp," \t// %s\n", unique_opnd_ident(idx));
}
}
if( *receiver != 0 ) {
// This value is used by generate_peepreplace when copying a node.
// Don't emit it in other cases since it can hide bugs with the
// use invalid idx's.
fprintf(fp," unsigned %sidx%d = %sreq(); \n", prefix, idx, receiver);
}
}
// ---------------------------
bool InstructForm::verify() {
// !!!!! !!!!!
// Check that a "label" operand occurs last in the operand list, if present
return true;
}
void InstructForm::dump() {
output(stderr);
}
void InstructForm::output(FILE *fp) {
fprintf(fp,"\nInstruction: %s\n", (_ident?_ident:""));
if (_matrule) _matrule->output(fp);
if (_insencode) _insencode->output(fp);
if (_constant) _constant->output(fp);
if (_opcode) _opcode->output(fp);
if (_attribs) _attribs->output(fp);
if (_predicate) _predicate->output(fp);
if (_effects.Size()) {
fprintf(fp,"Effects\n");
_effects.dump();
}
if (_exprule) _exprule->output(fp);
if (_rewrule) _rewrule->output(fp);
if (_format) _format->output(fp);
if (_peephole) _peephole->output(fp);
}
void MachNodeForm::dump() {
output(stderr);
}
void MachNodeForm::output(FILE *fp) {
fprintf(fp,"\nMachNode: %s\n", (_ident?_ident:""));
}
//------------------------------build_predicate--------------------------------
// Build instruction predicates. If the user uses the same operand name
// twice, we need to check that the operands are pointer-eequivalent in
// the DFA during the labeling process.
Predicate *InstructForm::build_predicate() {
const int buflen = 1024;
char buf[buflen], *s=buf;
Dict names(cmpstr,hashstr,Form::arena); // Map Names to counts
MatchNode *mnode =
strcmp(_matrule->_opType, "Set") ? _matrule : _matrule->_rChild;
if (mnode != NULL) mnode->count_instr_names(names);
uint first = 1;
// Start with the predicate supplied in the .ad file.
if (_predicate) {
if (first) first = 0;
strcpy(s, "("); s += strlen(s);
strncpy(s, _predicate->_pred, buflen - strlen(s) - 1);
s += strlen(s);
strcpy(s, ")"); s += strlen(s);
}
for( DictI i(&names); i.test(); ++i ) {
uintptr_t cnt = (uintptr_t)i._value;
if( cnt > 1 ) { // Need a predicate at all?
int path_bitmask = 0;
assert( cnt == 2, "Unimplemented" );
// Handle many pairs
if( first ) first=0;
else { // All tests must pass, so use '&&'
strcpy(s," && ");
s += strlen(s);
}
// Add predicate to working buffer
sprintf(s,"/*%s*/(",(char*)i._key);
s += strlen(s);
mnode->build_instr_pred(s,(char*)i._key, 0, path_bitmask, 0);
s += strlen(s);
strcpy(s," == "); s += strlen(s);
mnode->build_instr_pred(s,(char*)i._key, 1, path_bitmask, 0);
s += strlen(s);
strcpy(s,")"); s += strlen(s);
}
}
if( s == buf ) s = NULL;
else {
assert( strlen(buf) < sizeof(buf), "String buffer overflow" );
s = strdup(buf);
}
return new Predicate(s);
}
//------------------------------EncodeForm-------------------------------------
// Constructor
EncodeForm::EncodeForm()
: _encClass(cmpstr,hashstr, Form::arena) {
}
EncodeForm::~EncodeForm() {
}
// record a new register class
EncClass *EncodeForm::add_EncClass(const char *className) {
EncClass *encClass = new EncClass(className);
_eclasses.addName(className);
_encClass.Insert(className,encClass);
return encClass;
}
// Lookup the function body for an encoding class
EncClass *EncodeForm::encClass(const char *className) {
assert( className != NULL, "Must provide a defined encoding name");
EncClass *encClass = (EncClass*)_encClass[className];
return encClass;
}
// Lookup the function body for an encoding class
const char *EncodeForm::encClassBody(const char *className) {
if( className == NULL ) return NULL;
EncClass *encClass = (EncClass*)_encClass[className];
assert( encClass != NULL, "Encode Class is missing.");
encClass->_code.reset();
const char *code = (const char*)encClass->_code.iter();
assert( code != NULL, "Found an empty encode class body.");
return code;
}
// Lookup the function body for an encoding class
const char *EncodeForm::encClassPrototype(const char *className) {
assert( className != NULL, "Encode class name must be non NULL.");
return className;
}
void EncodeForm::dump() { // Debug printer
output(stderr);
}
void EncodeForm::output(FILE *fp) { // Write info to output files
const char *name;
fprintf(fp,"\n");
fprintf(fp,"-------------------- Dump EncodeForm --------------------\n");
for (_eclasses.reset(); (name = _eclasses.iter()) != NULL;) {
((EncClass*)_encClass[name])->output(fp);
}
fprintf(fp,"-------------------- end EncodeForm --------------------\n");
}
//------------------------------EncClass---------------------------------------
EncClass::EncClass(const char *name)
: _localNames(cmpstr,hashstr, Form::arena), _name(name) {
}
EncClass::~EncClass() {
}
// Add a parameter <type,name> pair
void EncClass::add_parameter(const char *parameter_type, const char *parameter_name) {
_parameter_type.addName( parameter_type );
_parameter_name.addName( parameter_name );
}
// Verify operand types in parameter list
bool EncClass::check_parameter_types(FormDict &globals) {
// !!!!!
return false;
}
// Add the decomposed "code" sections of an encoding's code-block
void EncClass::add_code(const char *code) {
_code.addName(code);
}
// Add the decomposed "replacement variables" of an encoding's code-block
void EncClass::add_rep_var(char *replacement_var) {
_code.addName(NameList::_signal);
_rep_vars.addName(replacement_var);
}
// Lookup the function body for an encoding class
int EncClass::rep_var_index(const char *rep_var) {
uint position = 0;
const char *name = NULL;
_parameter_name.reset();
while ( (name = _parameter_name.iter()) != NULL ) {
if ( strcmp(rep_var,name) == 0 ) return position;
++position;
}
return -1;
}
// Check after parsing
bool EncClass::verify() {
// 1!!!!
// Check that each replacement variable, '$name' in architecture description
// is actually a local variable for this encode class, or a reserved name
// "primary, secondary, tertiary"
return true;
}
void EncClass::dump() {
output(stderr);
}
// Write info to output files
void EncClass::output(FILE *fp) {
fprintf(fp,"EncClass: %s", (_name ? _name : ""));
// Output the parameter list
_parameter_type.reset();
_parameter_name.reset();
const char *type = _parameter_type.iter();
const char *name = _parameter_name.iter();
fprintf(fp, " ( ");
for ( ; (type != NULL) && (name != NULL);
(type = _parameter_type.iter()), (name = _parameter_name.iter()) ) {
fprintf(fp, " %s %s,", type, name);
}
fprintf(fp, " ) ");
// Output the code block
_code.reset();
_rep_vars.reset();
const char *code;
while ( (code = _code.iter()) != NULL ) {
if ( _code.is_signal(code) ) {
// A replacement variable
const char *rep_var = _rep_vars.iter();
fprintf(fp,"($%s)", rep_var);
} else {
// A section of code
fprintf(fp,"%s", code);
}
}
}
//------------------------------Opcode-----------------------------------------
Opcode::Opcode(char *primary, char *secondary, char *tertiary)
: _primary(primary), _secondary(secondary), _tertiary(tertiary) {
}
Opcode::~Opcode() {
}
Opcode::opcode_type Opcode::as_opcode_type(const char *param) {
if( strcmp(param,"primary") == 0 ) {
return Opcode::PRIMARY;
}
else if( strcmp(param,"secondary") == 0 ) {
return Opcode::SECONDARY;
}
else if( strcmp(param,"tertiary") == 0 ) {
return Opcode::TERTIARY;
}
return Opcode::NOT_AN_OPCODE;
}
bool Opcode::print_opcode(FILE *fp, Opcode::opcode_type desired_opcode) {
// Default values previously provided by MachNode::primary()...
const char *description = NULL;
const char *value = NULL;
// Check if user provided any opcode definitions
// Update 'value' if user provided a definition in the instruction
switch (desired_opcode) {
case PRIMARY:
description = "primary()";
if( _primary != NULL) { value = _primary; }
break;
case SECONDARY:
description = "secondary()";
if( _secondary != NULL ) { value = _secondary; }
break;
case TERTIARY:
description = "tertiary()";
if( _tertiary != NULL ) { value = _tertiary; }
break;
default:
assert( false, "ShouldNotReachHere();");
break;
}
if (value != NULL) {
fprintf(fp, "(%s /*%s*/)", value, description);
}
return value != NULL;
}
void Opcode::dump() {
output(stderr);
}
// Write info to output files
void Opcode::output(FILE *fp) {
if (_primary != NULL) fprintf(fp,"Primary opcode: %s\n", _primary);
if (_secondary != NULL) fprintf(fp,"Secondary opcode: %s\n", _secondary);
if (_tertiary != NULL) fprintf(fp,"Tertiary opcode: %s\n", _tertiary);
}
//------------------------------InsEncode--------------------------------------
InsEncode::InsEncode() {
}
InsEncode::~InsEncode() {
}
// Add "encode class name" and its parameters
NameAndList *InsEncode::add_encode(char *encoding) {
assert( encoding != NULL, "Must provide name for encoding");
// add_parameter(NameList::_signal);
NameAndList *encode = new NameAndList(encoding);
_encoding.addName((char*)encode);
return encode;
}
// Access the list of encodings
void InsEncode::reset() {
_encoding.reset();
// _parameter.reset();
}
const char* InsEncode::encode_class_iter() {
NameAndList *encode_class = (NameAndList*)_encoding.iter();
return ( encode_class != NULL ? encode_class->name() : NULL );
}
// Obtain parameter name from zero based index
const char *InsEncode::rep_var_name(InstructForm &inst, uint param_no) {
NameAndList *params = (NameAndList*)_encoding.current();
assert( params != NULL, "Internal Error");
const char *param = (*params)[param_no];
// Remove '$' if parser placed it there.
return ( param != NULL && *param == '$') ? (param+1) : param;
}
void InsEncode::dump() {
output(stderr);
}
// Write info to output files
void InsEncode::output(FILE *fp) {
NameAndList *encoding = NULL;
const char *parameter = NULL;
fprintf(fp,"InsEncode: ");
_encoding.reset();
while ( (encoding = (NameAndList*)_encoding.iter()) != 0 ) {
// Output the encoding being used
fprintf(fp,"%s(", encoding->name() );
// Output its parameter list, if any
bool first_param = true;
encoding->reset();
while ( (parameter = encoding->iter()) != 0 ) {
// Output the ',' between parameters
if ( ! first_param ) fprintf(fp,", ");
first_param = false;
// Output the parameter
fprintf(fp,"%s", parameter);
} // done with parameters
fprintf(fp,") ");
} // done with encodings
fprintf(fp,"\n");
}
//------------------------------Effect-----------------------------------------
static int effect_lookup(const char *name) {
if (!strcmp(name, "USE")) return Component::USE;
if (!strcmp(name, "DEF")) return Component::DEF;
if (!strcmp(name, "USE_DEF")) return Component::USE_DEF;
if (!strcmp(name, "KILL")) return Component::KILL;
if (!strcmp(name, "USE_KILL")) return Component::USE_KILL;
if (!strcmp(name, "TEMP")) return Component::TEMP;
if (!strcmp(name, "TEMP_DEF")) return Component::TEMP_DEF;
if (!strcmp(name, "INVALID")) return Component::INVALID;
if (!strcmp(name, "CALL")) return Component::CALL;
assert(false,"Invalid effect name specified\n");
return Component::INVALID;
}
const char *Component::getUsedefName() {
switch (_usedef) {
case Component::INVALID: return "INVALID"; break;
case Component::USE: return "USE"; break;
case Component::USE_DEF: return "USE_DEF"; break;
case Component::USE_KILL: return "USE_KILL"; break;
case Component::KILL: return "KILL"; break;
case Component::TEMP: return "TEMP"; break;
case Component::TEMP_DEF: return "TEMP_DEF"; break;
case Component::DEF: return "DEF"; break;
case Component::CALL: return "CALL"; break;
default: assert(false, "unknown effect");
}
return "Undefined Use/Def info";
}
Effect::Effect(const char *name) : _name(name), _use_def(effect_lookup(name)) {
_ftype = Form::EFF;
}
Effect::~Effect() {
}
// Dynamic type check
Effect *Effect::is_effect() const {
return (Effect*)this;
}
// True if this component is equal to the parameter.
bool Effect::is(int use_def_kill_enum) const {
return (_use_def == use_def_kill_enum ? true : false);
}
// True if this component is used/def'd/kill'd as the parameter suggests.
bool Effect::isa(int use_def_kill_enum) const {
return (_use_def & use_def_kill_enum) == use_def_kill_enum;
}
void Effect::dump() {
output(stderr);
}
void Effect::output(FILE *fp) { // Write info to output files
fprintf(fp,"Effect: %s\n", (_name?_name:""));
}
//------------------------------ExpandRule-------------------------------------
ExpandRule::ExpandRule() : _expand_instrs(),
_newopconst(cmpstr, hashstr, Form::arena) {
_ftype = Form::EXP;
}
ExpandRule::~ExpandRule() { // Destructor
}
void ExpandRule::add_instruction(NameAndList *instruction_name_and_operand_list) {
_expand_instrs.addName((char*)instruction_name_and_operand_list);
}
void ExpandRule::reset_instructions() {
_expand_instrs.reset();
}
NameAndList* ExpandRule::iter_instructions() {
return (NameAndList*)_expand_instrs.iter();
}
void ExpandRule::dump() {
output(stderr);
}
void ExpandRule::output(FILE *fp) { // Write info to output files
NameAndList *expand_instr = NULL;
const char *opid = NULL;
fprintf(fp,"\nExpand Rule:\n");
// Iterate over the instructions 'node' expands into
for(reset_instructions(); (expand_instr = iter_instructions()) != NULL; ) {
fprintf(fp,"%s(", expand_instr->name());
// iterate over the operand list
for( expand_instr->reset(); (opid = expand_instr->iter()) != NULL; ) {
fprintf(fp,"%s ", opid);
}
fprintf(fp,");\n");
}
}
//------------------------------RewriteRule------------------------------------
RewriteRule::RewriteRule(char* params, char* block)
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.356 Sekunden
(vorverarbeitet)
¤
|
Haftungshinweis
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.
|