/*******************************************************************************
 * Copyright IBM Corp. and others 2000
 *
 * This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License 2.0 which accompanies this
 * distribution and is available at https://www.eclipse.org/legal/epl-2.0/
 * or the Apache License, Version 2.0 which accompanies this distribution
 * and is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License, v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception [1] and GNU General Public
 * License, version 2 with the OpenJDK Assembly Exception [2].
 *
 * [1] https://www.gnu.org/software/classpath/license.html
 * [2] https://openjdk.org/legal/assembly-exception.html
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0
 *******************************************************************************/

#ifndef OMR_OPTIMIZATION_INCL
#define OMR_OPTIMIZATION_INCL

/*
 * The following #define and typedef must appear before any #includes in this file
 */
#ifndef OMR_OPTIMIZATION_CONNECTOR
#define OMR_OPTIMIZATION_CONNECTOR

namespace OMR {
class Optimization;
typedef OMR::Optimization OptimizationConnector;
} // namespace OMR
#endif

#include <stddef.h>
#include <stdint.h>
#include "compile/Compilation.hpp"
#include "env/TRMemory.hpp"
#include "il/ILOpCodes.hpp"
#include "infra/Assert.hpp"
#include "infra/Random.hpp"
#include "optimizer/OptimizationManager.hpp"
#include "optimizer/OptimizationManager_inlines.hpp"
#include "optimizer/Optimizations.hpp"
#include "optimizer/Optimizer.hpp"
class TR_Debug;
class TR_FrontEnd;

namespace TR {
class SymbolReferenceTable;
class Block;
class CodeGenerator;
class Node;
class Optimization;
} // namespace TR

namespace OMR {

class OMR_EXTENSIBLE Optimization : public TR_HasRandomGenerator {
public:
    static void *operator new(size_t size, TR::Allocator a) { return a.allocate(size); }

    static void operator delete(void *ptr, TR::Allocator a)
    {
        // If there is an exception thrown during construction, the compilation
        // will be aborted, and all memory associated with that compilation will get freed.
    }

    static void operator delete(void *ptr, size_t size)
    {
        ((Optimization *)ptr)->allocator().deallocate(ptr, size);
    } /* t->allocator() better return the same allocator as used for new */

    /* Virtual destructor is necessary for the above delete operator to work
     * See "Modern C++ Design" section 4.7
     */
    virtual ~Optimization() {}

    inline TR::Optimization *self();

    Optimization(TR::OptimizationManager *manager)
        : TR_HasRandomGenerator(manager->comp())
        , _manager(manager)
    {}

    virtual bool shouldPerform()
    {
        // Opts that need to do additional checks should override this method.
        return true;
    }

    virtual int32_t perform() = 0;

    // called once before perform is executed
    virtual void prePerform();

    // called once after perform is executed
    virtual void postPerform();

    virtual int32_t performOnBlock(TR::Block *block)
    {
        TR_ASSERT(0, "performOnBlock should be implemented if this opt is being enabled block by block\n");
        return 0;
    }

    // called once before performOnBlock is executed for all relevant blocks
    virtual void prePerformOnBlocks() {}

    // called once after performOnBlock is executed for all relevant blocks
    virtual void postPerformOnBlocks() {}

    TR::OptimizationManager *manager() { return _manager; }

    TR::Compilation *comp() { return _manager->comp(); }

    TR::Optimizer *optimizer() { return _manager->optimizer(); }

    TR::CodeGenerator *cg();
    TR_FrontEnd *fe();
    TR_Debug *getDebug();
    TR::SymbolReferenceTable *getSymRefTab();

    TR_Memory *trMemory();
    TR_StackMemory trStackMemory();
    TR_HeapMemory trHeapMemory();
    TR_PersistentMemory *trPersistentMemory();

    TR::Allocator allocator();

    OMR::Optimizations id();
    const char *name();
    virtual const char *optDetailString() const throw() = 0;

    inline bool trace();
    void setTrace(bool trace = true);
    /**
     * @brief Checks if opt has any trace options specified
     *
     * @return True if any trace option is specified, false otherwise
     */
    bool traceAny();

    bool getLastRun();

    void requestOpt(OMR::Optimizations optNum, bool value = true, TR::Block *block = NULL);

    // useful utility functions for opts
    void requestDeadTreesCleanup(bool value = true, TR::Block *block = NULL);

    void prepareToReplaceNode(TR::Node *node);
    void prepareToReplaceNode(TR::Node *node, TR::ILOpCodes opcode);

    // Code refactored from SimplifierCommon. In all methods using anchorTree,
    // children needing to be anchored will be anchored before the specified
    // treetop (via insertBefore)
    //
    // FIXME: The majority of these functions don't belong here, and should be moved.
    bool nodeIsOrderDependent(TR::Node *node, uint32_t depth, bool hasCommonedAncestor);
    void anchorChildren(TR::Node *node, TR::TreeTop *anchorTree, uint32_t depth = 0, bool hasCommonedAncestor = false,
        TR::Node *replacement = 0);
    void anchorAllChildren(TR::Node *node, TR::TreeTop *anchorTree);
    void generateAnchor(TR::Node *node, TR::TreeTop *anchorTree);
    void anchorNode(TR::Node *node, TR::TreeTop *anchorTree);

    // lower-level methods for folding branches
    // promoted to TR_Optimization, so the other opts can call this method
    bool removeOrconvertIfToGoto(TR::Node *&node, TR::Block *block, int takeBranch, TR::TreeTop *curTree,
        TR::TreeTop *&reachableTarget, TR::TreeTop *&unreachableTarget, const char *opt_details);
    TR::CFGEdge *changeConditionalToUnconditional(TR::Node *&node, TR::Block *block, int takeBranch,
        TR::TreeTop *curTree, const char *opt_details = "UNKNOWN OPT");

    void prepareToStopUsingNode(TR::Node *node, TR::TreeTop *anchorTree, bool anchorChildren = true);
    TR::Node *replaceNodeWithChild(TR::Node *node, TR::Node *child, TR::TreeTop *anchorTree, TR::Block *block,
        bool correctBCDPrecision = true);
    TR::Node *replaceNode(TR::Node *node, TR::Node *other, TR::TreeTop *anchorTree, bool anchorChildren = true);
    void removeNode(TR::Node *node, TR::TreeTop *anchorTree);

protected:
    TR::OptimizationManager *_manager;
};

} // namespace OMR

#endif
