/*
 * Copyright (C) 2011 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"
#include "Options.h"

#include <limits>
#include <wtf/NumberOfCores.h>
#include <wtf/PageBlock.h>

#if OS(DARWIN) && ENABLE(PARALLEL_GC)
#include <sys/sysctl.h>
#endif

// Set to 1 to control the heuristics using environment variables.
#define ENABLE_RUN_TIME_HEURISTICS 0

#if ENABLE(RUN_TIME_HEURISTICS)
#include <stdio.h>
#include <stdlib.h>
#include <wtf/StdLibExtras.h>
#endif

namespace JSC { namespace Options {

unsigned maximumOptimizationCandidateInstructionCount;

unsigned maximumFunctionForCallInlineCandidateInstructionCount;
unsigned maximumFunctionForConstructInlineCandidateInstructionCount;

unsigned maximumInliningDepth;

int32_t executionCounterValueForOptimizeAfterWarmUp;
int32_t executionCounterValueForOptimizeAfterLongWarmUp;
int32_t executionCounterValueForDontOptimizeAnytimeSoon;
int32_t executionCounterValueForOptimizeSoon;
int32_t executionCounterValueForOptimizeNextInvocation;

int32_t executionCounterIncrementForLoop;
int32_t executionCounterIncrementForReturn;

unsigned desiredSpeculativeSuccessFailRatio;

double likelyToTakeSlowCaseThreshold;
double couldTakeSlowCaseThreshold;
unsigned likelyToTakeSlowCaseMinimumCount;
unsigned couldTakeSlowCaseMinimumCount;

double osrExitProminenceForFrequentExitSite;

unsigned largeFailCountThresholdBase;
unsigned largeFailCountThresholdBaseForLoop;

unsigned reoptimizationRetryCounterMax;
unsigned reoptimizationRetryCounterStep;

unsigned minimumOptimizationDelay;
unsigned maximumOptimizationDelay;
double desiredProfileLivenessRate;
double desiredProfileFullnessRate;

double doubleVoteRatioForDoubleFormat;

unsigned minimumNumberOfScansBetweenRebalance;
unsigned gcMarkStackSegmentSize;
unsigned minimumNumberOfCellsToKeep;
unsigned maximumNumberOfSharedSegments;
unsigned sharedStackWakeupThreshold;
unsigned numberOfGCMarkers;
unsigned opaqueRootMergeThreshold;

#if ENABLE(RUN_TIME_HEURISTICS)
static bool parse(const char* string, int32_t& value)
{
    return sscanf(string, "%d", &value) == 1;
}

static bool parse(const char* string, unsigned& value)
{
    return sscanf(string, "%u", &value) == 1;
}

static bool parse(const char* string, double& value)
{
    return sscanf(string, "%lf", &value) == 1;
}

template<typename T, typename U>
void setHeuristic(T& variable, const char* name, U value)
{
    const char* stringValue = getenv(name);
    if (!stringValue) {
        variable = safeCast<T>(value);
        return;
    }
    
    if (parse(stringValue, variable))
        return;
    
    fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue);
    variable = safeCast<T>(value);
}

#define SET(variable, value) setHeuristic(variable, "JSC_" #variable, value)
#else
#define SET(variable, value) variable = value
#endif

void initializeOptions()
{
    SET(maximumOptimizationCandidateInstructionCount, 1100);
    
    SET(maximumFunctionForCallInlineCandidateInstructionCount, 180);
    SET(maximumFunctionForConstructInlineCandidateInstructionCount, 100);
    
    SET(maximumInliningDepth, 5);

    SET(executionCounterValueForOptimizeAfterWarmUp,     -1000);
    SET(executionCounterValueForOptimizeAfterLongWarmUp, -5000);
    SET(executionCounterValueForDontOptimizeAnytimeSoon, std::numeric_limits<int32_t>::min());
    SET(executionCounterValueForOptimizeSoon,            -1000);
    SET(executionCounterValueForOptimizeNextInvocation,  0);

    SET(executionCounterIncrementForLoop,   1);
    SET(executionCounterIncrementForReturn, 15);

    SET(desiredSpeculativeSuccessFailRatio, 6);
    
    SET(likelyToTakeSlowCaseThreshold,    0.15);
    SET(couldTakeSlowCaseThreshold,       0.05); // Shouldn't be zero because some ops will spuriously take slow case, for example for linking or caching.
    SET(likelyToTakeSlowCaseMinimumCount, 100);
    SET(couldTakeSlowCaseMinimumCount,    10);
    
    SET(osrExitProminenceForFrequentExitSite, 0.3);

    SET(largeFailCountThresholdBase,        20);
    SET(largeFailCountThresholdBaseForLoop, 1);

    SET(reoptimizationRetryCounterStep, 1);

    SET(minimumOptimizationDelay,   1);
    SET(maximumOptimizationDelay,   5);
    SET(desiredProfileLivenessRate, 0.75);
    SET(desiredProfileFullnessRate, 0.35);
    
    SET(doubleVoteRatioForDoubleFormat, 2);
    
    SET(minimumNumberOfScansBetweenRebalance, 10000);
    SET(gcMarkStackSegmentSize,               pageSize());
    SET(minimumNumberOfCellsToKeep,           10);
    SET(maximumNumberOfSharedSegments,        3);
    SET(sharedStackWakeupThreshold,           1);
    SET(opaqueRootMergeThreshold,             1000);

    int cpusToUse = 1;
#if ENABLE(PARALLEL_GC)
    cpusToUse = WTF::numberOfProcessorCores();
#endif
    // We don't scale so well beyond 4.
    if (cpusToUse > 4)
        cpusToUse = 4;
    // Be paranoid, it is the OS we're dealing with, after all.
    if (cpusToUse < 1)
        cpusToUse = 1;
    
    SET(numberOfGCMarkers, cpusToUse);

    ASSERT(executionCounterValueForDontOptimizeAnytimeSoon <= executionCounterValueForOptimizeAfterLongWarmUp);
    ASSERT(executionCounterValueForOptimizeAfterLongWarmUp <= executionCounterValueForOptimizeAfterWarmUp);
    ASSERT(executionCounterValueForOptimizeAfterWarmUp <= executionCounterValueForOptimizeSoon);
    ASSERT(executionCounterValueForOptimizeAfterWarmUp < 0);
    ASSERT(executionCounterValueForOptimizeSoon <= executionCounterValueForOptimizeNextInvocation);
    
    // Compute the maximum value of the reoptimization retry counter. This is simply
    // the largest value at which we don't overflow the execute counter, when using it
    // to left-shift the execution counter by this amount. Currently the value ends
    // up being 18, so this loop is not so terrible; it probably takes up ~100 cycles
    // total on a 32-bit processor.
    reoptimizationRetryCounterMax = 0;
    while ((static_cast<int64_t>(executionCounterValueForOptimizeAfterLongWarmUp) << (reoptimizationRetryCounterMax + 1)) >= static_cast<int64_t>(std::numeric_limits<int32_t>::min()))
        reoptimizationRetryCounterMax++;
    
    ASSERT((static_cast<int64_t>(executionCounterValueForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) < 0);
    ASSERT((static_cast<int64_t>(executionCounterValueForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) >= static_cast<int64_t>(std::numeric_limits<int32_t>::min()));
}

} } // namespace JSC::Options


