| /* | |
| * Copyright (c) 1998, 2024, 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_ADLC_FORMSOPT_HPP | |
| #define SHARE_ADLC_FORMSOPT_HPP | |
| // FORMSOPT.HPP - ADL Parser Target Specific Optimization Forms Classes | |
| // Class List | |
| class Form; | |
| class InstructForm; | |
| class OperandForm; | |
| class OpClassForm; | |
| class AttributeForm; | |
| class RegisterForm; | |
| class PipelineForm; | |
| class SourceForm; | |
| class EncodeForm; | |
| class Component; | |
| class Constraint; | |
| class Predicate; | |
| class MatchRule; | |
| class Attribute; | |
| class Effect; | |
| class ExpandRule; | |
| class Flag; | |
| class RewriteRule; | |
| class ConstructRule; | |
| class FormatRule; | |
| class EncClass; | |
| class Interface; | |
| class RegInterface; | |
| class ConstInterface; | |
| class MemInterface; | |
| class CondInterface; | |
| class Opcode; | |
| class InsEncode; | |
| class RegDef; | |
| class RegClass; | |
| class CodeSnippetRegClass; | |
| class ConditionalRegClass; | |
| class AllocClass; | |
| class ResourceForm; | |
| class PipeClassForm; | |
| class PipeClassOperandForm; | |
| class PipeClassResourceForm; | |
| class Peephole; | |
| class PeepPredicate; | |
| class PeepMatch; | |
| class PeepProcedure; | |
| class PeepConstraint; | |
| class PeepReplace; | |
| class MatchList; | |
| class ArchDesc; | |
| //==============================Register Allocation============================ | |
| //------------------------------RegisterForm----------------------------------- | |
| class RegisterForm : public Form { | |
| private: | |
| AllocClass *_current_ac; // State used by iter_RegDefs() | |
| public: | |
| // Public Data | |
| NameList _rdefs; // List of register definition names | |
| Dict _regDef; // map register name to RegDef* | |
| NameList _rclasses; // List of register class names | |
| Dict _regClass; // map register class name to RegClass* | |
| NameList _aclasses; // List of allocation class names | |
| Dict _allocClass; // Dictionary of allocation classes | |
| static int _reg_ctr; // Register counter | |
| static int RegMask_Size(); // Compute RegMask size | |
| // Public Methods | |
| RegisterForm(); | |
| ~RegisterForm(); | |
| void addRegDef(char *regName, char *callingConv, char *c_conv, | |
| char * idealtype, char *encoding, char* concreteName); | |
| template<typename T> T* addRegClass(const char* className); | |
| AllocClass *addAllocClass(char *allocName); | |
| void addSpillRegClass(); | |
| void addDynamicRegClass(); | |
| // Provide iteration over all register definitions | |
| // in the order used by the register allocator | |
| void reset_RegDefs(); | |
| RegDef *iter_RegDefs(); | |
| RegDef *getRegDef (const char *regName); | |
| RegClass *getRegClass(const char *className); | |
| // Return register mask, compressed chunk and register # | |
| uint reg_mask(char *register_class); | |
| // Check that register classes are compatible with chunks | |
| bool verify(); | |
| void dump(); // Debug printer | |
| void output(FILE *fp); // Write info to output files | |
| virtual void forms_do(FormClosure* f); | |
| }; | |
| //------------------------------RegDef----------------------------------------- | |
| class RegDef : public Form { | |
| public: | |
| // Public Data | |
| const char *_regname; // ADLC (Opto) Register name | |
| const char *_callconv; // Calling convention | |
| const char *_c_conv; // Native calling convention, 'C' | |
| const char *_idealtype; // Ideal Type for register save/restore | |
| const char *_concrete; // concrete register name | |
| private: | |
| const char *_register_encode; // The register encoding | |
| // The chunk and register mask bits define info for register allocation | |
| uint32 _register_num; // Which register am I | |
| public: | |
| // Public Methods | |
| RegDef(char *regname, char *callconv, char *c_conv, | |
| char *idealtype, char *encoding, char *concrete); | |
| ~RegDef(); // Destructor | |
| // Interface to define/redefine the register number | |
| void set_register_num(uint32 new_register_num); | |
| // Bit pattern used for generating machine code | |
| const char *register_encode() const; | |
| // Register number used in machine-independent code | |
| uint32 register_num() const; | |
| void dump(); // Debug printer | |
| void output(FILE *fp); // Write info to output files | |
| }; | |
| //------------------------------RegClass--------------------------------------- | |
| // Generic register class. This register class is the internal representation | |
| // for the following .ad file format: | |
| // | |
| // reg_class ptr(RAX, RBX, ...); | |
| // | |
| // where ptr is the name of the register class, RAX and RBX are registers. | |
| // | |
| // This register class allows registers to be spilled onto the stack. Spilling | |
| // is allowed is field _stack_or_reg is true. | |
| class RegClass : public Form { | |
| public: | |
| // Public Data | |
| const char *_classid; // Name of class | |
| NameList _regDefs; // List of registers in class | |
| Dict _regDef; // Dictionary of registers in class | |
| protected: | |
| bool _stack_or_reg; // Allowed on any stack slot | |
| public: | |
| // Public Methods | |
| RegClass(const char *classid);// Constructor | |
| virtual ~RegClass(); | |
| void addReg(RegDef *regDef); // Add a register to this class | |
| uint size() const; // Number of registers in class | |
| int regs_in_word( int wordnum, bool stack_also ); | |
| const RegDef *get_RegDef(const char *regDef_name) const; | |
| // Returns the lowest numbered register in the mask. | |
| const RegDef* find_first_elem(); | |
| // Iteration support | |
| void reset(); // Reset the following two iterators | |
| RegDef *RegDef_iter(); // which move jointly, | |
| const char *rd_name_iter(); // invoking either advances both. | |
| void dump(); // Debug printer | |
| void output(FILE *fp); // Write info to output files | |
| virtual void forms_do(FormClosure* f); | |
| virtual bool has_stack_version() { | |
| return _stack_or_reg; | |
| } | |
| virtual void set_stack_version(bool flag) { | |
| _stack_or_reg = flag; | |
| } | |
| virtual void declare_register_masks(FILE* fp); | |
| virtual void build_register_masks(FILE* fp); | |
| }; | |
| //------------------------------CodeSnippetRegClass---------------------------- | |
| // Register class that has an user-defined C++ code snippet attached to it | |
| // to determine at runtime which register class to use. This register class is | |
| // the internal representation for the following .ad file format: | |
| // | |
| // reg_class actual_dflt_reg %{ | |
| // if (VM_Version::has_vfp3_32()) { | |
| // return DFLT_REG_mask(); | |
| // } else { | |
| // return DFLT_LOW_REG_mask(); | |
| // } | |
| // %} | |
| // | |
| // where DFLT_REG_mask() and DFLT_LOW_REG_mask() are the internal names of the | |
| // masks of register classes dflt_reg and dflt_low_reg. | |
| // | |
| // The attached code snippet can select also between more than two register classes. | |
| // This register class can be, however, used only if the register class is not | |
| // cisc-spillable (i.e., the registers of this class are not allowed on the stack, | |
| // which is equivalent with _stack_or_reg being false). | |
| class CodeSnippetRegClass : public RegClass { | |
| protected: | |
| char* _code_snippet; | |
| public: | |
| CodeSnippetRegClass(const char* classid);// Constructor | |
| ~CodeSnippetRegClass(); | |
| void set_code_snippet(char* code) { | |
| _code_snippet = code; | |
| } | |
| char* code_snippet() { | |
| return _code_snippet; | |
| } | |
| void declare_register_masks(FILE* fp); | |
| void build_register_masks(FILE* fp) { | |
| // We do not need to generate register masks because we select at runtime | |
| // between register masks generated for other register classes. | |
| return; | |
| } | |
| }; | |
| //------------------------------ConditionalRegClass---------------------------- | |
| // Register class that has two register classes and a runtime condition attached | |
| // to it. The condition is evaluated at runtime and either one of the register | |
| // attached register classes is selected. This register class is the internal | |
| // representation for the following .ad format: | |
| // | |
| // reg_class_dynamic actual_dflt_reg(dflt_reg, low_reg, | |
| // %{ VM_Version::has_vfp3_32() }% | |
| // ); | |
| // | |
| // This example is equivalent to the example used with the CodeSnippetRegClass | |
| // register class. A ConditionalRegClass works also if a register class is cisc-spillable | |
| // (i.e., _stack_or_reg is true), but if can select only between two register classes. | |
| class ConditionalRegClass : public RegClass { | |
| protected: | |
| // reference to condition code | |
| char* _condition_code; // C++ condition code to dynamically determine which register class to use. | |
| // Example syntax (equivalent to previous example): | |
| // | |
| // reg_class actual_dflt_reg(dflt_reg, low_reg, | |
| // %{ VM_Version::has_vfp3_32() }% | |
| // ); | |
| // reference to conditional register classes | |
| RegClass* _rclasses[2]; // 0 is the register class selected if the condition code returns true | |
| // 1 is the register class selected if the condition code returns false | |
| public: | |
| ConditionalRegClass(const char* classid);// Constructor | |
| ~ConditionalRegClass(); | |
| virtual void set_stack_version(bool flag) { | |
| RegClass::set_stack_version(flag); | |
| assert((_rclasses[0] != nullptr), "Register class null for condition code == true"); | |
| assert((_rclasses[1] != nullptr), "Register class null for condition code == false"); | |
| _rclasses[0]->set_stack_version(flag); | |
| _rclasses[1]->set_stack_version(flag); | |
| } | |
| void declare_register_masks(FILE* fp); | |
| void build_register_masks(FILE* fp) { | |
| // We do not need to generate register masks because we select at runtime | |
| // between register masks generated for other register classes. | |
| return; | |
| } | |
| void set_rclass_at_index(int index, RegClass* rclass) { | |
| assert((0 <= index && index < 2), "Condition code can select only between two register classes"); | |
| _rclasses[index] = rclass; | |
| } | |
| void set_condition_code(char* code) { | |
| _condition_code = code; | |
| } | |
| char* condition_code() { | |
| return _condition_code; | |
| } | |
| virtual void forms_do(FormClosure* f) { | |
| if (_rclasses[0]) f->do_form(_rclasses[0]); | |
| if (_rclasses[1]) f->do_form(_rclasses[1]); | |
| } | |
| }; | |
| //------------------------------AllocClass------------------------------------- | |
| class AllocClass : public Form { | |
| private: | |
| public: | |
| // Public Data | |
| char *_classid; // Name of class | |
| NameList _regDefs; // List of registers in class | |
| Dict _regDef; // Dictionary of registers in class | |
| // Public Methods | |
| AllocClass(char *classid); // Constructor | |
| void addReg(RegDef *regDef); // Add a register to this class | |
| uint size() {return _regDef.Size();} // Number of registers in class | |
| void dump(); // Debug printer | |
| void output(FILE *fp); // Write info to output files | |
| virtual void forms_do(FormClosure* f); | |
| }; | |
| //==============================Frame Handling================================ | |
| //------------------------------FrameForm------------------------------------- | |
| class FrameForm : public Form { | |
| private: | |
| public: | |
| // Public Data | |
| char *_sync_stack_slots; | |
| char *_inline_cache_reg; | |
| char *_interpreter_frame_pointer_reg; | |
| char *_cisc_spilling_operand_name; | |
| char *_frame_pointer; | |
| char *_c_frame_pointer; | |
| char *_alignment; | |
| bool _return_addr_loc; | |
| bool _c_return_addr_loc; | |
| char *_return_addr; | |
| char *_c_return_addr; | |
| char *_varargs_C_out_slots_killed; | |
| char *_return_value; | |
| char *_c_return_value; | |
| // Public Methods | |
| FrameForm(); | |
| ~FrameForm(); | |
| void dump(); // Debug printer | |
| void output(FILE *fp); // Write info to output files | |
| }; | |
| //==============================Scheduling===================================== | |
| //------------------------------PipelineForm----------------------------------- | |
| class PipelineForm : public Form { | |
| private: | |
| public: | |
| // Public Data | |
| NameList _reslist; // List of pipeline resources | |
| FormDict _resdict; // Resource Name -> ResourceForm mapping | |
| int _rescount; // Number of resources (ignores OR cases) | |
| int _maxcycleused; // Largest cycle used relative to beginning of instruction | |
| NameList _stages; // List of pipeline stages on architecture | |
| int _stagecnt; // Number of stages listed | |
| NameList _classlist; // List of pipeline classes | |
| FormDict _classdict; // Class Name -> PipeClassForm mapping | |
| int _classcnt; // Number of classes | |
| NameList _noplist; // List of NOP instructions | |
| int _nopcnt; // Number of nop instructions | |
| bool _variableSizeInstrs; // Indicates if this architecture has variable sized instructions | |
| bool _branchHasDelaySlot; // Indicates that branches have delay slot instructions | |
| int _maxInstrsPerBundle; // Indicates the maximum number of instructions for ILP | |
| int _maxBundlesPerCycle; // Indicates the maximum number of bundles for ILP | |
| int _instrUnitSize; // The minimum instruction unit size, in bytes | |
| int _bundleUnitSize; // The bundle unit size, in bytes | |
| int _instrFetchUnitSize; // The size of the I-fetch unit, in bytes [must be power of 2] | |
| int _instrFetchUnits; // The number of I-fetch units processed per cycle | |
| // Public Methods | |
| PipelineForm(); | |
| ~PipelineForm(); | |
| void dump(); // Debug printer | |
| void output(FILE *fp); // Write info to output files | |
| }; | |
| //------------------------------ResourceForm----------------------------------- | |
| class ResourceForm : public Form { | |
| public: | |
| unsigned mask() const { return _resmask; }; | |
| // A discrete resource is a simple definition of a resource, while compound resources can be composed of multiple resources. | |
| // A discrete resource will always have a power of two mask, so this check succeeds in that case. | |
| // As compound resources have different masks added together, this check will not succeed there. | |
| bool is_discrete() const { | |
| return (_resmask & (_resmask - 1)) == 0; | |
| } | |
| private: | |
| // Public Data | |
| unsigned _resmask; // Resource Mask (OR of resource specifier bits) | |
| public: | |
| // Virtual Methods | |
| virtual ResourceForm *is_resource() const; | |
| // Public Methods | |
| ResourceForm(unsigned resmask); // Constructor | |
| ~ResourceForm(); // Destructor | |
| void dump(); // Debug printer | |
| void output(FILE *fp); // Write info to output files | |
| }; | |
| //------------------------------PipeClassOperandForm----------------------------- | |
| class PipeClassOperandForm : public Form { | |
| private: | |
| public: | |
| // Public Data | |
| const char *_stage; // Name of Stage | |
| unsigned _iswrite; // Read or Write | |
| unsigned _more_instrs; // Additional Instructions | |
| // Public Methods | |
| PipeClassOperandForm(const char *stage, unsigned iswrite, unsigned more_instrs) | |
| : _stage(stage) | |
| , _iswrite(iswrite) | |
| , _more_instrs(more_instrs) | |
| {}; | |
| ~PipeClassOperandForm() {}; // Destructor | |
| bool isWrite() const { return _iswrite != 0; } | |
| void dump(); // Debug printer | |
| void output(FILE *fp); // Write info to output files | |
| }; | |
| //------------------------------PipeClassResourceForm-------------------------- | |
| class PipeClassResourceForm : public Form { | |
| private: | |
| public: | |
| // Public Data | |
| const char *_resource; // Resource | |
| const char *_stage; // Stage the resource is used in | |
| int _cycles; // Number of cycles the resource is used | |
| // Public Methods | |
| PipeClassResourceForm(const char *resource, const char *stage, int cycles) | |
| // Constructor | |
| : _resource(resource) | |
| , _stage(stage) | |
| , _cycles(cycles) | |
| {}; | |
| ~PipeClassResourceForm() {}; // Destructor | |
| void dump(); // Debug printer | |
| void output(FILE *fp); // Write info to output files | |
| }; | |
| //------------------------------PipeClassForm---------------------------------- | |
| class PipeClassForm : public Form { | |
| private: | |
| public: | |
| // Public Data | |
| const char *_ident; // Name of class | |
| int _num; // Used in name of MachNode subclass | |
| NameList _parameters; // Locally defined names | |
| FormDict _localNames; // Table of operands & their types | |
| FormDict _localUsage; // Table of operand usage | |
| FormList _resUsage; // List of resource usage | |
| NameList _instructs; // List of instructions and machine nodes that use this pipeline class | |
| bool _has_fixed_latency; // Always takes this number of cycles | |
| int _fixed_latency; // Always takes this number of cycles | |
| int _instruction_count; // Number of instructions in first bundle | |
| bool _has_multiple_bundles; // Indicates if 1 or multiple bundles | |
| bool _has_branch_delay_slot; // Has branch delay slot as last instruction | |
| bool _force_serialization; // This node serializes relative to surrounding nodes | |
| bool _may_have_no_code; // This node may generate no code based on register allocation | |
| // Virtual Methods | |
| virtual PipeClassForm *is_pipeclass() const; | |
| // Public Methods | |
| PipeClassForm(const char *id, int num); | |
| // Constructor | |
| ~PipeClassForm(); // Destructor | |
| bool hasFixedLatency() { return _has_fixed_latency; } | |
| int fixedLatency() { return _fixed_latency; } | |
| void setFixedLatency(int fixed_latency) { _has_fixed_latency = 1; _fixed_latency = fixed_latency; } | |
| void setInstructionCount(int i) { _instruction_count = i; } | |
| void setMultipleBundles(bool b) { _has_multiple_bundles = b; } | |
| void setBranchDelay(bool s) { _has_branch_delay_slot = s; } | |
| void setForceSerialization(bool s) { _force_serialization = s; } | |
| void setMayHaveNoCode(bool s) { _may_have_no_code = s; } | |
| int InstructionCount() const { return _instruction_count; } | |
| bool hasMultipleBundles() const { return _has_multiple_bundles; } | |
| bool hasBranchDelay() const { return _has_branch_delay_slot; } | |
| bool forceSerialization() const { return _force_serialization; } | |
| bool mayHaveNoCode() const { return _may_have_no_code; } | |
| void dump(); // Debug printer | |
| void output(FILE *fp); // Write info to output files | |
| }; | |
| //==============================Peephole Optimization========================== | |
| //------------------------------Peephole--------------------------------------- | |
| class Peephole : public Form { | |
| private: | |
| static int _peephole_counter;// Incremented by each peephole rule parsed | |
| int _peephole_number;// Remember my order in architecture description | |
| PeepPredicate *_predicate; // Predicate to apply peep rule | |
| PeepMatch *_match; // Instruction pattern to match | |
| PeepProcedure *_procedure; // The detailed procedure to perform the rule | |
| PeepConstraint *_constraint; // List of additional constraints | |
| PeepReplace *_replace; // Instruction pattern to substitute in | |
| Peephole *_next; | |
| public: | |
| // Public Methods | |
| Peephole(); | |
| ~Peephole(); | |
| // Append a peephole rule with the same root instruction | |
| void append_peephole(Peephole *next_peephole); | |
| // Store the components of this peephole rule | |
| void add_predicate(PeepPredicate *only_one_predicate); | |
| void add_match(PeepMatch *only_one_match); | |
| void add_procedure(PeepProcedure *only_one_procedure); | |
| void append_constraint(PeepConstraint *next_constraint); | |
| void add_replace(PeepReplace *only_one_replacement); | |
| // Access the components of this peephole rule | |
| int peephole_number() { return _peephole_number; } | |
| PeepPredicate *predicate() { return _predicate; } | |
| PeepMatch *match() { return _match; } | |
| PeepProcedure *procedure() { return _procedure; } | |
| PeepConstraint *constraints() { return _constraint; } | |
| PeepReplace *replacement() { return _replace; } | |
| Peephole *next() { return _next; } | |
| void dump(); // Debug printer | |
| void output(FILE *fp); // Write info to output files | |
| virtual void forms_do(FormClosure* f); | |
| }; | |
| class PeepPredicate : public Form { | |
| private: | |
| const char* _rule; | |
| public: | |
| // Public Methods | |
| PeepPredicate(const char* rule); | |
| ~PeepPredicate(); | |
| const char* rule() const; | |
| void dump(); | |
| void output(FILE* fp); | |
| }; | |
| class PeepMatch : public Form { | |
| private: | |
| char *_rule; | |
| // NameList _depth; // Depth of this instruction | |
| NameList _parent; | |
| NameList _position; | |
| NameList _instrs; // List of instructions in match rule | |
| NameList _input; // input position in parent's instruction | |
| int _max_position; | |
| public: | |
| // Public Methods | |
| PeepMatch(char *rule); | |
| ~PeepMatch(); | |
| // Insert info into the match-rule | |
| void add_instruction(int parent, int position, const char *name, int input); | |
| // Access info about instructions in the peep-match rule | |
| int max_position(); | |
| const char *instruction_name(int position); | |
| // Iterate through all info on matched instructions | |
| void reset(); | |
| void next_instruction(int &parent, int &position, const char* &name, int &input); | |
| // 'true' if current position in iteration is a placeholder, not matched. | |
| bool is_placeholder(); | |
| void dump(); | |
| void output(FILE *fp); | |
| }; | |
| class PeepProcedure : public Form { | |
| private: | |
| const char* _name; | |
| public: | |
| // Public Methods | |
| PeepProcedure(const char* name); | |
| ~PeepProcedure(); | |
| const char* name() const; | |
| void dump(); | |
| void output(FILE* fp); | |
| }; | |
| class PeepConstraint : public Form { | |
| private: | |
| PeepConstraint *_next; // Additional constraints ANDed together | |
| public: | |
| const int _left_inst; | |
| const char* _left_op; | |
| const char* _relation; | |
| const int _right_inst; | |
| const char* _right_op; | |
| public: | |
| // Public Methods | |
| PeepConstraint(int left_inst, char* left_op, char* relation, | |
| int right_inst, char* right_op); | |
| ~PeepConstraint(); | |
| // Check if constraints use instruction at position | |
| bool constrains_instruction(int position); | |
| // Add another constraint | |
| void append(PeepConstraint *next_peep_constraint); | |
| // Access the next constraint in the list | |
| PeepConstraint *next(); | |
| void dump(); | |
| void output(FILE *fp); | |
| }; | |
| class PeepReplace : public Form { | |
| private: | |
| char *_rule; | |
| NameList _instruction; | |
| NameList _operand_inst_num; | |
| NameList _operand_op_name; | |
| public: | |
| // Public Methods | |
| PeepReplace(char *rule); | |
| ~PeepReplace(); | |
| // Add contents of peepreplace | |
| void add_instruction(char *root); | |
| void add_operand( int inst_num, char *inst_operand ); | |
| // Access contents of peepreplace | |
| void reset(); | |
| void next_instruction(const char * &root); | |
| void next_operand(int &inst_num, const char * &inst_operand ); | |
| // Utilities | |
| void dump(); | |
| void output(FILE *fp); | |
| }; | |
| class PeepChild : public Form { | |
| public: | |
| const int _inst_num; // Number of instruction (-1 if only named) | |
| const char *_inst_op; // Instruction's operand, null if number == -1 | |
| const char *_inst_name; // Name of the instruction | |
| public: | |
| PeepChild(char *inst_name) | |
| : _inst_num(-1), _inst_op(nullptr), _inst_name(inst_name) {}; | |
| PeepChild(int inst_num, char *inst_op, char *inst_name) | |
| : _inst_num(inst_num), _inst_op(inst_op), _inst_name(inst_name) {}; | |
| ~PeepChild(); | |
| bool use_leaf_operand() { return _inst_num != -1; }; | |
| bool generate_an_instruction() { return _inst_num == -1; } | |
| void dump(); | |
| void output(FILE *fp); | |
| }; | |
| #endif // SHARE_ADLC_FORMSOPT_HPP |