std::error_code SampleProfileReaderGCC::readOneFunctionProfile(
    const InlineCallStack &InlineStack, bool Update, uint32_t Offset) {
  uint64_t HeadCount = 0;
  if (InlineStack.size() == 0)
    if (!GcovBuffer.readInt64(HeadCount))
      return sampleprof_error::truncated;

  uint32_t NameIdx;
  if (!GcovBuffer.readInt(NameIdx))
    return sampleprof_error::truncated;

  StringRef Name(Names[NameIdx]);

  uint32_t NumPosCounts;
  if (!GcovBuffer.readInt(NumPosCounts))
    return sampleprof_error::truncated;

  uint32_t NumCallsites;
  if (!GcovBuffer.readInt(NumCallsites))
    return sampleprof_error::truncated;

  FunctionSamples *FProfile = nullptr;
  if (InlineStack.size() == 0) {
    // If this is a top function that we have already processed, do not
    // update its profile again.  This happens in the presence of
    // function aliases.  Since these aliases share the same function
    // body, there will be identical replicated profiles for the
    // original function.  In this case, we simply not bother updating
    // the profile of the original function.
    FProfile = &Profiles[Name];
    FProfile->addHeadSamples(HeadCount);
    if (FProfile->getTotalSamples() > 0)
      Update = false;
  } else {
    // Otherwise, we are reading an inlined instance. The top of the
    // inline stack contains the profile of the caller. Insert this
    // callee in the caller's CallsiteMap.
    FunctionSamples *CallerProfile = InlineStack.front();
    uint32_t LineOffset = Offset >> 16;
    uint32_t Discriminator = Offset & 0xffff;
    FProfile = &CallerProfile->functionSamplesAt(
        LineLocation(LineOffset, Discriminator))[Name];
  }
  FProfile->setName(Name);

  for (uint32_t I = 0; I < NumPosCounts; ++I) {
    uint32_t Offset;
    if (!GcovBuffer.readInt(Offset))
      return sampleprof_error::truncated;

    uint32_t NumTargets;
    if (!GcovBuffer.readInt(NumTargets))
      return sampleprof_error::truncated;

    uint64_t Count;
    if (!GcovBuffer.readInt64(Count))
      return sampleprof_error::truncated;

    // The line location is encoded in the offset as:
    //   high 16 bits: line offset to the start of the function.
    //   low 16 bits: discriminator.
    uint32_t LineOffset = Offset >> 16;
    uint32_t Discriminator = Offset & 0xffff;

    InlineCallStack NewStack;
    NewStack.push_back(FProfile);
    NewStack.insert(NewStack.end(), InlineStack.begin(), InlineStack.end());
    if (Update) {
      // Walk up the inline stack, adding the samples on this line to
      // the total sample count of the callers in the chain.
      for (auto CallerProfile : NewStack)
        CallerProfile->addTotalSamples(Count);

      // Update the body samples for the current profile.
      FProfile->addBodySamples(LineOffset, Discriminator, Count);
    }

    // Process the list of functions called at an indirect call site.
    // These are all the targets that a function pointer (or virtual
    // function) resolved at runtime.
    for (uint32_t J = 0; J < NumTargets; J++) {
      uint32_t HistVal;
      if (!GcovBuffer.readInt(HistVal))
        return sampleprof_error::truncated;

      if (HistVal != HIST_TYPE_INDIR_CALL_TOPN)
        return sampleprof_error::malformed;

      uint64_t TargetIdx;
      if (!GcovBuffer.readInt64(TargetIdx))
        return sampleprof_error::truncated;
      StringRef TargetName(Names[TargetIdx]);

      uint64_t TargetCount;
      if (!GcovBuffer.readInt64(TargetCount))
        return sampleprof_error::truncated;

      if (Update)
        FProfile->addCalledTargetSamples(LineOffset, Discriminator,
                                         TargetName, TargetCount);
    }
  }

  // Process all the inlined callers into the current function. These
  // are all the callsites that were inlined into this function.
  for (uint32_t I = 0; I < NumCallsites; I++) {
    // The offset is encoded as:
    //   high 16 bits: line offset to the start of the function.
    //   low 16 bits: discriminator.
    uint32_t Offset;
    if (!GcovBuffer.readInt(Offset))
      return sampleprof_error::truncated;
    InlineCallStack NewStack;
    NewStack.push_back(FProfile);
    NewStack.insert(NewStack.end(), InlineStack.begin(), InlineStack.end());
    if (std::error_code EC = readOneFunctionProfile(NewStack, Update, Offset))
      return EC;
  }

  return sampleprof_error::success;
}