/*******************************************************************************
 * 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 ESTIMATECS_INCL
#define ESTIMATECS_INCL

#include <stddef.h>
#include <stdint.h>
#include "env/TRMemory.hpp"
#include "infra/Flags.hpp"
#include "optimizer/Inliner.hpp"

class TR_CallStack;
namespace TR { class Compilation; }
struct TR_CallSite;
struct TR_CallTarget;

#define MAX_ECS_RECURSION_DEPTH 30

enum EcsCleanupErrorStates {
   ECS_NORMAL = 0,
   ECS_RECURSION_DEPTH_THRESHOLD_EXCEEDED,
   ECS_OPTIMISTIC_SIZE_THRESHOLD_EXCEEDED,
   ECS_VISITED_COUNT_THRESHOLD_EXCEEDED,
   ECS_REAL_SIZE_THRESHOLD_EXCEEDED,
   ECS_ARGUMENTS_INCOMPATIBLE,
   ECS_CALLSITES_CREATION_FAILED
};

class TR_EstimateCodeSize
   {
   public:

   void * operator new (size_t size, TR::Allocator allocator) { return allocator.allocate(size); }
   void operator delete (void *, TR::Allocator allocator) {}

   //    {
   //    TR_EstimateCodeSize::raiiWrapper lexicalScopeObject(....);
   //    TR_EstimateCodeSize *ecs = lexicalScopeObject->getCodeEstimator();
   //    ... do whatever you gotta do
   //    } // estimator will be automatically freed by lexicalScopeObject regardless of how scope is exited
   class raiiWrapper
      {
      public:
      raiiWrapper(TR_InlinerBase *inliner, TR_InlinerTracer *tracer, int32_t sizeThreshold)
         {
         _estimator = TR_EstimateCodeSize::get(inliner, tracer, sizeThreshold);
         }

      ~raiiWrapper()
         {
         TR_EstimateCodeSize::release(_estimator);
         }

      TR_EstimateCodeSize *getCodeEstimator()
         {
         return _estimator;
         }

      private:
      TR_EstimateCodeSize *_estimator;
      };

   TR_EstimateCodeSize() { }

   static TR_EstimateCodeSize *get(TR_InlinerBase *inliner, TR_InlinerTracer *tracer, int32_t sizeThreshold);
   static void release(TR_EstimateCodeSize *estimator);

   bool calculateCodeSize(TR_CallTarget *, TR_CallStack *, bool recurseDown = true);

   int32_t getSize()                   { return _realSize; }
   virtual int32_t getOptimisticSize() { return 0; } // override in subclasses that support partial inlining
   const char *getError();
   int32_t getSizeThreshold()          { return _sizeThreshold; }
   bool aggressivelyInlineThrows()     { return _aggressivelyInlineThrows; }
   bool recursedTooDeep()              { return _recursedTooDeep; }
   bool isLeaf()                       { return _isLeaf; }

   int32_t getNumOfEstimatedCalls()    { return _numOfEstimatedCalls; }
   /*
    * \brief
    *    tell whether this callsite has inlineable target
    */
   bool isInlineable(TR_CallStack *, TR_CallSite *callsite);

   TR::Compilation *comp()              { return _inliner->comp(); }
   TR_InlinerTracer *tracer()          { return _tracer; }
   TR_InlinerBase* getInliner()        { return _inliner; }

   protected:

   virtual bool estimateCodeSize(TR_CallTarget *, TR_CallStack * , bool recurseDown = true, int32_t callerAnalyzedSizeThreshold = 0) = 0;


   /*
    *  \brief common tasks requiring completion before returning from estimation
    *
    *  \param errorState
    *       an unique state used to identify where estimate code size bailed out
    */
   bool returnCleanup(EcsCleanupErrorStates errorState);

   /* Fields */

   bool _isLeaf;
   bool _foundThrow;
   bool _hasExceptionHandlers;
   bool _mayHaveVirtualCallProfileInfo;
   bool _aggressivelyInlineThrows;

   int32_t _recursionDepth;
   bool _recursedTooDeep;

   int32_t _sizeThreshold;
   int32_t _realSize;        // size once we know if we're doing a partial inline or not
   EcsCleanupErrorStates _error;

   int32_t _totalBCSize;     // Pure accumulation of the bytecode size. Used by HW-based inlining.

   TR_InlinerBase * _inliner;
   TR_InlinerTracer *_tracer;

   int32_t _numOfEstimatedCalls;
   bool _hasNonColdCalls;
   };

#endif
