//===-------------------------- TargetRecip.cpp ---------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class is used to customize machine-specific reciprocal estimate code
// generation in a target-independent way.
// If a target does not support operations in this specification, then code
// generation will default to using supported operations.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Target/TargetRecip.h"
#include <map>

using namespace llvm;

// These are the names of the individual reciprocal operations. These are
// the key strings for queries and command-line inputs.
// In addition, the command-line interface recognizes the global parameters
// "all", "none", and "default".
static const char *const RecipOps[] = {
  "divd",
  "divf",
  "vec-divd",
  "vec-divf",
  "sqrtd",
  "sqrtf",
  "vec-sqrtd",
  "vec-sqrtf",
};

// The uninitialized state is needed for the enabled settings and refinement
// steps because custom settings may arrive via the command-line before target
// defaults are set.
TargetRecip::TargetRecip() {
  unsigned NumStrings = llvm::array_lengthof(RecipOps);
  for (unsigned i = 0; i < NumStrings; ++i)
    RecipMap.insert(std::make_pair(RecipOps[i], RecipParams()));
}

static bool parseRefinementStep(StringRef In, size_t &Position,
                                uint8_t &Value) {
  const char RefStepToken = ':';
  Position = In.find(RefStepToken);
  if (Position == StringRef::npos)
    return false;

  StringRef RefStepString = In.substr(Position + 1);
  // Allow exactly one numeric character for the additional refinement
  // step parameter.
  if (RefStepString.size() == 1) {
    char RefStepChar = RefStepString[0];
    if (RefStepChar >= '0' && RefStepChar <= '9') {
      Value = RefStepChar - '0';
      return true;
    }
  }
  report_fatal_error("Invalid refinement step for -recip.");
}

bool TargetRecip::parseGlobalParams(const std::string &Arg) {
  StringRef ArgSub = Arg;

  // Look for an optional setting of the number of refinement steps needed
  // for this type of reciprocal operation.
  size_t RefPos;
  uint8_t RefSteps;
  StringRef RefStepString;
  if (parseRefinementStep(ArgSub, RefPos, RefSteps)) {
    // Split the string for further processing.
    RefStepString = ArgSub.substr(RefPos + 1);
    ArgSub = ArgSub.substr(0, RefPos);
  }
  bool Enable;
  bool UseDefaults;
  if (ArgSub == "all") {
    UseDefaults = false;
    Enable = true;
  } else if (ArgSub == "none") {
    UseDefaults = false;
    Enable = false;
  } else if (ArgSub == "default") {
    UseDefaults = true;
  } else {
    // Any other string is invalid or an individual setting.
    return false;
  }

  // All enable values will be initialized to target defaults if 'default' was
  // specified.
  if (!UseDefaults)
    for (auto &KV : RecipMap)
      KV.second.Enabled = Enable;

  // Custom refinement count was specified with all, none, or default.
  if (!RefStepString.empty())
    for (auto &KV : RecipMap)
      KV.second.RefinementSteps = RefSteps;
  
  return true;
}

void TargetRecip::parseIndividualParams(const std::vector<std::string> &Args) {
  static const char DisabledPrefix = '!';
  unsigned NumArgs = Args.size();

  for (unsigned i = 0; i != NumArgs; ++i) {
    StringRef Val = Args[i];
    
    bool IsDisabled = Val[0] == DisabledPrefix;
    // Ignore the disablement token for string matching.
    if (IsDisabled)
      Val = Val.substr(1);
    
    size_t RefPos;
    uint8_t RefSteps;
    StringRef RefStepString;
    if (parseRefinementStep(Val, RefPos, RefSteps)) {
      // Split the string for further processing.
      RefStepString = Val.substr(RefPos + 1);
      Val = Val.substr(0, RefPos);
    }

    RecipIter Iter = RecipMap.find(Val);
    if (Iter == RecipMap.end()) {
      // Try again specifying float suffix.
      Iter = RecipMap.find(Val.str() + 'f');
      if (Iter == RecipMap.end()) {
        Iter = RecipMap.find(Val.str() + 'd');
        assert(Iter == RecipMap.end() && "Float entry missing from map");
        report_fatal_error("Invalid option for -recip.");
      }
      
      // The option was specified without a float or double suffix.
      if (RecipMap[Val.str() + 'd'].Enabled != Uninitialized) {
        // Make sure that the double entry was not already specified.
        // The float entry will be checked below.
        report_fatal_error("Duplicate option for -recip.");
      }
    }
    
    if (Iter->second.Enabled != Uninitialized)
      report_fatal_error("Duplicate option for -recip.");
    
    // Mark the matched option as found. Do not allow duplicate specifiers.
    Iter->second.Enabled = !IsDisabled;
    if (!RefStepString.empty())
      Iter->second.RefinementSteps = RefSteps;
    
    // If the precision was not specified, the double entry is also initialized.
    if (Val.back() != 'f' && Val.back() != 'd') {
      RecipMap[Val.str() + 'd'].Enabled = !IsDisabled;
      if (!RefStepString.empty())
        RecipMap[Val.str() + 'd'].RefinementSteps = RefSteps;
    }
  }
}

TargetRecip::TargetRecip(const std::vector<std::string> &Args) :
  TargetRecip() {
  unsigned NumArgs = Args.size();

  // Check if "all", "default", or "none" was specified.
  if (NumArgs == 1 && parseGlobalParams(Args[0]))
    return;
 
  parseIndividualParams(Args);
}

bool TargetRecip::isEnabled(StringRef Key) const {
  ConstRecipIter Iter = RecipMap.find(Key);
  assert(Iter != RecipMap.end() && "Unknown name for reciprocal map");
  assert(Iter->second.Enabled != Uninitialized &&
         "Enablement setting was not initialized");
  return Iter->second.Enabled;
}

unsigned TargetRecip::getRefinementSteps(StringRef Key) const {
  ConstRecipIter Iter = RecipMap.find(Key);
  assert(Iter != RecipMap.end() && "Unknown name for reciprocal map");
  assert(Iter->second.RefinementSteps != Uninitialized &&
         "Refinement step setting was not initialized");
  return Iter->second.RefinementSteps;
}

/// Custom settings (previously initialized values) override target defaults.
void TargetRecip::setDefaults(StringRef Key, bool Enable,
                              unsigned RefSteps) {
  if (Key == "all") {
    for (auto &KV : RecipMap) {
      RecipParams &RP = KV.second;
      if (RP.Enabled == Uninitialized)
        RP.Enabled = Enable;
      if (RP.RefinementSteps == Uninitialized)
        RP.RefinementSteps = RefSteps;
    }
  } else {
    RecipParams &RP = RecipMap[Key];
    if (RP.Enabled == Uninitialized)
      RP.Enabled = Enable;
    if (RP.RefinementSteps == Uninitialized)
      RP.RefinementSteps = RefSteps;
  }
}

bool TargetRecip::operator==(const TargetRecip &Other) const {
  for (const auto &KV : RecipMap) {
    StringRef Op = KV.first;
    const RecipParams &RP = KV.second;
    const RecipParams &OtherRP = Other.RecipMap.find(Op)->second;
    if (RP.RefinementSteps != OtherRP.RefinementSteps)
      return false;
    if (RP.Enabled != OtherRP.Enabled)
      return false;
  }
  return true;
}
