//===-- nsan.cc -----------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// NumericalStabilitySanitizer runtime.
//
// This implements:
//  - The public nsan interface (include/sanitizer/nsan_interface.h).
//  - The private nsan interface (./nsan.h).
//  - The internal instrumentation interface. These are function emitted by the
//    instrumentation pass:
//        * __nsan_get_shadow_ptr_for_{float,double,longdouble}_load
//          These return the shadow memory pointer for loading the shadow value,
//          after checking that the types are consistent. If the types are not
//          consistent, returns nullptr.
//        * __nsan_get_shadow_ptr_for_{float,double,longdouble}_store
//          Sets the shadow types appropriately and returns the shadow memory
//          pointer for storing the shadow value.
//        * __nsan_internal_check_{float,double,long double}_{f,d,l} checks the
//          accuracy of a value against its shadow and emits a warning depending
//          on the runtime configuration. The middle part indicates the type of
//          the application value, the suffix (f,d,l) indicates the type of the
//          shadow, and depends on the instrumentation configuration.
//        * __nsan_fcmp_fail_* emits a warning for a fcmp instruction whose
//          corresponding shadow fcmp result differs.
//
//===----------------------------------------------------------------------===//

#include "nsan.h"
#include "nsan_flags.h"
#include "nsan_stats.h"
#include "nsan_suppressions.h"
#include "nsan_thread.h"

#include <assert.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_symbolizer.h"

using namespace __sanitizer;
using namespace __nsan;

constexpr int kMaxVectorWidth = 8;

// When copying application memory, we also copy its shadow and shadow type.
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__nsan_copy_values(const void *daddr, const void *saddr, uptr size) {
  internal_memmove(GetShadowTypeAddrFor(daddr), GetShadowTypeAddrFor(saddr),
                   size);
  internal_memmove(GetShadowAddrFor(daddr), GetShadowAddrFor(saddr),
                   size * kShadowScale);
}

#define NSAN_COPY_VALUES_N(N)                                                  \
  extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_copy_##N(               \
      const u8 *daddr, const u8 *saddr) {                                      \
    __builtin_memmove(GetShadowTypeAddrFor(daddr),                             \
                      GetShadowTypeAddrFor(saddr), N);                         \
    __builtin_memmove(GetShadowAddrFor(daddr), GetShadowAddrFor(saddr),        \
                      N *kShadowScale);                                        \
  }

NSAN_COPY_VALUES_N(4)
NSAN_COPY_VALUES_N(8)
NSAN_COPY_VALUES_N(16)

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__nsan_set_value_unknown(const void *addr, uptr size) {
  internal_memset(GetShadowTypeAddrFor(addr), 0, size);
}

#define NSAN_SET_VALUE_UNKNOWN_N(N)                                            \
  extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_set_value_unknown_##N(  \
      const u8 *daddr) {                                                       \
    __builtin_memset(GetShadowTypeAddrFor(daddr), 0, N);                       \
  }

NSAN_SET_VALUE_UNKNOWN_N(4)
NSAN_SET_VALUE_UNKNOWN_N(8)
NSAN_SET_VALUE_UNKNOWN_N(16)

const char *FTInfo<float>::kCppTypeName = "float";
const char *FTInfo<double>::kCppTypeName = "double";
const char *FTInfo<long double>::kCppTypeName = "long double";
const char *FTInfo<__float128>::kCppTypeName = "__float128";

const char FTInfo<float>::kTypePattern[sizeof(float)];
const char FTInfo<double>::kTypePattern[sizeof(double)];
const char FTInfo<long double>::kTypePattern[sizeof(long double)];

// Helper for __nsan_dump_shadow_mem: Reads the value at address `ptr`,
// identified by its type id.
template <typename ShadowFT>
static __float128 ReadShadowInternal(const u8 *ptr) {
  ShadowFT Shadow;
  __builtin_memcpy(&Shadow, ptr, sizeof(Shadow));
  return Shadow;
}

static __float128 ReadShadow(const u8 *ptr, const char ShadowTypeId) {
  switch (ShadowTypeId) {
  case 'd':
    return ReadShadowInternal<double>(ptr);
  case 'l':
    return ReadShadowInternal<long double>(ptr);
  case 'q':
    return ReadShadowInternal<__float128>(ptr);
  default:
    return 0.0;
  }
}

namespace {
class Decorator : public __sanitizer::SanitizerCommonDecorator {
public:
  Decorator() : SanitizerCommonDecorator() {}
  const char *Warning() { return Red(); }
  const char *Name() { return Green(); }
  const char *End() { return Default(); }
};

// Workaround for the fact that Printf() does not support floats.
struct PrintBuffer {
  char Buffer[64];
};
template <typename FT> struct FTPrinter {};

template <> struct FTPrinter<double> {
  static PrintBuffer dec(double value) {
    PrintBuffer result;
    snprintf(result.Buffer, sizeof(result.Buffer) - 1, "%.20f", value);
    return result;
  }
  static PrintBuffer hex(double value) {
    PrintBuffer result;
    snprintf(result.Buffer, sizeof(result.Buffer) - 1, "%.20a", value);
    return result;
  }
};

template <> struct FTPrinter<float> : FTPrinter<double> {};

template <> struct FTPrinter<long double> {
  static PrintBuffer dec(long double value) {
    PrintBuffer result;
    snprintf(result.Buffer, sizeof(result.Buffer) - 1, "%.20Lf", value);
    return result;
  }
  static PrintBuffer hex(long double value) {
    PrintBuffer result;
    snprintf(result.Buffer, sizeof(result.Buffer) - 1, "%.20La", value);
    return result;
  }
};

// FIXME: print with full precision.
template <> struct FTPrinter<__float128> : FTPrinter<long double> {};

// This is a template so that there are no implicit conversions.
template <typename FT> inline FT ftAbs(FT v);

template <> inline long double ftAbs(long double v) { return fabsl(v); }
template <> inline double ftAbs(double v) { return fabs(v); }

// We don't care about nans.
// std::abs(__float128) code is suboptimal and generates a function call to
// __getf2().
template <typename FT> inline FT ftAbs(FT v) { return v >= FT{0} ? v : -v; }

template <typename FT1, typename FT2, bool Enable> struct LargestFTImpl {
  using type = FT2;
};

template <typename FT1, typename FT2> struct LargestFTImpl<FT1, FT2, true> {
  using type = FT1;
};

template <typename FT1, typename FT2>
using LargestFT =
    typename LargestFTImpl<FT1, FT2, (sizeof(FT1) > sizeof(FT2))>::type;

template <typename T> T max(T a, T b) { return a < b ? b : a; }

} // end anonymous namespace

void __sanitizer::BufferedStackTrace::UnwindImpl(uptr pc, uptr bp,
                                                 void *context,
                                                 bool request_fast,
                                                 u32 max_depth) {
  using namespace __nsan;
  NsanThread *t = GetCurrentThread();
  if (!t || !StackTrace::WillUseFastUnwind(request_fast))
    return Unwind(max_depth, pc, bp, context, t ? t->stack_top() : 0,
                  t ? t->stack_bottom() : 0, false);
  if (StackTrace::WillUseFastUnwind(request_fast))
    Unwind(max_depth, pc, bp, nullptr, t->stack_top(), t->stack_bottom(), true);
  else
    Unwind(max_depth, pc, 0, context, 0, 0, false);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_print_accumulated_stats() {
  if (nsan_stats)
    nsan_stats->Print();
}

static void NsanAtexit() {
  Printf("Numerical Sanitizer exit stats:\n");
  __nsan_print_accumulated_stats();
  nsan_stats = nullptr;
}

// The next three functions return a pointer for storing a shadow value for `n`
// values, after setting the shadow types. We return the pointer instead of
// storing ourselves because it avoids having to rely on the calling convention
// around long double being the same for nsan and the target application.
// We have to have 3 versions because we need to know which type we are storing
// since we are setting the type shadow memory.
template <typename FT> static u8 *getShadowPtrForStore(u8 *store_addr, uptr n) {
  unsigned char *shadow_type = GetShadowTypeAddrFor(store_addr);
  for (uptr i = 0; i < n; ++i) {
    __builtin_memcpy(shadow_type + i * sizeof(FT), FTInfo<FT>::kTypePattern,
                     sizeof(FTInfo<FT>::kTypePattern));
  }
  return GetShadowAddrFor(store_addr);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE u8 *
__nsan_get_shadow_ptr_for_float_store(u8 *store_addr, uptr n) {
  return getShadowPtrForStore<float>(store_addr, n);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE u8 *
__nsan_get_shadow_ptr_for_double_store(u8 *store_addr, uptr n) {
  return getShadowPtrForStore<double>(store_addr, n);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE u8 *
__nsan_get_shadow_ptr_for_longdouble_store(u8 *store_addr, uptr n) {
  return getShadowPtrForStore<long double>(store_addr, n);
}

template <typename FT> static bool IsValidShadowType(const u8 *shadow_type) {
  return __builtin_memcmp(shadow_type, FTInfo<FT>::kTypePattern, sizeof(FT)) ==
         0;
}

template <int kSize, typename T> static bool IsZero(const T *ptr) {
  constexpr const char kZeros[kSize] = {}; // Zero initialized.
  return __builtin_memcmp(ptr, kZeros, kSize) == 0;
}

template <typename FT> static bool IsUnknownShadowType(const u8 *shadow_type) {
  return IsZero<sizeof(FTInfo<FT>::kTypePattern)>(shadow_type);
}

// The three folowing functions check that the address stores a complete
// shadow value of the given type and return a pointer for loading.
// They return nullptr if the type of the value is unknown or incomplete.
template <typename FT>
static const u8 *getShadowPtrForLoad(const u8 *load_addr, uptr n) {
  const u8 *const shadow_type = GetShadowTypeAddrFor(load_addr);
  for (uptr i = 0; i < n; ++i) {
    if (!IsValidShadowType<FT>(shadow_type + i * sizeof(FT))) {
      // If loadtracking stats are enabled, log loads with invalid types
      // (tampered with through type punning).
      if (flags().enable_loadtracking_stats) {
        if (IsUnknownShadowType<FT>(shadow_type + i * sizeof(FT))) {
          // Warn only if the value is non-zero. Zero is special because
          // applications typically initialize large buffers to zero in an
          // untyped way.
          if (!IsZero<sizeof(FT)>(load_addr)) {
            GET_CALLER_PC_BP;
            nsan_stats->AddUnknownLoadTrackingEvent(pc, bp);
          }
        } else {
          GET_CALLER_PC_BP;
          nsan_stats->AddInvalidLoadTrackingEvent(pc, bp);
        }
      }
      return nullptr;
    }
  }
  return GetShadowAddrFor(load_addr);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE const u8 *
__nsan_get_shadow_ptr_for_float_load(const u8 *load_addr, uptr n) {
  return getShadowPtrForLoad<float>(load_addr, n);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE const u8 *
__nsan_get_shadow_ptr_for_double_load(const u8 *load_addr, uptr n) {
  return getShadowPtrForLoad<double>(load_addr, n);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE const u8 *
__nsan_get_shadow_ptr_for_longdouble_load(const u8 *load_addr, uptr n) {
  return getShadowPtrForLoad<long double>(load_addr, n);
}

// Returns the raw shadow pointer. The returned pointer should be considered
// opaque.
extern "C" SANITIZER_INTERFACE_ATTRIBUTE u8 *
__nsan_internal_get_raw_shadow_ptr(const u8 *addr) {
  return GetShadowAddrFor(addr);
}

// Returns the raw shadow type pointer. The returned pointer should be
// considered opaque.
extern "C" SANITIZER_INTERFACE_ATTRIBUTE u8 *
__nsan_internal_get_raw_shadow_type_ptr(const u8 *addr) {
  return reinterpret_cast<u8 *>(GetShadowTypeAddrFor(addr));
}

static ValueType getValueType(u8 c) { return static_cast<ValueType>(c & 0x3); }

static int getValuePos(u8 c) { return c >> kValueSizeSizeBits; }

// Checks the consistency of the value types at the given type pointer.
// If the value is inconsistent, returns ValueType::kUnknown. Else, return the
// consistent type.
template <typename FT>
static bool checkValueConsistency(const u8 *shadow_type) {
  const int pos = getValuePos(*shadow_type);
  // Check that all bytes from the start of the value are ordered.
  for (uptr i = 0; i < sizeof(FT); ++i) {
    const u8 T = *(shadow_type - pos + i);
    if (!(getValueType(T) == FTInfo<FT>::kValueType && getValuePos(T) == i))
      return false;
  }
  return true;
}

// The instrumentation automatically appends `shadow_value_type_ids`, see
// maybeAddSuffixForNsanInterface.
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__nsan_dump_shadow_mem(const u8 *addr, size_t size_bytes, size_t bytes_per_line,
                       size_t shadow_value_type_ids) {
  const u8 *const shadow_type = GetShadowTypeAddrFor(addr);
  const u8 *const shadow = GetShadowAddrFor(addr);

  constexpr int kMaxNumDecodedValues = 16;
  __float128 decoded_values[kMaxNumDecodedValues];
  int num_decoded_values = 0;
  if (bytes_per_line > 4 * kMaxNumDecodedValues)
    bytes_per_line = 4 * kMaxNumDecodedValues;

  // We keep track of the current type and position as we go.
  ValueType LastValueTy = kUnknownValueType;
  int LastPos = -1;
  size_t Offset = 0;
  for (size_t R = 0; R < (size_bytes + bytes_per_line - 1) / bytes_per_line;
       ++R) {
    printf("%p:    ", (void *)(addr + R * bytes_per_line));
    for (size_t C = 0; C < bytes_per_line && Offset < size_bytes; ++C) {
      const ValueType ValueTy = getValueType(shadow_type[Offset]);
      const int pos = getValuePos(shadow_type[Offset]);
      if (ValueTy == LastValueTy && pos == LastPos + 1) {
        ++LastPos;
      } else {
        LastValueTy = ValueTy;
        LastPos = pos == 0 ? 0 : -1;
      }

      switch (ValueTy) {
      case kUnknownValueType:
        printf("__ ");
        break;
      case kFloatValueType:
        printf("f%x ", pos);
        if (LastPos == sizeof(float) - 1) {
          decoded_values[num_decoded_values] =
              ReadShadow(shadow + kShadowScale * (Offset + 1 - sizeof(float)),
                         static_cast<char>(shadow_value_type_ids & 0xff));
          ++num_decoded_values;
        }
        break;
      case kDoubleValueType:
        printf("d%x ", pos);
        if (LastPos == sizeof(double) - 1) {
          decoded_values[num_decoded_values] = ReadShadow(
              shadow + kShadowScale * (Offset + 1 - sizeof(double)),
              static_cast<char>((shadow_value_type_ids >> 8) & 0xff));
          ++num_decoded_values;
        }
        break;
      case kFp80ValueType:
        printf("l%x ", pos);
        if (LastPos == sizeof(long double) - 1) {
          decoded_values[num_decoded_values] = ReadShadow(
              shadow + kShadowScale * (Offset + 1 - sizeof(long double)),
              static_cast<char>((shadow_value_type_ids >> 16) & 0xff));
          ++num_decoded_values;
        }
        break;
      }
      ++Offset;
    }
    for (int i = 0; i < num_decoded_values; ++i) {
      printf("  (%s)", FTPrinter<__float128>::dec(decoded_values[i]).Buffer);
    }
    num_decoded_values = 0;
    printf("\n");
  }
}

alignas(64) SANITIZER_INTERFACE_ATTRIBUTE
    thread_local uptr __nsan_shadow_ret_tag = 0;

alignas(64) SANITIZER_INTERFACE_ATTRIBUTE
    thread_local char __nsan_shadow_ret_ptr[kMaxVectorWidth *
                                            sizeof(__float128)];

alignas(64) SANITIZER_INTERFACE_ATTRIBUTE
    thread_local uptr __nsan_shadow_args_tag = 0;

// Maximum number of args. This should be enough for anyone (tm). An alternate
// scheme is to have the generated code create an alloca and make
// __nsan_shadow_args_ptr point ot the alloca.
constexpr const int kMaxNumArgs = 128;
alignas(64) SANITIZER_INTERFACE_ATTRIBUTE
    thread_local char __nsan_shadow_args_ptr[kMaxVectorWidth * kMaxNumArgs *
                                             sizeof(__float128)];

enum ContinuationType { // Keep in sync with instrumentation pass.
  kContinueWithShadow = 0,
  kResumeFromValue = 1,
};

// Checks the consistency between application and shadow value. Returns true
// when the instrumented code should resume computations from the original value
// rather than the shadow value. This prevents one error to propagate to all
// subsequent operations. This behaviour is tunable with flags.
template <typename FT, typename ShadowFT>
int32_t checkFT(const FT value, ShadowFT Shadow, CheckTypeT CheckType,
                uptr CheckArg) {
  // We do all comparisons in the InternalFT domain, which is the largest FT
  // type.
  using InternalFT = LargestFT<FT, ShadowFT>;
  const InternalFT check_value = value;
  const InternalFT check_shadow = Shadow;

  // We only check for NaNs in the value, not the shadow.
  if (flags().check_nan && isnan(value)) {
    GET_CALLER_PC_BP;
    BufferedStackTrace stack;
    stack.Unwind(pc, bp, nullptr, false);
    if (GetSuppressionForStack(&stack, CheckKind::Consistency)) {
      // FIXME: optionally print.
      return flags().resume_after_suppression ? kResumeFromValue
                                              : kContinueWithShadow;
    }
    Decorator D;
    Printf("%s", D.Warning());
    Printf("WARNING: NumericalStabilitySanitizer: NaN detected\n");
    Printf("%s", D.Default());
    stack.Print();
    if (flags().halt_on_error) {
      if (common_flags()->abort_on_error)
        Printf("ABORTING\n");
      else
        Printf("Exiting\n");
      Die();
    }
    // Performing other tests for NaN values is meaningless when dealing with numbers.
    return kResumeFromValue;
  }

  // See this article for an interesting discussion of how to compare floats:
  // https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
  static constexpr const FT Eps = FTInfo<FT>::kEpsilon;

  const InternalFT abs_err = ftAbs(check_value - check_shadow);

  if (flags().enable_check_stats) {
    GET_CALLER_PC_BP;
    // We are re-computing `largest` here because this is a cold branch, and we
    // want to avoid having to move the computation of `largest` before the
    // absolute value check when this branch is not taken.
    const InternalFT largest = max(ftAbs(check_value), ftAbs(check_shadow));
    nsan_stats->AddCheck(CheckType, pc, bp, abs_err / largest);
  }

  // Note: writing the comparison that way ensures that when `abs_err` is Nan
  // (value and shadow are inf or -inf), we pass the test.
  if (!(abs_err >= flags().cached_absolute_error_threshold))
    return kContinueWithShadow;

  const InternalFT largest = max(ftAbs(check_value), ftAbs(check_shadow));
  if (abs_err * (1ull << flags().log2_max_relative_error) <= largest)
    return kContinueWithShadow; // No problem here.

  if (!flags().disable_warnings) {
    GET_CALLER_PC_BP;
    UNINITIALIZED BufferedStackTrace stack;
    stack.Unwind(pc, bp, nullptr, false);
    if (GetSuppressionForStack(&stack, CheckKind::Consistency)) {
      // FIXME: optionally print.
      return flags().resume_after_suppression ? kResumeFromValue
                                              : kContinueWithShadow;
    }

    Decorator D;
    Printf("%s", D.Warning());
    // Printf does not support float formatting.
    char RelErrBuf[64] = "inf";
    if (largest > Eps) {
      snprintf(RelErrBuf, sizeof(RelErrBuf) - 1, "%.20Lf%% (2^%.0Lf epsilons)",
               static_cast<long double>(100.0 * abs_err / largest),
               log2l(static_cast<long double>(abs_err / largest / Eps)));
    }
    char ulp_err_buf[128] = "";
    const double shadow_ulp_diff = GetULPDiff(check_value, check_shadow);
    if (shadow_ulp_diff != kMaxULPDiff) {
      // This is the ULP diff in the internal domain. The user actually cares
      // about that in the original domain.
      const double ulp_diff =
          shadow_ulp_diff / (u64{1} << (FTInfo<InternalFT>::kMantissaBits -
                                        FTInfo<FT>::kMantissaBits));
      snprintf(ulp_err_buf, sizeof(ulp_err_buf) - 1,
               "(%.0f ULPs == %.1f digits == %.1f bits)", ulp_diff,
               log10(ulp_diff), log2(ulp_diff));
    }
    Printf("WARNING: NumericalStabilitySanitizer: inconsistent shadow results");
    switch (CheckType) {
    case CheckTypeT::kUnknown:
    case CheckTypeT::kFcmp:
    case CheckTypeT::kMaxCheckType:
      break;
    case CheckTypeT::kRet:
      Printf(" while checking return value");
      break;
    case CheckTypeT::kArg:
      Printf(" while checking call argument #%d", static_cast<int>(CheckArg));
      break;
    case CheckTypeT::kLoad:
      Printf(
          " while checking load from address 0x%lx. This is due to incorrect "
          "shadow memory tracking, typically due to uninstrumented code "
          "writing to memory.",
          CheckArg);
      break;
    case CheckTypeT::kStore:
      Printf(" while checking store to address 0x%lx", CheckArg);
      break;
    case CheckTypeT::kInsert:
      Printf(" while checking vector insert");
      break;
    case CheckTypeT::kUser:
      Printf(" in user-initiated check");
      break;
    }
    using ValuePrinter = FTPrinter<FT>;
    using ShadowPrinter = FTPrinter<ShadowFT>;
    Printf("%s", D.Default());

    Printf("\n"
           "%-12s precision  (native): dec: %s  hex: %s\n"
           "%-12s precision  (shadow): dec: %s  hex: %s\n"
           "shadow truncated to %-12s: dec: %s  hex: %s\n"
           "Relative error: %s\n"
           "Absolute error: %s\n"
           "%s\n",
           FTInfo<FT>::kCppTypeName, ValuePrinter::dec(value).Buffer,
           ValuePrinter::hex(value).Buffer, FTInfo<ShadowFT>::kCppTypeName,
           ShadowPrinter::dec(Shadow).Buffer, ShadowPrinter::hex(Shadow).Buffer,
           FTInfo<FT>::kCppTypeName, ValuePrinter::dec(Shadow).Buffer,
           ValuePrinter::hex(Shadow).Buffer, RelErrBuf,
           ValuePrinter::hex(abs_err).Buffer, ulp_err_buf);
    stack.Print();
  }

  if (flags().enable_warning_stats) {
    GET_CALLER_PC_BP;
    nsan_stats->AddWarning(CheckType, pc, bp, abs_err / largest);
  }

  if (flags().halt_on_error) {
    if (common_flags()->abort_on_error)
      Printf("ABORTING\n");
    else
      Printf("Exiting\n");
    Die();
  }
  return flags().resume_after_warning ? kResumeFromValue : kContinueWithShadow;
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE int32_t __nsan_internal_check_float_d(
    float value, double shadow, int32_t check_type, uptr check_arg) {
  return checkFT(value, shadow, static_cast<CheckTypeT>(check_type), check_arg);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE int32_t __nsan_internal_check_double_l(
    double value, long double shadow, int32_t check_type, uptr check_arg) {
  return checkFT(value, shadow, static_cast<CheckTypeT>(check_type), check_arg);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE int32_t __nsan_internal_check_double_q(
    double value, __float128 shadow, int32_t check_type, uptr check_arg) {
  return checkFT(value, shadow, static_cast<CheckTypeT>(check_type), check_arg);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE int32_t
__nsan_internal_check_longdouble_q(long double value, __float128 shadow,
                                   int32_t check_type, uptr check_arg) {
  return checkFT(value, shadow, static_cast<CheckTypeT>(check_type), check_arg);
}

static const char *GetTruthValueName(bool v) { return v ? "true" : "false"; }

// This uses the same values as CmpInst::Predicate.
static const char *GetPredicateName(int v) {
  switch (v) {
  case 0:
    return "(false)";
  case 1:
    return "==";
  case 2:
    return ">";
  case 3:
    return ">=";
  case 4:
    return "<";
  case 5:
    return "<=";
  case 6:
    return "!=";
  case 7:
    return "(ordered)";
  case 8:
    return "(unordered)";
  case 9:
    return "==";
  case 10:
    return ">";
  case 11:
    return ">=";
  case 12:
    return "<";
  case 13:
    return "<=";
  case 14:
    return "!=";
  case 15:
    return "(true)";
  }
  return "??";
}

template <typename FT, typename ShadowFT>
void fCmpFailFT(const FT Lhs, const FT Rhs, ShadowFT LhsShadow,
                ShadowFT RhsShadow, int Predicate, bool result,
                bool ShadowResult) {
  if (result == ShadowResult) {
    // When a vector comparison fails, we fail each element of the comparison
    // to simplify instrumented code. Skip elements where the shadow comparison
    // gave the same result as the original one.
    return;
  }

  GET_CALLER_PC_BP;
  UNINITIALIZED BufferedStackTrace stack;
  stack.Unwind(pc, bp, nullptr, false);

  if (GetSuppressionForStack(&stack, CheckKind::Fcmp)) {
    // FIXME: optionally print.
    return;
  }

  if (flags().enable_warning_stats)
    nsan_stats->AddWarning(CheckTypeT::kFcmp, pc, bp, 0.0);

  if (flags().disable_warnings || !flags().check_cmp)
    return;

  // FIXME: ideally we would print the shadow value as FP128. Right now because
  // we truncate to long double we can sometimes see stuff like:
  // shadow <value> == <value> (false)
  using ValuePrinter = FTPrinter<FT>;
  using ShadowPrinter = FTPrinter<ShadowFT>;
  Decorator D;
  const char *const PredicateName = GetPredicateName(Predicate);
  Printf("%s", D.Warning());
  Printf("WARNING: NumericalStabilitySanitizer: floating-point comparison "
         "results depend on precision\n");
  Printf("%s", D.Default());
  Printf("%-12s precision dec (native): %s %s %s (%s)\n"
         "%-12s precision dec (shadow): %s %s %s (%s)\n"
         "%-12s precision hex (native): %s %s %s (%s)\n"
         "%-12s precision hex (shadow): %s %s %s (%s)\n"
         "%s",
         // Native, decimal.
         FTInfo<FT>::kCppTypeName, ValuePrinter::dec(Lhs).Buffer, PredicateName,
         ValuePrinter::dec(Rhs).Buffer, GetTruthValueName(result),
         // Shadow, decimal
         FTInfo<ShadowFT>::kCppTypeName, ShadowPrinter::dec(LhsShadow).Buffer,
         PredicateName, ShadowPrinter::dec(RhsShadow).Buffer,
         GetTruthValueName(ShadowResult),
         // Native, hex.
         FTInfo<FT>::kCppTypeName, ValuePrinter::hex(Lhs).Buffer, PredicateName,
         ValuePrinter::hex(Rhs).Buffer, GetTruthValueName(result),
         // Shadow, hex
         FTInfo<ShadowFT>::kCppTypeName, ShadowPrinter::hex(LhsShadow).Buffer,
         PredicateName, ShadowPrinter::hex(RhsShadow).Buffer,
         GetTruthValueName(ShadowResult), D.End());
  stack.Print();
  if (flags().halt_on_error) {
    Printf("Exiting\n");
    Die();
  }
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__nsan_fcmp_fail_float_d(float lhs, float rhs, double lhs_shadow,
                         double rhs_shadow, int predicate, bool result,
                         bool shadow_result) {
  fCmpFailFT(lhs, rhs, lhs_shadow, rhs_shadow, predicate, result,
             shadow_result);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__nsan_fcmp_fail_double_q(double lhs, double rhs, __float128 lhs_shadow,
                          __float128 rhs_shadow, int predicate, bool result,
                          bool shadow_result) {
  fCmpFailFT(lhs, rhs, lhs_shadow, rhs_shadow, predicate, result,
             shadow_result);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__nsan_fcmp_fail_double_l(double lhs, double rhs, long double lhs_shadow,
                          long double rhs_shadow, int predicate, bool result,
                          bool shadow_result) {
  fCmpFailFT(lhs, rhs, lhs_shadow, rhs_shadow, predicate, result,
             shadow_result);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__nsan_fcmp_fail_longdouble_q(long double lhs, long double rhs,
                              __float128 lhs_shadow, __float128 rhs_shadow,
                              int predicate, bool result, bool shadow_result) {
  fCmpFailFT(lhs, rhs, lhs_shadow, rhs_shadow, predicate, result,
             shadow_result);
}

template <typename FT> void checkFTFromShadowStack(const FT value) {
  // Get the shadow 2FT value from the shadow stack. Note that
  // __nsan_check_{float,double,long double} is a function like any other, so
  // the instrumentation will have placed the shadow value on the shadow stack.
  using ShadowFT = typename FTInfo<FT>::shadow_type;
  ShadowFT Shadow;
  __builtin_memcpy(&Shadow, __nsan_shadow_args_ptr, sizeof(ShadowFT));
  checkFT(value, Shadow, CheckTypeT::kUser, 0);
}

// FIXME: Add suffixes and let the instrumentation pass automatically add
// suffixes.
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_check_float(float value) {
  assert(__nsan_shadow_args_tag == (uptr)&__nsan_check_float &&
         "__nsan_check_float called from non-instrumented function");
  checkFTFromShadowStack(value);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__nsan_check_double(double value) {
  assert(__nsan_shadow_args_tag == (uptr)&__nsan_check_double &&
         "__nsan_check_double called from non-instrumented function");
  checkFTFromShadowStack(value);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__nsan_check_longdouble(long double value) {
  assert(__nsan_shadow_args_tag == (uptr)&__nsan_check_longdouble &&
         "__nsan_check_longdouble called from non-instrumented function");
  checkFTFromShadowStack(value);
}

template <typename FT> static void dumpFTFromShadowStack(const FT value) {
  // Get the shadow 2FT value from the shadow stack. Note that
  // __nsan_dump_{float,double,long double} is a function like any other, so
  // the instrumentation will have placed the shadow value on the shadow stack.
  using ShadowFT = typename FTInfo<FT>::shadow_type;
  ShadowFT shadow;
  __builtin_memcpy(&shadow, __nsan_shadow_args_ptr, sizeof(ShadowFT));
  using ValuePrinter = FTPrinter<FT>;
  using ShadowPrinter = FTPrinter<typename FTInfo<FT>::shadow_type>;
  printf("value  dec:%s hex:%s\n"
         "shadow dec:%s hex:%s\n",
         ValuePrinter::dec(value).Buffer, ValuePrinter::hex(value).Buffer,
         ShadowPrinter::dec(shadow).Buffer, ShadowPrinter::hex(shadow).Buffer);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_dump_float(float value) {
  assert(__nsan_shadow_args_tag == (uptr)&__nsan_dump_float &&
         "__nsan_dump_float called from non-instrumented function");
  dumpFTFromShadowStack(value);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_dump_double(double value) {
  assert(__nsan_shadow_args_tag == (uptr)&__nsan_dump_double &&
         "__nsan_dump_double called from non-instrumented function");
  dumpFTFromShadowStack(value);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__nsan_dump_longdouble(long double value) {
  assert(__nsan_shadow_args_tag == (uptr)&__nsan_dump_longdouble &&
         "__nsan_dump_longdouble called from non-instrumented function");
  dumpFTFromShadowStack(value);
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_dump_shadow_ret() {
  printf("ret tag: %lx\n", __nsan_shadow_ret_tag);
  double v;
  __builtin_memcpy(&v, __nsan_shadow_ret_ptr, sizeof(double));
  printf("double value: %f\n", v);
  // FIXME: float128 value.
}

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_dump_shadow_args() {
  printf("args tag: %lx\n", __nsan_shadow_args_tag);
}

bool __nsan::nsan_initialized;
bool __nsan::nsan_init_is_running;

extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_init() {
  CHECK(!nsan_init_is_running);
  if (nsan_initialized)
    return;
  nsan_init_is_running = true;
  SanitizerToolName = "NumericalStabilitySanitizer";

  InitializeFlags();
  InitializeSuppressions();
  InitializePlatformEarly();

  DisableCoreDumperIfNecessary();

  if (!MmapFixedNoReserve(TypesAddr(), AllocatorAddr() - TypesAddr()))
    Die();

  InitializeInterceptors();
  NsanTSDInit(NsanTSDDtor);
  NsanAllocatorInit();

  NsanThread *main_thread = NsanThread::Create(nullptr, nullptr);
  SetCurrentThread(main_thread);
  main_thread->Init();

  InitializeStats();
  if (flags().print_stats_on_exit)
    Atexit(NsanAtexit);

  nsan_init_is_running = false;
  nsan_initialized = true;
}
