Version 2.19.0-61.0.dev

Merge commit 'a6f56aec88ddbfec4589997cc03a85b924b5f8e2' into 'dev'
diff --git a/build/find_depot_tools.py b/build/find_depot_tools.py
new file mode 100644
index 0000000..903fc25
--- /dev/null
+++ b/build/find_depot_tools.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# For Dart/Flutter developers:
+# This file is copied from Chromium:
+#   https://cs.chromium.org/chromium/src/build/find_depot_tools.py
+# When updating replace reference to python on the first line with python3.
+#
+"""Small utility function to find depot_tools and add it to the python path.
+
+Will throw an ImportError exception if depot_tools can't be found since it
+imports breakpad.
+
+This can also be used as a standalone script to print out the depot_tools
+directory location.
+"""
+
+from __future__ import print_function
+
+import os
+import sys
+
+
+# Path to //src
+SRC = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
+
+
+def IsRealDepotTools(path):
+  expanded_path = os.path.expanduser(path)
+  return os.path.isfile(os.path.join(expanded_path, 'gclient.py'))
+
+
+def add_depot_tools_to_path():
+  """Search for depot_tools and add it to sys.path."""
+  # First, check if we have a DEPS'd in "depot_tools".
+  deps_depot_tools = os.path.join(SRC, 'third_party', 'depot_tools')
+  if IsRealDepotTools(deps_depot_tools):
+    # Put the pinned version at the start of the sys.path, in case there
+    # are other non-pinned versions already on the sys.path.
+    sys.path.insert(0, deps_depot_tools)
+    return deps_depot_tools
+
+  # Then look if depot_tools is already in PYTHONPATH.
+  for i in sys.path:
+    if i.rstrip(os.sep).endswith('depot_tools') and IsRealDepotTools(i):
+      return i
+  # Then look if depot_tools is in PATH, common case.
+  for i in os.environ['PATH'].split(os.pathsep):
+    if IsRealDepotTools(i):
+      sys.path.append(i.rstrip(os.sep))
+      return i
+  # Rare case, it's not even in PATH, look upward up to root.
+  root_dir = os.path.dirname(os.path.abspath(__file__))
+  previous_dir = os.path.abspath(__file__)
+  while root_dir and root_dir != previous_dir:
+    i = os.path.join(root_dir, 'depot_tools')
+    if IsRealDepotTools(i):
+      sys.path.append(i)
+      return i
+    previous_dir = root_dir
+    root_dir = os.path.dirname(root_dir)
+  print('Failed to find depot_tools', file=sys.stderr)
+  return None
+
+DEPOT_TOOLS_PATH = add_depot_tools_to_path()
+
+# pylint: disable=W0611
+import breakpad
+
+
+def main():
+  if DEPOT_TOOLS_PATH is None:
+    return 1
+  print(DEPOT_TOOLS_PATH)
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/toolchain/win/BUILD.gn b/build/toolchain/win/BUILD.gn
index d70d49e..bc54953 100644
--- a/build/toolchain/win/BUILD.gn
+++ b/build/toolchain/win/BUILD.gn
@@ -146,9 +146,10 @@
     }
 
     tool("solink") {
-      dllname = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"  # e.g. foo.dll
-      libname =
-          "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib"  # e.g. foo.dll.lib
+      dllname = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}"  # e.g.
+                                                                               # foo.dll
+      libname = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib"  # e.g.
+                                                                                   # foo.dll.lib
       rspfile = "${dllname}.rsp"
 
       link_command = "$python_path $tool_wrapper_path link-wrapper $env False link.exe /nologo /IMPLIB:$libname /DLL /OUT:$dllname /PDB:${dllname}.pdb @$rspfile"
@@ -175,7 +176,9 @@
     }
 
     tool("solink_module") {
-      dllname = "{{output_dir}}/{{target_output_name}}{{output_extension}}"  # e.g. foo.dll
+      dllname =
+          "{{output_dir}}/{{target_output_name}}{{output_extension}}"  # e.g.
+                                                                       # foo.dll
       pdbname = "${dllname}.pdb"
       rspfile = "${dllname}.rsp"
 
@@ -230,8 +233,7 @@
     }
 
     tool("copy") {
-      command =
-          "$python_path $tool_wrapper_path recursive-mirror {{source}} {{output}}"
+      command = "$python_path $tool_wrapper_path recursive-mirror {{source}} {{output}}"
       description = "COPY {{source}} {{output}}"
     }
   }
@@ -246,7 +248,9 @@
                                      visual_studio_path,
                                      windows_sdk_path,
                                      visual_studio_runtime_dirs,
+                                     "win",
                                      toolchain_arch,
+                                     "environment." + toolchain_arch,
                                    ],
                                    "scope")
 
diff --git a/build/toolchain/win/setup_toolchain.py b/build/toolchain/win/setup_toolchain.py
index 10b878a..e680ba0 100644
--- a/build/toolchain/win/setup_toolchain.py
+++ b/build/toolchain/win/setup_toolchain.py
@@ -10,63 +10,183 @@
 # win tool. The script assumes that the root build directory is the current dir
 # and the files will be written to the current directory.
 
+from __future__ import print_function
+
 import errno
+import json
 import os
 import re
 import subprocess
 import sys
 
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
+import gn_helpers
+
+SCRIPT_DIR = os.path.dirname(__file__)
 
 def _ExtractImportantEnvironment(output_of_set):
   """Extracts environment variables required for the toolchain to run from
   a textual dump output by the cmd.exe 'set' command."""
   envvars_to_save = (
+      'cipd_cache_dir', # needed by vpython
+      'homedrive', # needed by vpython
+      'homepath', # needed by vpython
       'goma_.*', # TODO(scottmg): This is ugly, but needed for goma.
       'include',
       'lib',
       'libpath',
+      'luci_context', # needed by vpython
       'path',
       'pathext',
       'systemroot',
       'temp',
       'tmp',
+      'userprofile', # needed by vpython
+      'vpython_virtualenv_root' # needed by vpython
       )
   env = {}
+  # This occasionally happens and leads to misleading SYSTEMROOT error messages
+  # if not caught here.
+  if output_of_set.count('=') == 0:
+    raise Exception('Invalid output_of_set. Value is:\n%s' % output_of_set)
   for line in output_of_set.splitlines():
     for envvar in envvars_to_save:
-      if re.match(envvar + '=', line.decode().lower()):
-        var, setting = line.decode().split('=', 1)
+      if re.match(envvar + '=', line.lower()):
+        var, setting = line.split('=', 1)
         if envvar == 'path':
-          # Our own rules (for running gyp-win-tool) and other actions in
-          # Chromium rely on python being in the path. Add the path to this
-          # python here so that if it's not in the path when ninja is run
-          # later, python will still be found.
+          # Our own rules and actions in Chromium rely on python being in the
+          # path. Add the path to this python here so that if it's not in the
+          # path when ninja is run later, python will still be found.
           setting = os.path.dirname(sys.executable) + os.pathsep + setting
+        if envvar in ['include', 'lib']:
+          # Make sure that the include and lib paths point to directories that
+          # exist. This ensures a (relatively) clear error message if the
+          # required SDK is not installed.
+          for part in setting.split(';'):
+            if not os.path.exists(part) and len(part) != 0:
+              raise Exception(
+                  'Path "%s" from environment variable "%s" does not exist. '
+                  'Make sure the necessary SDK is installed.' % (part, envvar))
         env[var.upper()] = setting
         break
-  for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
-    if required not in env:
-      raise Exception('Environment variable "%s" '
-                      'required to be set to valid path' % required)
+  if sys.platform in ('win32', 'cygwin'):
+    for required in ('SYSTEMROOT', 'TEMP', 'TMP'):
+      if required not in env:
+        raise Exception('Environment variable "%s" '
+                        'required to be set to valid path' % required)
   return env
 
 
-def _SetupScript(target_cpu, sdk_dir):
-  """Returns a command (with arguments) to be used to set up the
-  environment."""
+def _DetectVisualStudioPath():
+  """Return path to the installed Visual Studio.
+  """
+
+  # Use the code in build/vs_toolchain.py to avoid duplicating code.
+  chromium_dir = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..', '..'))
+  sys.path.append(os.path.join(chromium_dir, 'build'))
+  import vs_toolchain
+  return vs_toolchain.DetectVisualStudioPath()
+
+
+def _LoadEnvFromBat(args):
+  """Given a bat command, runs it and returns env vars set by it."""
+  args = args[:]
+  args.extend(('&&', 'set'))
+  popen = subprocess.Popen(
+      args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+  variables, _ = popen.communicate()
+  if popen.returncode != 0:
+    raise Exception('"%s" failed with error %d' % (args, popen.returncode))
+  return variables.decode(errors='ignore')
+
+
+def _LoadToolchainEnv(cpu, toolchain_root, sdk_dir, target_store):
+  """Returns a dictionary with environment variables that must be set while
+  running binaries from the toolchain (e.g. INCLUDE and PATH for cl.exe)."""
   # Check if we are running in the SDK command line environment and use
-  # the setup script from the SDK if so.
-  assert target_cpu in ('x86', 'x64', 'arm64')
+  # the setup script from the SDK if so. |cpu| should be either
+  # 'x86' or 'x64' or 'arm' or 'arm64'.
+  assert cpu in ('x86', 'x64', 'arm', 'arm64')
   if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and sdk_dir:
-    return [os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.Cmd')),
-            '/' + target_cpu]
+    # Load environment from json file.
+    env = os.path.normpath(os.path.join(sdk_dir, 'bin/SetEnv.%s.json' % cpu))
+    env = json.load(open(env))['env']
+    if env['VSINSTALLDIR'] == [["..", "..\\"]]:
+      # Old-style paths were relative to the win_sdk\bin directory.
+      json_relative_dir = os.path.join(sdk_dir, 'bin')
+    else:
+      # New-style paths are relative to the toolchain directory.
+      json_relative_dir = toolchain_root
+    for k in env:
+      entries = [os.path.join(*([json_relative_dir] + e)) for e in env[k]]
+      # clang-cl wants INCLUDE to be ;-separated even on non-Windows,
+      # lld-link wants LIB to be ;-separated even on non-Windows.  Path gets :.
+      # The separator for INCLUDE here must match the one used in main() below.
+      sep = os.pathsep if k == 'PATH' else ';'
+      env[k] = sep.join(entries)
+    # PATH is a bit of a special case, it's in addition to the current PATH.
+    env['PATH'] = env['PATH'] + os.pathsep + os.environ['PATH']
+    # Augment with the current env to pick up TEMP and friends.
+    for k in os.environ:
+      if k not in env:
+        env[k] = os.environ[k]
+
+    varlines = []
+    for k in sorted(env.keys()):
+      varlines.append('%s=%s' % (str(k), str(env[k])))
+    variables = '\n'.join(varlines)
+
+    # Check that the json file contained the same environment as the .cmd file.
+    if sys.platform in ('win32', 'cygwin'):
+      script = os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.cmd'))
+      arg = '/' + cpu
+      json_env = _ExtractImportantEnvironment(variables)
+      cmd_env = _ExtractImportantEnvironment(_LoadEnvFromBat([script, arg]))
+      assert _LowercaseDict(json_env) == _LowercaseDict(cmd_env)
   else:
+    if 'GYP_MSVS_OVERRIDE_PATH' not in os.environ:
+      os.environ['GYP_MSVS_OVERRIDE_PATH'] = _DetectVisualStudioPath()
     # We only support x64-hosted tools.
-    # TODO(scottmg|dpranke): Non-depot_tools toolchain: need to get Visual
-    # Studio install location from registry.
-    return [os.path.normpath(os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'],
-                                          'VC/Auxiliary/Build/vcvarsall.bat')),
-            'amd64_x86' if target_cpu == 'x86' else 'amd64']
+    script_path = os.path.normpath(os.path.join(
+                                       os.environ['GYP_MSVS_OVERRIDE_PATH'],
+                                       'VC/vcvarsall.bat'))
+    if not os.path.exists(script_path):
+      # vcvarsall.bat for VS 2017 fails if run after running vcvarsall.bat from
+      # VS 2013 or VS 2015. Fix this by clearing the vsinstalldir environment
+      # variable. Since vcvarsall.bat appends to the INCLUDE, LIB, and LIBPATH
+      # environment variables we need to clear those to avoid getting double
+      # entries when vcvarsall.bat has been run before gn gen. vcvarsall.bat
+      # also adds to PATH, but there is no clean way of clearing that and it
+      # doesn't seem to cause problems.
+      if 'VSINSTALLDIR' in os.environ:
+        del os.environ['VSINSTALLDIR']
+        if 'INCLUDE' in os.environ:
+          del os.environ['INCLUDE']
+        if 'LIB' in os.environ:
+          del os.environ['LIB']
+        if 'LIBPATH' in os.environ:
+          del os.environ['LIBPATH']
+      other_path = os.path.normpath(os.path.join(
+                                        os.environ['GYP_MSVS_OVERRIDE_PATH'],
+                                        'VC/Auxiliary/Build/vcvarsall.bat'))
+      if not os.path.exists(other_path):
+        raise Exception('%s is missing - make sure VC++ tools are installed.' %
+                        script_path)
+      script_path = other_path
+    cpu_arg = "amd64"
+    if (cpu != 'x64'):
+      # x64 is default target CPU thus any other CPU requires a target set
+      cpu_arg += '_' + cpu
+    args = [script_path, cpu_arg, ]
+    # Store target must come before any SDK version declaration
+    if (target_store):
+      args.append('store')
+    # Explicitly specifying the SDK version to build with to avoid accidentally
+    # building with a new and untested SDK. This should stay in sync with the
+    # packaged toolchain in build/vs_toolchain.py.
+    args.append('10.0.20348.0')
+    variables = _LoadEnvFromBat(args)
+  return _ExtractImportantEnvironment(variables)
 
 
 def _FormatAsEnvironmentBlock(envvar_dict):
@@ -81,76 +201,114 @@
   return block
 
 
-def _CopyTool(source_path):
-  """Copies the given tool to the current directory, including a warning not
-  to edit it."""
-  with open(source_path) as source_file:
-    tool_source = source_file.readlines()
+def _LowercaseDict(d):
+  """Returns a copy of `d` with both key and values lowercased.
 
-  # Add header and write it out to the current directory (which should be the
-  # root build dir).
-  with open("gyp-win-tool", 'w') as tool_file:
-    tool_file.write(''.join([tool_source[0],
-                             '# Generated by setup_toolchain.py do not edit.\n']
-                            + tool_source[1:]))
+  Args:
+    d: dict to lowercase (e.g. {'A': 'BcD'}).
+
+  Returns:
+    A dict with both keys and values lowercased (e.g.: {'a': 'bcd'}).
+  """
+  return {k.lower(): d[k].lower() for k in d}
+
+
+def FindFileInEnvList(env, env_name, separator, file_name, optional=False):
+  parts = env[env_name].split(separator)
+  for path in parts:
+    if os.path.exists(os.path.join(path, file_name)):
+      return os.path.realpath(path)
+  assert optional, "%s is not found in %s:\n%s\nCheck if it is installed." % (
+      file_name, env_name, '\n'.join(parts))
+  return ''
 
 
 def main():
-  if len(sys.argv) != 5:
+  if len(sys.argv) != 7:
     print('Usage setup_toolchain.py '
           '<visual studio path> <win sdk path> '
-          '<runtime dirs> <target_cpu>')
+          '<runtime dirs> <target_os> <target_cpu> '
+          '<environment block name|none>')
     sys.exit(2)
+  # toolchain_root and win_sdk_path are only read if the hermetic Windows
+  # toolchain is set, that is if DEPOT_TOOLS_WIN_TOOLCHAIN is not set to 0.
+  # With the hermetic Windows toolchain, the visual studio path in argv[1]
+  # is the root of the Windows toolchain directory.
+  toolchain_root = sys.argv[1]
   win_sdk_path = sys.argv[2]
-  runtime_dirs = sys.argv[3]
-  target_cpu = sys.argv[4]
 
-  cpus = ('x86', 'x64', 'arm64')
+  runtime_dirs = sys.argv[3]
+  target_os = sys.argv[4]
+  target_cpu = sys.argv[5]
+  environment_block_name = sys.argv[6]
+  if (environment_block_name == 'none'):
+    environment_block_name = ''
+
+  if (target_os == 'winuwp'):
+    target_store = True
+  else:
+    target_store = False
+
+  cpus = ('x86', 'x64', 'arm', 'arm64')
   assert target_cpu in cpus
   vc_bin_dir = ''
+  include = ''
+  lib = ''
 
   # TODO(scottmg|goma): Do we need an equivalent of
   # ninja_use_custom_environment_files?
 
+  def relflag(s):  # Make s relative to builddir when cwd and sdk on same drive.
+    try:
+      return os.path.relpath(s).replace('\\', '/')
+    except ValueError:
+      return s
+
+  def q(s):  # Quote s if it contains spaces or other weird characters.
+    return s if re.match(r'^[a-zA-Z0-9._/\\:-]*$', s) else '"' + s + '"'
+
   for cpu in cpus:
-    # Extract environment variables for subprocesses.
-    args = _SetupScript(cpu, win_sdk_path)
-    args.extend(('&&', 'set'))
-    popen = subprocess.Popen(
-        args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-    stdout_data, stderr_data = popen.communicate()
-    if popen.returncode != 0:
-      print('Error, got returncode:', popen.returncode)
-      print('## stdout:')
-      print(stdout_data)
-      print('## stderr:')
-      print(stderr_data)
-      sys.exit(2)
-    env = _ExtractImportantEnvironment(stdout_data)
-    env['PATH'] = runtime_dirs + ';' + env['PATH']
-
     if cpu == target_cpu:
-      for path in env['PATH'].split(os.pathsep):
-        if os.path.exists(os.path.join(path, 'cl.exe')):
-          vc_bin_dir = os.path.realpath(path)
-          break
+      # Extract environment variables for subprocesses.
+      env = _LoadToolchainEnv(cpu, toolchain_root, win_sdk_path, target_store)
+      env['PATH'] = runtime_dirs + os.pathsep + env['PATH']
 
-    # The Windows SDK include directories must be first. They both have a sal.h,
-    # and the SDK one is newer and the SDK uses some newer features from it not
-    # present in the Visual Studio one.
+      vc_bin_dir = FindFileInEnvList(env, 'PATH', os.pathsep, 'cl.exe')
 
-    if win_sdk_path:
-      additional_includes = ('{sdk_dir}\\Include\\shared;' +
-                             '{sdk_dir}\\Include\\um;' +
-                             '{sdk_dir}\\Include\\winrt;').format(
-                                  sdk_dir=win_sdk_path)
-      env['INCLUDE'] = additional_includes + env['INCLUDE']
-    env_block = _FormatAsEnvironmentBlock(env)
-    with open('environment.' + cpu, 'wb') as f:
-      f.write(env_block.encode())
+      # The separator for INCLUDE here must match the one used in
+      # _LoadToolchainEnv() above.
+      include = [p.replace('"', r'\"') for p in env['INCLUDE'].split(';') if p]
+      include = list(map(relflag, include))
 
-  assert vc_bin_dir
-  print('vc_bin_dir = "%s"' % vc_bin_dir)
+      lib = [p.replace('"', r'\"') for p in env['LIB'].split(';') if p]
+      lib = list(map(relflag, lib))
+
+      include_I = ' '.join([q('/I' + i) for i in include])
+      include_imsvc = ' '.join([q('-imsvc' + i) for i in include])
+      libpath_flags = ' '.join([q('-libpath:' + i) for i in lib])
+
+      if (environment_block_name != ''):
+        env_block = _FormatAsEnvironmentBlock(env)
+        with open(environment_block_name, 'w') as f:
+          f.write(env_block)
+
+  print('vc_bin_dir = ' + gn_helpers.ToGNString(vc_bin_dir))
+  assert include_I
+  print('include_flags_I = ' + gn_helpers.ToGNString(include_I))
+  assert include_imsvc
+  if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and win_sdk_path:
+    print('include_flags_imsvc = ' +
+          gn_helpers.ToGNString(q('/winsysroot' + relflag(toolchain_root))))
+  else:
+    print('include_flags_imsvc = ' + gn_helpers.ToGNString(include_imsvc))
+  print('paths = ' + gn_helpers.ToGNString(env['PATH']))
+  assert libpath_flags
+  print('libpath_flags = ' + gn_helpers.ToGNString(libpath_flags))
+  if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and win_sdk_path:
+    print('libpath_lldlink_flags = ' +
+          gn_helpers.ToGNString(q('/winsysroot:' + relflag(toolchain_root))))
+  else:
+    print('libpath_lldlink_flags = ' + gn_helpers.ToGNString(libpath_flags))
 
 
 if __name__ == '__main__':
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 6314e5e..9b0a7c8 100644
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -1,18 +1,14 @@
 #!/usr/bin/env python3
-#
 # Copyright 2014 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
+#
 # For Dart/Flutter developers:
-# This file keeps the MSVC toolchain up-to-date for Google developers.
-# It is copied from Chromium:
+# This file is copied from Chromium:
 #   https://cs.chromium.org/chromium/src/build/vs_toolchain.py
-# with modifications that update paths, and remove dependencies on gyp.
-# To update to a new MSVC toolchain, copy the updated script from the Chromium
-# tree, and edit to make it work in the Dart tree by updating paths in the original script.
-
-
+# When updating replace reference to python on the first line with python3.
+#
+from __future__ import print_function
 
 import collections
 import glob
@@ -26,21 +22,47 @@
 import subprocess
 import sys
 
-
 from gn_helpers import ToGNString
 
+# VS 2019 16.61 with 10.0.20348.0 SDK, 10.0.19041 version of Debuggers
+# with ARM64 libraries and UWP support.
+# See go/chromium-msvc-toolchain for instructions about how to update the
+# toolchain.
+#
+# When updating the toolchain, consider the following areas impacted by the
+# toolchain version:
+#
+# * //base/win/windows_version.cc NTDDI preprocessor check
+#   Triggers a compiler error if the available SDK is older than the minimum.
+# * //build/config/win/BUILD.gn NTDDI_VERSION value
+#   Affects the availability of APIs in the toolchain headers.
+# * //docs/windows_build_instructions.md mentions of VS or Windows SDK.
+#   Keeps the document consistent with the toolchain version.
+TOOLCHAIN_HASH = '1023ce2e82'
+
 script_dir = os.path.dirname(os.path.realpath(__file__))
 json_data_file = os.path.join(script_dir, 'win_toolchain.json')
 
-sys.path.insert(0, os.path.join(script_dir, '..', 'tools'))
-
 # VS versions are listed in descending order of priority (highest first).
+# The first version is assumed by this script to be the one that is packaged,
+# which makes a difference for the arm64 runtime.
 MSVS_VERSIONS = collections.OrderedDict([
-  ('2017', '15.0'),
-  ('2019', '16.0'),
-  ('2022', '17.0'),
+    ('2019', '16.0'),  # Default and packaged version of Visual Studio.
+    ('2022', '17.0'),
+    ('2017', '15.0'),
 ])
 
+# List of preferred VC toolset version based on MSVS
+# Order is not relevant for this dictionary.
+MSVC_TOOLSET_VERSION = {
+    '2022': 'VC143',
+    '2019': 'VC142',
+    '2017': 'VC141',
+}
+
+def _HostIsWindows():
+  """Returns True if running on a Windows host (including under cygwin)."""
+  return sys.platform in ('win32', 'cygwin')
 
 def SetEnvironmentAndGetRuntimeDllDirs():
   """Sets up os.environ to use the depot_tools VS toolchain with gyp, and
@@ -56,7 +78,7 @@
       bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
   # When running on a non-Windows host, only do this if the SDK has explicitly
   # been downloaded before (in which case json_data_file will exist).
-  if ((sys.platform in ('win32', 'cygwin') or os.path.exists(json_data_file))
+  if ((_HostIsWindows() or os.path.exists(json_data_file))
       and depot_tools_win_toolchain):
     if ShouldUpdateToolchain():
       if len(sys.argv) > 1 and sys.argv[1] == 'update':
@@ -71,8 +93,6 @@
     toolchain = toolchain_data['path']
     version = toolchain_data['version']
     win_sdk = toolchain_data.get('win_sdk')
-    if not win_sdk:
-      win_sdk = toolchain_data['win8sdk']
     wdk = toolchain_data['wdk']
     # TODO(scottmg): The order unfortunately matters in these. They should be
     # split into separate keys for x64/x86/arm64. (See CopyDlls call below).
@@ -85,7 +105,6 @@
       vs_runtime_dll_dirs.append('Arm64Unused')
 
     os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain
-    os.environ['GYP_MSVS_VERSION'] = version
 
     os.environ['WINDOWSSDKDIR'] = win_sdk
     os.environ['WDK_DIR'] = wdk
@@ -95,8 +114,6 @@
   elif sys.platform == 'win32' and not depot_tools_win_toolchain:
     if not 'GYP_MSVS_OVERRIDE_PATH' in os.environ:
       os.environ['GYP_MSVS_OVERRIDE_PATH'] = DetectVisualStudioPath()
-    if not 'GYP_MSVS_VERSION' in os.environ:
-      os.environ['GYP_MSVS_VERSION'] = GetVisualStudioVersion()
 
     # When using an installed toolchain these files aren't needed in the output
     # directory in order to run binaries locally, but they are needed in order
@@ -126,12 +143,12 @@
     contents of the registry key's value, or None on failure.  Throws
     ImportError if _winreg is unavailable.
   """
-  import winreg
+  import _winreg
   try:
     root, subkey = key.split('\\', 1)
     assert root == 'HKLM'  # Only need HKLM for now.
-    with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, subkey) as hkey:
-      return winreg.QueryValueEx(hkey, value)[0]
+    with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, subkey) as hkey:
+      return _winreg.QueryValueEx(hkey, value)[0]
   except WindowsError:
     return None
 
@@ -146,31 +163,38 @@
 def GetVisualStudioVersion():
   """Return best available version of Visual Studio.
   """
-
-  env_version = os.environ.get('GYP_MSVS_VERSION')
-  if env_version:
-    return env_version
-
   supported_versions = list(MSVS_VERSIONS.keys())
 
   # VS installed in depot_tools for Googlers
   if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))):
-    return list(supported_versions)[0]
+    return supported_versions[0]
 
   # VS installed in system for external developers
   supported_versions_str = ', '.join('{} ({})'.format(v,k)
-      for k,v in list(MSVS_VERSIONS.items()))
+      for k,v in MSVS_VERSIONS.items())
   available_versions = []
   for version in supported_versions:
-    for path in (
-        os.environ.get('vs%s_install' % version),
-        os.path.expandvars('%ProgramFiles(x86)%' +
-                           '/Microsoft Visual Studio/%s' % version),
-        os.path.expandvars('%ProgramFiles%' +
-                           '/Microsoft Visual Studio/%s' % version)):
-      if path and os.path.exists(path):
-        available_versions.append(version)
-        break
+    # Checking vs%s_install environment variables.
+    # For example, vs2019_install could have the value
+    # "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community".
+    # Only vs2017_install, vs2019_install and vs2022_install are supported.
+    path = os.environ.get('vs%s_install' % version)
+    if path and os.path.exists(path):
+      available_versions.append(version)
+      break
+    # Detecting VS under possible paths.
+    if version >= '2022':
+      program_files_path_variable = '%ProgramFiles%'
+    else:
+      program_files_path_variable = '%ProgramFiles(x86)%'
+    path = os.path.expandvars(program_files_path_variable +
+                              '/Microsoft Visual Studio/%s' % version)
+    if path and any(
+        os.path.exists(os.path.join(path, edition))
+        for edition in ('Enterprise', 'Professional', 'Community', 'Preview',
+                        'BuildTools')):
+      available_versions.append(version)
+      break
 
   if not available_versions:
     raise Exception('No supported Visual Studio can be found.'
@@ -179,7 +203,7 @@
 
 
 def DetectVisualStudioPath():
-  """Return path to the GYP_MSVS_VERSION of Visual Studio.
+  """Return path to the installed Visual Studio.
   """
 
   # Note that this code is used from
@@ -190,18 +214,30 @@
   # the registry. For details see:
   # https://blogs.msdn.microsoft.com/heaths/2016/09/15/changes-to-visual-studio-15-setup/
   # For now we use a hardcoded default with an environment variable override.
-  possible_install_paths = (
-      os.path.expandvars('%s/Microsoft Visual Studio/%s/%s' %
-                         (program_path_var, version_as_year, product))
-      for program_path_var in ('%ProgramFiles%', '%ProgramFiles(x86)%')
-      for product in ('Enterprise', 'Professional', 'Community', 'Preview'))
-  for path in (
-      os.environ.get('vs%s_install' % version_as_year), *possible_install_paths):
+  if version_as_year >= '2022':
+    program_files_path_variable = '%ProgramFiles%'
+  else:
+    program_files_path_variable = '%ProgramFiles(x86)%'
+  for path in (os.environ.get('vs%s_install' % version_as_year),
+               os.path.expandvars(program_files_path_variable +
+                                  '/Microsoft Visual Studio/%s/Enterprise' %
+                                  version_as_year),
+               os.path.expandvars(program_files_path_variable +
+                                  '/Microsoft Visual Studio/%s/Professional' %
+                                  version_as_year),
+               os.path.expandvars(program_files_path_variable +
+                                  '/Microsoft Visual Studio/%s/Community' %
+                                  version_as_year),
+               os.path.expandvars(program_files_path_variable +
+                                  '/Microsoft Visual Studio/%s/Preview' %
+                                  version_as_year),
+               os.path.expandvars(program_files_path_variable +
+                                  '/Microsoft Visual Studio/%s/BuildTools' %
+                                  version_as_year)):
     if path and os.path.exists(path):
       return path
 
-  raise Exception('Visual Studio Version %s (from GYP_MSVS_VERSION)'
-                  ' not found.' % version_as_year)
+  raise Exception('Visual Studio Version %s not found.' % version_as_year)
 
 
 def _CopyRuntimeImpl(target, source, verbose=True):
@@ -243,21 +279,30 @@
 
   list_of_str_versions.sort(key=to_number_sequence, reverse=True)
 
-def _CopyUCRTRuntime(target_dir, source_dir, target_cpu, dll_pattern, suffix):
+
+def _CopyUCRTRuntime(target_dir, source_dir, target_cpu, suffix):
   """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't
   exist, but the target directory does exist."""
   if target_cpu == 'arm64':
     # Windows ARM64 VCRuntime is located at {toolchain_root}/VC/Redist/MSVC/
-    # {x.y.z}/[debug_nonredist/]arm64/Microsoft.VC141.CRT/.
+    # {x.y.z}/[debug_nonredist/]arm64/Microsoft.VC14x.CRT/.
+    # Select VC toolset directory based on Visual Studio version
     vc_redist_root = FindVCRedistRoot()
     if suffix.startswith('.'):
+      vc_toolset_dir = 'Microsoft.{}.CRT' \
+         .format(MSVC_TOOLSET_VERSION[GetVisualStudioVersion()])
       source_dir = os.path.join(vc_redist_root,
-                                'arm64', 'Microsoft.VC141.CRT')
+                                'arm64', vc_toolset_dir)
     else:
+      vc_toolset_dir = 'Microsoft.{}.DebugCRT' \
+         .format(MSVC_TOOLSET_VERSION[GetVisualStudioVersion()])
       source_dir = os.path.join(vc_redist_root, 'debug_nonredist',
-                                'arm64', 'Microsoft.VC141.DebugCRT')
-  for file_part in ('msvcp', 'vccorlib', 'vcruntime'):
-    dll = dll_pattern % file_part
+                                'arm64', vc_toolset_dir)
+  file_parts = ('msvcp140', 'vccorlib140', 'vcruntime140')
+  if target_cpu == 'x64' and GetVisualStudioVersion() != '2017':
+    file_parts = file_parts + ('vcruntime140_1', )
+  for file_part in file_parts:
+    dll = file_part + suffix
     target = os.path.join(target_dir, dll)
     source = os.path.join(source_dir, dll)
     _CopyRuntimeImpl(target, source)
@@ -317,14 +362,13 @@
   assert ('GYP_MSVS_OVERRIDE_PATH' in os.environ)
   vc_component_msvc_root = os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'],
       'VC', component, 'MSVC')
-  vc_component_msvc_contents = os.listdir(vc_component_msvc_root)
+  vc_component_msvc_contents = glob.glob(
+      os.path.join(vc_component_msvc_root, '14.*'))
   # Select the most recent toolchain if there are several.
   _SortByHighestVersionNumberFirst(vc_component_msvc_contents)
   for directory in vc_component_msvc_contents:
-    if not os.path.isdir(os.path.join(vc_component_msvc_root, directory)):
-      continue
-    if re.match(r'14\.\d+\.\d+', directory):
-      return os.path.join(vc_component_msvc_root, directory)
+    if os.path.isdir(directory):
+      return directory
   raise Exception('Unable to find the VC %s directory.' % component)
 
 
@@ -342,8 +386,7 @@
   directory does exist. Handles VS 2015, 2017 and 2019."""
   suffix = 'd.dll' if debug else '.dll'
   # VS 2015, 2017 and 2019 use the same CRT DLLs.
-  _CopyUCRTRuntime(target_dir, source_dir, target_cpu, '%s140' + suffix,
-                    suffix)
+  _CopyUCRTRuntime(target_dir, source_dir, target_cpu, suffix)
 
 
 def CopyDlls(target_dir, configuration, target_cpu):
@@ -394,17 +437,20 @@
   # List of debug files that should be copied, the first element of the tuple is
   # the name of the file and the second indicates if it's optional.
   debug_files = [('dbghelp.dll', False), ('dbgcore.dll', True)]
+  # The UCRT is not a redistributable component on arm64.
+  if target_cpu != 'arm64':
+    debug_files.extend([('api-ms-win-downlevel-kernel32-l2-1-0.dll', False),
+                        ('api-ms-win-eventing-provider-l1-1-0.dll', False)])
   for debug_file, is_optional in debug_files:
     full_path = os.path.join(win_sdk_dir, 'Debuggers', target_cpu, debug_file)
     if not os.path.exists(full_path):
       if is_optional:
         continue
       else:
-        # TODO(crbug.com/773476): remove version requirement.
-        raise Exception('%s not found in "%s"\r\nYou must install the '
-                        '"Debugging Tools for Windows" feature from the Windows'
-                        ' 10 SDK.'
-                        % (debug_file, full_path))
+        raise Exception('%s not found in "%s"\r\nYou must install '
+                        'Windows 10 SDK version 10.0.20348.0 including the '
+                        '"Debugging Tools for Windows" feature.' %
+                        (debug_file, full_path))
     target_path = os.path.join(target_dir, debug_file)
     _CopyRuntimeImpl(target_path, full_path)
 
@@ -412,17 +458,10 @@
 def _GetDesiredVsToolchainHashes():
   """Load a list of SHA1s corresponding to the toolchains that we want installed
   to build with."""
-  env_version = GetVisualStudioVersion()
-  if env_version == '2017':
-    # VS 2017 Update 9 (15.9.12) with 10.0.18362 SDK, 10.0.17763 version of
-    # Debuggers, and 10.0.17134 version of d3dcompiler_47.dll, with ARM64
-    # libraries.
-    toolchain_hash = '418b3076791776573a815eb298c8aa590307af63'
-    # Third parties that do not have access to the canonical toolchain can map
-    # canonical toolchain version to their own toolchain versions.
-    toolchain_hash_mapping_key = 'GYP_MSVS_HASH_%s' % toolchain_hash
-    return [os.environ.get(toolchain_hash_mapping_key, toolchain_hash)]
-  raise Exception('Unsupported VS version %s' % env_version)
+  # Third parties that do not have access to the canonical toolchain can map
+  # canonical toolchain version to their own toolchain versions.
+  toolchain_hash_mapping_key = 'GYP_MSVS_HASH_%s' % TOOLCHAIN_HASH
+  return [os.environ.get(toolchain_hash_mapping_key, TOOLCHAIN_HASH)]
 
 
 def ShouldUpdateToolchain():
@@ -453,8 +492,7 @@
 
   depot_tools_win_toolchain = \
       bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1')))
-  if ((sys.platform in ('win32', 'cygwin') or force) and
-        depot_tools_win_toolchain):
+  if (_HostIsWindows() or force) and depot_tools_win_toolchain:
     import find_depot_tools
     depot_tools_path = find_depot_tools.add_depot_tools_to_path()
 
@@ -482,9 +520,6 @@
       subprocess.check_call([
           ciopfs, '-o', 'use_ino', toolchain_dir + '.ciopfs', toolchain_dir])
 
-    # Necessary so that get_toolchain_if_necessary.py will put the VS toolkit
-    # in the correct directory.
-    os.environ['GYP_MSVS_VERSION'] = GetVisualStudioVersion()
     get_toolchain_args = [
         sys.executable,
         os.path.join(depot_tools_path,
diff --git a/runtime/vm/compiler/backend/block_builder.h b/runtime/vm/compiler/backend/block_builder.h
index 4a36631..4566280 100644
--- a/runtime/vm/compiler/backend/block_builder.h
+++ b/runtime/vm/compiler/backend/block_builder.h
@@ -24,8 +24,7 @@
                                   flow_graph->inlining_id())),
         entry_(entry),
         current_(entry),
-        dummy_env_(
-            new Environment(0, 0, 0, flow_graph->parsed_function(), nullptr)) {
+        dummy_env_(new Environment(0, 0, 0, flow_graph->function(), nullptr)) {
     // Some graph transformations use environments from block entries.
     entry->SetEnvironment(dummy_env_);
   }
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index e7f9172..5330a4e 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -5867,9 +5867,9 @@
                                intptr_t fixed_parameter_count,
                                intptr_t lazy_deopt_pruning_count,
                                const ParsedFunction& parsed_function) {
-  Environment* env =
-      new (zone) Environment(definitions.length(), fixed_parameter_count,
-                             lazy_deopt_pruning_count, parsed_function, NULL);
+  Environment* env = new (zone)
+      Environment(definitions.length(), fixed_parameter_count,
+                  lazy_deopt_pruning_count, parsed_function.function(), NULL);
   for (intptr_t i = 0; i < definitions.length(); ++i) {
     env->values_.Add(new (zone) Value(definitions[i]));
   }
@@ -5882,9 +5882,9 @@
 
 Environment* Environment::DeepCopy(Zone* zone, intptr_t length) const {
   ASSERT(length <= values_.length());
-  Environment* copy = new (zone) Environment(
-      length, fixed_parameter_count_, LazyDeoptPruneCount(), parsed_function_,
-      (outer_ == NULL) ? NULL : outer_->DeepCopy(zone));
+  Environment* copy = new (zone)
+      Environment(length, fixed_parameter_count_, LazyDeoptPruneCount(),
+                  function_, (outer_ == NULL) ? NULL : outer_->DeepCopy(zone));
   copy->SetDeoptId(DeoptIdBits::decode(bitfield_));
   copy->SetLazyDeoptToBeforeDeoptId(LazyDeoptToBeforeDeoptId());
   if (locations_ != NULL) {
@@ -6355,16 +6355,10 @@
     intptr_t deopt_id,
     MethodRecognizer::Kind recognized_kind,
     const InstructionSource& source)
-    : PureDefinition(source, deopt_id),
-      inputs_(inputs),
+    : VariadicDefinition(inputs, source, deopt_id),
       recognized_kind_(recognized_kind),
       token_pos_(source.token_pos) {
   ASSERT(inputs_->length() == ArgumentCountFor(recognized_kind_));
-  for (intptr_t i = 0; i < inputs_->length(); ++i) {
-    ASSERT((*inputs)[i] != NULL);
-    (*inputs)[i]->set_instruction(this);
-    (*inputs)[i]->set_use_index(i);
-  }
 }
 
 intptr_t InvokeMathCFunctionInstr::ArgumentCountFor(
@@ -6599,7 +6593,7 @@
   }
 
   // Moves for arguments.
-  compiler::ffi::FrameRebase rebase(zone_, /*old_base=*/FPREG,
+  compiler::ffi::FrameRebase rebase(compiler->zone(), /*old_base=*/FPREG,
                                     /*new_base=*/saved_fp,
                                     /*stack_delta=*/0);
   intptr_t def_index = 0;
@@ -6627,7 +6621,8 @@
           : arg_target.IsMultiple() ? *arg_target.AsMultiple().locations()[i]
           : arg_target.IsPointerToMemory()
               ? arg_target.AsPointerToMemory().pointer_location()
-              : /*arg_target.IsStack()*/ arg_target.Split(zone_, num_defs, i);
+              : /*arg_target.IsStack()*/ arg_target.Split(compiler->zone(),
+                                                          num_defs, i);
 
       ConstantTemporaryAllocator temp_alloc(temp0);
       if (origin.IsConstant()) {
@@ -6692,8 +6687,8 @@
 
       // TypedData/Pointer data pointed to in temp.
       const auto& dst = compiler::ffi::NativeRegistersLocation(
-          zone_, pointer_loc.payload_type(), pointer_loc.container_type(),
-          temp0);
+          compiler->zone(), pointer_loc.payload_type(),
+          pointer_loc.container_type(), temp0);
       compiler->EmitNativeMove(dst, pointer_loc, &temp_alloc);
       __ LoadField(temp0,
                    compiler::FieldAddress(
@@ -6715,8 +6710,8 @@
       __ MoveRegister(temp0, SPREG);
       __ AddImmediate(temp0, sp_offset);
       const auto& src = compiler::ffi::NativeRegistersLocation(
-          zone_, pointer_loc.payload_type(), pointer_loc.container_type(),
-          temp0);
+          compiler->zone(), pointer_loc.payload_type(),
+          pointer_loc.container_type(), temp0);
       __ Comment("pointer_loc %s <- src %s", pointer_loc.ToCString(),
                  src.ToCString());
       compiler->EmitNativeMove(pointer_loc, src, &temp_alloc);
@@ -7001,21 +6996,15 @@
 }
 
 CCallInstr::CCallInstr(
-    Zone* zone,
     const compiler::ffi::NativeCallingConvention& native_calling_convention,
     InputsArray* inputs)
-    : Definition(DeoptId::kNone),
-      zone_(zone),
-      native_calling_convention_(native_calling_convention),
-      inputs_(inputs) {
+    : VariadicDefinition(inputs, DeoptId::kNone),
+      native_calling_convention_(native_calling_convention) {
 #ifdef DEBUG
   const intptr_t num_inputs =
       native_calling_convention.argument_locations().length() + 1;
   ASSERT(num_inputs == inputs->length());
 #endif
-  for (intptr_t i = 0, n = inputs_->length(); i < n; ++i) {
-    SetInputAt(i, (*inputs_)[i]);
-  }
 }
 
 Representation CCallInstr::RequiredInputRepresentation(intptr_t idx) const {
@@ -7037,7 +7026,7 @@
   }
 
   ConstantTemporaryAllocator temp_alloc(temp0);
-  compiler::ffi::FrameRebase rebase(zone_, /*old_base=*/FPREG,
+  compiler::ffi::FrameRebase rebase(compiler->zone(), /*old_base=*/FPREG,
                                     /*new_base=*/saved_fp,
                                     /*stack_delta=*/0);
 
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index af9f8b1..a9e3066 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -1488,7 +1488,7 @@
 // predecessors.  Targets are all other basic block entries.  The types
 // enforce edge-split form---joins are forbidden as the successors of
 // branches.
-class BlockEntryInstr : public Instruction {
+class BlockEntryInstr : public TemplateInstruction<0, NoThrow> {
  public:
   virtual intptr_t PredecessorCount() const = 0;
   virtual BlockEntryInstr* PredecessorAt(intptr_t index) const = 0;
@@ -1558,12 +1558,6 @@
                      GrowableArray<BlockEntryInstr*>* preorder,
                      GrowableArray<intptr_t>* parent);
 
-  virtual intptr_t InputCount() const { return 0; }
-  virtual Value* InputAt(intptr_t i) const {
-    UNREACHABLE();
-    return NULL;
-  }
-
   virtual bool CanBecomeDeoptimizationTarget() const {
     // BlockEntry environment is copied to Goto and Branch instructions
     // when we insert new blocks targeting this block.
@@ -1574,8 +1568,6 @@
 
   virtual bool HasUnknownSideEffects() const { return false; }
 
-  virtual bool MayThrow() const { return false; }
-
   intptr_t try_index() const { return try_index_; }
   void set_try_index(intptr_t index) { try_index_ = index; }
 
@@ -1629,7 +1621,7 @@
                   intptr_t try_index,
                   intptr_t deopt_id,
                   intptr_t stack_depth)
-      : Instruction(deopt_id),
+      : TemplateInstruction(deopt_id),
         block_id_(block_id),
         try_index_(try_index),
         stack_depth_(stack_depth),
@@ -1642,8 +1634,6 @@
                              BitVector* block_marks);
 
  private:
-  virtual void RawSetInputAt(intptr_t i, Value* value) { UNREACHABLE(); }
-
   virtual void ClearPredecessors() = 0;
   virtual void AddPredecessor(BlockEntryInstr* predecessor) = 0;
 
@@ -2253,17 +2243,6 @@
     }
   }
 
-  static bool Parse(const char* str, AliasIdentity* out) {
-#define VALUE_CASE(name, val)                                                  \
-  if (strcmp(str, #name) == 0) {                                               \
-    out->value_ = k##name;                                                     \
-    return true;                                                               \
-  }
-    FOR_EACH_ALIAS_IDENTITY_VALUE(VALUE_CASE)
-#undef VALUE_CASE
-    return false;
-  }
-
   bool IsUnknown() const { return value_ == kUnknown; }
   bool IsAliased() const { return value_ == kAliased; }
   bool IsNotAliased() const { return (value_ & kNotAliased) != 0; }
@@ -2553,18 +2532,67 @@
   virtual void RawSetInputAt(intptr_t i, Value* value) { inputs_[i] = value; }
 };
 
-class PhiInstr : public Definition {
+class VariadicDefinition : public Definition {
  public:
-  PhiInstr(JoinEntryInstr* block, intptr_t num_inputs)
-      : block_(block),
-        inputs_(num_inputs),
-        representation_(kTagged),
-        is_alive_(false),
-        is_receiver_(kUnknownReceiver) {
-    for (intptr_t i = 0; i < num_inputs; ++i) {
-      inputs_.Add(NULL);
+  explicit VariadicDefinition(InputsArray* inputs,
+                              intptr_t deopt_id = DeoptId::kNone)
+      : Definition(deopt_id), inputs_(inputs) {
+    for (intptr_t i = 0, n = inputs_->length(); i < n; ++i) {
+      SetInputAt(i, (*inputs_)[i]);
     }
   }
+  VariadicDefinition(InputsArray* inputs,
+                     const InstructionSource& source,
+                     intptr_t deopt_id = DeoptId::kNone)
+      : Definition(source, deopt_id), inputs_(inputs) {
+    for (intptr_t i = 0, n = inputs_->length(); i < n; ++i) {
+      SetInputAt(i, (*inputs_)[i]);
+    }
+  }
+
+  intptr_t InputCount() const { return inputs_->length(); }
+  Value* InputAt(intptr_t i) const { return (*inputs_)[i]; }
+
+ protected:
+  InputsArray* inputs_;
+
+ private:
+  void RawSetInputAt(intptr_t i, Value* value) { (*inputs_)[i] = value; }
+};
+
+class VariadicDefinitionWithEmbeddedInputs : public Definition {
+ public:
+  explicit VariadicDefinitionWithEmbeddedInputs(
+      const intptr_t num_inputs,
+      intptr_t deopt_id = DeoptId::kNone)
+      : Definition(deopt_id), inputs_(num_inputs) {
+    inputs_.EnsureLength(num_inputs, nullptr);
+  }
+  VariadicDefinitionWithEmbeddedInputs(const intptr_t num_inputs,
+                                       const InstructionSource& source,
+                                       intptr_t deopt_id = DeoptId::kNone)
+      : Definition(source, deopt_id), inputs_(num_inputs) {
+    inputs_.EnsureLength(num_inputs, nullptr);
+  }
+
+  intptr_t InputCount() const { return inputs_.length(); }
+  Value* InputAt(intptr_t i) const { return inputs_[i]; }
+
+ protected:
+  GrowableArray<Value*> inputs_;
+
+ private:
+  void RawSetInputAt(intptr_t i, Value* value) { inputs_[i] = value; }
+};
+
+class PhiInstr : public VariadicDefinitionWithEmbeddedInputs {
+ public:
+  PhiInstr(JoinEntryInstr* block, intptr_t num_inputs)
+      : VariadicDefinitionWithEmbeddedInputs(num_inputs),
+        block_(block),
+        representation_(kTagged),
+        is_alive_(false),
+        is_receiver_(kUnknownReceiver) {}
 
   // Get the block entry for that instruction.
   virtual BlockEntryInstr* GetBlock() { return block(); }
@@ -2573,10 +2601,6 @@
   virtual CompileType ComputeType() const;
   virtual bool RecomputeType();
 
-  intptr_t InputCount() const { return inputs_.length(); }
-
-  Value* InputAt(intptr_t i) const { return inputs_[i]; }
-
   virtual bool ComputeCanDeoptimize() const { return false; }
 
   virtual bool HasUnknownSideEffects() const { return false; }
@@ -2644,10 +2668,7 @@
   // predecessors.
   friend class ConstantPropagator;
 
-  void RawSetInputAt(intptr_t i, Value* value) { inputs_[i] = value; }
-
   JoinEntryInstr* block_;
-  GrowableArray<Value*> inputs_;
   Representation representation_;
   BitVector* reaching_defs_ = nullptr;
   bool is_alive_;
@@ -2663,7 +2684,7 @@
 // (0 is the very first parameter, 1 is next and so on). When [base_reg] is
 // set to SPREG, value [index] needs to be reversed (0 is the very last
 // parameter, 1 is next and so on) to get the sp relative position.
-class ParameterInstr : public Definition {
+class ParameterInstr : public TemplateDefinition<0, NoThrow> {
  public:
   ParameterInstr(intptr_t index,
                  intptr_t param_offset,
@@ -2687,12 +2708,6 @@
   virtual BlockEntryInstr* GetBlock() { return block_; }
   void set_block(BlockEntryInstr* block) { block_ = block; }
 
-  intptr_t InputCount() const { return 0; }
-  Value* InputAt(intptr_t i) const {
-    UNREACHABLE();
-    return NULL;
-  }
-
   virtual Representation representation() const { return representation_; }
 
   virtual Representation RequiredInputRepresentation(intptr_t index) const {
@@ -2711,13 +2726,9 @@
 
   virtual CompileType ComputeType() const;
 
-  virtual bool MayThrow() const { return false; }
-
   PRINT_OPERANDS_TO_SUPPORT
 
  private:
-  virtual void RawSetInputAt(intptr_t i, Value* value) { UNREACHABLE(); }
-
   const intptr_t index_;
 
   // The offset (in words) of the last slot of the parameter, relative
@@ -2739,7 +2750,7 @@
 // NativeEntryInstr::EmitNativeCode for more details.
 //
 // TOOD(33549): Unify with ParameterInstr.
-class NativeParameterInstr : public Definition {
+class NativeParameterInstr : public TemplateDefinition<0, NoThrow> {
  public:
   NativeParameterInstr(const compiler::ffi::CallbackMarshaller& marshaller,
                        intptr_t def_index)
@@ -2751,12 +2762,6 @@
     return marshaller_.RepInFfiCall(def_index_);
   }
 
-  intptr_t InputCount() const { return 0; }
-  Value* InputAt(intptr_t i) const {
-    UNREACHABLE();
-    return NULL;
-  }
-
   virtual bool ComputeCanDeoptimize() const { return false; }
 
   virtual bool HasUnknownSideEffects() const { return false; }
@@ -2764,13 +2769,9 @@
   // TODO(sjindel): We can make this more precise.
   virtual CompileType ComputeType() const { return CompileType::Dynamic(); }
 
-  virtual bool MayThrow() const { return false; }
-
   PRINT_OPERANDS_TO_SUPPORT
 
  private:
-  virtual void RawSetInputAt(intptr_t i, Value* value) { UNREACHABLE(); }
-
   const compiler::ffi::CallbackMarshaller& marshaller_;
   const intptr_t def_index_;
 
@@ -4013,23 +4014,19 @@
 };
 
 template <intptr_t kExtraInputs>
-class TemplateDartCall : public Definition {
+class TemplateDartCall : public VariadicDefinition {
  public:
   TemplateDartCall(intptr_t deopt_id,
                    intptr_t type_args_len,
                    const Array& argument_names,
                    InputsArray* inputs,
                    const InstructionSource& source)
-      : Definition(source, deopt_id),
+      : VariadicDefinition(inputs, source, deopt_id),
         type_args_len_(type_args_len),
         argument_names_(argument_names),
-        inputs_(inputs),
         token_pos_(source.token_pos) {
     ASSERT(argument_names.IsZoneHandle() || argument_names.InVMIsolateHeap());
     ASSERT(inputs_->length() >= kExtraInputs);
-    for (intptr_t i = 0, n = inputs_->length(); i < n; ++i) {
-      SetInputAt(i, (*inputs_)[i]);
-    }
   }
 
   inline StringPtr Selector();
@@ -4037,8 +4034,6 @@
   virtual bool MayThrow() const { return true; }
   virtual bool CanCallDart() const { return true; }
 
-  virtual intptr_t InputCount() const { return inputs_->length(); }
-  virtual Value* InputAt(intptr_t i) const { return inputs_->At(i); }
   virtual bool ComputeCanDeoptimize() const { return false; }
   virtual bool ComputeCanDeoptimizeAfterCall() const {
     return !CompilerState::Current().is_aot();
@@ -4095,13 +4090,8 @@
   }
 
  private:
-  virtual void RawSetInputAt(intptr_t i, Value* value) {
-    (*inputs_)[i] = value;
-  }
-
   intptr_t type_args_len_;
   const Array& argument_names_;
-  InputsArray* inputs_;
   PushArgumentsArray* push_arguments_ = nullptr;
   TokenPosition token_pos_;
 
@@ -5063,17 +5053,17 @@
 class DropTempsInstr : public Definition {
  public:
   DropTempsInstr(intptr_t num_temps, Value* value)
-      : num_temps_(num_temps), value_(NULL) {
-    if (value != NULL) {
+      : num_temps_(num_temps), has_input_(value != nullptr) {
+    if (has_input_) {
       SetInputAt(0, value);
     }
   }
 
   DECLARE_INSTRUCTION(DropTemps)
 
-  virtual intptr_t InputCount() const { return value_ != NULL ? 1 : 0; }
+  virtual intptr_t InputCount() const { return has_input_ ? 1 : 0; }
   virtual Value* InputAt(intptr_t i) const {
-    ASSERT((value_ != NULL) && (i == 0));
+    ASSERT(has_input_ && (i == 0));
     return value_;
   }
 
@@ -5097,10 +5087,14 @@
   PRINT_OPERANDS_TO_SUPPORT
 
  private:
-  virtual void RawSetInputAt(intptr_t i, Value* value) { value_ = value; }
+  virtual void RawSetInputAt(intptr_t i, Value* value) {
+    ASSERT(has_input_);
+    value_ = value;
+  }
 
   const intptr_t num_temps_;
-  Value* value_;
+  const bool has_input_;
+  Value* value_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(DropTempsInstr);
 };
@@ -5194,8 +5188,8 @@
 
 class NativeCallInstr : public TemplateDartCall<0> {
  public:
-  NativeCallInstr(const String* name,
-                  const Function* function,
+  NativeCallInstr(const String& name,
+                  const Function& function,
                   bool link_lazily,
                   const InstructionSource& source,
                   InputsArray* args)
@@ -5207,14 +5201,14 @@
         is_auto_scope_(true),
         link_lazily_(link_lazily),
         token_pos_(source.token_pos) {
-    ASSERT(name->IsZoneHandle());
-    ASSERT(function->IsZoneHandle());
+    ASSERT(name.IsZoneHandle());
+    ASSERT(function.IsZoneHandle());
   }
 
   DECLARE_INSTRUCTION(NativeCall)
 
-  const String& native_name() const { return *native_name_; }
-  const Function& function() const { return *function_; }
+  const String& native_name() const { return native_name_; }
+  const Function& function() const { return function_; }
   NativeFunction native_c_function() const { return native_c_function_; }
   bool is_bootstrap_native() const { return is_bootstrap_native_; }
   bool is_auto_scope() const { return is_auto_scope_; }
@@ -5240,8 +5234,8 @@
   void set_is_bootstrap_native(bool value) { is_bootstrap_native_ = value; }
   void set_is_auto_scope(bool value) { is_auto_scope_ = value; }
 
-  const String* native_name_;
-  const Function* function_;
+  const String& native_name_;
+  const Function& function_;
   NativeFunction native_c_function_;
   bool is_bootstrap_native_;
   bool is_auto_scope_;
@@ -5260,22 +5254,17 @@
 // - The arguments to the native call, marshalled in IL as far as possible.
 // - The argument address.
 // - A TypedData for the return value to populate in machine code (optional).
-class FfiCallInstr : public Definition {
+class FfiCallInstr : public VariadicDefinitionWithEmbeddedInputs {
  public:
-  FfiCallInstr(Zone* zone,
-               intptr_t deopt_id,
+  FfiCallInstr(intptr_t deopt_id,
                const compiler::ffi::CallMarshaller& marshaller,
                bool is_leaf)
-      : Definition(deopt_id),
-        zone_(zone),
+      : VariadicDefinitionWithEmbeddedInputs(
+            marshaller.NumDefinitions() + 1 +
+                (marshaller.PassTypedData() ? 1 : 0),
+            deopt_id),
         marshaller_(marshaller),
-        inputs_(marshaller.NumDefinitions() + 1 +
-                (marshaller.PassTypedData() ? 1 : 0)),
-        is_leaf_(is_leaf) {
-    inputs_.FillWith(
-        nullptr, 0,
-        marshaller.NumDefinitions() + 1 + (marshaller.PassTypedData() ? 1 : 0));
-  }
+        is_leaf_(is_leaf) {}
 
   DECLARE_INSTRUCTION(FfiCall)
 
@@ -5288,8 +5277,6 @@
     return marshaller_.NumDefinitions() + 1;
   }
 
-  virtual intptr_t InputCount() const { return inputs_.length(); }
-  virtual Value* InputAt(intptr_t i) const { return inputs_[i]; }
   virtual bool MayThrow() const {
     // By Dart_PropagateError.
     return true;
@@ -5320,8 +5307,6 @@
   PRINT_OPERANDS_TO_SUPPORT
 
  private:
-  virtual void RawSetInputAt(intptr_t i, Value* value) { inputs_[i] = value; }
-
   LocationSummary* MakeLocationSummaryInternal(Zone* zone,
                                                bool is_optimizing,
                                                const RegList temps) const;
@@ -5338,21 +5323,16 @@
                        const Register temp0,
                        const Register temp1);
 
-  Zone* const zone_;
   const compiler::ffi::CallMarshaller& marshaller_;
-
-  GrowableArray<Value*> inputs_;
-
   bool is_leaf_;
 
   DISALLOW_COPY_AND_ASSIGN(FfiCallInstr);
 };
 
 // Has the target address in a register passed as the last input in IL.
-class CCallInstr : public Definition {
+class CCallInstr : public VariadicDefinition {
  public:
   CCallInstr(
-      Zone* zone,
       const compiler::ffi::NativeCallingConvention& native_calling_convention,
       InputsArray* inputs);
 
@@ -5366,8 +5346,6 @@
     return native_calling_convention_.argument_locations().length();
   }
 
-  virtual intptr_t InputCount() const { return inputs_->length(); }
-  virtual Value* InputAt(intptr_t i) const { return inputs_->At(i); }
   virtual bool MayThrow() const { return false; }
 
   virtual bool ComputeCanDeoptimize() const { return false; }
@@ -5386,13 +5364,7 @@
   PRINT_OPERANDS_TO_SUPPORT
 
  private:
-  virtual void RawSetInputAt(intptr_t i, Value* value) {
-    (*inputs_)[i] = value;
-  }
-
-  Zone* const zone_;
   const compiler::ffi::NativeCallingConvention& native_calling_convention_;
-  InputsArray* inputs_;
 
   DISALLOW_COPY_AND_ASSIGN(CCallInstr);
 };
@@ -6392,11 +6364,12 @@
                       Value* type_arguments = nullptr)
       : AllocationInstr(source, deopt_id),
         cls_(cls),
+        has_type_arguments_(type_arguments != nullptr),
         type_arguments_(type_arguments) {
     ASSERT(cls.IsZoneHandle());
     ASSERT(!cls.IsNull());
-    ASSERT((cls.NumTypeArguments() > 0) == (type_arguments != nullptr));
-    if (type_arguments != nullptr) {
+    ASSERT((cls.NumTypeArguments() > 0) == has_type_arguments_);
+    if (has_type_arguments_) {
       SetInputAt(kTypeArgumentsPos, type_arguments);
       type_arguments_slot_ =
           &Slot::GetTypeArgumentsSlotFor(Thread::Current(), cls);
@@ -6409,11 +6382,9 @@
   const Class& cls() const { return cls_; }
   Value* type_arguments() const { return type_arguments_; }
 
-  virtual intptr_t InputCount() const {
-    return (type_arguments_ != nullptr) ? 1 : 0;
-  }
+  virtual intptr_t InputCount() const { return has_type_arguments_ ? 1 : 0; }
   virtual Value* InputAt(intptr_t i) const {
-    ASSERT(type_arguments_ != nullptr && i == kTypeArgumentsPos);
+    ASSERT(has_type_arguments_ && i == kTypeArgumentsPos);
     return type_arguments_;
   }
 
@@ -6435,12 +6406,12 @@
 
  private:
   virtual void RawSetInputAt(intptr_t i, Value* value) {
-    ASSERT((type_arguments_ != nullptr) && (i == kTypeArgumentsPos));
-    ASSERT(value != nullptr);
+    ASSERT(has_type_arguments_ && (i == kTypeArgumentsPos));
     type_arguments_ = value;
   }
 
   const Class& cls_;
+  const bool has_type_arguments_;
   Value* type_arguments_;
   const Slot* type_arguments_slot_ = nullptr;
 
@@ -6529,26 +6500,22 @@
 // This instruction captures the state of the object which had its allocation
 // removed during the AllocationSinking pass.
 // It does not produce any real code only deoptimization information.
-class MaterializeObjectInstr : public Definition {
+class MaterializeObjectInstr : public VariadicDefinition {
  public:
   MaterializeObjectInstr(AllocationInstr* allocation,
                          const Class& cls,
                          intptr_t num_elements,
                          const ZoneGrowableArray<const Slot*>& slots,
                          ZoneGrowableArray<Value*>* values)
-      : allocation_(allocation),
+      : VariadicDefinition(values),
+        allocation_(allocation),
         cls_(cls),
         num_elements_(num_elements),
         slots_(slots),
-        values_(values),
         locations_(nullptr),
         visited_for_liveness_(false),
         registers_remapped_(false) {
-    ASSERT(slots_.length() == values_->length());
-    for (intptr_t i = 0; i < InputCount(); i++) {
-      InputAt(i)->set_instruction(this);
-      InputAt(i)->set_use_index(i);
-    }
+    ASSERT(slots_.length() == values->length());
   }
 
   AllocationInstr* allocation() const { return allocation_; }
@@ -6564,10 +6531,6 @@
 
   DECLARE_INSTRUCTION(MaterializeObject)
 
-  virtual intptr_t InputCount() const { return values_->length(); }
-
-  virtual Value* InputAt(intptr_t i) const { return (*values_)[i]; }
-
   // SelectRepresentations pass is run once more while MaterializeObject
   // instructions are still in the graph. To avoid any redundant boxing
   // operations inserted by that pass we should indicate that this
@@ -6594,15 +6557,10 @@
   PRINT_OPERANDS_TO_SUPPORT
 
  private:
-  virtual void RawSetInputAt(intptr_t i, Value* value) {
-    (*values_)[i] = value;
-  }
-
   AllocationInstr* allocation_;
   const Class& cls_;
   intptr_t num_elements_;
   const ZoneGrowableArray<const Slot*>& slots_;
-  ZoneGrowableArray<Value*>* values_;
   Location* locations_;
 
   bool visited_for_liveness_;
@@ -8692,7 +8650,7 @@
 };
 
 // TODO(sjindel): Replace with FFICallInstr.
-class InvokeMathCFunctionInstr : public PureDefinition {
+class InvokeMathCFunctionInstr : public VariadicDefinition {
  public:
   InvokeMathCFunctionInstr(ZoneGrowableArray<Value*>* inputs,
                            intptr_t deopt_id,
@@ -8726,9 +8684,8 @@
 
   virtual intptr_t DeoptimizationTarget() const { return GetDeoptId(); }
 
-  virtual intptr_t InputCount() const { return inputs_->length(); }
-
-  virtual Value* InputAt(intptr_t i) const { return (*inputs_)[i]; }
+  virtual bool AllowsCSE() const { return true; }
+  virtual bool HasUnknownSideEffects() const { return false; }
 
   virtual bool AttributesEqual(const Instruction& other) const {
     auto const other_invoke = other.AsInvokeMathCFunction();
@@ -8744,11 +8701,6 @@
   PRINT_OPERANDS_TO_SUPPORT
 
  private:
-  virtual void RawSetInputAt(intptr_t i, Value* value) {
-    (*inputs_)[i] = value;
-  }
-
-  ZoneGrowableArray<Value*>* inputs_;
   const MethodRecognizer::Kind recognized_kind_;
   const TokenPosition token_pos_;
 
@@ -9853,7 +9805,7 @@
     return count;
   }
 
-  const Function& function() const { return parsed_function_.function(); }
+  const Function& function() const { return function_; }
 
   Environment* DeepCopy(Zone* zone) const { return DeepCopy(zone, Length()); }
 
@@ -9893,14 +9845,14 @@
   Environment(intptr_t length,
               intptr_t fixed_parameter_count,
               intptr_t lazy_deopt_pruning_count,
-              const ParsedFunction& parsed_function,
+              const Function& function,
               Environment* outer)
       : values_(length),
         fixed_parameter_count_(fixed_parameter_count),
         bitfield_(DeoptIdBits::encode(DeoptId::kNone) |
                   LazyDeoptToBeforeDeoptId::encode(false) |
                   LazyDeoptPruningBits::encode(lazy_deopt_pruning_count)),
-        parsed_function_(parsed_function),
+        function_(function),
         outer_(outer) {}
 
   void SetDeoptId(intptr_t deopt_id) {
@@ -9919,7 +9871,7 @@
   // Deoptimization id associated with this environment. Only set for
   // outer environments.
   uintptr_t bitfield_;
-  const ParsedFunction& parsed_function_;
+  const Function& function_;
   Environment* outer_;
 
   DISALLOW_COPY_AND_ASSIGN(Environment);
diff --git a/runtime/vm/compiler/backend/il_test.cc b/runtime/vm/compiler/backend/il_test.cc
index d004834..8ae38a1 100644
--- a/runtime/vm/compiler/backend/il_test.cc
+++ b/runtime/vm/compiler/backend/il_test.cc
@@ -961,7 +961,7 @@
   FlowGraph* flow_graph = pipeline->RunPasses({CompilerPass::kComputeSSA});
 
   // Make an FfiCall based on ffi_trampoline that calls our native function.
-  auto ffi_call = new FfiCallInstr(zone, DeoptId::kNone, marshaller, is_leaf);
+  auto ffi_call = new FfiCallInstr(DeoptId::kNone, marshaller, is_leaf);
   RELEASE_ASSERT(ffi_call->InputCount() == 1);
   // TargetAddress is the function pointer called.
   const Representation address_repr =
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 1e3707e..66de960 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -394,9 +394,8 @@
     const compiler::ffi::CallMarshaller& marshaller) {
   Fragment body;
 
-  FfiCallInstr* const call =
-      new (Z) FfiCallInstr(Z, GetNextDeoptId(), marshaller,
-                           parsed_function_->function().FfiIsLeaf());
+  FfiCallInstr* const call = new (Z) FfiCallInstr(
+      GetNextDeoptId(), marshaller, parsed_function_->function().FfiIsLeaf());
 
   for (intptr_t i = call->InputCount() - 1; i >= 0; --i) {
     call->SetInputAt(i, Pop());
@@ -419,8 +418,7 @@
   for (intptr_t i = num_arguments - 1; i >= 0; --i) {
     (*arguments)[i] = Pop();
   }
-  auto* const call =
-      new (Z) CCallInstr(Z, native_calling_convention, arguments);
+  auto* const call = new (Z) CCallInstr(native_calling_convention, arguments);
 
   Push(call);
   body <<= call;
@@ -559,15 +557,15 @@
   return instructions;
 }
 
-Fragment FlowGraphBuilder::NativeCall(const String* name,
-                                      const Function* function) {
+Fragment FlowGraphBuilder::NativeCall(const String& name,
+                                      const Function& function) {
   InlineBailout("kernel::FlowGraphBuilder::NativeCall");
   const intptr_t num_args =
-      function->NumParameters() + (function->IsGeneric() ? 1 : 0);
+      function.NumParameters() + (function.IsGeneric() ? 1 : 0);
   InputsArray* arguments = GetArguments(num_args);
   NativeCallInstr* call = new (Z)
       NativeCallInstr(name, function, FLAG_link_natives_lazily,
-                      InstructionSource(function->end_token_pos()), arguments);
+                      InstructionSource(function.end_token_pos()), arguments);
   Push(call);
   return Fragment(call);
 }
@@ -838,7 +836,7 @@
   for (intptr_t i = 0; i < function.NumParameters(); ++i) {
     body += LoadLocal(parsed_function_->RawParameterVariable(i));
   }
-  body += NativeCall(&name, &function);
+  body += NativeCall(name, function);
   // We typecheck results of native calls for type safety.
   body +=
       Return(TokenPosition::kNoSource, /* omit_result_type_check = */ false);
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index b184503..045750e 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -197,7 +197,7 @@
   Fragment StoreLateField(const Field& field,
                           LocalVariable* instance,
                           LocalVariable* setter_value);
-  Fragment NativeCall(const String* name, const Function* function);
+  Fragment NativeCall(const String& name, const Function& function);
   Fragment Return(
       TokenPosition position,
       bool omit_result_type_check = false,
diff --git a/tools/VERSION b/tools/VERSION
index 98ebbad..1145bf5 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 19
 PATCH 0
-PRERELEASE 60
+PRERELEASE 61
 PRERELEASE_PATCH 0
\ No newline at end of file