| /* | |
| * Copyright (c) 1999, 2025, 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. | |
| * | |
| */ | |
| #ifndef SHARE_C1_C1_VALUEMAP_HPP | |
| #define SHARE_C1_C1_VALUEMAP_HPP | |
| #include "c1/c1_Instruction.hpp" | |
| #include "c1/c1_ValueSet.hpp" | |
| #include "memory/allocation.hpp" | |
| class ValueMapEntry: public CompilationResourceObj { | |
| private: | |
| intx _hash; | |
| Value _value; | |
| int _nesting; | |
| ValueMapEntry* _next; | |
| public: | |
| ValueMapEntry(intx hash, Value value, int nesting, ValueMapEntry* next) | |
| : _hash(hash) | |
| , _value(value) | |
| , _nesting(nesting) | |
| , _next(next) | |
| { | |
| } | |
| intx hash() { return _hash; } | |
| Value value() { return _value; } | |
| int nesting() { return _nesting; } | |
| ValueMapEntry* next() { return _next; } | |
| void set_next(ValueMapEntry* next) { _next = next; } | |
| }; | |
| typedef GrowableArray<ValueMapEntry*> ValueMapEntryArray; | |
| typedef GrowableArray<ValueMapEntry*> ValueMapEntryList; | |
| // ValueMap implements nested hash tables for value numbering. It | |
| // maintains a set _killed_values which represents the instructions | |
| // which have been killed so far and an array of linked lists of | |
| // ValueMapEntries names _entries. Each ValueMapEntry has a nesting | |
| // which indicates what ValueMap nesting it belongs to. Higher | |
| // nesting values are always before lower values in the linked list. | |
| // This allows cloning of parent ValueMaps by simply copying the heads | |
| // of the list. _entry_count represents the number of reachable | |
| // entries in the ValueMap. A ValueMap is only allowed to mutate | |
| // ValueMapEntries with the same nesting level. Adding or removing | |
| // entries at the current nesting level requires updating | |
| // _entry_count. Elements in the parent's list that get killed can be | |
| // skipped if they are at the head of the list by simply moving to the | |
| // next element in the list and decrementing _entry_count. | |
| class ValueMap: public CompilationResourceObj { | |
| private: | |
| int _nesting; | |
| ValueMapEntryArray _entries; | |
| ValueSet _killed_values; | |
| int _entry_count; | |
| int nesting() { return _nesting; } | |
| bool is_local_value_numbering() { return _nesting == 0; } | |
| bool is_global_value_numbering() { return _nesting > 0; } | |
| int entry_count() { return _entry_count; } | |
| int size() { return _entries.length(); } | |
| ValueMapEntry* entry_at(int i) { return _entries.at(i); } | |
| // calculates the index of a hash value in a hash table of size n | |
| int entry_index(intx hash, int n) { return (unsigned int)hash % n; } | |
| // if entry_count > size_threshold, the size of the hash table is increased | |
| int size_threshold() { return size(); } | |
| // management of the killed-bitset for global value numbering | |
| void kill_value(Value v) { if (is_global_value_numbering()) _killed_values.put(v); } | |
| bool is_killed(Value v) { if (is_global_value_numbering()) return _killed_values.contains(v); else return false; } | |
| // helper functions | |
| void increase_table_size(); | |
| #ifndef PRODUCT | |
| static int _number_of_finds; | |
| static int _number_of_hits; | |
| static int _number_of_kills; | |
| #endif // PRODUCT | |
| public: | |
| // creation | |
| ValueMap(); // empty value map | |
| ValueMap(ValueMap* old); // value map with increased nesting | |
| // manipulation | |
| Value find_insert(Value x); | |
| void kill_memory(); | |
| void kill_field(ciField* field, bool all_offsets); | |
| void kill_array(ValueType* type); | |
| void kill_map(ValueMap* map); | |
| void kill_all(); | |
| #ifndef PRODUCT | |
| // debugging/printing | |
| void print(); | |
| static void reset_statistics(); | |
| static void print_statistics(); | |
| #endif | |
| }; | |
| typedef GrowableArray<ValueMap*> ValueMapArray; | |
| class ValueNumberingVisitor: public InstructionVisitor { | |
| protected: | |
| // called by visitor functions for instructions that kill values | |
| virtual void kill_memory() = 0; | |
| virtual void kill_field(ciField* field, bool all_offsets) = 0; | |
| virtual void kill_array(ValueType* type) = 0; | |
| // visitor functions | |
| void do_StoreField (StoreField* x) { | |
| if (x->is_init_point() || // putstatic is an initialization point so treat it as a wide kill | |
| // This is actually too strict and the JMM doesn't require | |
| // this in all cases (e.g. load a; volatile store b; load a) | |
| // but possible future optimizations might require this. | |
| x->field()->is_volatile()) { | |
| kill_memory(); | |
| } else { | |
| kill_field(x->field(), x->needs_patching()); | |
| } | |
| } | |
| void do_StoreIndexed (StoreIndexed* x) { kill_array(x->type()); } | |
| void do_MonitorEnter (MonitorEnter* x) { kill_memory(); } | |
| void do_MonitorExit (MonitorExit* x) { kill_memory(); } | |
| void do_Invoke (Invoke* x) { kill_memory(); } | |
| void do_UnsafePut (UnsafePut* x) { kill_memory(); } | |
| void do_UnsafeGetAndSet(UnsafeGetAndSet* x) { kill_memory(); } | |
| void do_UnsafeGet (UnsafeGet* x) { | |
| if (x->is_volatile()) { // the JMM requires this | |
| kill_memory(); | |
| } | |
| } | |
| void do_Intrinsic (Intrinsic* x) { if (!x->preserves_state()) kill_memory(); } | |
| void do_Phi (Phi* x) { /* nothing to do */ } | |
| void do_Local (Local* x) { /* nothing to do */ } | |
| void do_Constant (Constant* x) { | |
| if (x->kills_memory()) { | |
| assert(x->can_trap(), "already linked"); | |
| kill_memory(); | |
| } | |
| } | |
| void do_LoadField (LoadField* x) { | |
| if (x->is_init_point() || // getstatic is an initialization point so treat it as a wide kill | |
| x->field()->is_volatile()) { // the JMM requires this | |
| kill_memory(); | |
| } | |
| } | |
| void do_ArrayLength (ArrayLength* x) { /* nothing to do */ } | |
| void do_LoadIndexed (LoadIndexed* x) { /* nothing to do */ } | |
| void do_NegateOp (NegateOp* x) { /* nothing to do */ } | |
| void do_ArithmeticOp (ArithmeticOp* x) { /* nothing to do */ } | |
| void do_ShiftOp (ShiftOp* x) { /* nothing to do */ } | |
| void do_LogicOp (LogicOp* x) { /* nothing to do */ } | |
| void do_CompareOp (CompareOp* x) { /* nothing to do */ } | |
| void do_IfOp (IfOp* x) { /* nothing to do */ } | |
| void do_Convert (Convert* x) { /* nothing to do */ } | |
| void do_NullCheck (NullCheck* x) { /* nothing to do */ } | |
| void do_TypeCast (TypeCast* x) { /* nothing to do */ } | |
| void do_NewInstance (NewInstance* x) { | |
| ciInstanceKlass* c = x->klass(); | |
| if (c != nullptr && !c->is_initialized() && | |
| (!c->is_loaded() || c->has_class_initializer())) { | |
| kill_memory(); | |
| } | |
| } | |
| void do_NewTypeArray (NewTypeArray* x) { /* nothing to do */ } | |
| void do_NewObjectArray (NewObjectArray* x) { /* nothing to do */ } | |
| void do_NewMultiArray (NewMultiArray* x) { /* nothing to do */ } | |
| void do_CheckCast (CheckCast* x) { /* nothing to do */ } | |
| void do_InstanceOf (InstanceOf* x) { /* nothing to do */ } | |
| void do_BlockBegin (BlockBegin* x) { /* nothing to do */ } | |
| void do_Goto (Goto* x) { /* nothing to do */ } | |
| void do_If (If* x) { /* nothing to do */ } | |
| void do_TableSwitch (TableSwitch* x) { /* nothing to do */ } | |
| void do_LookupSwitch (LookupSwitch* x) { /* nothing to do */ } | |
| void do_Return (Return* x) { /* nothing to do */ } | |
| void do_Throw (Throw* x) { /* nothing to do */ } | |
| void do_Base (Base* x) { /* nothing to do */ } | |
| void do_OsrEntry (OsrEntry* x) { /* nothing to do */ } | |
| void do_ExceptionObject(ExceptionObject* x) { /* nothing to do */ } | |
| void do_ProfileCall (ProfileCall* x) { /* nothing to do */ } | |
| void do_ProfileReturnType (ProfileReturnType* x) { /* nothing to do */ } | |
| void do_ProfileInvoke (ProfileInvoke* x) { /* nothing to do */ }; | |
| void do_RuntimeCall (RuntimeCall* x) { /* nothing to do */ }; | |
| void do_MemBar (MemBar* x) { /* nothing to do */ }; | |
| void do_RangeCheckPredicate(RangeCheckPredicate* x) { /* nothing to do */ }; | |
| #ifdef ASSERT | |
| void do_Assert (Assert* x) { /* nothing to do */ }; | |
| #endif | |
| }; | |
| class ValueNumberingEffects: public ValueNumberingVisitor { | |
| private: | |
| ValueMap* _map; | |
| public: | |
| // implementation for abstract methods of ValueNumberingVisitor | |
| void kill_memory() { _map->kill_memory(); } | |
| void kill_field(ciField* field, bool all_offsets) { _map->kill_field(field, all_offsets); } | |
| void kill_array(ValueType* type) { _map->kill_array(type); } | |
| ValueNumberingEffects(ValueMap* map): _map(map) {} | |
| }; | |
| class GlobalValueNumbering: public ValueNumberingVisitor { | |
| private: | |
| Compilation* _compilation; // compilation data | |
| ValueMap* _current_map; // value map of current block | |
| ValueMapArray _value_maps; // list of value maps for all blocks | |
| ValueSet _processed_values; // marker for instructions that were already processed | |
| bool _has_substitutions; // set to true when substitutions must be resolved | |
| public: | |
| // accessors | |
| Compilation* compilation() const { return _compilation; } | |
| ValueMap* current_map() { return _current_map; } | |
| ValueMap* value_map_of(BlockBegin* block) { return _value_maps.at(block->linear_scan_number()); } | |
| void set_value_map_of(BlockBegin* block, ValueMap* map) { assert(value_map_of(block) == nullptr, ""); _value_maps.at_put(block->linear_scan_number(), map); } | |
| bool is_processed(Value v) { return _processed_values.contains(v); } | |
| void set_processed(Value v) { _processed_values.put(v); } | |
| // implementation for abstract methods of ValueNumberingVisitor | |
| void kill_memory() { current_map()->kill_memory(); } | |
| void kill_field(ciField* field, bool all_offsets) { current_map()->kill_field(field, all_offsets); } | |
| void kill_array(ValueType* type) { current_map()->kill_array(type); } | |
| // main entry point that performs global value numbering | |
| GlobalValueNumbering(IR* ir); | |
| void substitute(Instruction* instr); // substitute instruction if it is contained in current value map | |
| }; | |
| #endif // SHARE_C1_C1_VALUEMAP_HPP |