# Copyright 2016 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import("//build/config/chrome_build.gni")
import("//build/config/clang/clang.gni")
import("//build/config/compiler/compiler.gni")
import("//build/config/compiler/pgo/pgo.gni")
import("//build/config/features.gni")
import("//build/toolchain/toolchain.gni")

# Configuration that enables PGO instrumentation.
config("pgo_instrumentation_flags") {
  visibility = [ ":default_pgo_flags" ]

  # Only add flags when chrome_pgo_phase == 1, so that variables we would use
  # are not required to be defined when we're not actually using PGO.
  if (chrome_pgo_phase == 1 && is_clang && is_a_target_toolchain) {
    cflags = [ "-fprofile-generate" ]
    if (temporal_pgo_profile) {
      cflags += [
        "-mllvm",
        "-pgo-temporal-instrumentation",
      ]
    }
    if (!is_win) {
      # Windows directly calls link.exe instead of the compiler driver when
      # linking, and embeds the path to the profile runtime library as
      # dependent library into each object file.
      ldflags = [ "-fprofile-generate" ]
    }
  }
}

# Configuration that enables optimization using profile data.
config("pgo_optimization_flags") {
  visibility = [ ":default_pgo_flags" ]

  # Only add flags when chrome_pgo_phase == 2, so that variables we would use
  # are not required to be defined when we're not actually using PGO.
  if (chrome_pgo_phase == 2 && is_clang && is_a_target_toolchain) {
    _pgo_target = ""

    # There are txt files used by //tools/update_pgo_profiles.py to decide which
    # profiles to use, adding them as inputs so that analyzer recognizes the
    # dependencies.
    inputs = []

    if (is_win) {
      if (target_cpu == "arm64") {
        _pgo_target = "win-arm64"
      } else if (target_cpu == "x64") {
        _pgo_target = "win64"
      } else {
        _pgo_target = "win32"
      }
    } else if (is_mac) {
      if (target_cpu == "arm64") {
        _pgo_target = "mac-arm"
      } else {
        _pgo_target = "mac"
      }
    } else if (is_linux) {
      _pgo_target = "linux"
    } else if (is_android) {
      # Use |current_cpu| and not |target_cpu|; for Android we may built both.
      if (is_desktop_android && current_cpu == "x64") {
        _pgo_target = "android-desktop-x64"
      } else if (current_cpu == "arm64") {
        _pgo_target = "android-arm64"
      } else {
        _pgo_target = "android-arm32"
      }
    } else if (is_fuchsia) {
      if (target_cpu == "arm64") {
        _pgo_target = "mac-arm"
      } else {
        _pgo_target = "mac"
      }
    } else if (is_ios && use_blink) {
      if (target_cpu == "arm64") {
        _pgo_target = "mac-arm"
      } else {
        _pgo_target = "mac"
      }
    }

    if (_pgo_target == "win-arm64") {
      inputs = [ "//chrome/build/win-arm64.pgo.txt" ]
    } else if (_pgo_target == "win64") {
      inputs = [ "//chrome/build/win64.pgo.txt" ]
    } else if (_pgo_target == "win32") {
      inputs = [ "//chrome/build/win32.pgo.txt" ]
    } else if (_pgo_target == "mac-arm") {
      inputs = [ "//chrome/build/mac-arm.pgo.txt" ]
    } else if (_pgo_target == "mac") {
      inputs = [ "//chrome/build/mac.pgo.txt" ]
    } else if (_pgo_target == "linux") {
      inputs = [ "//chrome/build/linux.pgo.txt" ]
    } else if (_pgo_target == "android-arm32") {
      inputs = [ "//chrome/build/android-arm32.pgo.txt" ]
    } else if (_pgo_target == "android-desktop-x64") {
      inputs = [ "//chrome/build/android-desktop-x64.pgo.txt" ]
    }

    if (_pgo_target != "" && pgo_data_path == "") {
      common_args = [
        "--target",
        _pgo_target,
      ]
      if (pgo_override_filename != "") {
        common_args += [
          "--override-filename",
          pgo_override_filename,
        ]

        # This is slow but it is only expected to be run on the orderfile bots
        # that require this special step.
        exec_script("//tools/update_pgo_profiles.py",
                    common_args + [
                          "update",
                          "--gs-url-base=$pgo_gs_bucket/$pgo_gs_bucket_path",
                        ])
      }
      pgo_data_path = exec_script("//tools/update_pgo_profiles.py",
                                  common_args + [ "get_profile_path" ],
                                  "value")
    }
    assert(pgo_data_path != "",
           "Please set pgo_data_path to point at the profile data")

    # Ensure that the actual PGO profile that is used in the build is also
    # listed in inputs in addition to any sha1 .txt files. This way the object
    # files are rebuilt whenever the .txt files change or whenever the actual
    # profile changes.
    inputs += [ pgo_data_path ]

    cflags = [
      #"-fprofile-use=" + rebase_path(pgo_data_path, root_build_dir),

      # It's possible to have some profile data legitimately missing,
      # and at least some profile data always ends up being considered
      # out of date, so make sure we don't error for those cases.
      "-Wno-profile-instr-unprofiled",
      "-Wno-profile-instr-out-of-date",

      # Some hashing conflict results in a lot of warning like this when doing
      # a PGO build:
      #   warning: foo.cc: Function control flow change detected (hash mismatch)
      #   [-Wbackend-plugin]
      # See https://crbug.com/978401
      "-Wno-backend-plugin",
    ]

    # TODO(crbug.com/383537825): Try enabling on all platforms.
    if (is_android || is_fuchsia) {
      cflags += [
        "-mllvm",
        "-pgo-cold-func-opt=minsize",
      ]
    }

    # Enable basic block layout based on the extended TSP problem. This aims to
    # improve icache utilization and reduce the binary size.
    if (use_thin_lto) {
      if (is_win) {
        ldflags = [ "-mllvm:-enable-ext-tsp-block-placement=1" ]
      } else {
        ldflags = [ "-Wl,-mllvm,-enable-ext-tsp-block-placement=1" ]
      }
    } else {
      cflags += [
        "-mllvm",
        "-enable-ext-tsp-block-placement=1",
      ]
    }
  }
}

# Applies flags necessary when profile-guided optimization is used.
# Flags are only added if PGO is enabled, so that this config is safe to
# include by default.
config("default_pgo_flags") {
  if (chrome_pgo_phase == 0) {
    # Nothing. This config should be a no-op when chrome_pgo_phase == 0.
  } else if (chrome_pgo_phase == 1) {
    configs = [ ":pgo_instrumentation_flags" ]
  } else if (chrome_pgo_phase == 2) {
    configs = [ ":pgo_optimization_flags" ]
  }
}