Merge remote-tracking branch 'tint/main' into HEAD

Integrates Tint repo into Dawn

KIs:
- Building docs for Tint is turned off, because it fails due to lack
  of annotations in Dawn source files.
- Dawn CQ needs to be updated to run Tint specific tests
- Significant post-merge cleanup needed

R=bclayton,cwallez
BUG=dawn:1339

Change-Id: I6c9714a0030934edd6c51f3cac4684dcd59d1ea3
diff --git a/.clang-format b/.clang-format
index 4d9e9c8..ff58eea 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,3 +1,4 @@
+# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
 BasedOnStyle: Chromium
 Standard: Cpp11
 
diff --git a/.gitignore b/.gitignore
index a028df0..42d8414 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,12 +9,15 @@
 /testing
 /third_party/abseil-cpp/
 /third_party/angle
+/third_party/benchmark
+/third_party/binutils
 /third_party/catapult
 /third_party/clang-format
+/third_party/cpplint
 /third_party/glfw
 /third_party/googletest
 /third_party/gpuweb
-/third_party/webgpu-cts
+/third_party/gpuweb-cts
 /third_party/jinja2
 /third_party/jsoncpp
 /third_party/llvm-build
@@ -22,10 +25,11 @@
 /third_party/node
 /third_party/node-addon-api
 /third_party/node-api-headers
+/third_party/protobuf
 /third_party/swiftshader
-/third_party/tint
 /third_party/vulkan-deps
 /third_party/vulkan_memory_allocator
+/third_party/webgpu-cts
 /third_party/zlib
 /tools/clang
 /tools/cmake
@@ -33,7 +37,7 @@
 /tools/memory
 /out
 
-# Modified from https://www.gitignore.io/api/vim,macos,linux,emacs,windows,sublimetext,visualstudio,visualstudiocode
+# Modified from https://www.gitignore.io/api/vim,macos,linux,emacs,windows,sublimetext,visualstudio,visualstudiocode,intellij
 
 ### Emacs ###
 *~
@@ -101,8 +105,19 @@
 Desktop.ini
 $RECYCLE.BIN/
 
+### Intellij ###
+.idea
+
 ### Dawn node tools binaries
 src/dawn/node/tools/bin/
 
 ### Cached node transpiled tools
 /.node_transpile_work_dir
+
+# Misc inherited from Tint
+/test.wgsl
+coverage.summary
+default.profraw
+lcov.info
+/cmake-build-*/
+/testing
diff --git a/AUTHORS b/AUTHORS
index 32a6c3c..bded374 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,6 +1,7 @@
-# This is the list of Dawn authors for copyright purposes.
+# This is the list of Dawn & Tint authors for copyright purposes.
 #
 # This does not necessarily list everyone who has contributed code, since in
 # some cases, their employer may be the copyright holder.  To see the full list
 # of contributors, see the revision history in source control.
-Google Inc.
+Google LLC
+Vasyl Teliman
diff --git a/AUTHORS.dawn b/AUTHORS.dawn
new file mode 100644
index 0000000..32a6c3c
--- /dev/null
+++ b/AUTHORS.dawn
@@ -0,0 +1,6 @@
+# This is the list of Dawn authors for copyright purposes.
+#
+# This does not necessarily list everyone who has contributed code, since in
+# some cases, their employer may be the copyright holder.  To see the full list
+# of contributors, see the revision history in source control.
+Google Inc.
diff --git a/AUTHORS.tint b/AUTHORS.tint
new file mode 100644
index 0000000..a66d09e
--- /dev/null
+++ b/AUTHORS.tint
@@ -0,0 +1,8 @@
+# This is the list of the Tint authors for copyright purposes.
+#
+# This does not necessarily list everyone who has contributed code, since in
+# some cases, their employer may be the copyright holder.  To see the full list
+# of contributors, see the revision history in source control.
+
+Google LLC
+Vasyl Teliman
diff --git a/BUILD.gn b/BUILD.gn
index 99df240..c33776d 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -21,9 +21,15 @@
     "src/dawn/native:webgpu_dawn",
     "src/dawn/tests",
     "src/fuzzers/dawn:dawn_fuzzers",
+    "src/tint/fuzzers",
+    "src/tint:libtint",
+    "test/tint:tint_unittests",
   ]
   if (dawn_standalone) {
-    deps += [ "samples/dawn:samples" ]
+    deps += [
+      "samples/dawn:samples",
+      "src/tint/cmd:tint",
+    ]
   }
 }
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bdcfdf1..f125476 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2020 The Dawn Authors
+# Copyright 2022 The Dawn & Tint Authors
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.10.2)
 
 # When upgrading to CMake 3.11 we can remove DAWN_DUMMY_FILE because source-less add_library
 # becomes available.
@@ -26,13 +26,18 @@
     DESCRIPTION "Dawn, a WebGPU implementation"
     LANGUAGES C CXX
 )
+enable_testing()
 
 set_property(GLOBAL PROPERTY USE_FOLDERS ON)
 
-if(NOT CMAKE_BUILD_TYPE)
-    message(WARNING "CMAKE_BUILD_TYPE not set, forcing it to Debug")
-    set(CMAKE_BUILD_TYPE "Debug" CACHE STRING
-        "Build type (Debug, Release, RelWithDebInfo, MinSizeRel)" FORCE)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_DEBUG_POSTFIX "")
+
+if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
+  message(STATUS "No build type selected, default to Debug")
+  set(CMAKE_BUILD_TYPE "Debug")
 endif()
 
 set(DAWN_BUILD_GEN_DIR "${Dawn_BINARY_DIR}/gen")
@@ -99,7 +104,7 @@
 endif()
 
 # GLFW is not supported in UWP
-if((WIN32 AND NOT WINDOWS_STORE) OR UNIX AND NOT ANDROID)
+if ((WIN32 AND NOT WINDOWS_STORE) OR UNIX AND NOT ANDROID)
     set(DAWN_SUPPORTS_GLFW_FOR_WINDOWING ON)
 endif()
 
@@ -132,7 +137,7 @@
 set_if_not_defined(DAWN_JINJA2_DIR "${DAWN_THIRD_PARTY_DIR}/jinja2" "Directory in which to find Jinja2")
 set_if_not_defined(DAWN_SPIRV_HEADERS_DIR "${DAWN_THIRD_PARTY_DIR}/vulkan-deps/spirv-headers/src" "Directory in which to find SPIRV-Headers")
 set_if_not_defined(DAWN_SPIRV_TOOLS_DIR "${DAWN_THIRD_PARTY_DIR}/vulkan-deps/spirv-tools/src" "Directory in which to find SPIRV-Tools")
-set_if_not_defined(DAWN_TINT_DIR "${DAWN_THIRD_PARTY_DIR}/tint" "Directory in which to find Tint")
+set_if_not_defined(DAWN_TINT_DIR "${Dawn_SOURCE_DIR}" "Directory in which to find Tint")
 set_if_not_defined(DAWN_VULKAN_HEADERS_DIR "${DAWN_THIRD_PARTY_DIR}/vulkan-deps/vulkan-headers/src" "Directory in which to find Vulkan-Headers")
 
 # Dependencies for DAWN_BUILD_NODE_BINDINGS
@@ -204,10 +209,354 @@
 set(CMAKE_CXX_STANDARD "17")
 
 ################################################################################
+# Tint
+################################################################################
+
+# TINT_IS_SUBPROJECT is 1 if added via add_subdirectory() from another project.
+get_directory_property(TINT_IS_SUBPROJECT PARENT_DIRECTORY)
+if(TINT_IS_SUBPROJECT)
+  set(TINT_IS_SUBPROJECT 1)
+
+  # If tint is used as a subproject, default to disabling the building of
+  # documentation and tests. These are unlikely to be desirable, but can be
+  # enabled.
+  set(TINT_BUILD_DOCS_DEFAULT OFF)
+  set(TINT_BUILD_TESTS_DEFAULT OFF)
+else()
+  set(TINT_BUILD_DOCS_DEFAULT ON)
+  set(TINT_BUILD_TESTS_DEFAULT ON)
+endif()
+
+# Forcing building docs off right now, since currently this will try to build docs for both Tint & Dawn, and Dawn isn't annotated yet.
+set(TINT_BUILD_DOCS_DEFAULT OFF)
+
+option_if_not_defined(TINT_BUILD_SAMPLES "Build samples" ON)
+option_if_not_defined(TINT_BUILD_DOCS "Build documentation" ${TINT_BUILD_DOCS_DEFAULT})
+option_if_not_defined(TINT_DOCS_WARN_AS_ERROR "When building documentation, treat warnings as errors" OFF)
+option_if_not_defined(TINT_BUILD_SPV_READER "Build the SPIR-V input reader" ON)
+option_if_not_defined(TINT_BUILD_WGSL_READER "Build the WGSL input reader" ON)
+option_if_not_defined(TINT_BUILD_GLSL_WRITER "Build the GLSL output writer" ON)
+option_if_not_defined(TINT_BUILD_HLSL_WRITER "Build the HLSL output writer" ON)
+option_if_not_defined(TINT_BUILD_MSL_WRITER "Build the MSL output writer" ON)
+option_if_not_defined(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" ON)
+option_if_not_defined(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON)
+option_if_not_defined(TINT_BUILD_FUZZERS "Build fuzzers" OFF)
+option_if_not_defined(TINT_BUILD_SPIRV_TOOLS_FUZZER "Build SPIRV-Tools fuzzer" OFF)
+option_if_not_defined(TINT_BUILD_AST_FUZZER "Build AST fuzzer" OFF)
+option_if_not_defined(TINT_BUILD_REGEX_FUZZER "Build regex fuzzer" OFF)
+option_if_not_defined(TINT_BUILD_BENCHMARKS "Build benchmarks" OFF)
+option_if_not_defined(TINT_BUILD_TESTS "Build tests" ${TINT_BUILD_TESTS_DEFAULT})
+option_if_not_defined(TINT_BUILD_AS_OTHER_OS "Override OS detection to force building of *_other.cc files" OFF)
+option_if_not_defined(TINT_BUILD_REMOTE_COMPILE "Build the remote-compile tool for validating shaders on a remote machine" OFF)
+
+set(TINT_LIB_FUZZING_ENGINE_LINK_OPTIONS "" CACHE STRING "Used by OSS-Fuzz to control, via link options, which fuzzing engine should be used")
+
+option_if_not_defined(TINT_ENABLE_MSAN "Enable memory sanitizer" OFF)
+option_if_not_defined(TINT_ENABLE_ASAN "Enable address sanitizer" OFF)
+option_if_not_defined(TINT_ENABLE_UBSAN "Enable undefined behaviour sanitizer" OFF)
+
+option_if_not_defined(TINT_ENABLE_BREAK_IN_DEBUGGER "Enable tint::debugger::Break()" OFF)
+
+option_if_not_defined(TINT_EMIT_COVERAGE "Emit code coverage information" OFF)
+
+option_if_not_defined(TINT_CHECK_CHROMIUM_STYLE "Check for [chromium-style] issues during build" OFF)
+
+option_if_not_defined(TINT_SYMBOL_STORE_DEBUG_NAME "Enable storing of name in tint::ast::Symbol to help debugging the AST" OFF)
+
+message(STATUS "Tint build samples: ${TINT_BUILD_SAMPLES}")
+message(STATUS "Tint build docs: ${TINT_BUILD_DOCS}")
+message(STATUS "Tint build docs with warn as error: ${TINT_DOCS_WARN_AS_ERROR}")
+message(STATUS "Tint build SPIR-V reader: ${TINT_BUILD_SPV_READER}")
+message(STATUS "Tint build WGSL reader: ${TINT_BUILD_WGSL_READER}")
+message(STATUS "Tint build GLSL writer: ${TINT_BUILD_GLSL_WRITER}")
+message(STATUS "Tint build HLSL writer: ${TINT_BUILD_HLSL_WRITER}")
+message(STATUS "Tint build MSL writer: ${TINT_BUILD_MSL_WRITER}")
+message(STATUS "Tint build SPIR-V writer: ${TINT_BUILD_SPV_WRITER}")
+message(STATUS "Tint build WGSL writer: ${TINT_BUILD_WGSL_WRITER}")
+message(STATUS "Tint build fuzzers: ${TINT_BUILD_FUZZERS}")
+message(STATUS "Tint build SPIRV-Tools fuzzer: ${TINT_BUILD_SPIRV_TOOLS_FUZZER}")
+message(STATUS "Tint build AST fuzzer: ${TINT_BUILD_AST_FUZZER}")
+message(STATUS "Tint build regex fuzzer: ${TINT_BUILD_REGEX_FUZZER}")
+message(STATUS "Tint build benchmarks: ${TINT_BUILD_BENCHMARKS}")
+message(STATUS "Tint build tests: ${TINT_BUILD_TESTS}")
+message(STATUS "Tint build with ASAN: ${TINT_ENABLE_ASAN}")
+message(STATUS "Tint build with MSAN: ${TINT_ENABLE_MSAN}")
+message(STATUS "Tint build with UBSAN: ${TINT_ENABLE_UBSAN}")
+message(STATUS "Tint build checking [chromium-style]: ${TINT_CHECK_CHROMIUM_STYLE}")
+message(STATUS "Tint build remote-compile tool: ${TINT_BUILD_REMOTE_COMPILE}")
+
+if (NOT ${TINT_LIB_FUZZING_ENGINE_LINK_OPTIONS} STREQUAL "")
+  message(STATUS "Using provided LIB_FUZZING_ENGINE options: ${TINT_LIB_FUZZING_ENGINE_LINK_OPTIONS}")
+endif()
+
+message(STATUS "Using python3")
+find_package(PythonInterp 3 REQUIRED)
+
+if (${TINT_BUILD_SPIRV_TOOLS_FUZZER})
+  message(STATUS "TINT_BUILD_SPIRV_TOOLS_FUZZER is ON - setting
+      TINT_BUILD_FUZZERS
+      TINT_BUILD_SPV_READER
+      TINT_BUILD_SPV_WRITER
+      TINT_BUILD_WGSL_READER
+      TINT_BUILD_WGSL_WRITER
+      TINT_BUILD_GLSL_WRITER
+      TINT_BUILD_HLSL_WRITER
+      TINT_BUILD_MSL_WRITER to ON")
+  set(TINT_BUILD_FUZZERS ON CACHE BOOL "Build tint fuzzers" FORCE)
+  set(TINT_BUILD_SPV_READER ON CACHE BOOL "Build SPIR-V reader" FORCE)
+  set(TINT_BUILD_SPV_WRITER ON CACHE BOOL "Build SPIR-V writer" FORCE)
+  set(TINT_BUILD_WGSL_READER ON CACHE BOOL "Build WGSL reader" FORCE)
+  set(TINT_BUILD_WGSL_WRITER ON CACHE BOOL "Build WGSL writer" FORCE)
+  set(TINT_BUILD_GLSL_WRITER ON CACHE BOOL "Build HLSL writer" FORCE)
+  set(TINT_BUILD_HLSL_WRITER ON CACHE BOOL "Build HLSL writer" FORCE)
+  set(TINT_BUILD_MSL_WRITER ON CACHE BOOL "Build MSL writer" FORCE)
+endif()
+
+if (${TINT_BUILD_AST_FUZZER})
+  message(STATUS "TINT_BUILD_AST_FUZZER is ON - setting
+      TINT_BUILD_FUZZERS
+      TINT_BUILD_WGSL_READER
+      TINT_BUILD_WGSL_WRITER
+      TINT_BUILD_SPV_WRITER
+      TINT_BUILD_MSL_WRITER
+      TINT_BUILD_GLSL_WRITER
+      TINT_BUILD_HLSL_WRITER to ON")
+  set(TINT_BUILD_FUZZERS ON CACHE BOOL "Build tint fuzzers" FORCE)
+  set(TINT_BUILD_WGSL_READER ON CACHE BOOL "Build WGSL reader" FORCE)
+  set(TINT_BUILD_WGSL_WRITER ON CACHE BOOL "Build WGSL writer" FORCE)
+  set(TINT_BUILD_SPV_WRITER ON CACHE BOOL "Build SPIR-V writer" FORCE)
+  set(TINT_BUILD_MSL_WRITER ON CACHE BOOL "Build MSL writer" FORCE)
+  set(TINT_BUILD_GLSL_WRITER ON CACHE BOOL "Build GLSL writer" FORCE)
+  set(TINT_BUILD_HLSL_WRITER ON CACHE BOOL "Build HLSL writer" FORCE)
+endif()
+
+if (${TINT_BUILD_REGEX_FUZZER})
+  message(STATUS "TINT_BUILD_REGEX_FUZZER is ON - setting
+      TINT_BUILD_FUZZERS
+      TINT_BUILD_WGSL_READER
+      TINT_BUILD_WGSL_WRITER
+      TINT_BUILD_SPV_WRITER
+      TINT_BUILD_MSL_WRITER
+      TINT_BUILD_GLSL_WRITER
+      TINT_BUILD_HLSL_WRITER to ON")
+      set(TINT_BUILD_FUZZERS ON CACHE BOOL "Build tint fuzzers" FORCE)
+      set(TINT_BUILD_WGSL_READER ON CACHE BOOL "Build WGSL reader" FORCE)
+      set(TINT_BUILD_WGSL_WRITER ON CACHE BOOL "Build WGSL writer" FORCE)
+      set(TINT_BUILD_SPV_WRITER ON CACHE BOOL "Build SPIR-V writer" FORCE)
+      set(TINT_BUILD_MSL_WRITER ON CACHE BOOL "Build MSL writer" FORCE)
+      set(TINT_BUILD_GLSL_WRITER ON CACHE BOOL "Build GLSL writer" FORCE)
+      set(TINT_BUILD_HLSL_WRITER ON CACHE BOOL "Build HLSL writer" FORCE)
+endif()
+
+set(TINT_ROOT_SOURCE_DIR ${PROJECT_SOURCE_DIR})
+
+# CMake < 3.15 sets /W3 in CMAKE_CXX_FLAGS. Remove it if it's there.
+# See https://gitlab.kitware.com/cmake/cmake/-/issues/18317
+if (MSVC)
+  if (CMAKE_CXX_FLAGS MATCHES "/W3")
+    string(REPLACE "/W3" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+  endif()
+endif()
+
+if (${TINT_CHECK_CHROMIUM_STYLE})
+   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -add-plugin -Xclang find-bad-constructs")
+endif()
+
+if (${TINT_BUILD_SPV_READER})
+  include_directories("${DAWN_THIRD_PARTY_DIR}/vulkan-deps/spirv-tools/include")
+endif()
+
+if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND (CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"))
+  set(COMPILER_IS_CLANG_CL TRUE)
+endif()
+
+if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR
+    (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") OR
+    ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND
+     (NOT COMPILER_IS_CLANG_CL)))
+  set(COMPILER_IS_LIKE_GNU TRUE)
+endif()
+
+# Enable msbuild multiprocessor builds
+if (MSVC AND NOT COMPILER_IS_CLANG_CL)
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
+endif()
+
+set(TINT_OS_CC_SUFFIX "other")
+if (NOT TINT_BUILD_AS_OTHER_OS)
+  if(UNIX OR APPLE)
+    set(TINT_OS_CC_SUFFIX "posix")
+    set(TINT_OS_CC_SUFFIX "posix")
+  elseif(WIN32)
+    set(TINT_OS_CC_SUFFIX "windows")
+    set(TINT_OS_CC_SUFFIX "windows")
+  endif()
+endif()
+
+if(${TINT_BUILD_DOCS})
+  find_package(Doxygen)
+  if(DOXYGEN_FOUND)
+    set(DOXYGEN_WARN_AS_ERROR NO)
+    if(TINT_DOCS_WARN_AS_ERROR)
+      set(DOXYGEN_WARN_AS_ERROR YES)
+    endif()
+
+    set(DOXYGEN_WARN_FORMAT "$file:$line: $text")
+    if (MSVC)
+      set(DOXYGEN_WARN_FORMAT "$file($line): $text")
+    endif()
+
+    add_custom_target(tint-docs ALL
+        COMMAND ${CMAKE_COMMAND}
+          -E env
+          "DOXYGEN_OUTPUT_DIRECTORY=${CMAKE_BINARY_DIR}/docs"
+          "DOXYGEN_WARN_AS_ERROR=${DOXYGEN_WARN_AS_ERROR}"
+          "DOXYGEN_WARN_FORMAT=${DOXYGEN_WARN_FORMAT}"
+          ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile
+        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+        COMMENT "Generating API documentation"
+        VERBATIM)
+  else()
+    message("Doxygen not found. Skipping documentation")
+  endif(DOXYGEN_FOUND)
+endif()
+
+function(tint_core_compile_options TARGET)
+  target_include_directories(${TARGET} PUBLIC "${TINT_ROOT_SOURCE_DIR}")
+  target_include_directories(${TARGET} PUBLIC "${TINT_ROOT_SOURCE_DIR}/include")
+
+  if (${TINT_BUILD_SPV_READER} OR ${TINT_BUILD_SPV_WRITER})
+    target_include_directories(${TARGET} PUBLIC
+        "${DAWN_THIRD_PARTY_DIR}/spirv-headers/include")
+  endif()
+
+  target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_SPV_READER=$<BOOL:${TINT_BUILD_SPV_READER}>)
+  target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_WGSL_READER=$<BOOL:${TINT_BUILD_WGSL_READER}>)
+  target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_GLSL_WRITER=$<BOOL:${TINT_BUILD_GLSL_WRITER}>)
+  target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_HLSL_WRITER=$<BOOL:${TINT_BUILD_HLSL_WRITER}>)
+  target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_MSL_WRITER=$<BOOL:${TINT_BUILD_MSL_WRITER}>)
+  target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_SPV_WRITER=$<BOOL:${TINT_BUILD_SPV_WRITER}>)
+  target_compile_definitions(${TARGET} PUBLIC -DTINT_BUILD_WGSL_WRITER=$<BOOL:${TINT_BUILD_WGSL_WRITER}>)
+
+  if (COMPILER_IS_LIKE_GNU)
+    target_compile_options(${TARGET} PRIVATE
+      -std=c++17
+      -fno-exceptions
+      -fno-rtti
+    )
+
+    if (${TINT_ENABLE_MSAN})
+      target_compile_options(${TARGET} PRIVATE -fsanitize=memory)
+      target_link_options(${TARGET} PRIVATE -fsanitize=memory)
+    elseif (${TINT_ENABLE_ASAN})
+      target_compile_options(${TARGET} PRIVATE -fsanitize=address)
+      target_link_options(${TARGET} PRIVATE -fsanitize=address)
+    elseif (${TINT_ENABLE_UBSAN})
+      target_compile_options(${TARGET} PRIVATE -fsanitize=undefined)
+      target_link_options(${TARGET} PRIVATE -fsanitize=undefined)
+    endif()
+  endif(COMPILER_IS_LIKE_GNU)
+
+  if (TINT_EMIT_COVERAGE)
+    if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+        target_compile_options(${TARGET} PRIVATE "--coverage")
+        target_link_options(${TARGET} PRIVATE "gcov")
+    elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+        target_compile_options(${TARGET} PRIVATE "-fprofile-instr-generate" "-fcoverage-mapping")
+        target_link_options(${TARGET} PRIVATE "-fprofile-instr-generate" "-fcoverage-mapping")
+    else()
+        message(FATAL_ERROR "Coverage generation not supported for the ${CMAKE_CXX_COMPILER_ID} toolchain")
+    endif()
+  endif(TINT_EMIT_COVERAGE)
+endfunction()
+
+function(tint_default_compile_options TARGET)
+  tint_core_compile_options(${TARGET})
+
+  set(COMMON_GNU_OPTIONS
+    -Wall
+    -Werror
+    -Wextra
+    -Wno-documentation-unknown-command
+    -Wno-padded
+    -Wno-switch-enum
+    -Wno-unknown-pragmas
+  )
+
+  set(COMMON_CLANG_OPTIONS
+    -Wno-c++98-compat
+    -Wno-c++98-compat-pedantic
+    -Wno-format-pedantic
+    -Wno-return-std-move-in-c++11
+    -Wno-unknown-warning-option
+    -Wno-undefined-var-template
+    -Wno-used-but-marked-unused
+    -Weverything
+  )
+
+  if (COMPILER_IS_LIKE_GNU)
+    target_compile_options(${TARGET} PRIVATE
+      -pedantic-errors
+      ${COMMON_GNU_OPTIONS}
+    )
+
+    if (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") OR
+        ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang"))
+      target_compile_options(${TARGET} PRIVATE
+        ${COMMON_CLANG_OPTIONS}
+      )
+    endif()
+  endif(COMPILER_IS_LIKE_GNU)
+
+  if (MSVC)
+    # Specify /EHs for exception handling.
+    target_compile_options(${TARGET} PRIVATE
+      /bigobj
+      /EHsc
+      /W4
+      /WX
+      /wd4068
+      /wd4127
+      /wd4244
+      /wd4267
+      /wd4324
+      /wd4458
+      /wd4514
+      /wd4571
+      /wd4625
+      /wd4626
+      /wd4710
+      /wd4774
+      /wd4820
+      /wd5026
+      /wd5027
+    )
+
+    # When building with clang-cl on Windows, try to match our clang build
+    # options as much as possible.
+    if (COMPILER_IS_CLANG_CL)
+      target_compile_options(${TARGET} PRIVATE
+        ${COMMON_GNU_OPTIONS}
+        ${COMMON_CLANG_OPTIONS}
+        # Disable warnings that are usually disabled in downstream deps for
+        # gcc/clang, but aren't for clang-cl.
+        -Wno-global-constructors
+        -Wno-zero-as-null-pointer-constant
+        -Wno-shorten-64-to-32
+        -Wno-shadow-field-in-constructor
+        -Wno-reserved-id-macro
+        -Wno-language-extension-token
+      )
+    endif()
+  endif()
+endfunction()
+
+################################################################################
 # Run on all subdirectories
 ################################################################################
 
 add_subdirectory(third_party)
+add_subdirectory(src/tint)
 add_subdirectory(generator)
 add_subdirectory(src/dawn)
 
@@ -220,3 +569,42 @@
     #add_subdirectory(src/utils)
     add_subdirectory(samples/dawn)
 endif()
+
+if (TINT_BUILD_SAMPLES)
+  add_subdirectory(src/tint/cmd)
+endif()
+
+if (TINT_BUILD_FUZZERS)
+  add_subdirectory(src/tint/fuzzers)
+endif()
+
+add_custom_target(tint-lint
+  COMMAND ./tools/lint
+  WORKING_DIRECTORY ${TINT_ROOT_SOURCE_DIR}
+  COMMENT "Running linter"
+  VERBATIM)
+
+add_custom_target(tint-format
+  COMMAND ./tools/format
+  WORKING_DIRECTORY ${TINT_ROOT_SOURCE_DIR}
+  COMMENT "Running formatter"
+  VERBATIM)
+
+
+if (TINT_EMIT_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+  # Generates a lcov.info file at the project root.
+  # This can be used by tools such as VSCode's Coverage Gutters extension to
+  # visualize code coverage in the editor.
+  get_filename_component(CLANG_BIN_DIR ${CMAKE_C_COMPILER} DIRECTORY)
+  set(PATH_WITH_CLANG "${CLANG_BIN_DIR}:$ENV{PATH}")
+  add_custom_target(tint-generate-coverage
+    COMMAND ${CMAKE_COMMAND} -E env PATH=${PATH_WITH_CLANG} ./tools/tint-generate-coverage $<TARGET_FILE:tint_unittests>
+    DEPENDS tint_unittests
+    WORKING_DIRECTORY ${TINT_ROOT_SOURCE_DIR}
+    COMMENT "Generating tint coverage data"
+    VERBATIM)
+endif()
+
+if (TINT_BUILD_REMOTE_COMPILE)
+  add_subdirectory(tools/src/cmd/remote-compile)
+endif()
diff --git a/CMakeSettings.json b/CMakeSettings.json
new file mode 100644
index 0000000..ee3ee56
--- /dev/null
+++ b/CMakeSettings.json
@@ -0,0 +1,100 @@
+{

+  "configurations": [

+    {

+      "name": "x64-Debug",

+      "generator": "Ninja",

+      "configurationType": "Debug",

+      "inheritEnvironments": [ "msvc_x64_x64" ],

+      "buildRoot": "${projectDir}\\out\\build\\${name}",

+      "installRoot": "${projectDir}\\out\\install\\${name}",

+      "cmakeCommandArgs": "",

+      "buildCommandArgs": "",

+      "ctestCommandArgs": "",

+      "variables": []

+    },

+    {

+      "name": "x64-Release",

+      "generator": "Ninja",

+      "configurationType": "RelWithDebInfo",

+      "buildRoot": "${projectDir}\\out\\build\\${name}",

+      "installRoot": "${projectDir}\\out\\install\\${name}",

+      "cmakeCommandArgs": "",

+      "buildCommandArgs": "",

+      "ctestCommandArgs": "",

+      "inheritEnvironments": [ "msvc_x64_x64" ],

+      "variables": []

+    },

+    {

+      "name": "x86-Debug",

+      "generator": "Ninja",

+      "configurationType": "Debug",

+      "buildRoot": "${projectDir}\\out\\build\\${name}",

+      "installRoot": "${projectDir}\\out\\install\\${name}",

+      "cmakeCommandArgs": "",

+      "buildCommandArgs": "",

+      "ctestCommandArgs": "",

+      "inheritEnvironments": [ "msvc_x86" ],

+      "variables": []

+    },

+    {

+      "name": "x86-Release",

+      "generator": "Ninja",

+      "configurationType": "RelWithDebInfo",

+      "buildRoot": "${projectDir}\\out\\build\\${name}",

+      "installRoot": "${projectDir}\\out\\install\\${name}",

+      "cmakeCommandArgs": "",

+      "buildCommandArgs": "",

+      "ctestCommandArgs": "",

+      "inheritEnvironments": [ "msvc_x86" ],

+      "variables": []

+    },

+    {

+      "name": "x64-Clang-Debug",

+      "generator": "Ninja",

+      "configurationType": "Debug",

+      "buildRoot": "${projectDir}\\out\\build\\${name}",

+      "installRoot": "${projectDir}\\out\\install\\${name}",

+      "cmakeCommandArgs": "",

+      "buildCommandArgs": "",

+      "ctestCommandArgs": "",

+      "inheritEnvironments": [ "clang_cl_x64_x64" ],

+      "variables": []

+    },

+    {

+      "name": "x64-Clang-Release",

+      "generator": "Ninja",

+      "configurationType": "RelWithDebInfo",

+      "buildRoot": "${projectDir}\\out\\build\\${name}",

+      "installRoot": "${projectDir}\\out\\install\\${name}",

+      "cmakeCommandArgs": "",

+      "buildCommandArgs": "",

+      "ctestCommandArgs": "",

+      "inheritEnvironments": [ "clang_cl_x64_x64" ],

+      "variables": []

+    },

+    {

+      "name": "x86-Clang-Debug",

+      "generator": "Ninja",

+      "configurationType": "Debug",

+      "buildRoot": "${projectDir}\\out\\build\\${name}",

+      "installRoot": "${projectDir}\\out\\install\\${name}",

+      "cmakeCommandArgs": "",

+      "buildCommandArgs": "",

+      "ctestCommandArgs": "",

+      "inheritEnvironments": [ "clang_cl_x86" ],

+      "variables": []

+    },

+    {

+      "name": "x86-Clang-Release",

+      "generator": "Ninja",

+      "configurationType": "RelWithDebInfo",

+      "buildRoot": "${projectDir}\\out\\build\\${name}",

+      "installRoot": "${projectDir}\\out\\install\\${name}",

+      "cmakeCommandArgs": "",

+      "buildCommandArgs": "",

+      "ctestCommandArgs": "",

+      "inheritEnvironments": [ "clang_cl_x86" ],

+      "variables": []

+    }

+  ]

+}
\ No newline at end of file
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..12921d9
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,93 @@
+# Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, gender identity and expression, level of
+experience, education, socio-economic status, nationality, personal appearance,
+race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+*   Using welcoming and inclusive language
+*   Being respectful of differing viewpoints and experiences
+*   Gracefully accepting constructive criticism
+*   Focusing on what is best for the community
+*   Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+*   The use of sexualized language or imagery and unwelcome sexual attention or
+    advances
+*   Trolling, insulting/derogatory comments, and personal or political attacks
+*   Public or private harassment
+*   Publishing others' private information, such as a physical or electronic
+    address, without explicit permission
+*   Other conduct which could reasonably be considered inappropriate in a
+    professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, or to ban temporarily or permanently any
+contributor for other behaviors that they deem inappropriate, threatening,
+offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+This Code of Conduct also applies outside the project spaces when the Project
+Steward has a reasonable belief that an individual's behavior may have a
+negative impact on the project or its community.
+
+## Conflict Resolution
+
+We do not believe that all conflict is bad; healthy debate and disagreement
+often yield positive results. However, it is never okay to be disrespectful or
+to engage in behavior that violates the project’s code of conduct.
+
+If you see someone violating the code of conduct, you are encouraged to address
+the behavior directly with those involved. Many issues can be resolved quickly
+and easily, and this gives people more control over the outcome of their
+dispute. If you are unable to resolve the matter for any reason, or if the
+behavior is threatening or harassing, report it. We are dedicated to providing
+an environment where participants feel welcome and safe.
+
+Reports should be directed to David Neto <dneto@google.com>, the
+Project Steward(s) for Tint. It is the Project Steward’s duty to
+receive and address reported violations of the code of conduct. They will then
+work with a committee consisting of representatives from the Open Source
+Programs Office and the Google Open Source Strategy team. If for any reason you
+are uncomfortable reaching out the Project Steward, please email
+opensource@google.com.
+
+We will investigate every complaint, but you may not receive a direct response.
+We will use our discretion in determining when and how to follow up on reported
+incidents, which may range from not taking action to permanent expulsion from
+the project and project-sponsored spaces. We will notify the accused of the
+report and provide them an opportunity to discuss it before any action is taken.
+The identity of the reporter will be omitted from the details of the report
+supplied to the accused. In potentially harmful situations, such as ongoing
+harassment or threats to anyone's safety, we may take action without notice.
+
+## Attribution
+
+This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
+available at
+https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..329011e
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,45 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement. You (or your employer) retain the copyright to your contribution;
+this simply gives us permission to use and redistribute your contributions as
+part of the project. Head over to <https://cla.developers.google.com/> to see
+your current agreements on file or to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code reviews
+
+All submissions, including submissions by project members, require review. We
+use [Dawn's Gerrit](https://dawn-review.googlesource.com/) for this purpose.
+
+Submissions should follow the [Tint style guide](docs/tint/style_guide.md).
+
+## Pushing to Gerrit
+
+Each change requires a `Change-Id` field in the commit message, which is generated by the [Gerrit commit-msg hook](](https://gerrit-review.googlesource.com/Documentation/cmd-hook-commit-msg.html)). \
+In a bash terminal, with the current path set to your tint source tree, this can be obtained by running the following:
+
+```bash
+f=`git rev-parse --git-dir`/hooks/commit-msg ; mkdir -p $(dirname $f) ; curl -Lo $f https://gerrit-review.googlesource.com/tools/hooks/commit-msg ; chmod +x $f
+```
+
+If you've already locally committed a change without the `Change-Id`, running `git commit --amend` will add the missing `Change-Id`.
+
+To create a Gerrit change for review, type:
+
+```bash
+git push origin HEAD:refs/for/main
+```
+
+## Community Guidelines
+
+This project follows
+[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/).
diff --git a/CPPLINT.cfg b/CPPLINT.cfg
new file mode 100644
index 0000000..36d9cb2
--- /dev/null
+++ b/CPPLINT.cfg
@@ -0,0 +1 @@
+set noparent
diff --git a/DEPS b/DEPS
index 79df40c..0c13f63 100644
--- a/DEPS
+++ b/DEPS
@@ -42,7 +42,6 @@
     'url': '{chromium_git}/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@99803d74e35962f63a775f29477882afd4d57d94',
     'condition': 'dawn_standalone',
   },
-
   'buildtools/linux64': {
     'packages': [{
       'package': 'gn/gn/linux-amd64',
@@ -116,11 +115,6 @@
     'condition': 'dawn_standalone',
   },
 
-  # WGSL support
-  'third_party/tint': {
-    'url': '{dawn_git}/tint@a730eb738e9f00fb52e9ac38cebe978373602a1e',
-  },
-
   # GLFW for tests and samples
   'third_party/glfw': {
     'url': '{chromium_git}/external/github.com/glfw/glfw@94773111300fee0453844a4c9407af7e880b4df8',
@@ -176,6 +170,10 @@
     'url': '{github_git}/gpuweb/gpuweb.git@881403b5fda2d9ac9ffc5daa24e34738205bf155',
     'condition': 'dawn_node',
   },
+  'third_party/gpuweb-cts': {
+    'url': '{chromium_git}/external/github.com/gpuweb/cts@b0291fd966b55a5efc496772555b94842bde1085',
+    'condition': 'dawn_standalone',
+  },
 
   'tools/golang': {
     'condition': 'dawn_node',
@@ -194,6 +192,16 @@
     }],
     'dep_type': 'cipd',
   },
+
+  # Misc dependencies inherited from Tint
+  'third_party/benchmark': {
+    'url': '{chromium_git}/external/github.com/google/benchmark.git@e991355c02b93fe17713efe04cbc2e278e00fdbd',
+    'condition': 'dawn_standalone',
+  },
+  'third_party/protobuf': {
+    'url': '{chromium_git}/external/github.com/protocolbuffers/protobuf.git@fde7cf7358ec7cd69e8db9be4f1fa6a5c431386a',
+    'condition': 'dawn_standalone',
+  },
 }
 
 hooks = [
@@ -253,33 +261,22 @@
     'condition': 'dawn_standalone and host_os == "win"',
     'action': [ 'download_from_google_storage',
                 '--no_resume',
+                '--platform=win32',
                 '--no_auth',
                 '--bucket', 'chromium-clang-format',
                 '-s', 'buildtools/win/clang-format.exe.sha1',
     ],
   },
   {
-    'name': 'clang_format_mac_x64',
+    'name': 'clang_format_mac',
     'pattern': '.',
-    'condition': 'dawn_standalone and host_os == "mac" and host_cpu == "x64"',
+    'condition': 'dawn_standalone and host_os == "mac"',
     'action': [ 'download_from_google_storage',
                 '--no_resume',
+                '--platform=darwin',
                 '--no_auth',
                 '--bucket', 'chromium-clang-format',
-                '-s', 'buildtools/mac/clang-format.x64.sha1',
-                '-o', 'buildtools/mac/clang-format',
-    ],
-  },
-  {
-    'name': 'clang_format_mac_arm64',
-    'pattern': '.',
-    'condition': 'dawn_standalone and host_os == "mac" and host_cpu == "arm64"',
-    'action': [ 'download_from_google_storage',
-                '--no_resume',
-                '--no_auth',
-                '--bucket', 'chromium-clang-format',
-                '-s', 'buildtools/mac/clang-format.arm64.sha1',
-                '-o', 'buildtools/mac/clang-format',
+                '-s', 'buildtools/mac/clang-format.sha1',
     ],
   },
   {
@@ -288,11 +285,59 @@
     'condition': 'dawn_standalone and host_os == "linux"',
     'action': [ 'download_from_google_storage',
                 '--no_resume',
+                '--platform=linux*',
                 '--no_auth',
                 '--bucket', 'chromium-clang-format',
                 '-s', 'buildtools/linux64/clang-format.sha1',
     ],
   },
+  # Pull the compilers and system libraries for hermetic builds
+  {
+    'name': 'sysroot_x86',
+    'pattern': '.',
+    'condition': 'checkout_linux and ((checkout_x86 or checkout_x64))',
+    'action': ['python3', 'build/linux/sysroot_scripts/install-sysroot.py',
+               '--arch=x86'],
+  },
+  {
+    'name': 'sysroot_x64',
+    'pattern': '.',
+    'condition': 'checkout_linux and (checkout_x64)',
+    'action': ['python3', 'build/linux/sysroot_scripts/install-sysroot.py',
+               '--arch=x64'],
+  },
+  {
+    # Update the Mac toolchain if necessary.
+    'name': 'mac_toolchain',
+    'pattern': '.',
+    'condition': 'checkout_mac',
+    'action': ['python3', 'build/mac_toolchain.py'],
+  },
+  {
+    # Update the Windows toolchain if necessary. Must run before 'clang' below.
+    'name': 'win_toolchain',
+    'pattern': '.',
+    'condition': 'checkout_win',
+    'action': ['python3', 'build/vs_toolchain.py', 'update', '--force'],
+  },
+  {
+    # Note: On Win, this should run after win_toolchain, as it may use it.
+    'name': 'clang',
+    'pattern': '.',
+    'action': ['python3', 'tools/clang/scripts/update.py'],
+  },
+  {
+    # Pull rc binaries using checked-in hashes.
+    'name': 'rc_win',
+    'pattern': '.',
+    'condition': 'checkout_win and (host_os == "win")',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--no_auth',
+                '--bucket', 'chromium-browser-clang/rc',
+                '-s', 'build/toolchain/win/rc/win/rc.exe.sha1',
+    ],
+  },
   # Update build/util/LASTCHANGE.
   {
     'name': 'lastchange',
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 0000000..08eac60
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,2474 @@
+# Doxyfile 1.8.14
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "Tint"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          = Tint
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = $(DOXYGEN_OUTPUT_DIRECTORY)
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 2
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS   = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = yes
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = yes
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = YES
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = YES
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = YES
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = YES
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = YES
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR          = $(DOXYGEN_WARN_AS_ERROR)
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT             = $(DOXYGEN_WARN_FORMAT)
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = CODE_OF_CONDUCT.md \
+                         src/tint/fuzzers/tint_spirv_tools_fuzzer \
+                         src \
+                         tools/src \
+                         src/tint/fuzzers/tint_spirv_tools_fuzzer \
+                         src/tint/fuzzers/tint_ast_fuzzer
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.idl \
+                         *.ddl \
+                         *.odl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.cs \
+                         *.d \
+                         *.php \
+                         *.php4 \
+                         *.php5 \
+                         *.phtml \
+                         *.inc \
+                         *.m \
+                         *.markdown \
+                         *.md \
+                         *.mm \
+                         *.dox \
+                         *.py \
+                         *.pyw \
+                         *.f90 \
+                         *.f95 \
+                         *.f03 \
+                         *.f08 \
+                         *.f \
+                         *.for \
+                         *.tcl \
+                         *.vhd \
+                         *.vhdl \
+                         *.ucf \
+                         *.qsf
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = *_test.cc
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = ./README.md
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via Javascript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have Javascript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+#HTML_DYNAMIC_MENUS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: https://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             = DOXYGEN
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+#PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+#MSCGEN_PATH            =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH      =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE      =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH  =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/OWNERS b/OWNERS
index 1856f30..a7cef5e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -9,3 +9,12 @@
 per-file dawn.json=kainino@chromium.org
 per-file DEPS=*
 per-file README.md=file://docs/dawn/OWNERS
+
+# Tint specific OWNERS
+amaiorano@google.com
+bclayton@chromium.org
+bclayton@google.com
+cwallez@chromium.org
+dneto@google.com
+jrprice@google.com
+rharrison@chromium.org
diff --git a/OWNERS.dawn b/OWNERS.dawn
new file mode 100644
index 0000000..1856f30
--- /dev/null
+++ b/OWNERS.dawn
@@ -0,0 +1,11 @@
+cwallez@chromium.org
+enga@chromium.org
+jiawei.shao@intel.com
+
+# Backup reviewers if needed.
+bclayton@google.com
+kainino@chromium.org
+
+per-file dawn.json=kainino@chromium.org
+per-file DEPS=*
+per-file README.md=file://docs/dawn/OWNERS
diff --git a/OWNERS.tint b/OWNERS.tint
new file mode 100644
index 0000000..18239af
--- /dev/null
+++ b/OWNERS.tint
@@ -0,0 +1,7 @@
+amaiorano@google.com
+bclayton@chromium.org
+bclayton@google.com
+cwallez@chromium.org
+dneto@google.com
+jrprice@google.com
+rharrison@chromium.org
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 899e0e2..968a27c 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1,4 +1,4 @@
-# Copyright 2018 The Dawn Authors
+# Copyright 2022 The Dawn & Tint Authors
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,12 +12,97 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import os
-import platform
-import subprocess
+import re
 
 USE_PYTHON3 = True
 
+NONINCLUSIVE_REGEXES = [
+    r"(?i)black[-_]?list",
+    r"(?i)white[-_]?list",
+    r"(?i)gr[ea]y[-_]?list",
+    r"(?i)(first class citizen)",
+    r"(?i)black[-_]?hat",
+    r"(?i)white[-_]?hat",
+    r"(?i)gr[ea]y[-_]?hat",
+    r"(?i)master",
+    r"(?i)slave",
+    r"(?i)\bhim\b",
+    r"(?i)\bhis\b",
+    r"(?i)\bshe\b",
+    r"(?i)\bher\b",
+    r"(?i)\bguys\b",
+    r"(?i)\bhers\b",
+    r"(?i)\bman\b",
+    r"(?i)\bwoman\b",
+    r"(?i)\she\s",
+    r"(?i)\she$",
+    r"(?i)^he\s",
+    r"(?i)^he$",
+    r"(?i)\she['|\u2019]d\s",
+    r"(?i)\she['|\u2019]d$",
+    r"(?i)^he['|\u2019]d\s",
+    r"(?i)^he['|\u2019]d$",
+    r"(?i)\she['|\u2019]s\s",
+    r"(?i)\she['|\u2019]s$",
+    r"(?i)^he['|\u2019]s\s",
+    r"(?i)^he['|\u2019]s$",
+    r"(?i)\she['|\u2019]ll\s",
+    r"(?i)\she['|\u2019]ll$",
+    r"(?i)^he['|\u2019]ll\s",
+    r"(?i)^he['|\u2019]ll$",
+    r"(?i)grandfather",
+    r"(?i)\bmitm\b",
+    r"(?i)\bcrazy\b",
+    r"(?i)\binsane\b",
+    r"(?i)\bblind\sto\b",
+    r"(?i)\bflying\sblind\b",
+    r"(?i)\bblind\seye\b",
+    r"(?i)\bcripple\b",
+    r"(?i)\bcrippled\b",
+    r"(?i)\bdumb\b",
+    r"(?i)\bdummy\b",
+    r"(?i)\bparanoid\b",
+    r"(?i)\bsane\b",
+    r"(?i)\bsanity\b",
+    r"(?i)red[-_]?line",
+]
+
+NONINCLUSIVE_REGEX_LIST = []
+for reg in NONINCLUSIVE_REGEXES:
+    NONINCLUSIVE_REGEX_LIST.append(re.compile(reg))
+
+
+def _CheckNonInclusiveLanguage(input_api, output_api, source_file_filter=None):
+    """Checks the files for non-inclusive language."""
+
+    matches = []
+    for f in input_api.AffectedFiles(include_deletes=False,
+                                     file_filter=source_file_filter):
+        for line_num, line in f.ChangedContents():
+            for reg in NONINCLUSIVE_REGEX_LIST:
+                match = reg.search(line)
+                if match:
+                    matches.append(
+                        "{} ({}): found non-inclusive language: {}".format(
+                            f.LocalPath(), line_num, match.group(0)))
+
+    if len(matches):
+        return [
+            output_api.PresubmitPromptWarning('Non-inclusive language found:',
+                                              items=matches)
+        ]
+
+    return []
+
+
+def _NonInclusiveFileFilter(file):
+    filter_list = [
+        "PRESUBMIT.py",  # Non-inclusive language check data
+        "docs/tint/spirv-input-output-variables.md",  # External URL
+        "test/tint/samples/compute_boids.wgsl ",  # External URL
+    ]
+    return file in filter_list
+
 
 def _DoCommonChecks(input_api, output_api):
     results = []
@@ -27,6 +112,30 @@
         input_api.canned_checks.CheckPatchFormatted(input_api,
                                                     output_api,
                                                     check_python=True))
+    results.extend(
+        input_api.canned_checks.CheckChangeHasDescription(
+            input_api, output_api))
+    results.extend(
+        input_api.canned_checks.CheckGNFormatted(input_api, output_api))
+    results.extend(
+        input_api.canned_checks.CheckChangeHasNoCrAndHasOnlyOneEol(
+            input_api, output_api))
+    results.extend(
+        input_api.canned_checks.CheckChangeHasNoTabs(input_api, output_api))
+    results.extend(
+        input_api.canned_checks.CheckChangeTodoHasOwner(input_api, output_api))
+    results.extend(
+        input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
+            input_api, output_api))
+    results.extend(
+        input_api.canned_checks.CheckDoNotSubmit(input_api, output_api))
+    results.extend(
+        input_api.canned_checks.CheckChangeLintsClean(input_api,
+                                                      output_api,
+                                                      lint_filters=""))
+    results.extend(
+        _CheckNonInclusiveLanguage(input_api, output_api,
+                                   _NonInclusiveFileFilter))
     return results
 
 
diff --git a/PRESUBMIT.py.dawn b/PRESUBMIT.py.dawn
new file mode 100644
index 0000000..899e0e2
--- /dev/null
+++ b/PRESUBMIT.py.dawn
@@ -0,0 +1,38 @@
+# Copyright 2018 The Dawn Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import platform
+import subprocess
+
+USE_PYTHON3 = True
+
+
+def _DoCommonChecks(input_api, output_api):
+    results = []
+    results.extend(
+        input_api.canned_checks.CheckChangedLUCIConfigs(input_api, output_api))
+    results.extend(
+        input_api.canned_checks.CheckPatchFormatted(input_api,
+                                                    output_api,
+                                                    check_python=True))
+    return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+    return _DoCommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+    return _DoCommonChecks(input_api, output_api)
diff --git a/PRESUBMIT.py.tint b/PRESUBMIT.py.tint
new file mode 100755
index 0000000..97623c1
--- /dev/null
+++ b/PRESUBMIT.py.tint
@@ -0,0 +1,167 @@
+# Copyright 2020 The Tint Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Presubmit script for Tint.
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+import re
+
+USE_PYTHON3 = True
+
+
+def _LicenseHeader(input_api):
+    """Returns the license header regexp."""
+    # Accept any year number from 2019 to the current year
+    current_year = int(input_api.time.strftime('%Y'))
+    allowed_years = (str(s) for s in reversed(xrange(2019, current_year + 1)))
+    years_re = '(' + '|'.join(allowed_years) + ')'
+    license_header = (
+        r'.*? Copyright( \(c\))? %(year)s The Tint [Aa]uthors\n '
+        r'.*?\n'
+        r'.*? Licensed under the Apache License, Version 2.0 (the "License");\n'
+        r'.*? you may not use this file except in compliance with the License.\n'
+        r'.*? You may obtain a copy of the License at\n'
+        r'.*?\n'
+        r'.*?     http://www.apache.org/licenses/LICENSE-2.0\n'
+        r'.*?\n'
+        r'.*? Unless required by applicable law or agreed to in writing, software\n'
+        r'.*? distributed under the License is distributed on an "AS IS" BASIS,\n'
+        r'.*? WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
+        r'.*? See the License for the specific language governing permissions and\n'
+        r'.*? limitations under the License.\n') % {
+            'year': years_re,
+        }
+    return license_header
+
+
+REGEXES = [
+    r"(?i)black[-_]?list",
+    r"(?i)white[-_]?list",
+    r"(?i)gr[ea]y[-_]?list",
+    r"(?i)(first class citizen)",
+    r"(?i)black[-_]?hat",
+    r"(?i)white[-_]?hat",
+    r"(?i)gr[ea]y[-_]?hat",
+    r"(?i)master",
+    r"(?i)slave",
+    r"(?i)\bhim\b",
+    r"(?i)\bhis\b",
+    r"(?i)\bshe\b",
+    r"(?i)\bher\b",
+    r"(?i)\bguys\b",
+    r"(?i)\bhers\b",
+    r"(?i)\bman\b",
+    r"(?i)\bwoman\b",
+    r"(?i)\she\s",
+    r"(?i)\she$",
+    r"(?i)^he\s",
+    r"(?i)^he$",
+    r"(?i)\she['|\u2019]d\s",
+    r"(?i)\she['|\u2019]d$",
+    r"(?i)^he['|\u2019]d\s",
+    r"(?i)^he['|\u2019]d$",
+    r"(?i)\she['|\u2019]s\s",
+    r"(?i)\she['|\u2019]s$",
+    r"(?i)^he['|\u2019]s\s",
+    r"(?i)^he['|\u2019]s$",
+    r"(?i)\she['|\u2019]ll\s",
+    r"(?i)\she['|\u2019]ll$",
+    r"(?i)^he['|\u2019]ll\s",
+    r"(?i)^he['|\u2019]ll$",
+    r"(?i)grandfather",
+    r"(?i)\bmitm\b",
+    r"(?i)\bcrazy\b",
+    r"(?i)\binsane\b",
+    r"(?i)\bblind\sto\b",
+    r"(?i)\bflying\sblind\b",
+    r"(?i)\bblind\seye\b",
+    r"(?i)\bcripple\b",
+    r"(?i)\bcrippled\b",
+    r"(?i)\bdumb\b",
+    r"(?i)\bdummy\b",
+    r"(?i)\bparanoid\b",
+    r"(?i)\bsane\b",
+    r"(?i)\bsanity\b",
+    r"(?i)red[-_]?line",
+]
+
+REGEX_LIST = []
+for reg in REGEXES:
+    REGEX_LIST.append(re.compile(reg))
+
+def CheckNonInclusiveLanguage(input_api, output_api, source_file_filter=None):
+    """Checks the files for non-inclusive language."""
+
+    matches = []
+    for f in input_api.AffectedFiles(include_deletes=False,
+                                     file_filter=source_file_filter):
+        for line_num, line in f.ChangedContents():
+            for reg in REGEX_LIST:
+                match = reg.search(line)
+                if match:
+                    matches.append(
+                        "{} ({}): found non-inclusive language: {}".format(
+                            f.LocalPath(), line_num, match.group(0)))
+
+    if len(matches):
+        return [
+            output_api.PresubmitPromptWarning('Non-inclusive language found:',
+                                              items=matches)
+        ]
+
+    return []
+
+
+def CheckChange(input_api, output_api):
+    results = []
+
+    results += input_api.canned_checks.CheckChangeHasDescription(
+        input_api, output_api)
+    results += input_api.canned_checks.CheckPatchFormatted(input_api,
+                                                           output_api,
+                                                           check_python=True)
+    results += input_api.canned_checks.CheckGNFormatted(input_api, output_api)
+    results += input_api.canned_checks.CheckChangeHasNoCrAndHasOnlyOneEol(
+        input_api, output_api)
+    results += input_api.canned_checks.CheckChangeHasNoTabs(
+        input_api, output_api)
+    results += input_api.canned_checks.CheckChangeTodoHasOwner(
+        input_api, output_api)
+    results += input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
+        input_api, output_api)
+    results += input_api.canned_checks.CheckDoNotSubmit(input_api, output_api)
+    results += input_api.canned_checks.CheckChangeLintsClean(input_api,
+                                                             output_api,
+                                                             lint_filters="")
+
+    def NonInclusiveFileFilter(file):
+        filter_list = [
+            "docs/tint/spirv-input-output-variables.md",  # External URL
+            "test/tint/samples/compute_boids.wgsl ",  # External URL
+        ]
+        return file in filter_list
+
+    results += CheckNonInclusiveLanguage(input_api, output_api,
+                                         NonInclusiveFileFilter)
+
+    return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+    return CheckChange(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+    return CheckChange(input_api, output_api)
diff --git a/README.md b/README.md
index 1871388..a430410 100644
--- a/README.md
+++ b/README.md
@@ -50,3 +50,110 @@
 ## Disclaimer
 
 This is not an officially supported Google product.
+
+# Tint
+
+Tint is a compiler for the WebGPU Shader Language (WGSL).
+
+This is not an officially supported Google product.
+
+## Requirements
+ * Git
+ * CMake (3.10.2 or later)
+ * Ninja (or other build tool)
+ * Python, for fetching dependencies
+ * [depot_tools] in your path
+
+## Build options
+ * `TINT_BUILD_SPV_READER` : enable the SPIR-V input reader (off by default)
+ * `TINT_BUILD_WGSL_READER` : enable the WGSL input reader (on by default)
+ * `TINT_BUILD_SPV_WRITER` : enable the SPIR-V output writer (on by default)
+ * `TINT_BUILD_WGSL_WRITER` : enable the WGSL output writer (on by default)
+ * `TINT_BUILD_FUZZERS` : enable building fuzzzers (off by default)
+
+## Building
+Tint uses Chromium dependency management so you need to install [depot_tools]
+and add it to your PATH.
+
+[depot_tools]: http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up
+
+### Getting source & dependencies
+
+```sh
+# Clone the repo as "tint"
+git clone https://dawn.googlesource.com/tint tint
+cd tint
+
+# Bootstrap the gclient configuration
+cp standalone.gclient .gclient
+
+# Fetch external dependencies and toolchains with gclient
+gclient sync
+```
+
+### Compiling using CMake + Ninja
+```sh
+mkdir -p out/Debug
+cd out/Debug
+cmake -GNinja ../..
+ninja # or autoninja
+```
+
+### Compiling using CMake + make
+```sh
+mkdir -p out/Debug
+cd out/Debug
+cmake ../..
+make # -j N for N-way parallel build
+```
+
+### Compiling using gn + ninja
+```sh
+mkdir -p out/Debug
+gn gen out/Debug
+autoninja -C out/Debug
+```
+
+### Fuzzers on MacOS
+If you are attempting fuzz, using `TINT_BUILD_FUZZERS=ON`, the version of llvm
+in the XCode SDK does not have the needed libfuzzer functionality included.
+
+The build error that you will see from using the XCode SDK will look something
+like this:
+```
+ld: file not found:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.fuzzer_osx.a
+```
+
+The solution to this problem is to use a full version llvm, like what you would
+get via homebrew, `brew install llvm`, and use something like `CC=<path to full
+clang> cmake ..` to setup a build using that toolchain.
+
+### Checking [chromium-style] issues in CMake builds
+The gn based work flow uses the Chromium toolchain for building in anticipation
+of integration of Tint into Chromium based projects. This toolchain has
+additional plugins for checking for style issues, which are marked with
+[chromium-style] in log messages. This means that this toolchain is more strict
+then the default clang toolchain.
+
+In the future we will have a CQ that will build this work flow and flag issues
+automatically. Until that is in place, to avoid causing breakages you can run
+the [chromium-style] checks using the CMake based work flows. This requires
+setting `CC` to the version of clang checked out by `gclient sync` and setting
+the `TINT_CHECK_CHROMIUM_STYLE` to `ON`.
+
+```sh
+mkdir -p out/style
+cd out/style
+cmake ../..
+CC=../../third_party/llvm-build/Release+Asserts/bin/clang cmake -DTINT_CHECK_CHROMIUM_STYLE=ON ../../ # add -GNinja for ninja builds
+```
+
+## Issues
+Please file any issues or feature requests at
+https://bugs.chromium.org/p/tint/issues/entry
+
+## Contributing
+Please see the CONTRIBUTING and CODE_OF_CONDUCT files on how to contribute to
+Tint.
+
+Tint has a process for supporting [experimental extensions](docs/tint/experimental_extensions.md).
diff --git a/README.md.dawn b/README.md.dawn
new file mode 100644
index 0000000..1871388
--- /dev/null
+++ b/README.md.dawn
@@ -0,0 +1,52 @@
+![Dawn's logo: a sun rising behind a stylized mountain inspired by the WebGPU logo. The text "Dawn" is written below it.](docs/imgs/dawn_logo.png "Dawn's logo")
+
+# Dawn, a WebGPU implementation
+
+Dawn is an open-source and cross-platform implementation of the work-in-progress [WebGPU](https://webgpu.dev) standard.
+More precisely it implements [`webgpu.h`](https://github.com/webgpu-native/webgpu-headers/blob/master/webgpu.h) that is a one-to-one mapping with the WebGPU IDL.
+Dawn is meant to be integrated as part of a larger system and is the underlying implementation of WebGPU in Chromium.
+
+Dawn provides several WebGPU building blocks:
+ - **WebGPU C/C++ headers** that applications and other building blocks use.
+   - The `webgpu.h` version that Dawn implements.
+   - A C++ wrapper for the `webgpu.h`.
+ - **A "native" implementation of WebGPU** using platforms' GPU APIs:
+   - **D3D12** on Windows 10
+   - **Metal** on macOS and iOS
+   - **Vulkan** on Windows, Linux, ChromeOS, Android and Fuchsia
+   - OpenGL as best effort where available
+ - **A client-server implementation of WebGPU** for applications that are in a sandbox without access to native drivers
+
+Helpful links:
+
+ - [Dawn's bug tracker](https://bugs.chromium.org/p/dawn/issues/entry) if you find issues with Dawn.
+ - [Dawn's mailing list](https://groups.google.com/forum/#!members/dawn-graphics) for other discussions related to Dawn.
+ - [Dawn's source code](https://dawn.googlesource.com/dawn)
+ - [Dawn's Matrix chatroom](https://matrix.to/#/#webgpu-dawn:matrix.org) for live discussion around contributing or using Dawn.
+ - [WebGPU's Matrix chatroom](https://matrix.to/#/#WebGPU:matrix.org)
+
+## Documentation table of content
+
+Developer documentation:
+
+ - [Dawn overview](docs/dawn/overview.md)
+ - [Building Dawn](docs/dawn/building.md)
+ - [Contributing to Dawn](docs/dawn/contributing.md)
+ - [Testing Dawn](docs/dawn/testing.md)
+ - [Debugging Dawn](docs/dawn/debugging.md)
+ - [Dawn's infrastructure](docs/dawn/infra.md)
+ - [Dawn errors](docs/dawn/errors.md)
+
+User documentation: (TODO, figure out what overlaps with the webgpu.h docs)
+
+## Status
+
+(TODO)
+
+## License
+
+Apache 2.0 Public License, please see [LICENSE](/LICENSE).
+
+## Disclaimer
+
+This is not an officially supported Google product.
diff --git a/README.md.tint b/README.md.tint
new file mode 100644
index 0000000..fbe6cfb
--- /dev/null
+++ b/README.md.tint
@@ -0,0 +1,106 @@
+# Tint
+
+Tint is a compiler for the WebGPU Shader Language (WGSL).
+
+This is not an officially supported Google product.
+
+## Requirements
+ * Git
+ * CMake (3.10.2 or later)
+ * Ninja (or other build tool)
+ * Python, for fetching dependencies
+ * [depot_tools] in your path
+
+## Build options
+ * `TINT_BUILD_SPV_READER` : enable the SPIR-V input reader (off by default)
+ * `TINT_BUILD_WGSL_READER` : enable the WGSL input reader (on by default)
+ * `TINT_BUILD_SPV_WRITER` : enable the SPIR-V output writer (on by default)
+ * `TINT_BUILD_WGSL_WRITER` : enable the WGSL output writer (on by default)
+ * `TINT_BUILD_FUZZERS` : enable building fuzzzers (off by default)
+
+## Building
+Tint uses Chromium dependency management so you need to install [depot_tools]
+and add it to your PATH.
+
+[depot_tools]: http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up
+
+### Getting source & dependencies
+
+```sh
+# Clone the repo as "tint"
+git clone https://dawn.googlesource.com/tint tint
+cd tint
+
+# Bootstrap the gclient configuration
+cp standalone.gclient .gclient
+
+# Fetch external dependencies and toolchains with gclient
+gclient sync
+```
+
+### Compiling using CMake + Ninja
+```sh
+mkdir -p out/Debug
+cd out/Debug
+cmake -GNinja ../..
+ninja # or autoninja
+```
+
+### Compiling using CMake + make
+```sh
+mkdir -p out/Debug
+cd out/Debug
+cmake ../..
+make # -j N for N-way parallel build
+```
+
+### Compiling using gn + ninja
+```sh
+mkdir -p out/Debug
+gn gen out/Debug
+autoninja -C out/Debug
+```
+
+### Fuzzers on MacOS
+If you are attempting fuzz, using `TINT_BUILD_FUZZERS=ON`, the version of llvm
+in the XCode SDK does not have the needed libfuzzer functionality included.
+
+The build error that you will see from using the XCode SDK will look something
+like this:
+```
+ld: file not found:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.fuzzer_osx.a
+```
+
+The solution to this problem is to use a full version llvm, like what you would
+get via homebrew, `brew install llvm`, and use something like `CC=<path to full
+clang> cmake ..` to setup a build using that toolchain.
+
+### Checking [chromium-style] issues in CMake builds
+The gn based work flow uses the Chromium toolchain for building in anticipation
+of integration of Tint into Chromium based projects. This toolchain has
+additional plugins for checking for style issues, which are marked with
+[chromium-style] in log messages. This means that this toolchain is more strict
+then the default clang toolchain.
+
+In the future we will have a CQ that will build this work flow and flag issues
+automatically. Until that is in place, to avoid causing breakages you can run
+the [chromium-style] checks using the CMake based work flows. This requires
+setting `CC` to the version of clang checked out by `gclient sync` and setting
+the `TINT_CHECK_CHROMIUM_STYLE` to `ON`.
+
+```sh
+mkdir -p out/style
+cd out/style
+cmake ../..
+CC=../../third_party/llvm-build/Release+Asserts/bin/clang cmake -DTINT_CHECK_CHROMIUM_STYLE=ON ../../ # add -GNinja for ninja builds
+```
+
+## Issues
+Please file any issues or feature requests at
+https://bugs.chromium.org/p/tint/issues/entry
+
+## Contributing
+Please see the CONTRIBUTING and CODE_OF_CONDUCT files on how to contribute to
+Tint.
+
+Tint has a process for supporting [experimental extensions](docs/tint/experimental_extensions.md).
diff --git a/build_overrides/build.gni b/build_overrides/build.gni
index e883854..8717867 100644
--- a/build_overrides/build.gni
+++ b/build_overrides/build.gni
@@ -1,4 +1,4 @@
-# Copyright 2018 The Dawn Authors
+# Copyright 2022 The Dawn Authors
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/build_overrides/dawn.gni b/build_overrides/dawn.gni
index bbded06..87e1ded 100644
--- a/build_overrides/dawn.gni
+++ b/build_overrides/dawn.gni
@@ -34,7 +34,6 @@
 dawn_googletest_dir = "//third_party/googletest"
 dawn_spirv_tools_dir = "//third_party/vulkan-deps/spirv-tools/src"
 dawn_swiftshader_dir = "//third_party/swiftshader"
-dawn_tint_dir = "//third_party/tint"
 dawn_vulkan_loader_dir = "//third_party/vulkan-deps/vulkan-loader/src"
 dawn_vulkan_validation_layers_dir =
     "//third_party/vulkan-deps/vulkan-validation-layers/src"
diff --git a/build_overrides/tint.gni b/build_overrides/tint.gni
index c4d4d12..8349998 100644
--- a/build_overrides/tint.gni
+++ b/build_overrides/tint.gni
@@ -12,7 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-tint_root_dir = "//third_party/tint"
 tint_spirv_tools_dir = "//third_party/vulkan-deps/spirv-tools/src"
 tint_spirv_headers_dir = "//third_party/vulkan-deps/spirv-headers/src"
 
diff --git a/docs/tint/arch.md b/docs/tint/arch.md
new file mode 100644
index 0000000..204ff64
--- /dev/null
+++ b/docs/tint/arch.md
@@ -0,0 +1,195 @@
+# Tint Architecture
+
+```
+                   ┏━━━━━━━━┓                   ┏━━━━━━┓
+                   ┃ SPIR━V ┃                   ┃ WGSL ┃
+                   ┗━━━━┃━━━┛                   ┗━━━┃━━┛
+                        ▼                           ▼
+              ┏━━━━━━━━━┃━━━━━━━━━━━━━━━━━━━━━━━━━━━┃━━━━━━━━┓
+              ┃         ┃          Reader           ┃        ┃
+              ┃         ┃                           ┃        ┃
+              ┃ ┏━━━━━━━┻━━━━━━┓             ┏━━━━━━┻━━━━━━┓ ┃
+              ┃ ┃ SPIRV-Reader ┃             ┃ WGSL-Reader ┃ ┃
+              ┃ ┗━━━━━━━━━━━━━━┛             ┗━━━━━━━━━━━━━┛ ┃
+              ┗━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┛
+                                      ▼
+                    ┏━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━┓
+                    ┃           ProgramBuilder          ┃
+                    ┃             (mutable)             ┃
+      ┏━━━━━━━━━━━━►┫   ┏━━━━━┓ ┏━━━━━━━┓ ┏━━━━━━━━━┓   ┃
+      ┃             ┃   ┃ AST ┃ ┃ Types ┃ ┃ Symbols ┃   ┃
+      ┃             ┃   ┗━━━━━┛ ┗━━━━━━━┛ ┗━━━━━━━━━┛   ┃
+      ┃             ┗━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┛
+      ┃                               ▼
+      ┃             ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┃┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
+      ▲             ┆ Build           ▼                ┆
+  ┏━━━┻━━━┓         ┆        ┏━━━━━━━━┻━━━━━━━━┓       ┆
+  ┃ Clone ┃         ┆        ┃    Resolver     ┃       ┆
+  ┗━━━┳━━━┛         ┆        ┗━━━━━━━━━━━━━━━━━┛       ┆
+      ▲             └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┃┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
+      ┃                               ▼
+      ┃       ┏━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━┓
+      ┃       ┃                    Program                   ┃
+      ┃       ┃                  (immutable)                 ┃
+      ┣━━━━━━◄┫  ┏━━━━━┓ ┏━━━━━━━┓ ┏━━━━━━━━━━┓ ┏━━━━━━━━━┓  ┃
+      ┃       ┃  ┃ AST ┃ ┃ Types ┃ ┃ Semantic ┃ ┃ Symbols ┃  ┃
+      ┃       ┃  ┗━━━━━┛ ┗━━━━━━━┛ ┗━━━━━━━━━━┛ ┗━━━━━━━━━┛  ┃
+      ┃       ┗━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┛
+      ▲                               ▼
+┏━━━━━┻━━━━━┓                         ┃             ┏━━━━━━━━━━━┓
+┃ Transform ┃◄━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━►┃ Inspector ┃
+┗━━━━━━━━━━━┛                         ┃             ┗━━━━━━━━━━━┛
+                                      ▼
+┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+┃                                  Writers                                    ┃
+┃                                                                             ┃
+┃ ┏━━━━━━━━━━━━━━┓┏━━━━━━━━━━━━━┓┏━━━━━━━━━━━━━┓┏━━━━━━━━━━━━━┓┏━━━━━━━━━━━━┓ ┃
+┃ ┃ SPIRV-Writer ┃┃ WGSL-Writer ┃┃ HLSL-Writer ┃┃ GLSL-Writer ┃┃ MSL-Writer ┃ ┃
+┃ ┗━━━━━━━┳━━━━━━┛┗━━━━━━┳━━━━━━┛┗━━━━━━┳━━━━━━┛┗━━━━━━┳━━━━━━┛┗━━━━━━┳━━━━━┛ ┃
+┗━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━┛
+          ▼              ▼              ▼              ▼              ▼
+     ┏━━━━┻━━━┓      ┏━━━┻━━┓       ┏━━━┻━━┓       ┏━━━┻━━┓        ┏━━┻━━┓
+     ┃ SPIR-V ┃      ┃ WGSL ┃       ┃ HLSL ┃       ┃ GLSL ┃        ┃ MSL ┃
+     ┗━━━━━━━━┛      ┗━━━━━━┛       ┗━━━━━━┛       ┗━━━━━━┛        ┗━━━━━┛
+```
+
+## Reader
+
+Readers are responsible for parsing a shader program and populating a
+`ProgramBuilder` with the parsed AST, type and symbol information.
+
+The WGSL reader is a recursive descent parser. It closely follows the WGSL
+grammar in the naming of the parse methods.
+
+## ProgramBuilder
+
+A `ProgramBuilder` is the primary interface to construct an immutable `Program`.
+There are a number of methods exposed which make creating of the `Program`
+simpler. A `ProgramBuilder` can only be used once, and must be discarded after
+the `Program` is constructed.
+
+A `Program` is built from the `ProgramBuilder` by `std::move()`ing the
+`ProgramBuilder` to a new `Program` object. When built, resolution is performed
+so the produced `Program` will contain all the needed semantic information.
+
+At any time before building the `Program`, `ProgramBuilder::IsValid()` may be
+called to ensure the AST is **structurally** correct. This checks that things
+like `if` statements have a condition and body attached.
+
+If further changes to the `Program` are needed (say via a `Transform`) then a
+new `ProgramBuilder` can be produced by cloning the `Program` into a new
+`ProgramBuilder`.
+
+Unlike `Program`s, `ProgramBuilder`s are not part of the public Tint API.
+
+## AST
+
+The Abstract Syntax Tree is a directed acyclic graph of `ast::Node`s which
+encode the syntactic structure of the WGSL program.
+
+The root of the AST is the `ast::Module` class which holds each of the declared
+functions, variables and user defined types (type aliases and structures).
+
+Each `ast::Node` represents a **single** part of the program's source, and so
+`ast::Node`s are not shared.
+
+The AST does not perform any verification of its content. For example, the
+`ast::StrideAttribute` node has numeric stride parameter, which is a count of
+the number of bytes from the start of one array element to the start of the
+next. The AST node itself does not constrain the set of stride values that you
+can set, aside from storing it as an unsigned integer.
+
+## Types
+
+Types are constructed during the Reader and resolution phases, and are
+held by the `Program` or `ProgramBuilder`. AST and semantic nodes can both
+reference types.
+
+Each `type::Type` node **uniquely** represents a particular spelling of a WGSL
+type within the program, so you can compare `type::Type*` pointers to check for
+equivalence of type expressions.
+For example, there is only one `type::Type` node for the `i32` type, no matter
+how many times it is mentioned in the source program.
+However, if `MyI32` is a type alias for `i32`, then they will have two different
+type nodes.
+
+## Semantic information
+
+Semantic information is held by `sem::Node`s which describe the program at
+a higher / more abstract level than the AST. This includes information such as
+the resolved type of each expression, the resolved overload of a builtin
+function call, and the module scoped variables used by each function.
+
+Semantic information is generated by the `Resolver` when the `Program`
+is built from a `ProgramBuilder`.
+
+The `sem::Info` class holds a map of `ast::Node`s to `sem::Node`s.
+This map is **many-to-one** - i.e. while a AST node might have a single
+corresponding semantic node, the reverse may not be true. For example:
+many `ast::IdentifierExpression` nodes may map to a single `sem::Variable`,
+and so the `sem::Variable` does not have a single corresponding
+`ast::Node`.
+
+Unlike `ast::Node`s, semantic nodes may not necessarily form a directed acyclic
+graph, and the semantic graph may contain diamonds.
+
+## Symbols
+
+Symbols represent a unique string identifier in the source program. These string
+identifiers are transformed into symbols within the `Reader`s.
+
+During the Writer phase, symbols may be emitted as strings using a `Namer`.
+A `Namer` may output the symbol in any form that preserves the uniqueness of
+that symbol.
+
+## Resolver
+
+The `Resolver` will automatically run when a `Program` is built.
+A `Resolver` creates the `Program`s semantic information by analyzing the
+`Program`s AST and type information.
+
+The `Resolver` will validate to make sure the generated `Program` is
+semantically valid.
+
+## Program
+
+A `Program` holds an immutable version of the information from the
+`ProgramBuilder` along with semantic information generated by the
+`Resolver`.
+
+Like `ProgramBuilder`, `Program::IsValid()` may be called to ensure the AST is
+structurally correct and semantically valid, and that the `Resolver` did not
+report any errors.
+
+Unlike the `ProgramBuilder`, a `Program` is fully immutable, and is part of the
+public Tint API. The immutable nature of `Program`s make these entirely safe
+to share between multiple threads without the use of synchronization primitives.
+
+## Inspector
+
+The inspectors job is to go through the `Program` and pull out various pieces of
+information. The information may be used to pass information into the downstream
+compilers (things like specialization constants) or may be used to pass into
+transforms to update the AST before generating the resulting code.
+
+The input `Program` to the inspector must be valid (pass validation).
+
+## Transforms
+
+There maybe various transforms we want to run over the `Program`.
+This is for things like Vertex Pulling or Robust Buffer Access.
+
+A transform operates by cloning the input `Program` into a new `ProgramBuilder`,
+applying the required changes, and then finally building and returning a new
+output `Program`. As the resolver is always run when a `Program` is built,
+Transforms will always emit a `Program` with semantic information.
+
+The input `Program` to a transform must be valid (pass validation).
+If the input `Program` of a transform is valid then the transform must guarantee
+that the output program is also valid.
+
+## Writers
+
+A writer is responsible for writing the `Program` in the target shader language.
+
+The input `Program` to a writer must be valid (pass validation).
diff --git a/docs/tint/compound_statements.md b/docs/tint/compound_statements.md
new file mode 100644
index 0000000..a113cce
--- /dev/null
+++ b/docs/tint/compound_statements.md
@@ -0,0 +1,119 @@
+# Compound Statements
+
+Compound statements are statements that can hold other statements.
+
+This document maps the WGSL compound statements to their semantic tree representations.
+
+## if statement
+
+WGSL:
+```
+if (condition_a) {
+    statement_a;
+} else if (condition_b) {
+    statement_b;
+} else {
+    statement_c;
+}
+```
+
+Semantic tree:
+```
+sem::IfStatement {
+    condition_a
+    sem::BlockStatement {
+        statement_a
+    }
+    sem::ElseStatement {
+        condition_b
+        sem::BlockStatement {
+            statement_b
+        }
+    }
+    sem::ElseStatement {
+        sem::BlockStatement {
+            statement_c
+        }
+    }
+}
+```
+
+## for loop
+
+WGSL:
+```
+for (initializer; condition; continuing) {
+    statement;
+}
+```
+
+Semantic tree:
+```
+sem::ForLoopStatement {
+    sem::Statement  initializer
+    sem::Expression condition
+    sem::Statement  continuing
+
+    sem::LoopBlockStatement {
+        sem::Statement statement
+    }
+}
+```
+
+## loop
+
+WGSL:
+```
+loop (condition) {
+    statement_a;
+    continuing {
+        statement_b;
+    }
+}
+```
+
+Semantic tree:
+```
+sem::LoopStatement {
+    sem::Expression condition
+
+    sem::LoopBlockStatement {
+        sem::Statement statement_a
+        sem::LoopContinuingBlockStatement {
+            sem::Statement statement_b
+        }
+    }
+}
+```
+
+
+## switch statement
+
+WGSL:
+```
+switch (condition) {
+    case literal_a, literal_b: {
+        statement_a;
+    }
+    default {
+        statement_b;
+    }
+}
+```
+
+Semantic tree:
+```
+sem::SwitchStatement {
+    sem::Expression condition
+    sem::CaseStatement {
+        sem::BlockStatement {
+            sem::Statement statement_a
+        }
+    }
+    sem::CaseStatement {
+        sem::BlockStatement {
+            sem::Statement statement_b
+        }
+    }
+}
+```
diff --git a/docs/tint/coverage-info.md b/docs/tint/coverage-info.md
new file mode 100644
index 0000000..fdb79df
--- /dev/null
+++ b/docs/tint/coverage-info.md
@@ -0,0 +1,24 @@
+# Generating and viewing Tint code-coverage
+
+Requirements:
+
+* Host running Linux or macOS
+* Clang toolchain on the `PATH` environment variable
+
+## Building Tint with coverage generation enabled
+
+Follow the steps [to build Tint with CMake](../README.md), but include the additional `-DTINT_EMIT_COVERAGE=1` CMake flag.
+
+## Generate coverage information
+
+Use the `<tint>/tools/tint-generate-coverage` script to run the tint executable or unit tests and generate the coverage information.
+
+The script takes the executable to invoke as the first command line argument, followed by additional arguments to pass to the executable.
+
+For example, to see the code coverage for all unit tests, run:
+`<tint>/tools/tint-generate-coverage <build>/tint_unittests --gtest_brief`
+
+The script will emit two files at the root of the tint directory:
+
+* `coverage.summary` - A text file giving a coverage summary for all Tint source files.
+* `lcov.info` - A binary coverage file that can be consumed with the [VSCode Coverage Gutters](https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters) extension.
diff --git a/docs/tint/diagnostics_guide.md b/docs/tint/diagnostics_guide.md
new file mode 100644
index 0000000..fc8fa22
--- /dev/null
+++ b/docs/tint/diagnostics_guide.md
@@ -0,0 +1,126 @@
+# Tint diagnostic style guide
+
+This guide provides a set of best practices when writing code that emits
+diagnostic messages in Tint. These diagnostics are messages presented to the
+user in case of error or warning.
+
+The goal of this document is to have our diagnostic messages be clear and
+understandable to our users, so that problems are easy to fix, and to try and
+keep a consistent style.
+
+## Message style
+
+* Start diagnostic messages with a lower-case letter
+* Try to keep the message to a single sentence, if possible
+* Do not end the message with punctuation (full stop, exclamation mark, etc)
+
+**Don't:**
+
+```
+shader.wgsl:7:1 error: Cannot take the address of expression.
+```
+
+**Do:**
+
+```
+shader.wgsl:7:1 error: cannot take the address of expression
+```
+
+**Justification:**
+
+Succinct messages are more important than grammatical correctness. \
+This style matches the style found in most other compilers.
+
+## Prefer to use a `Source` location instead of quoting the code in the message
+
+**Don't:**
+
+```
+shader.wgsl:5:7 error: cannot multiply 'expr_a * expr_b' with types i32 and f32
+
+var res : f32 = expr_a * expr_b
+                ^^^^^^^^^^^^^^^
+```
+
+**Do:**
+
+```
+shader.wgsl:5:7 error: cannot multiply types i32 and f32
+
+var res : f32 = expr_a * expr_b
+                ^^^^^^^^^^^^^^^
+```
+
+**Justification:**
+
+The highlighted line provides even more contextual information than the quoted
+source, and duplicating this information doesn't provide any more help to the
+developer. \
+Quoting single word identifiers or keywords from the source is not discouraged.
+
+## Use `note` diagnostics for providing additional links to relevant code
+
+**Don't:**
+
+```
+shader.wgsl:5:11 error: type cannot be used in storage class 'storage' as it is non-host-shareable
+
+    cond : bool;
+           ^^^^
+```
+
+**Do:**
+
+```
+shader.wgsl:5:11 error: type cannot be used in storage class 'storage' as it is non-host-shareable
+
+    cond : bool;
+           ^^^^
+
+shader.wgsl:8:4 note: while instantiating variable 'StorageBuffer'
+
+var<storage> sb : StorageBuffer;
+             ^^
+```
+
+**Justification:**
+
+To properly understand some diagnostics requires looking at more than a single
+line. \
+Multi-source links can greatly reduce the time it takes to properly
+understand a diagnostic message. \
+This is especially important for diagnostics raised from complex whole-program
+analysis, but can also greatly aid simple diagnostics like symbol collision errors.
+
+## Use simple terminology
+
+**Don't:**
+
+```
+shader.wgsl:7:1 error: the originating variable of the left-hand side of an assignment expression must not be declared with read access control.
+```
+
+**Do:**
+
+```
+shader.wgsl:7:1 error: cannot assign to variable with read access control
+
+x.y = 1;
+^^^^^^^
+
+shader.wgsl:2:8 note: read access control declared here
+
+var<storage, read> x : i32;
+             ^^^^
+```
+
+**Justification:**
+
+Diagnostics will be read by Web developers who may not be native English
+speakers and are unlikely to be familiar with WGSL specification terminology.
+Too much technical jargon can be intimidating and confusing. \
+Diagnostics should give enough information to explain what's wrong, and most
+importantly, give enough information so that a fix actionable.
+
+**Caution:** Be careful to not over simplify. Use the specification terminology
+if there's potential ambiguity by not including it.
diff --git a/docs/tint/end-to-end-tests.md b/docs/tint/end-to-end-tests.md
new file mode 100644
index 0000000..8a34f76
--- /dev/null
+++ b/docs/tint/end-to-end-tests.md
@@ -0,0 +1,35 @@
+# Tint end-to-end tests
+
+This repo contains a large number of end-to-end tests at `<tint>/test`.
+
+## Test files
+
+Test input files have either the `.wgsl`, `.spv` or `.spvasm` file extension.
+
+Each test input file is tested against each of the Tint backends. There are `<number-of-input-files>` &times; `<number-of-tint-backends>` tests that are performed on an unfiltered end-to-end test run.
+
+Each backend test can have an **expectation file**. This expectation file sits next to the input file, with a `<input-file>.expected.<format>` extension. For example the test `test/foo.wgsl` would have the HLSL expectation file `test/foo.wgsl.expected.hlsl`.
+
+An expectation file contains the expected output of Tint, when passed the input file for the given backend.
+
+If the first line of the expectation file starts `SKIP`, then the test will be skipped instead of failing the end-to-end test run. It is good practice to include after the `SKIP` a reason for why the test is being skipped, along with any additional details, such as compiler error messages.
+
+## Running
+
+To run the end-to-end tests use the `<tint>/test/test-all.sh` script, passing the path to the tint executable as the first command line argument.
+
+You can pass `--help` to see the full list of command line flags.\
+The most commonly used flags are:
+
+| flag                 | description |
+|----------------------|-------------|
+|`--filter`            | Filters the testing to subset of the tests. The filter argument is a glob pattern that can include `*` for any substring of a file or directory, and `**` for any number of directories.<br>Example: `--filter 'expressions/**/i32.wgsl'` will test all the `i32.wgsl` expression tests.
+|`--format`            | Filters the tests to the particular backend.<br>Example: `--format hlsl` will just test the HLSL backend.
+|`--generate-expected` | Generate expectation files for the tests that previously had no expectation file, or were marked as `SKIP` but now pass.
+|`--generate-skip`     | Generate `SKIP` expectation files for tests that are not currently passing.
+
+## Authoring guidelines
+
+Each test should be as small as possible, and focused on the particular feature being tested.
+
+Use sub-directories whenever possible to group similar tests, and try to keep the pattern of directories as consistent as possible between different tests. This helps filter tests using the `--filter` glob patterns.
diff --git a/docs/tint/experimental_extensions.md b/docs/tint/experimental_extensions.md
new file mode 100644
index 0000000..8fccd13
--- /dev/null
+++ b/docs/tint/experimental_extensions.md
@@ -0,0 +1,44 @@
+# Experimental extensions
+
+Sometimes a language feature proposed for WGSL requires experiementation
+to prove its worth.  Tint needs to support these, in general to enable
+that experimentation.
+
+The steps for doing so are:
+
+1. Choose a name for the feature, to be used in an `enable` directive.
+   An experimental extension should use prefix of `google_experimental_`
+   Example:
+
+      enable google_experimental_f16;
+
+2. Write down what the feature is supposed to mean.
+   This informs the Tint implementation, and tells shader authors what
+   has changed.
+   Ideally, this will take the form of one of the following:
+
+   - A PR against the WGSL spec.
+
+   - A description of what the contents of that PR would be, committed
+     as a document in this Tint repository.
+
+3. File a tracking bug for adding the feature.
+   Note: Should the Tint repo have a label for experimental features?
+
+4. File a tracking bug for removing the feature or converting it to
+   non-experimental.
+
+5. Write a plan for removal of the experiment.
+   - Ideally, this plan is committed to this repository, especially the
+     description of public activities and commitments. However, we recognize
+     that some internal goals or metrics may be sensitive, and can be hidden.
+   - The plan is about process, not technical details.  It should include:
+       - Who is the point of contact for this feature? The point of contact
+         is responsible when the feature causes an issue or gets in the way.
+       - What is your target date for declaring the experiment a success or
+         failure. In Chrome an experiment must be shipped or removed, in
+         finite time.
+       - What experience are you hoping to gain?  Do you have target metrics?
+       - What approvals, if any, do you need from W3C? What is your plan to
+         present your case to W3C?
+       - The bug tracking removal of the experiment.
diff --git a/docs/tint/origin-trial-changes.md b/docs/tint/origin-trial-changes.md
new file mode 100644
index 0000000..3f3c61d
--- /dev/null
+++ b/docs/tint/origin-trial-changes.md
@@ -0,0 +1,133 @@
+# Tint changes during Origin Trial
+
+## Changes for M102
+
+### New Features
+
+* Parentheses are no longer required around expressions for if and switch statements [tint:1424](crbug.com/tint/1424)
+* Compound assignment statements are now supported. [tint:1325](https://crbug.com/tint/1325)
+* The colon in case statements is now optional. [tint:1485](crbug.com/tint/1485)
+
+### Breaking changes
+
+* Struct members are now separated by commas. [tint:1475](crbug.com/tint/1475)
+* The `@block` attribute has been removed. [tint:1324](crbug.com/tint/1324)
+* The `@stride` attribute has been removed. [tint:1381](crbug.com/tint/1381)
+* Attributes using `[[attribute]]` syntax are no longer supported. [tint:1382](crbug.com/tint/1382)
+* The `elseif` keyword is no longer supported. [tint:1289](crbug.com/tint/1289)
+
+### Deprecated Features
+
+* The `smoothStep()` builtin has been renamed to `smoothstep()`. [tint:1483](crbug.com/tint/1483)
+
+## Changes for M101
+
+### New Features
+
+* Tint now supports unicode identifiers. [tint:1437](crbug.com/tint/1437)
+
+### Breaking changes
+
+* The `isNan()`, `isInf()`, `isFinite()`, and `isNormal()` builtins have been removed. [tint:1312](https://crbug.com/tint/1312)
+
+## Changes for M100
+
+### Breaking changes
+
+* The `@interpolate(flat)` attribute must now be specified on integral user-defined IO. [tint:1224](crbug.com/tint/1224)
+* The `ignore()` intrinsic has been removed. Use phoney-assignment instead: `ignore(expr);` -> `_ = expr;`.
+* `break` statements in `continuing` blocks are now correctly validated.
+
+### New Features
+
+* Module-scope declarations can now be declared in any order. [tint:1266](crbug.com/tint/1266)
+* The `override` keyword and `@id()` attribute for pipeline-overridable constants are now supported, replacing the `@override` attribute. [tint:1403](crbug.com/tint/1403)
+
+## Changes for M99
+
+### Breaking changes
+
+Obviously infinite loops (no condition, no break) are now a validation error.
+
+### Deprecated Features
+
+The following features have been deprecated and will be removed in M102:
+
+* The `[[block]]` attribute has been deprecated. [tint:1324](https://crbug.com/tint/1324)
+* Attributes now use the `@decoration` syntax instead of the `[[decoration]]` syntax. [tint:1382](https://crbug.com/tint/1382)
+* `elseif` has been replaced with `else if`. [tint:1289](https://crbug.com/tint/1289)
+* The `[[stride]]` attribute has been deprecated. [tint:1381](https://crbug.com/tint/1381)
+
+### New Features
+
+* Vector and matrix element type can now be inferred from constructor argument types. [tint:1334](https://crbug.com/tint/1334)
+* Added builtins `degrees()` and `radians()` for converting between degrees and radians. [tint:1329](https://crbug.com/tint/1329)
+* `let` arrays and matrices can now be dynamically indexed. [tint:1352](https://crbug.com/tint/1352)
+* Storage and Uniform buffer types no longer have to be structures. [tint:1372](crbug.com/tint/1372)
+* A struct declaration does not have to be followed by a semicolon. [tint:1380](crbug.com/tint/1380)
+
+### Fixes
+
+* Fixed an issue where for-loops that contain array or structure constructors in the loop initializer statements, condition expressions or continuing statements could fail to compile. [tint:1364](https://crbug.com/tint/1364)
+
+## Changes for M98
+
+### Breaking Changes
+
+* Taking the address of a vector component is no longer allowed.
+* Module-scope declarations can no longer alias a builtin name. [tint:1318](https://crbug.com/tint/1318)
+* It is now an error to call a function either directly or transitively, from a loop continuing block, that uses `discard`. [tint:1302](https://crbug.com/tint/1302)
+
+### Deprecated Features
+
+* The `isNan()`, `isInf()`, `isFinite()` and `isNormal()` builtins has been deprecated and will be removed in M101. [tint:1312](https://crbug.com/tint/1312)
+
+### New Features
+
+* New texture gather builtins: `textureGather()` and `textureGatherCompare()`. [tint:1330](https://crbug.com/tint/1330)
+* Shadowing is now fully supported. [tint:819](https://crbug.com/tint/819)
+* The `dot()` builtin now supports integer vector types.
+* Identifiers can now start with a single leading underscore.  [tint:1292](https://crbug.com/tint/1292)
+* Control flow analysis has been improved, and functions no longer need to `return` if the statement is unreachable. [tint:1302](https://crbug.com/tint/1302)
+* Unreachable statements now produce a warning instead of an error, to allow WGSL code to be updated to the new analysis behavior. These warnings may become errors in the future [gpuweb#2378](https://github.com/gpuweb/gpuweb/issues/2378)
+
+### Fixes
+
+* Fixed an issue where using a module-scoped `let` in a `workgroup_size` may result in a compilation error. [tint:1320](https://crbug.com/tint/1320)
+
+## Changes for M97
+
+### Breaking Changes
+
+* Deprecated `modf()` and `frexp()` builtin overloads that take a pointer second parameter have been removed.
+* Deprecated texture builtin functions that accepted a `read` access controlled storage texture have been removed.
+* Storage textures must now only use the `write` access control.
+
+### Deprecated Features
+
+* The `ignore()` builtin has been replaced with phony-assignment. [gpuweb#2127](https://github.com/gpuweb/gpuweb/pull/2127)
+
+### New Features
+
+* `any()` and `all()` now support a `bool` parameter. These simply return the passed argument. [tint:1253](https://crbug.com/tint/1253)
+* Call statements may now include functions that return a value (`ignore()` is no longer needed).
+* The `interpolate(flat)` attribute can now be specified on integral user-defined IO. It will eventually become an error to define integral user-defined IO without this attribute.
+* Matrix construction from scalar element values is now supported.
+
+### Fixes
+
+* Swizzling of `vec3` types in `storage` and `uniform` buffers has been fixed for Metal 1.x. [tint:1249](https://crbug.com/tint/1249)
+* Calling a function that returns an unused value no longer produces an FXC compilation error. [tint:1259](https://crbug.com/tint/1259)
+* `abs()` fixed for unsigned integers on SPIR-V backend
+
+## Changes for M95
+
+### New Features
+
+* The size of an array can now be defined using a non-overridable module-scope constant
+* The `num_workgroups` builtin is now supported.
+
+### Fixes
+
+* Hex floats: now correctly errors when the magnitude is non-zero, and the exponent would cause overflow. [tint:1150](https://crbug.com/tint/1150), [tint:1166](https://crbug.com/tint/1166)
+* Identifiers beginning with an underscore are now correctly rejected.  [tint:1179](https://crbug.com/tint/1179)
diff --git a/docs/tint/spirv-input-output-variables.md b/docs/tint/spirv-input-output-variables.md
new file mode 100644
index 0000000..0f149e0
--- /dev/null
+++ b/docs/tint/spirv-input-output-variables.md
@@ -0,0 +1,267 @@
+# SPIR-V translation of shader input and output variables
+
+WGSL [MR 1315](https://github.com/gpuweb/gpuweb/issues/1315) changed WGSL so
+that pipeline inputs and outputs are handled similar to HLSL:
+
+- Shader pipeline inputs are the WGSL entry point function arguments.
+- Shader pipeline outputs are the WGSL entry point return value.
+
+Note: In both cases, a struct may be used to pack multiple values together.
+In that case, I/O specific attributes appear on struct members at the struct declaration.
+
+Resource variables, e.g. buffers, samplers, and textures, are still declared
+as variables at module scope.
+
+## Vulkan SPIR-V today
+
+SPIR-V for Vulkan models inputs and outputs as module-scope variables in
+the Input and Output storage classes, respectively.
+
+The `OpEntryPoint` instruction has a list of module-scope variables that must
+be a superset of all the input and output variables that are statically
+accessed in the shader call tree.
+From SPIR-V 1.4 onward, all interface variables that might be statically accessed
+must appear on that list.
+So that includes all resource variables that might be statically accessed
+by the shader call tree.
+
+## Translation scheme for SPIR-V to WGSL
+
+A translation scheme from SPIR-V to WGSL is as follows:
+
+Each SPIR-V entry point maps to a set of Private variables proxying the
+inputs and outputs, and two functions:
+
+- An inner function with no arguments or return values, and whose body
+  is the same as the original SPIR-V entry point.
+- Original input variables are mapped to pseudo-in Private variables
+  with the same store types, but no other attributes or properties copied.
+  In Vulkan, Input variables don't have initalizers.
+- Original output variables are mapped to pseudo-out Private variables
+  with the same store types and optional initializer, but no other attributes
+  or properties are copied.
+- A wrapper entry point function whose arguments correspond in type, location
+  and builtin attributes the original input variables, and whose return type is
+  a structure containing members correspond in type, location, and builtin
+  attributes to the original output variables.
+  The body of the wrapper function the following phases:
+  - Copy formal parameter values into pseudo-in variables.
+    - Insert a bitcast if the WGSL builtin variable has different signedness
+      from the SPIR-V declared type.
+  - Execute the inner function.
+  - Copy pseudo-out variables into the return structure.
+    - Insert a bitcast if the WGSL builtin variable has different signedness
+      from the SPIR-V declared type.
+  - Return the return structure.
+
+- Replace uses of the the original input/output variables to the pseudo-in and
+  pseudo-out variables, respectively.
+- Remap pointer-to-Input with pointer-to-Private
+- Remap pointer-to-Output with pointer-to-Private
+
+We are not concerned with the cost of extra copying input/output values.
+First, the pipeline inputs/outputs tend to be small.
+Second, we expect the backend compiler in the driver will be able to see
+through the copying and optimize the result.
+
+### Example
+
+
+```glsl
+    #version 450
+
+    layout(location = 0) out vec4 frag_colour;
+    layout(location = 0) in vec4 the_colour;
+
+    void bar() {
+      frag_colour = the_colour;
+    }
+
+    void main() {
+        bar();
+    }
+```
+
+Current translation, through SPIR-V, SPIR-V reader, WGSL writer:
+
+```groovy
+    @location(0) var<out> frag_colour : vec4<f32>;
+    @location(0) var<in> the_colour : vec4<f32>;
+
+    fn bar_() -> void {
+      const x_14 : vec4<f32> = the_colour;
+      frag_colour = x_14;
+      return;
+    }
+
+    @stage(fragment)
+    fn main() -> void {
+      bar_();
+      return;
+    }
+```
+
+Proposed translation, through SPIR-V, SPIR-V reader, WGSL writer:
+
+```groovy
+    // 'in' variables are now 'private'.
+    var<private> frag_colour : vec4<f32>;
+    var<private> the_colour : vec4<f32>;
+
+    fn bar_() -> void {
+      // Accesses to the module-scope variables do not change.
+      // This is a big simplifying advantage.
+      const x_14 : vec4<f32> = the_colour;
+      frag_colour = x_14;
+      return;
+    }
+
+    fn main_inner() -> void {
+      bar_();
+      return;
+    }
+
+    // Declare a structure type to collect the return values.
+    struct main_result_type {
+      @location(0) frag_color : vec4<f32>;
+    };
+
+    @stage(fragment)
+    fn main(
+
+      // 'in' variables are entry point parameters
+      @location(0) the_color_arg : vec4<f32>
+
+    ) -> main_result_type {
+
+      // Save 'in' arguments to 'private' variables.
+      the_color = the_color_arg;
+
+      // Initialize 'out' variables.
+      // Use the zero value, since no initializer was specified.
+      frag_color = vec4<f32>();
+
+      // Invoke the original entry point.
+      main_inner();
+
+      // Collect outputs into a structure and return it.
+      var result : main_outer_result_type;
+      result.frag_color = frag_color;
+      return result;
+    }
+```
+
+Alternately, we could emit the body of the original entry point at
+the point of invocation.
+However that is more complex because the original entry point function
+may return from multiple locations, and we would like to have only
+a single exit path to construct and return the result value.
+
+### Handling fragment discard
+
+In SPIR-V `OpKill` causes immediate termination of the shader.
+Is the shader obligated to write its outputs when `OpKill` is executed?
+
+The Vulkan fragment operations are as follows:
+(see [6. Fragment operations](https://www.khronos.org/registry/vulkan/specs/1.2/html/vkspec.html#fragops)).
+
+* Scissor test
+* Sample mask test
+* Fragment shading
+* Multisample coverage
+* Depth bounds test
+* Stencil test
+* Depth test
+* Sample counting
+* Coverage reduction
+
+After that, the fragment results are used to update output attachments, including
+colour, depth, and stencil attachments.
+
+Vulkan says:
+
+> If a fragment operation results in all bits of the coverage mask being 0,
+> the fragment is discarded, and no further operations are performed.
+> Fragments can also be programmatically discarded in a fragment shader by executing one of
+>
+>     OpKill.
+
+I interpret this to mean that the outputs of a discarded fragment are ignored.
+
+Therefore, `OpKill` does not require us to modify the basic scheme from the previous
+section.
+
+The `OpDemoteToHelperInvocationEXT`
+instruction is an alternative way to throw away a fragment, but which
+does not immediately terminate execution of the invocation.
+It is introduced in the [`SPV_EXT_demote_to_helper_invocation](http://htmlpreview.github.io/?https://github.com/KhronosGroup/SPIRV-Registry/blob/master/extensions/EXT/SPV_EXT_demote_to_helper_invocation.html)
+extension.  WGSL does not have this feature, but we expect it will be introduced by a
+future WGSL extension.  The same analysis applies to demote-to-helper.  When introduced,
+it will not affect translation of pipeline outputs.
+
+### Handling depth-replacing mode
+
+A Vulkan fragment shader must write to the fragment depth builtin if and only if it
+has a `DepthReplacing` execution mode. Otherwise behaviour is undefined.
+
+We will ignore the case where the SPIR-V shader writes to the `FragDepth` builtin
+and then discards the fragment.
+This is justified because "no further operations" are performed by the pipeline
+after the fragment is discarded, and that includes writing to depth output attachments.
+
+Assuming the shader is valid, no special translation is required.
+
+### Handling output sample mask
+
+By the same reasoning as for depth-replacing, it is ok to incidentally not write
+to the sample-mask builtin variable when the fragment is discarded.
+
+### Handling clip distance and cull distance
+
+Most builtin variables are scalars or vectors.
+However, the `ClipDistance` and `CullDistance` builtin variables are arrays of 32-bit float values.
+Each entry defines a clip half-plane (respectively cull half-plane)
+A Vulkan implementation must support array sizes of up to 8 elements.
+
+How prevalent are shaders that use these features?
+These variables are supported when Vulkan features `shaderClipDistance` and `shaderCullDistance`
+are supported.
+According to gpuinfo.org as of this writing, those
+Vulkan features appear to be nearly universally supported on Windows devices (>99%),
+but by only 70% on Android.
+It appears that Qualcomm devices support them, but Mali devices do not (e.g. Mali-G77).
+
+The proposed translation scheme forces a copy of each array from private
+variables into the return value of a vertex shader, or into a private
+variable of a fragment shader.
+In addition to the register pressure, there may be a performance degradation
+due to the bulk copying of data.
+
+We think this is an acceptable tradeoff for the gain in usability and
+consistency with other pipeline inputs and outputs.
+
+## Translation scheme for WGSL AST to SPIR-V
+
+To translate from the WGSL AST to SPIR-V, do the following:
+
+- Each entry point formal parameter is mapped to a SPIR-V `Input` variable.
+  - Struct and array inputs may have to be broken down into individual variables.
+- The return of the entry point is broken down into fields, with one
+  `Output` variable per field.
+- In the above, builtins must be separated from user attributes.
+  - Builtin attributes are moved to the corresponding variable.
+  - Location and interpolation attributes are moved to the corresponding
+    variables.
+- This translation relies on the fact that pipeline inputs and pipeline
+  outputs are IO-shareable types. IO-shareable types are always storable,
+  and can be the store type of input/output variables.
+- Input function parameters will be automatically initialized by the system
+  as part of setting up the pipeline inputs to the entry point.
+- Replace each return statement in the entry point with a code sequence
+  which writes the return value components to the synthesized output variables,
+  and then executes an `OpReturn` (without value).
+
+This translation is sufficient even for fragment shaders with discard.
+In that case, outputs will be ignored because downstream pipeline
+operations will not be performed.
+This is the same rationale as for translation from SPIR-V to WGSL AST.
diff --git a/docs/tint/spirv-ptr-ref.md b/docs/tint/spirv-ptr-ref.md
new file mode 100644
index 0000000..615e12c
--- /dev/null
+++ b/docs/tint/spirv-ptr-ref.md
@@ -0,0 +1,115 @@
+# SPIR-V translation of WGSL pointers and references
+
+WGSL was updated to have two kinds of memory views: pointers and references.
+See https://github.com/gpuweb/gpuweb/pull/1569
+
+In summary:
+
+* Reference types are never explicitly mentioned in WGSL source.
+* A use of a variable is a value of reference type corresponding
+  to the reference memory view of the storage allocated for the
+  variable.
+* Let-declared constants can be of pointer type, but not reference
+  type.
+* Function parameter can be of pointer type, but not reference type.
+* A variable's store type is never a pointer type, and never a
+  reference type.
+* The "Load Rule" allows a reference to decay to the underlying
+  store type, by issuing a load of the value in the underlying memory.
+* For an assignment:
+  * The right-hand side evaluates to a non-reference type (atomic-free
+    plain type).
+  * The left-hand side evaluates to a reference type, whose store
+    type is the same as the result of evaluating the right hand side.
+* The address-of (unary `&`) operator converts a reference to a
+  pointer.
+* The dereference (unary `*`) operator converts a pointer to a
+  reference.
+
+TODO: Passing textures and samplers to helper functions might be
+done by "handler value", or by pointer-to-handle.
+
+## Writing SPIR-V from WGSL
+
+The distinction in WGSL between reference and pointer disappears
+at the SPIR-V level.  Both types map into pointer types in SPIR-V.
+
+To translate a valid WGSL program to SPIR-V:
+
+* The dereference operator (unary `*`) is the identity operation.
+* The address-of operator (unary `&`) is the identity operation.
+* Assignment maps to OpStore.
+* The Load Rule translates to OpLoad.
+
+## Reading SPIR-V to create WGSL
+
+The main changes to the SPIR-V reader are:
+
+* When translating a SPIR-V pointer expression, track whether the
+  corresponding WGSL expression is of corresponding WGSL pointer
+  type or correspoinding WGSL type.
+* Insert dereference (unary-`*`) or address-of (unary-`&`) operators
+  as needed to generate valid WGSL expressions.
+
+The choices can be made deterministic, as described below.
+
+The SPIR-V reader only supports baseline functionality in Vulkan.
+Therefore we assume no VariablePointers or VariablePointersStorageBuffer
+capabilities.  All pointers are
+[SPIR-V logical pointers](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#LogicalPointerType).
+The [SPIR-V Universal Validation Rules](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_universal_validation_rules)
+specify where logical pointers can appear as results of instructions
+or operands of instructions.
+
+Each SPIR-V pointer result expression is a logical pointer, and
+therefore is one of:
+
+* OpVariable: map to the reference type.
+* OpFunctionParameter: map to the pointer type.
+* OpCopyObject:
+   * When these only have one use, then these often fold away.
+     Otherwise, they map to a a let-declared constant.
+   * Map to the pointer type.
+* OpAccessChain, OpInBoundsAccessChain:
+   * This could map to either pointer or reference, and adjustments
+     in other areas could make it work.  However, we recommend mapping
+     this to the reference type.
+* OpImageTexelPointer is not supported in WGSL.
+   It is used to get a pointer into a storage texture, for use with
+   atomic instructions.  But image atomics is not supported in
+   WebGPU/WGSL.
+
+Each SPIR-V pointer operand is also a logical pointer, and is an
+operand to one of:
+* OpLoad Pointer operand:
+   * Map to reference, inserting a dereference operator if needed.
+* OpStore Pointer operand:
+   * Map to reference, inserting a dereference operator if needed.
+* OpStore Pointer operand:
+* OpAccessChain, OpInBoundsAccessChain Base operand:
+   * WGSL array-access and subfield access only works on references.
+      * [Gpuweb issue 1530](https://github.com/gpuweb/gpuweb/issues/1530)
+        is filed to allow those operations to work on pointers.
+   * Map to reference, inserting a dereference operator if needed.
+* OpFunctionCall function argument pointer operands
+   * Function operands can't be references.
+   * Map to pointer, inserting an address-of operator if needed.
+* OpAtomic instruction Pointer operand
+   * These map to WGSL atomic builtins.
+   * Map to pointer, inserting an address-of operator if needed.
+   * Note: As of this writing, the atomic instructions are not supported
+     by the SPIR-V reader.
+* OpCopyObject source operand
+   * This could have been mapped either way, but it's easiest to
+     map to pointer, to match the choice for OpCopyObject result type.
+   * Map to pointer, inserting an address-of operator if needed.
+* OpCopyMemory, source and destination operands
+   * This acts as an assignment.
+   * Map both source and destination to reference, inserting dereference
+     operators if needed.
+   * Note: As of this writing, OpCopyMemory is not supported by the
+     SPIR-V reader.
+* Extended instruction set instructions Modf and Frexp
+   * These map to builtins.
+   * Map the pointer operand to pointer, inserting an address-of
+     operator if needed.
diff --git a/docs/tint/style_guide.md b/docs/tint/style_guide.md
new file mode 100644
index 0000000..52b08c5
--- /dev/null
+++ b/docs/tint/style_guide.md
@@ -0,0 +1,47 @@
+# Tint style guide
+
+* Generally, follow the [Chromium style guide for C++](https://chromium.googlesource.com/chromium/src/+/HEAD/styleguide/c++/c++.md)
+  which itself is built on the [Google C++ style guide](https://google.github.io/styleguide/cppguide.html).
+
+* Overall try to use the same style and convention as code around your change.
+
+* Code must be formatted. Use `clang-format` with the provided [.clang-format](../.clang-format)
+  file.  The `tools/format` script runs the formatter.
+
+* Code should not have linting errors.
+    The `tools/lint` script runs the linter. So does `git cl upload`.
+
+* Do not use C++ exceptions
+
+* Do not use C++ RTTI.
+   Instead, use `tint::Castable::As<T>()` from
+   [src/castable.h](../src/castable.h)
+
+* Generally, avoid `assert`.  Instead, issue a [diagnostic](../src/diagnostic.h)
+  and fail gracefully, possibly by returning an error sentinel value.
+  Code that should not be reachable should call `TINT_UNREACHABLE` macro
+  and other internal error conditions should call the `TINT_ICE` macro.
+  See [src/debug.h](../src/debug.h)
+
+* Use `type` as part of a name only when the name refers to a type
+  in WGSL or another shader language processed by Tint.  If the concept you are
+  trying to name is about distinguishing between alternatives, use `kind` instead.
+
+## Compiler support
+
+Tint requires C++17.
+
+Tint uses the Chromium build system and will stay synchronized with that system.
+Compiler configurations beyond that baseline is on a best-effort basis.
+We strive to support recent GCC and MSVC compilers.
+
+## Test code
+
+We might relax the above rules rules for test code, since test code
+shouldn't ship to users.
+
+However, test code should still be readable and maintainable.
+
+For test code, the tradeoff between readability and maintainability
+and other factors is weighted even more strongly toward readability
+and maintainability.
diff --git a/docs/tint/translations.md b/docs/tint/translations.md
new file mode 100644
index 0000000..5119434
--- /dev/null
+++ b/docs/tint/translations.md
@@ -0,0 +1,187 @@
+# Translations
+
+This document attempts to document how WGSL translates into the various backends
+for the cases where the translation is not a direct mapping.
+
+# Access Control
+
+## HLSL
+ * ReadOnly -> `ByteAddressBuffer`
+ * ReadWrite -> `RWByteAddressBuffer`
+
+## MSL
+ * ReadOnly -> `const`
+
+## SPIR-V
+There are two ways this can be achieved in SPIR-V. Either the variable can be
+decorated with `NonWritable` or each member of the struct can be decorated with
+`NonWritable`. We chose to go the struct member route.
+ * The read-only becomes part of the type in this case. Otherwise, you are
+   treating the readonly type information as part of the variable which is
+   confusing.
+ * Treating the readonly as part of the variable means we should be
+   deduplicating the types behind the access control, which causes confusing
+   with the type_names and various tracking systems within Tint.
+
+
+# Builtin Decorations
+| Name | SPIR-V | MSL | HLSL |
+|------|--------|-----|------|
+| position | SpvBuiltInPosition |position | SV_Position |
+| vertex_index | SpvBuiltInVertexIndex |vertex_id | SV_VertexID |
+| instance_index | SpvBuiltInInstanceIndex | instance_id| SV_InstanceID |
+| front_facing | SpvBuiltInFrontFacing | front_facing | SV_IsFrontFacing |
+| frag_coord | SpvBuiltInFragCoord | position | SV_Position |
+| frag_depth | SpvBuiltInFragDepth | depth(any) | SV_Depth |
+| local_invocation_id | SpvBuiltInLocalInvocationId | thread_position_in_threadgroup | SV_GroupThreadID |
+| local_invocation_index | SpvBuiltInLocalInvocationIndex | thread_index_in_threadgroup | SV_GroupIndex |
+| global_invocation_id | SpvBuiltInGlobalInvocationId | thread_position_in_grid | SV_DispatchThreadID |
+
+
+# Builtins Methods
+| Name | SPIR-V | MSL | HLSL |
+| ------|--------|-----|------ |
+| abs | GLSLstd450FAbs or GLSLstd450SAbs| fabs or abs | abs |
+| acos | GLSLstd450Acos | acos | acos |
+| all | SpvOpAll | all | all |
+| any | SpvOpAny | any | any |
+| arrayLength | SpvOpArrayLength | | |
+| asin | GLSLstd450Asin | asin | asin |
+| atan | GLSLstd450Atan | atan | atan |
+| atan2 | GLSLstd450Atan2| atan2 | atan2 |
+| ceil | GLSLstd450Ceil| ceil | ceil |
+| clamp | GLSLstd450NClamp or GLSLstd450UClamp or GLSLstd450SClamp| clamp | clamp |
+| cos | GLSLstd450Cos | cos | cos |
+| cosh | GLSLstd450Cosh | cosh | cosh |
+| countOneBits | SpvOpBitCount | popcount | countbits |
+| cross | GLSLstd450Cross | cross | cross |
+| determinant | GLSLstd450Determinant | determinant | determinant |
+| distance | GLSLstd450Distance | distance | distance |
+| dot | SpOpDot | dot | dot |
+| dpdx | SpvOpDPdx | dpdx | ddx |
+| dpdxCoarse | SpvOpDPdxCoarse | dpdx | ddx_coarse |
+| dpdxFine | SpvOpDPdxFine | dpdx | ddx_fine |
+| dpdy | SpvOpDPdy | dpdy | ddy |
+| dpdyCoarse | SpvOpDPdyCoarse | dpdy | ddy_coarse |
+| dpdyFine | SpvOpDPdyFine | dpdy | ddy_fine |
+| exp | GLSLstd450Exp | exp |  exp |
+| exp2 | GLSLstd450Exp2 | exp2 | exp2 |
+| faceForward | GLSLstd450FaceForward | faceforward | faceforward |
+| floor | GLSLstd450Floor | floor | floor |
+| fma | GLSLstd450Fma | fma | fma |
+| fract | GLSLstd450Fract | fract | frac |
+| frexp | GLSLstd450Frexp | | |
+| fwidth | SpvOpFwidth | fwidth | fwidth |
+| fwidthCoarse | SpvOpFwidthCoarse | fwidth | fwidth |
+| fwidthFine | SpvOpFwidthFine | fwidth | fwidth |
+| inverseSqrt | GLSLstd450InverseSqrt | rsqrt | rsqrt |
+| ldexp | GLSLstd450Ldexp | | |
+| length | GLSLstd450Length | length | length |
+| log | GLSLstd450Log | log | log |
+| log2 | GLSLstd450Log2 | log2 | log2 |
+| max | GLSLstd450NMax or GLSLstd450SMax or GLSLstd450UMax | fmax or max | max |
+| min | GLSLstd450NMin or GLSLstd450SMin or GLSLstd450UMin | fmin or min | min |
+| mix | GLSLstd450FMix | mix | mix |
+| modf | GLSLstd450Modf | | |
+| normalize | GLSLstd450Normalize | normalize | normalize |
+| pow | GLSLstd450Pow | pow | pow |
+| reflect | GLSLstd450Reflect | reflect | reflect |
+| reverseBits | SpvOpBitReverse | reverse_bits | reversebits |
+| round | GLSLstd450Round | round | round |
+| select | SpvOpSelect | select | |
+| sign | GLSLstd450FSign | sign | sign |
+| sin | GLSLstd450Sin | sin | sin |
+| sinh | GLSLstd450Sinh | sinh | sinh |
+| smoothStep | GLSLstd450SmoothStep | smoothstep | smoothstep |
+| sqrt | GLSLstd450Sqrt | sqrt | sqrt |
+| step | GLSLstd450Step | step | step |
+| tan | GLSLstd450Tan | tan | tan |
+| tanh | GLSLstd450Tanh | tanh | tanh |
+| trunc | GLSLstd450Trunc | trunc | trunc |
+
+# Types
+## Sampler Types
+| WGSL | SPIR-V | MSL | HLSL |
+|------|--------|-----|------|
+| sampler | OpTypeSampler | sampler | SamplerState |
+| sampler_comparison | OpTypeSampler | sampler | SamplerComparisonState |
+
+## Texture Types
+| WGSL | SPIR-V | MSL | HLSL |
+|------|--------|-----|------|
+| texture_1d&lt;type&gt; | OpTypeImage 1D Sampled=1 | texture1d&lt;type, access::sample&gt; | Texture1D |
+| texture_2d&lt;type&gt; | OpTypeImage 2D Sampled=1 | texture2d&lt;type, access::sample&gt; | Texture2D |
+| texture_2d_array&lt;type&gt; | OpTypeImage 2D Arrayed=1 Sampled=1 | texture2d_array&lt;type, access::sample&gt; | Texture2DArray |
+| texture_3d&lt;type&gt; | OpTypeImage 3D Sampled=1 | texture3d&lt;type, access::sample&gt; | Texture3D |
+| texture_cube&lt;type&gt; | OpTypeImage Cube Sampled=1 | texturecube&lt;type, access::sample&gt; | TextureCube |
+| texture_cube_array&lt;type&gt; | OpTypeImage Cube Arrayed=1 Sampled=1 | texturecube_array&lt;type, access::sample&gt; | TextureCubeArray |
+| | | |
+| texture_multisampled_2d&lt;type&gt; | OpTypeImage 2D MS=1 Sampled=1 | texture2d_ms&lt;type, access::sample&gt; | Texture2D |
+| | | |
+| texture_depth_2d | OpTypeImage 2D Depth=1 Sampled=1 | depth2d&lt;float, access::sample&gt;| Texture2D |
+| texture_depth_2d_array | OpTypeImage 2D Depth=1 Arrayed=1 Sampled=1 | depth2d_array&lt;float, access::sample&gt; | Texture2DArray |
+| texture_depth_cube | OpTypeImage Cube Depth=1 Sampled=1 | depthcube&lt;float, access::sample&gt; | TextureCube |
+| texture_depth_cube_array | OpTypeImage Cube Depth=1 Arrayed=1 Sampled=1 | depthcube_array&lt;float, access::sample&gt; | TextureCubeArray |
+| texture_depth_multisampled_2d | OpTypeImage 2D Depth=1 MS=1 Sampled=1 | depth2d&lt;float, access::sample&gt;| Texture2DMSArray |
+| | | |
+| texture_storage_1d&lt;image_storage_type&gt; | OpTypeImage 1D Sampled=2| texture1d&lt;type, access::read&gt; | RWTexture1D |
+| texture_storage_2d&lt;image_storage_type&gt; | OpTypeImage 2D Sampled=2 | texture2d&lt;type, access::read&gt; | RWTexture2D |
+| texture_storage_2d_array&lt;image_storage_type&gt; | OpTypeImage 2D Arrayed=1 Sampled=2 | texture2d_array&lt;type, access::read&gt; | RWTexture2DArray |
+| texture_storage_3d&lt;image_storage_type&gt; | OpTypeImage 3D Sampled=2 | texture3d&lt;type, access::read&gt; | RWTexture3D |
+| | | |
+| texture_storage_1d&lt;image_storage_type&gt; | OpTypeImage 1D Sampled=2 | texture1d&lt;type, access::write&gt; | RWTexture1D |
+| texture_storage_2d&lt;image_storage_type&gt; | OpTypeImage 2D Sampled=1 | texture2d&lt;type, access::write&gt; | RWTexture2D |
+| texture_storage_2d_array&lt;image_storage_type&gt; | OpTypeImage 2D Arrayed=1 Sampled=2 | texture2d_array&lt;type, access::write&gt; | RWTexture2DArray |
+| texture_storage_3d&lt;image_storage_type&gt; | OpTypeImage 3D Sampled=2 | texture3d&lt;type, access::write&gt; | RWTexture3D|
+
+# Short-circuting
+## HLSL
+TODO(dsinclair): Nested if's
+
+## SPIR-V
+TODO(dsinclair): Nested if's
+
+# Storage classes
+TODO(dsinclair): do ...
+
+# Storage buffers
+## HLSL
+TODO(dsinclair): Rewriting of accessors to loads
+
+# Loop blocks
+## HLSL
+TODO(dsinclair): Rewrite with bools
+
+## MSL
+TODO(dsinclair): Rewrite with bools
+
+# Input / Output storage class
+## HLSL
+TODO(dsinclair): Structs and params
+
+## MSL
+TODO(dsinclair): Structs and params
+
+# Discard
+## HLSL
+ * `discard`
+
+## MSL
+ * `discard_fragment()`
+
+
+# Specialization constants
+## HLSL
+```
+#ifndef WGSL_SPEC_CONSTANT_<id>
+-- if default provided
+#define WGSL_SPEC_CONSTANT_<id> default value
+-- else
+#error spec constant required for constant id
+--
+#endif
+static const <type> <name> = WGSL_SPEC_CONSTANT_<id>
+```
+
+## MSL
+`@function_constant(<id>)`
diff --git a/include/tint/tint.h b/include/tint/tint.h
new file mode 100644
index 0000000..1a04196
--- /dev/null
+++ b/include/tint/tint.h
@@ -0,0 +1,68 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef INCLUDE_TINT_TINT_H_
+#define INCLUDE_TINT_TINT_H_
+
+// TODO(tint:88): When implementing support for an install target, all of these
+//                headers will need to be moved to include/tint/.
+
+#include "src/tint/ast/pipeline_stage.h"
+#include "src/tint/demangler.h"
+#include "src/tint/diagnostic/printer.h"
+#include "src/tint/inspector/inspector.h"
+#include "src/tint/reader/reader.h"
+#include "src/tint/sem/type_manager.h"
+#include "src/tint/transform/binding_remapper.h"
+#include "src/tint/transform/first_index_offset.h"
+#include "src/tint/transform/fold_trivial_single_use_lets.h"
+#include "src/tint/transform/manager.h"
+#include "src/tint/transform/multiplanar_external_texture.h"
+#include "src/tint/transform/renamer.h"
+#include "src/tint/transform/robustness.h"
+#include "src/tint/transform/single_entry_point.h"
+#include "src/tint/transform/vertex_pulling.h"
+#include "src/tint/writer/writer.h"
+
+#if TINT_BUILD_SPV_READER
+#include "src/tint/reader/spirv/parser.h"
+#endif  // TINT_BUILD_SPV_READER
+
+#if TINT_BUILD_WGSL_READER
+#include "src/tint/reader/wgsl/parser.h"
+#endif  // TINT_BUILD_WGSL_READER
+
+#if TINT_BUILD_SPV_WRITER
+#include "spirv-tools/libspirv.hpp"
+#include "src/tint/writer/spirv/generator.h"
+#endif  // TINT_BUILD_SPV_WRITER
+
+#if TINT_BUILD_WGSL_WRITER
+#include "src/tint/writer/wgsl/generator.h"
+#endif  // TINT_BUILD_WGSL_WRITER
+
+#if TINT_BUILD_MSL_WRITER
+#include "src/tint/writer/msl/generator.h"
+#endif  // TINT_BUILD_MSL_WRITER
+
+#if TINT_BUILD_HLSL_WRITER
+#include "src/tint/writer/hlsl/generator.h"
+#endif  // TINT_BUILD_HLSL_WRITER
+
+#if TINT_BUILD_GLSL_WRITER
+#include "src/tint/transform/glsl.h"
+#include "src/tint/writer/glsl/generator.h"
+#endif  // TINT_BUILD_GLSL_WRITER
+
+#endif  // INCLUDE_TINT_TINT_H_
diff --git a/kokoro/linux/build.sh b/kokoro/linux/build.sh
new file mode 100755
index 0000000..430f6c5
--- /dev/null
+++ b/kokoro/linux/build.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Copyright 2021 The Tint Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e # Fail on any error.
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )"
+
+# Inside the docker VM, we clone the project to a new directory.
+# We do this so that the docker script can be tested in a local development
+# checkout, without having the build litter the local checkout with artifacts.
+# This directory is mapped to the host temporary directory.
+# Kokoro uses a '/tmpfs' root, where as most linux enviroments just have '/tmp'
+if [ -d "/tmpfs" ]; then
+    TMP_DIR=/tmpfs
+else
+    TMP_DIR=/tmp
+fi
+
+
+# --privileged is required for some sanitizer builds, as they seem to require PTRACE privileges
+docker run --rm -i \
+  --privileged \
+  --volume "${ROOT_DIR}:${ROOT_DIR}" \
+  --volume "${TMP_DIR}/kokoro/tint:/tint" \
+  --volume "${KOKORO_ARTIFACTS_DIR}:/mnt/artifacts" \
+  --workdir "${ROOT_DIR}" \
+  --env SRC_DIR="/tint/src" \
+  --env BUILD_DIR="/tint/build" \
+  --env BUILD_TYPE=$BUILD_TYPE \
+  --env BUILD_SYSTEM=$BUILD_SYSTEM \
+  --env BUILD_SANITIZER=$BUILD_SANITIZER \
+  --env BUILD_TOOLCHAIN=$BUILD_TOOLCHAIN \
+  --entrypoint "${SCRIPT_DIR}/docker.sh" \
+  "gcr.io/shaderc-build/radial-build:latest"
diff --git a/kokoro/linux/cmake-clang-debug-asan/build.sh b/kokoro/linux/cmake-clang-debug-asan/build.sh
new file mode 100755
index 0000000..5548770
--- /dev/null
+++ b/kokoro/linux/cmake-clang-debug-asan/build.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright 2021 The Tint Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e # Fail on any error.
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+
+export BUILD_SYSTEM=cmake
+export BUILD_TOOLCHAIN=clang
+export BUILD_TYPE=Debug
+export BUILD_SANITIZER=asan
+
+${SCRIPT_DIR}/../build.sh
diff --git a/kokoro/linux/cmake-clang-debug-asan/presubmit.cfg b/kokoro/linux/cmake-clang-debug-asan/presubmit.cfg
new file mode 100644
index 0000000..bf23a27
--- /dev/null
+++ b/kokoro/linux/cmake-clang-debug-asan/presubmit.cfg
@@ -0,0 +1,3 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "tint/kokoro/linux/cmake-clang-debug-asan/build.sh"
diff --git a/kokoro/linux/cmake-clang-debug-ubsan/build.sh b/kokoro/linux/cmake-clang-debug-ubsan/build.sh
new file mode 100755
index 0000000..55f4db1
--- /dev/null
+++ b/kokoro/linux/cmake-clang-debug-ubsan/build.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright 2021 The Tint Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e # Fail on any error.
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+
+export BUILD_SYSTEM=cmake
+export BUILD_TOOLCHAIN=clang
+export BUILD_TYPE=Debug
+export BUILD_SANITIZER=ubsan
+
+${SCRIPT_DIR}/../build.sh
diff --git a/kokoro/linux/cmake-clang-debug-ubsan/presubmit.cfg b/kokoro/linux/cmake-clang-debug-ubsan/presubmit.cfg
new file mode 100644
index 0000000..5fa246e
--- /dev/null
+++ b/kokoro/linux/cmake-clang-debug-ubsan/presubmit.cfg
@@ -0,0 +1,3 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "tint/kokoro/linux/cmake-clang-debug-ubsan/build.sh"
diff --git a/kokoro/linux/cmake-clang-debug/build.sh b/kokoro/linux/cmake-clang-debug/build.sh
new file mode 100755
index 0000000..fa4fb4c
--- /dev/null
+++ b/kokoro/linux/cmake-clang-debug/build.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Copyright 2021 The Tint Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e # Fail on any error.
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+
+export BUILD_SYSTEM=cmake
+export BUILD_TOOLCHAIN=clang
+export BUILD_TYPE=Debug
+
+${SCRIPT_DIR}/../build.sh
diff --git a/kokoro/linux/cmake-clang-debug/presubmit.cfg b/kokoro/linux/cmake-clang-debug/presubmit.cfg
new file mode 100644
index 0000000..0cc8654
--- /dev/null
+++ b/kokoro/linux/cmake-clang-debug/presubmit.cfg
@@ -0,0 +1,3 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "tint/kokoro/linux/cmake-clang-debug/build.sh"
diff --git a/kokoro/linux/cmake-clang-release-asan/build.sh b/kokoro/linux/cmake-clang-release-asan/build.sh
new file mode 100755
index 0000000..ed598ae
--- /dev/null
+++ b/kokoro/linux/cmake-clang-release-asan/build.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright 2021 The Tint Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e # Fail on any error.
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+
+export BUILD_SYSTEM=cmake
+export BUILD_TOOLCHAIN=clang
+export BUILD_TYPE=Release
+export BUILD_SANITIZER=asan
+
+${SCRIPT_DIR}/../build.sh
diff --git a/kokoro/linux/cmake-clang-release-asan/presubmit.cfg b/kokoro/linux/cmake-clang-release-asan/presubmit.cfg
new file mode 100644
index 0000000..460c927
--- /dev/null
+++ b/kokoro/linux/cmake-clang-release-asan/presubmit.cfg
@@ -0,0 +1,3 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "tint/kokoro/linux/cmake-clang-release-asan/build.sh"
diff --git a/kokoro/linux/cmake-clang-release-ubsan/build.sh b/kokoro/linux/cmake-clang-release-ubsan/build.sh
new file mode 100755
index 0000000..c476289
--- /dev/null
+++ b/kokoro/linux/cmake-clang-release-ubsan/build.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Copyright 2021 The Tint Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e # Fail on any error.
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+
+export BUILD_SYSTEM=cmake
+export BUILD_TOOLCHAIN=clang
+export BUILD_TYPE=Release
+export BUILD_SANITIZER=ubsan
+
+${SCRIPT_DIR}/../build.sh
diff --git a/kokoro/linux/cmake-clang-release-ubsan/presubmit.cfg b/kokoro/linux/cmake-clang-release-ubsan/presubmit.cfg
new file mode 100644
index 0000000..f968295
--- /dev/null
+++ b/kokoro/linux/cmake-clang-release-ubsan/presubmit.cfg
@@ -0,0 +1,3 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "tint/kokoro/linux/cmake-clang-release-ubsan/build.sh"
diff --git a/kokoro/linux/cmake-clang-release/build.sh b/kokoro/linux/cmake-clang-release/build.sh
new file mode 100755
index 0000000..394f5bb
--- /dev/null
+++ b/kokoro/linux/cmake-clang-release/build.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Copyright 2021 The Tint Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e # Fail on any error.
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+
+export BUILD_SYSTEM=cmake
+export BUILD_TOOLCHAIN=clang
+export BUILD_TYPE=Release
+
+${SCRIPT_DIR}/../build.sh
diff --git a/kokoro/linux/cmake-clang-release/presubmit.cfg b/kokoro/linux/cmake-clang-release/presubmit.cfg
new file mode 100644
index 0000000..ccc9651
--- /dev/null
+++ b/kokoro/linux/cmake-clang-release/presubmit.cfg
@@ -0,0 +1,3 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "tint/kokoro/linux/cmake-clang-release/build.sh"
diff --git a/kokoro/linux/cmake-gcc-debug/build.sh b/kokoro/linux/cmake-gcc-debug/build.sh
new file mode 100755
index 0000000..2bd68bd
--- /dev/null
+++ b/kokoro/linux/cmake-gcc-debug/build.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Copyright 2021 The Tint Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e # Fail on any error.
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+
+export BUILD_SYSTEM=cmake
+export BUILD_TOOLCHAIN=gcc
+export BUILD_TYPE=Debug
+
+${SCRIPT_DIR}/../build.sh
diff --git a/kokoro/linux/cmake-gcc-debug/presubmit.cfg b/kokoro/linux/cmake-gcc-debug/presubmit.cfg
new file mode 100644
index 0000000..eb1d4d8
--- /dev/null
+++ b/kokoro/linux/cmake-gcc-debug/presubmit.cfg
@@ -0,0 +1,3 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "tint/kokoro/linux/cmake-gcc-debug/build.sh"
diff --git a/kokoro/linux/cmake-gcc-release/build.sh b/kokoro/linux/cmake-gcc-release/build.sh
new file mode 100755
index 0000000..83af742
--- /dev/null
+++ b/kokoro/linux/cmake-gcc-release/build.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Copyright 2021 The Tint Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e # Fail on any error.
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+
+export BUILD_SYSTEM=cmake
+export BUILD_TOOLCHAIN=gcc
+export BUILD_TYPE=Release
+
+${SCRIPT_DIR}/../build.sh
diff --git a/kokoro/linux/cmake-gcc-release/presubmit.cfg b/kokoro/linux/cmake-gcc-release/presubmit.cfg
new file mode 100644
index 0000000..95a6cfc
--- /dev/null
+++ b/kokoro/linux/cmake-gcc-release/presubmit.cfg
@@ -0,0 +1,3 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "tint/kokoro/linux/cmake-gcc-release/build.sh"
diff --git a/kokoro/linux/docker.sh b/kokoro/linux/docker.sh
new file mode 100755
index 0000000..d0ec4c5
--- /dev/null
+++ b/kokoro/linux/docker.sh
@@ -0,0 +1,183 @@
+#!/bin/bash
+
+# Copyright 2021 The Tint Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is the bash script invoked inside a docker container.
+# The script expects that the CWD points to a clean checkout of Tint.
+# As `gclient sync` will litter the tint checkout with fetched tools and
+# projects, this script will first clone the pristine tint checkout to
+# ${SRC_DIR}. This allows developers to locally run this script without having
+# to worry about their local tint copy being touched.
+#
+# This script expects the following environment variables to be set on entry:
+#
+# SRC_DIR         - Path to where the local Tint copy will be made. See above.
+# BUILD_DIR       - Path to where Tint will be built.
+# BUILD_TYPE      - Either: 'Debug' or 'Release'
+# BUILD_SYSTEM    - Must be 'cmake'
+# BUILD_SANITIZER - Either: '', 'asan', or 'ubstan'
+# BUILD_TOOLCHAIN - Either: 'clang' or 'gcc'
+
+set -e # Fail on any error.
+
+function show_cmds { set -x; }
+function hide_cmds { { set +x; } 2>/dev/null; }
+function task_begin {
+    TASK_NAME="$@"
+    SECONDS=0
+}
+function print_last_task_duration {
+    if [ ! -z "${TASK_NAME}" ]; then
+        echo "${TASK_NAME} completed in $(($SECONDS / 3600))h$((($SECONDS / 60) % 60))m$(($SECONDS % 60))s"
+    fi
+}
+function status {
+    echo ""
+    echo ""
+    print_last_task_duration
+    echo ""
+    echo "*****************************************************************"
+    echo "* $@"
+    echo "*****************************************************************"
+    echo ""
+    task_begin $@
+}
+function with_retry {
+  local MAX_ATTEMPTS=5
+  local RETRY_DELAY_SECS=5
+  local ATTEMPT=1
+  while true; do
+    "$@" && break
+    if [[ $ATTEMPT -ge $MAX_ATTEMPTS ]]; then
+        echo "The command has failed after $ATTEMPT attempts."
+        exit $?
+    fi
+    ((ATTEMPT++))
+    echo "'$@' failed. Attempt ($ATTEMPT/$MAX_ATTEMPTS). Retrying..."
+    sleep $RETRY_DELAY_SECS;
+  done
+}
+
+CLONE_SRC_DIR="$(pwd)"
+
+. /bin/using.sh # Declare the bash `using` function for configuring toolchains.
+
+using depot_tools
+using go-1.14.4      # Speeds up ./tools/lint
+using doxygen-1.8.18
+
+status "Creating source directory '${SRC_DIR}' and build directory '${BUILD_DIR}'"
+mkdir -p ${SRC_DIR}
+mkdir -p ${BUILD_DIR}
+
+status "Cloning to source directory '${SRC_DIR}'"
+cd ${SRC_DIR}
+git clone ${CLONE_SRC_DIR} .
+
+status "Fetching dependencies"
+cp standalone.gclient .gclient
+with_retry gclient sync
+
+status "Linting"
+./tools/lint
+
+status "Configuring build system"
+if [ "$BUILD_SYSTEM" == "cmake" ]; then
+    using cmake-3.17.2
+
+    COMMON_CMAKE_FLAGS=""
+    COMMON_CMAKE_FLAGS+=" -DCMAKE_BUILD_TYPE=${BUILD_TYPE}"
+    COMMON_CMAKE_FLAGS+=" -DTINT_DOCS_WARN_AS_ERROR=1"
+    COMMON_CMAKE_FLAGS+=" -DTINT_BUILD_BENCHMARKS=1"
+
+    if [ "$BUILD_TOOLCHAIN" == "clang" ]; then
+        using clang-10.0.0
+        COMMON_CMAKE_FLAGS+=" -DTINT_BUILD_FUZZERS=1"
+        COMMON_CMAKE_FLAGS+=" -DTINT_BUILD_SPIRV_TOOLS_FUZZER=1"
+        COMMON_CMAKE_FLAGS+=" -DTINT_BUILD_AST_FUZZER=1"
+        COMMON_CMAKE_FLAGS+=" -DTINT_BUILD_REGEX_FUZZER=1"
+    elif [ "$BUILD_TOOLCHAIN" == "gcc" ]; then
+        using gcc-9
+    fi
+
+    if [ "$BUILD_SANITIZER" == "asan" ]; then
+        COMMON_CMAKE_FLAGS+=" -DTINT_ENABLE_ASAN=1"
+    elif [ "$BUILD_SANITIZER" == "ubsan" ]; then
+        COMMON_CMAKE_FLAGS+=" -DTINT_ENABLE_UBSAN=1"
+        export UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1
+    fi
+
+    cd ${BUILD_DIR}
+
+    status "Running Doxygen"
+    echo "NOTE: This will fail on first warning. Run with -DTINT_DOCS_WARN_AS_ERROR=OFF to see all warnings".
+    echo ""
+    show_cmds
+        # NOTE: If we upgrade Doxygen to a more recent version, we can set DOXYGEN_WARN_AS_ERROR to
+        # "FAIL_ON_WARNINGS" instead of "YES" in our CMakeLists.txt so see all warnings, and then
+        # fail. See https://www.doxygen.nl/manual/config.html#cfg_warn_as_error
+        cmake ${SRC_DIR} ${CMAKE_FLAGS} ${COMMON_CMAKE_FLAGS}
+        cmake --build . --target tint-docs
+    hide_cmds
+
+    status "Building tint in '${BUILD_DIR}'"
+    show_cmds
+        cmake ${SRC_DIR} ${CMAKE_FLAGS} ${COMMON_CMAKE_FLAGS}
+        cmake --build . -- --jobs=$(nproc)
+    hide_cmds
+
+    status "Running tint_unittests"
+    show_cmds
+        ./tint_unittests
+    hide_cmds
+
+    if [ -f ./tint_ast_fuzzer_unittests ]; then
+        status "Running tint_ast_fuzzer_unittests"
+        show_cmds
+            ./tint_ast_fuzzer_unittests
+        hide_cmds
+    fi
+
+    if [ -f ./tint_regex_fuzzer_unittests ]; then
+        status "Running tint_regex_fuzzer_unittests"
+        show_cmds
+            ./tint_regex_fuzzer_unittests
+        hide_cmds
+    fi
+
+    status "Testing test/tint/test-all.sh"
+    show_cmds
+        ${SRC_DIR}/test/tint/test-all.sh "${BUILD_DIR}/tint" --verbose
+    hide_cmds
+
+    status "Checking _other.cc files also build"
+    show_cmds
+        cmake ${SRC_DIR} ${CMAKE_FLAGS} ${COMMON_CMAKE_FLAGS} -DTINT_BUILD_AS_OTHER_OS=ON
+        cmake --build . -- --jobs=$(nproc)
+        cmake ${SRC_DIR} ${CMAKE_FLAGS} ${COMMON_CMAKE_FLAGS} -DTINT_BUILD_AS_OTHER_OS=OFF
+    hide_cmds
+
+    status "Checking disabling all readers and writers also builds"
+    show_cmds
+        cmake ${SRC_DIR} ${CMAKE_FLAGS} ${COMMON_CMAKE_FLAGS} -DTINT_BUILD_SPV_READER=OFF -DTINT_BUILD_SPV_WRITER=OFF -DTINT_BUILD_WGSL_READER=OFF -DTINT_BUILD_WGSL_WRITER=OFF -DTINT_BUILD_MSL_WRITER=OFF -DTINT_BUILD_HLSL_WRITER=OFF -DTINT_BUILD_BENCHMARKS=OFF
+        cmake --build . -- --jobs=$(nproc)
+        cmake ${SRC_DIR} ${CMAKE_FLAGS} ${COMMON_CMAKE_FLAGS} -DTINT_BUILD_SPV_READER=ON -DTINT_BUILD_SPV_WRITER=ON -DTINT_BUILD_WGSL_READER=ON -DTINT_BUILD_WGSL_WRITER=ON -DTINT_BUILD_MSL_WRITER=ON -DTINT_BUILD_HLSL_WRITER=ON -DTINT_BUILD_BENCHMARKS=ON
+    hide_cmds
+else
+    status "Unsupported build system: $BUILD_SYSTEM"
+    exit 1
+fi
+
+status "Done"
diff --git a/kokoro/windows/build.bat b/kokoro/windows/build.bat
new file mode 100644
index 0000000..c6df367
--- /dev/null
+++ b/kokoro/windows/build.bat
@@ -0,0 +1,167 @@
+@rem Copyright 2021 The Tint Authors.

+@rem

+@rem Licensed under the Apache License, Version 2.0 (the "License");

+@rem you may not use this file except in compliance with the License.

+@rem You may obtain a copy of the License at

+@rem

+@rem     http://www.apache.org/licenses/LICENSE-2.0

+@rem

+@rem Unless required by applicable law or agreed to in writing, software

+@rem distributed under the License is distributed on an "AS IS" BASIS,

+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+@rem See the License for the specific language governing permissions and

+@rem limitations under the License.

+

+@echo off

+SETLOCAL ENABLEDELAYEDEXPANSION

+

+goto :main

+

+:task_begin

+set TASK_NAME=%~1

+echo %TASK_NAME% starting at %Time%

+exit /b 0

+

+:print_last_task_duration

+if not "%TASK_NAME%" == "" (

+    echo %TASK_NAME% completed at %Time%

+)

+exit /b 0

+

+:status

+echo.

+echo.

+call :print_last_task_duration

+echo.

+echo *****************************************************************

+echo %~1

+echo *****************************************************************

+echo.

+call :task_begin "%~1"

+exit /b 0

+

+:main

+

+set ORIGINAL_SRC_DIR= %~dp0\..\..

+set TEMP_DIR=%TEMP%\tint-temp

+set SRC_DIR="%TEMP_DIR%\tint-src"

+set BUILD_DIR="%TEMP_DIR%\tint-build"

+

+cd /d %ORIGINAL_SRC_DIR%

+if not exist ".git\" (

+    echo "ORIGINAL_SRC_DIR should point to project root: %ORIGINAL_SRC_DIR%"

+    goto :error

+)

+

+if exist %TEMP_DIR% (

+    call :status "Deleting %TEMP_DIR%"

+    del /q/f/s %TEMP_DIR% > NUL || goto :error

+    rmdir /q/s %TEMP_DIR% > NUL || goto :error

+)

+mkdir %TEMP_DIR% || goto :error

+

+call :status "Fetching and installing DXC"

+@echo on

+set DXC_RELEASE="https://github.com/microsoft/DirectXShaderCompiler/releases/download/v1.6.2112/dxc_2021_12_08.zip"

+curl -k -L %DXC_RELEASE% --output "%TEMP_DIR%\dxc_release.zip" || goto :error

+powershell.exe -Command "Expand-Archive -LiteralPath '%TEMP_DIR%\dxc_release.zip' -DestinationPath '%TEMP_DIR%\dxc'" || goto :error

+set DXC_PATH=%TEMP_DIR%\dxc\bin\x64

+

+rem Patch with artifact build that contains fixes not present in the release build

+set DXC_ARTIFACT="https://ci.appveyor.com/api/projects/dnovillo/directxshadercompiler/artifacts/build%%2FRelease%%2Fdxc-artifacts.zip?branch=master&pr=false&job=image%%3A%%20Visual%%20Studio%%202019"

+curl -k -L %DXC_ARTIFACT% --output "%TEMP_DIR%\dxc_artifact.zip" || goto :error

+powershell.exe -Command "Expand-Archive -Force -LiteralPath '%TEMP_DIR%\dxc_artifact.zip' -DestinationPath '%TEMP_DIR%\dxc_artifact'" || goto :error

+move /Y %TEMP_DIR%\dxc_artifact\bin\* %DXC_PATH%

+@echo off

+

+call :status "Fetching and installing Windows SDK for d3dcompiler DLL"

+@echo on

+set WINSDK_DLL_INSTALLER=https://go.microsoft.com/fwlink/?linkid=2164145

+set WINSDK_VERSION=10.0.20348.0

+curl -k -L %WINSDK_DLL_INSTALLER% --output "%TEMP_DIR%\winsdksetup.exe" || goto :error

+start "download" /wait "%TEMP_DIR%\winsdksetup.exe" /quiet /norestart /ceip off /features OptionId.DesktopCPPx64 /layout "%TEMP_DIR%\winsdkinstall" || goto :error

+start "install" /wait "%TEMP_DIR%\winsdkinstall\Installers\Windows SDK for Windows Store Apps Tools-x86_en-us.msi" || goto :error

+set D3DCOMPILER_PATH=C:\Program Files (x86)\Windows Kits\10\bin\%WINSDK_VERSION%\x64

+@echo off

+

+call :status "Installing depot_tools"

+@echo on

+pushd %TEMP_DIR%

+rem For Windows, we must download and extract a bundle.

+rem See https://chromium.googlesource.com/chromium/src/+/HEAD/docs/windows_build_instructions.md#install

+powershell -Command "(New-Object Net.WebClient).DownloadFile('https://storage.googleapis.com/chrome-infra/depot_tools.zip', 'depot_tools.zip')" || goto :error

+powershell -Command "Expand-Archive -Force 'depot_tools.zip' 'depot_tools'" || goto :error

+rem Run gclient once to install deps

+set PATH=%TEMP_DIR%\depot_tools;%PATH%

+set DEPOT_TOOLS_UPDATE=1

+set DEPOT_TOOLS_WIN_TOOLCHAIN=0

+call gclient || goto :error

+@echo off

+popd

+

+call :status "Cloning to clean source directory"

+@echo on

+mkdir %SRC_DIR% || goto :error

+cd /d %SRC_DIR% || goto :error

+call git clone %ORIGINAL_SRC_DIR% . || goto :error

+@echo off

+

+call :status "Fetching dependencies"

+@echo on

+copy standalone.gclient .gclient || goto :error

+call gclient sync || goto :error

+@echo off

+

+call :status "Configuring build system"

+@echo on

+mkdir %BUILD_DIR%

+cd /d %BUILD_DIR%

+set COMMON_CMAKE_FLAGS=-DTINT_BUILD_DOCS=O -DTINT_BUILD_BENCHMARKS=1 -DCMAKE_BUILD_TYPE=%BUILD_TYPE%

+@echo off

+

+call :status "Building tint"

+@echo on

+rem Disable msbuild "Intermediate or Output directory cannot reside in Temporary directory"

+set IgnoreWarnIntDirInTempDetected=true

+rem Add Python3 to path as this Kokoro image only has Python2 in it

+set PATH=C:\Python37;%PATH%

+rem To use ninja with CMake requires VC env vars

+call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"

+@echo on

+rem Note that we need to specify the C and C++ compiler only because Cygwin is in PATH and CMake finds GCC and picks that over MSVC

+cmake %SRC_DIR% -G "Ninja" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_CXX_COMPILER="cl.exe" %COMMON_CMAKE_FLAGS% || goto :error

+cmake --build . || goto :error

+call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" /clean_env

+@echo off

+

+call :status "Running tint_unittests"

+@echo on

+tint_unittests.exe || goto :error

+@echo off

+

+call :status "Testing test/tint/test-all.sh"

+@echo on

+cd /d %SRC_DIR% || goto :error

+rem Run tests with DXC and Metal validation

+set OLD_PATH=%PATH%

+set PATH=C:\Program Files\Metal Developer Tools\macos\bin;%PATH%

+where metal.exe

+set PATH=%DXC_PATH%;%OLD_PATH%

+where dxc.exe dxil.dll

+call git bash -- ./test/tint/test-all.sh ../tint-build/tint.exe --verbose || goto :error

+@echo on

+set PATH=%OLD_PATH%

+rem Run again to test with FXC validation

+set PATH=%D3DCOMPILER_PATH%;%OLD_PATH%

+where d3dcompiler_47.dll

+call git bash -- ./test/tint/test-all.sh ../tint-build/tint.exe --verbose --format hlsl --fxc || goto :error

+@echo on

+set PATH=%OLD_PATH%

+@echo off

+

+call :status "Done"

+exit /b 0

+

+:error

+echo BUILD FAILED! errorlevel: %errorlevel%

+exit /b %errorlevel%

diff --git a/kokoro/windows/cmake-msvc2019-debug/build.bat b/kokoro/windows/cmake-msvc2019-debug/build.bat
new file mode 100644
index 0000000..25db347
--- /dev/null
+++ b/kokoro/windows/cmake-msvc2019-debug/build.bat
@@ -0,0 +1,18 @@
+@rem Copyright 2021 The Tint Authors.

+@rem

+@rem Licensed under the Apache License, Version 2.0 (the "License");

+@rem you may not use this file except in compliance with the License.

+@rem You may obtain a copy of the License at

+@rem

+@rem     http://www.apache.org/licenses/LICENSE-2.0

+@rem

+@rem Unless required by applicable law or agreed to in writing, software

+@rem distributed under the License is distributed on an "AS IS" BASIS,

+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+@rem See the License for the specific language governing permissions and

+@rem limitations under the License.

+

+@echo on

+set BUILD_TYPE=Debug

+call %~dp0\..\build.bat

+exit /b %errorlevel%

diff --git a/kokoro/windows/cmake-msvc2019-debug/presubmit.cfg b/kokoro/windows/cmake-msvc2019-debug/presubmit.cfg
new file mode 100644
index 0000000..c977aa8
--- /dev/null
+++ b/kokoro/windows/cmake-msvc2019-debug/presubmit.cfg
@@ -0,0 +1,3 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "tint/kokoro/windows/cmake-msvc2019-debug/build.bat"
diff --git a/kokoro/windows/cmake-msvc2019-release/build.bat b/kokoro/windows/cmake-msvc2019-release/build.bat
new file mode 100644
index 0000000..a2f6072
--- /dev/null
+++ b/kokoro/windows/cmake-msvc2019-release/build.bat
@@ -0,0 +1,18 @@
+@rem Copyright 2021 The Tint Authors.

+@rem

+@rem Licensed under the Apache License, Version 2.0 (the "License");

+@rem you may not use this file except in compliance with the License.

+@rem You may obtain a copy of the License at

+@rem

+@rem     http://www.apache.org/licenses/LICENSE-2.0

+@rem

+@rem Unless required by applicable law or agreed to in writing, software

+@rem distributed under the License is distributed on an "AS IS" BASIS,

+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+@rem See the License for the specific language governing permissions and

+@rem limitations under the License.

+

+@echo on

+set BUILD_TYPE=Release

+call %~dp0\..\build.bat

+exit /b %errorlevel%

diff --git a/kokoro/windows/cmake-msvc2019-release/presubmit.cfg b/kokoro/windows/cmake-msvc2019-release/presubmit.cfg
new file mode 100644
index 0000000..00acdd6
--- /dev/null
+++ b/kokoro/windows/cmake-msvc2019-release/presubmit.cfg
@@ -0,0 +1,3 @@
+# Format: //devtools/kokoro/config/proto/build.proto
+
+build_file: "tint/kokoro/windows/cmake-msvc2019-release/build.bat"
diff --git a/scripts/dawn_overrides_with_defaults.gni b/scripts/dawn_overrides_with_defaults.gni
index c667693..46f44ef 100644
--- a/scripts/dawn_overrides_with_defaults.gni
+++ b/scripts/dawn_overrides_with_defaults.gni
@@ -75,11 +75,6 @@
   dawn_vulkan_validation_layers_dir = ""
 }
 
-if (!defined(dawn_tint_dir)) {
-  # Default to Tint being Dawn's DEPS
-  dawn_tint_dir = "${dawn_root}/third_party/tint"
-}
-
 if (!defined(dawn_abseil_dir)) {
   dawn_abseil_dir = "//third_party/abseil-cpp"
 }
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index ce7d97f..5d97a8e 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -163,7 +163,7 @@
     "${dawn_root}/src/dawn/common",
     "${dawn_spirv_tools_dir}:spvtools_opt",
     "${dawn_spirv_tools_dir}:spvtools_val",
-    "${dawn_tint_dir}/src/tint:libtint",
+    "${dawn_root}/src/tint:libtint",
   ]
   defines = []
   libs = []
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
new file mode 100644
index 0000000..58ec6a4
--- /dev/null
+++ b/src/tint/BUILD.gn
@@ -0,0 +1,799 @@
+# Copyright 2021 The Tint Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build_overrides/build.gni")
+import("../../tint_overrides_with_defaults.gni")
+
+###############################################################################
+# Common - Configs, etc. shared across targets
+###############################################################################
+
+config("tint_common_config") {
+  include_dirs = [
+    "${target_gen_dir}",
+    "${tint_root_dir}/",
+    "${tint_spirv_headers_dir}/include",
+    "${tint_spirv_tools_dir}/",
+    "${tint_spirv_tools_dir}/include",
+  ]
+}
+
+config("tint_public_config") {
+  defines = []
+  if (tint_build_spv_reader) {
+    defines += [ "TINT_BUILD_SPV_READER=1" ]
+  } else {
+    defines += [ "TINT_BUILD_SPV_READER=0" ]
+  }
+
+  if (tint_build_spv_writer) {
+    defines += [ "TINT_BUILD_SPV_WRITER=1" ]
+  } else {
+    defines += [ "TINT_BUILD_SPV_WRITER=0" ]
+  }
+
+  if (tint_build_wgsl_reader) {
+    defines += [ "TINT_BUILD_WGSL_READER=1" ]
+  } else {
+    defines += [ "TINT_BUILD_WGSL_READER=0" ]
+  }
+
+  if (tint_build_wgsl_writer) {
+    defines += [ "TINT_BUILD_WGSL_WRITER=1" ]
+  } else {
+    defines += [ "TINT_BUILD_WGSL_WRITER=0" ]
+  }
+
+  if (tint_build_msl_writer) {
+    defines += [ "TINT_BUILD_MSL_WRITER=1" ]
+  } else {
+    defines += [ "TINT_BUILD_MSL_WRITER=0" ]
+  }
+
+  if (tint_build_hlsl_writer) {
+    defines += [ "TINT_BUILD_HLSL_WRITER=1" ]
+  } else {
+    defines += [ "TINT_BUILD_HLSL_WRITER=0" ]
+  }
+
+  if (tint_build_glsl_writer) {
+    defines += [ "TINT_BUILD_GLSL_WRITER=1" ]
+  } else {
+    defines += [ "TINT_BUILD_GLSL_WRITER=0" ]
+  }
+
+  include_dirs = [
+    "${tint_root_dir}/",
+    "${tint_root_dir}/include/",
+    "${tint_spirv_headers_dir}/include",
+  ]
+}
+
+config("tint_config") {
+  include_dirs = []
+  if (tint_build_spv_reader || tint_build_spv_writer) {
+    include_dirs += [ "${tint_spirv_tools_dir}/include/" ]
+  }
+}
+
+###############################################################################
+# Helper library for IO operations
+# Only to be used by tests and sample executable
+###############################################################################
+source_set("tint_utils_io") {
+  sources = [
+    "utils/io/command.h",
+    "utils/io/tmpfile.h",
+  ]
+
+  if (is_linux || is_mac) {
+    sources += [ "utils/io/command_posix.cc" ]
+    sources += [ "utils/io/tmpfile_posix.cc" ]
+  } else if (is_win) {
+    sources += [ "utils/io/command_windows.cc" ]
+    sources += [ "utils/io/tmpfile_windows.cc" ]
+  } else {
+    sources += [ "utils/io/command_other.cc" ]
+    sources += [ "utils/io/tmpfile_other.cc" ]
+  }
+
+  public_deps = [ ":libtint_core_all_src" ]
+}
+
+###############################################################################
+# Helper library for validating generated shaders
+# As this depends on tint_utils_io, this is only to be used by tests and sample
+# executable
+###############################################################################
+source_set("tint_val") {
+  sources = [
+    "val/hlsl.cc",
+    "val/msl.cc",
+    "val/val.h",
+  ]
+  public_deps = [ ":tint_utils_io" ]
+}
+
+###############################################################################
+# Library - Tint core and optional modules of libtint
+###############################################################################
+# libtint source sets are divided into a non-optional core in :libtint_core_src
+# and optional :libtint_*_src subsets, because ninja does not like having
+# multiple source files with the same name, like function.cc, in the same
+# source set
+# target.
+#
+# Targets that want to use tint as a library should depend on ":libtint" and
+# use the build flags to control what is included, instead of trying to specify
+# the subsets that they want.
+
+template("libtint_source_set") {
+  source_set(target_name) {
+    forward_variables_from(invoker, "*", [ "configs" ])
+
+    if (!defined(invoker.deps)) {
+      deps = []
+    }
+    deps += [
+      "${tint_spirv_headers_dir}:spv_headers",
+      "${tint_spirv_tools_dir}:spvtools_core_enums_unified1",
+      "${tint_spirv_tools_dir}:spvtools_core_tables_unified1",
+      "${tint_spirv_tools_dir}:spvtools_headers",
+      "${tint_spirv_tools_dir}:spvtools_language_header_cldebuginfo100",
+      "${tint_spirv_tools_dir}:spvtools_language_header_debuginfo",
+      "${tint_spirv_tools_dir}:spvtools_language_header_vkdebuginfo100",
+    ]
+
+    if (defined(invoker.configs)) {
+      configs += invoker.configs
+    }
+    configs += [ ":tint_common_config" ]
+    if (build_with_chromium) {
+      configs -= [ "//build/config/compiler:chromium_code" ]
+      configs += [ "//build/config/compiler:no_chromium_code" ]
+    }
+
+    if (!defined(invoker.public_configs)) {
+      public_configs = []
+    }
+    public_configs += [ ":tint_public_config" ]
+  }
+}
+
+libtint_source_set("libtint_core_all_src") {
+  sources = [
+    "ast/access.cc",
+    "ast/access.h",
+    "ast/alias.cc",
+    "ast/alias.h",
+    "ast/array.cc",
+    "ast/array.h",
+    "ast/assignment_statement.cc",
+    "ast/assignment_statement.h",
+    "ast/ast_type.cc",  # TODO(bclayton) - rename to type.cc
+    "ast/atomic.cc",
+    "ast/atomic.h",
+    "ast/attribute.cc",
+    "ast/attribute.h",
+    "ast/binary_expression.cc",
+    "ast/binary_expression.h",
+    "ast/binding_attribute.cc",
+    "ast/binding_attribute.h",
+    "ast/bitcast_expression.cc",
+    "ast/bitcast_expression.h",
+    "ast/block_statement.cc",
+    "ast/block_statement.h",
+    "ast/bool.cc",
+    "ast/bool.h",
+    "ast/bool_literal_expression.cc",
+    "ast/bool_literal_expression.h",
+    "ast/break_statement.cc",
+    "ast/break_statement.h",
+    "ast/builtin.cc",
+    "ast/builtin.h",
+    "ast/builtin_attribute.cc",
+    "ast/builtin_attribute.h",
+    "ast/call_expression.cc",
+    "ast/call_expression.h",
+    "ast/call_statement.cc",
+    "ast/call_statement.h",
+    "ast/case_statement.cc",
+    "ast/case_statement.h",
+    "ast/compound_assignment_statement.cc",
+    "ast/compound_assignment_statement.h",
+    "ast/continue_statement.cc",
+    "ast/continue_statement.h",
+    "ast/depth_multisampled_texture.cc",
+    "ast/depth_multisampled_texture.h",
+    "ast/depth_texture.cc",
+    "ast/depth_texture.h",
+    "ast/disable_validation_attribute.cc",
+    "ast/disable_validation_attribute.h",
+    "ast/discard_statement.cc",
+    "ast/discard_statement.h",
+    "ast/else_statement.cc",
+    "ast/else_statement.h",
+    "ast/expression.cc",
+    "ast/expression.h",
+    "ast/external_texture.cc",
+    "ast/external_texture.h",
+    "ast/f32.cc",
+    "ast/f32.h",
+    "ast/fallthrough_statement.cc",
+    "ast/fallthrough_statement.h",
+    "ast/float_literal_expression.cc",
+    "ast/float_literal_expression.h",
+    "ast/for_loop_statement.cc",
+    "ast/for_loop_statement.h",
+    "ast/function.cc",
+    "ast/function.h",
+    "ast/group_attribute.cc",
+    "ast/group_attribute.h",
+    "ast/i32.cc",
+    "ast/i32.h",
+    "ast/id_attribute.cc",
+    "ast/id_attribute.h",
+    "ast/identifier_expression.cc",
+    "ast/identifier_expression.h",
+    "ast/if_statement.cc",
+    "ast/if_statement.h",
+    "ast/index_accessor_expression.cc",
+    "ast/index_accessor_expression.h",
+    "ast/int_literal_expression.cc",
+    "ast/int_literal_expression.h",
+    "ast/internal_attribute.cc",
+    "ast/internal_attribute.h",
+    "ast/interpolate_attribute.cc",
+    "ast/interpolate_attribute.h",
+    "ast/invariant_attribute.cc",
+    "ast/invariant_attribute.h",
+    "ast/literal_expression.cc",
+    "ast/literal_expression.h",
+    "ast/location_attribute.cc",
+    "ast/location_attribute.h",
+    "ast/loop_statement.cc",
+    "ast/loop_statement.h",
+    "ast/matrix.cc",
+    "ast/matrix.h",
+    "ast/member_accessor_expression.cc",
+    "ast/member_accessor_expression.h",
+    "ast/module.cc",
+    "ast/module.h",
+    "ast/multisampled_texture.cc",
+    "ast/multisampled_texture.h",
+    "ast/node.cc",
+    "ast/node.h",
+    "ast/phony_expression.cc",
+    "ast/phony_expression.h",
+    "ast/pipeline_stage.cc",
+    "ast/pipeline_stage.h",
+    "ast/pointer.cc",
+    "ast/pointer.h",
+    "ast/return_statement.cc",
+    "ast/return_statement.h",
+    "ast/sampled_texture.cc",
+    "ast/sampled_texture.h",
+    "ast/sampler.cc",
+    "ast/sampler.h",
+    "ast/sint_literal_expression.cc",
+    "ast/sint_literal_expression.h",
+    "ast/stage_attribute.cc",
+    "ast/stage_attribute.h",
+    "ast/statement.cc",
+    "ast/statement.h",
+    "ast/storage_class.cc",
+    "ast/storage_class.h",
+    "ast/storage_texture.cc",
+    "ast/storage_texture.h",
+    "ast/stride_attribute.cc",
+    "ast/stride_attribute.h",
+    "ast/struct.cc",
+    "ast/struct.h",
+    "ast/struct_member.cc",
+    "ast/struct_member.h",
+    "ast/struct_member_align_attribute.cc",
+    "ast/struct_member_align_attribute.h",
+    "ast/struct_member_offset_attribute.cc",
+    "ast/struct_member_offset_attribute.h",
+    "ast/struct_member_size_attribute.cc",
+    "ast/struct_member_size_attribute.h",
+    "ast/switch_statement.cc",
+    "ast/switch_statement.h",
+    "ast/texture.cc",
+    "ast/texture.h",
+    "ast/traverse_expressions.h",
+    "ast/type.h",
+    "ast/type_decl.cc",
+    "ast/type_decl.h",
+    "ast/type_name.cc",
+    "ast/type_name.h",
+    "ast/u32.cc",
+    "ast/u32.h",
+    "ast/uint_literal_expression.cc",
+    "ast/uint_literal_expression.h",
+    "ast/unary_op.cc",
+    "ast/unary_op.h",
+    "ast/unary_op_expression.cc",
+    "ast/unary_op_expression.h",
+    "ast/variable.cc",
+    "ast/variable.h",
+    "ast/variable_decl_statement.cc",
+    "ast/variable_decl_statement.h",
+    "ast/vector.cc",
+    "ast/vector.h",
+    "ast/void.cc",
+    "ast/void.h",
+    "ast/workgroup_attribute.cc",
+    "ast/workgroup_attribute.h",
+    "builtin_table.cc",
+    "builtin_table.h",
+    "builtin_table.inl",
+    "castable.cc",
+    "castable.h",
+    "clone_context.cc",
+    "clone_context.h",
+    "debug.cc",
+    "debug.h",
+    "demangler.cc",
+    "demangler.h",
+    "diagnostic/diagnostic.cc",
+    "diagnostic/diagnostic.h",
+    "diagnostic/formatter.cc",
+    "diagnostic/formatter.h",
+    "diagnostic/printer.cc",
+    "diagnostic/printer.h",
+    "inspector/entry_point.cc",
+    "inspector/entry_point.h",
+    "inspector/inspector.cc",
+    "inspector/inspector.h",
+    "inspector/resource_binding.cc",
+    "inspector/resource_binding.h",
+    "inspector/scalar.cc",
+    "inspector/scalar.h",
+    "program.cc",
+    "program.h",
+    "program_builder.cc",
+    "program_builder.h",
+    "program_id.cc",
+    "program_id.h",
+    "reader/reader.cc",
+    "reader/reader.h",
+    "resolver/dependency_graph.cc",
+    "resolver/dependency_graph.h",
+    "resolver/resolver.cc",
+    "resolver/resolver.h",
+    "resolver/resolver_constants.cc",
+    "resolver/resolver_validation.cc",
+    "scope_stack.h",
+    "sem/array.h",
+    "sem/atomic_type.h",
+    "sem/behavior.h",
+    "sem/binding_point.h",
+    "sem/bool_type.h",
+    "sem/builtin.h",
+    "sem/builtin_type.h",
+    "sem/call.h",
+    "sem/call_target.h",
+    "sem/constant.h",
+    "sem/depth_multisampled_texture_type.h",
+    "sem/depth_texture_type.h",
+    "sem/expression.h",
+    "sem/external_texture_type.h",
+    "sem/f32_type.h",
+    "sem/for_loop_statement.h",
+    "sem/i32_type.h",
+    "sem/if_statement.h",
+    "sem/info.h",
+    "sem/loop_statement.h",
+    "sem/matrix_type.h",
+    "sem/module.h",
+    "sem/multisampled_texture_type.h",
+    "sem/node.h",
+    "sem/parameter_usage.h",
+    "sem/pipeline_stage_set.h",
+    "sem/pointer_type.h",
+    "sem/reference_type.h",
+    "sem/sampled_texture_type.h",
+    "sem/sampler_texture_pair.h",
+    "sem/sampler_type.h",
+    "sem/storage_texture_type.h",
+    "sem/switch_statement.h",
+    "sem/texture_type.h",
+    "sem/type.h",
+    "sem/type_constructor.h",
+    "sem/type_conversion.h",
+    "sem/type_manager.h",
+    "sem/type_mappings.h",
+    "sem/u32_type.h",
+    "sem/vector_type.h",
+    "sem/void_type.h",
+    "source.cc",
+    "source.h",
+    "symbol.cc",
+    "symbol.h",
+    "symbol_table.cc",
+    "symbol_table.h",
+    "text/unicode.cc",
+    "text/unicode.h",
+    "traits.h",
+    "transform/add_empty_entry_point.cc",
+    "transform/add_empty_entry_point.h",
+    "transform/add_spirv_block_attribute.cc",
+    "transform/add_spirv_block_attribute.h",
+    "transform/array_length_from_uniform.cc",
+    "transform/array_length_from_uniform.h",
+    "transform/binding_remapper.cc",
+    "transform/binding_remapper.h",
+    "transform/builtin_polyfill.cc",
+    "transform/builtin_polyfill.h",
+    "transform/calculate_array_length.cc",
+    "transform/calculate_array_length.h",
+    "transform/canonicalize_entry_point_io.cc",
+    "transform/canonicalize_entry_point_io.h",
+    "transform/combine_samplers.cc",
+    "transform/combine_samplers.h",
+    "transform/decompose_memory_access.cc",
+    "transform/decompose_memory_access.h",
+    "transform/decompose_strided_array.cc",
+    "transform/decompose_strided_array.h",
+    "transform/decompose_strided_matrix.cc",
+    "transform/decompose_strided_matrix.h",
+    "transform/first_index_offset.cc",
+    "transform/first_index_offset.h",
+    "transform/fold_constants.cc",
+    "transform/fold_constants.h",
+    "transform/fold_trivial_single_use_lets.cc",
+    "transform/fold_trivial_single_use_lets.h",
+    "transform/for_loop_to_loop.cc",
+    "transform/for_loop_to_loop.h",
+    "transform/expand_compound_assignment.cc",
+    "transform/expand_compound_assignment.h",
+    "transform/localize_struct_array_assignment.cc",
+    "transform/localize_struct_array_assignment.h",
+    "transform/loop_to_for_loop.cc",
+    "transform/loop_to_for_loop.h",
+    "transform/manager.cc",
+    "transform/manager.h",
+    "transform/module_scope_var_to_entry_point_param.cc",
+    "transform/module_scope_var_to_entry_point_param.h",
+    "transform/multiplanar_external_texture.cc",
+    "transform/multiplanar_external_texture.h",
+    "transform/num_workgroups_from_uniform.cc",
+    "transform/num_workgroups_from_uniform.h",
+    "transform/promote_initializers_to_const_var.cc",
+    "transform/promote_initializers_to_const_var.h",
+    "transform/promote_side_effects_to_decl.cc",
+    "transform/promote_side_effects_to_decl.h",
+    "transform/remove_continue_in_switch.cc",
+    "transform/remove_continue_in_switch.h",
+    "transform/remove_phonies.cc",
+    "transform/remove_phonies.h",
+    "transform/remove_unreachable_statements.cc",
+    "transform/remove_unreachable_statements.h",
+    "transform/renamer.cc",
+    "transform/renamer.h",
+    "transform/robustness.cc",
+    "transform/robustness.h",
+    "transform/simplify_pointers.cc",
+    "transform/simplify_pointers.h",
+    "transform/single_entry_point.cc",
+    "transform/single_entry_point.h",
+    "transform/transform.cc",
+    "transform/transform.h",
+    "transform/unshadow.cc",
+    "transform/unshadow.h",
+    "transform/unwind_discard_functions.cc",
+    "transform/unwind_discard_functions.h",
+    "transform/utils/get_insertion_point.cc",
+    "transform/utils/get_insertion_point.h",
+    "transform/utils/hoist_to_decl_before.cc",
+    "transform/utils/hoist_to_decl_before.h",
+    "transform/var_for_dynamic_index.cc",
+    "transform/var_for_dynamic_index.h",
+    "transform/vectorize_scalar_matrix_constructors.cc",
+    "transform/vectorize_scalar_matrix_constructors.h",
+    "transform/vertex_pulling.cc",
+    "transform/vertex_pulling.h",
+    "transform/wrap_arrays_in_structs.cc",
+    "transform/wrap_arrays_in_structs.h",
+    "transform/zero_init_workgroup_memory.cc",
+    "transform/zero_init_workgroup_memory.h",
+    "utils/block_allocator.h",
+    "utils/crc32.h",
+    "utils/debugger.cc",
+    "utils/debugger.h",
+    "utils/enum_set.h",
+    "utils/hash.h",
+    "utils/map.h",
+    "utils/math.h",
+    "utils/scoped_assignment.h",
+    "utils/string.h",
+    "utils/unique_allocator.h",
+    "utils/unique_vector.h",
+    "writer/append_vector.cc",
+    "writer/append_vector.h",
+    "writer/array_length_from_uniform_options.cc",
+    "writer/array_length_from_uniform_options.h",
+    "writer/float_to_string.cc",
+    "writer/float_to_string.h",
+    "writer/generate_external_texture_bindings.cc",
+    "writer/generate_external_texture_bindings.h",
+    "writer/text.cc",
+    "writer/text.h",
+    "writer/text_generator.cc",
+    "writer/text_generator.h",
+    "writer/writer.cc",
+    "writer/writer.h",
+  ]
+
+  if (is_linux) {
+    sources += [ "diagnostic/printer_linux.cc" ]
+  } else if (is_win) {
+    sources += [ "diagnostic/printer_windows.cc" ]
+  } else {
+    sources += [ "diagnostic/printer_other.cc" ]
+  }
+}
+
+libtint_source_set("libtint_sem_src") {
+  sources = [
+    "sem/array.cc",
+    "sem/array.h",
+    "sem/atomic_type.cc",
+    "sem/atomic_type.h",
+    "sem/behavior.cc",
+    "sem/behavior.h",
+    "sem/binding_point.h",
+    "sem/block_statement.cc",
+    "sem/bool_type.cc",
+    "sem/bool_type.h",
+    "sem/builtin.cc",
+    "sem/builtin.h",
+    "sem/builtin_type.cc",
+    "sem/builtin_type.h",
+    "sem/call.cc",
+    "sem/call.h",
+    "sem/call_target.cc",
+    "sem/call_target.h",
+    "sem/constant.cc",
+    "sem/constant.h",
+    "sem/depth_multisampled_texture_type.cc",
+    "sem/depth_multisampled_texture_type.h",
+    "sem/depth_texture_type.cc",
+    "sem/depth_texture_type.h",
+    "sem/expression.cc",
+    "sem/expression.h",
+    "sem/external_texture_type.cc",
+    "sem/external_texture_type.h",
+    "sem/f32_type.cc",
+    "sem/f32_type.h",
+    "sem/for_loop_statement.cc",
+    "sem/for_loop_statement.h",
+    "sem/function.cc",
+    "sem/i32_type.cc",
+    "sem/i32_type.h",
+    "sem/if_statement.cc",
+    "sem/if_statement.h",
+    "sem/info.cc",
+    "sem/info.h",
+    "sem/loop_statement.cc",
+    "sem/loop_statement.h",
+    "sem/matrix_type.cc",
+    "sem/matrix_type.h",
+    "sem/member_accessor_expression.cc",
+    "sem/module.cc",
+    "sem/module.h",
+    "sem/multisampled_texture_type.cc",
+    "sem/multisampled_texture_type.h",
+    "sem/node.cc",
+    "sem/node.h",
+    "sem/parameter_usage.cc",
+    "sem/parameter_usage.h",
+    "sem/pipeline_stage_set.h",
+    "sem/pointer_type.cc",
+    "sem/pointer_type.h",
+    "sem/reference_type.cc",
+    "sem/reference_type.h",
+    "sem/sampled_texture_type.cc",
+    "sem/sampled_texture_type.h",
+    "sem/sampler_type.cc",
+    "sem/sampler_type.h",
+    "sem/statement.cc",
+    "sem/storage_texture_type.cc",
+    "sem/storage_texture_type.h",
+    "sem/struct.cc",
+    "sem/switch_statement.cc",
+    "sem/switch_statement.h",
+    "sem/texture_type.cc",
+    "sem/texture_type.h",
+    "sem/type.cc",
+    "sem/type.h",
+    "sem/type_constructor.cc",
+    "sem/type_constructor.h",
+    "sem/type_conversion.cc",
+    "sem/type_conversion.h",
+    "sem/type_manager.cc",
+    "sem/type_manager.h",
+    "sem/type_mappings.h",
+    "sem/u32_type.cc",
+    "sem/u32_type.h",
+    "sem/variable.cc",
+    "sem/vector_type.cc",
+    "sem/vector_type.h",
+    "sem/void_type.cc",
+    "sem/void_type.h",
+  ]
+
+  public_deps = [ ":libtint_core_all_src" ]
+}
+
+libtint_source_set("libtint_core_src") {
+  public_deps = [
+    ":libtint_core_all_src",
+    ":libtint_sem_src",
+  ]
+}
+
+libtint_source_set("libtint_spv_reader_src") {
+  sources = [
+    "reader/spirv/construct.cc",
+    "reader/spirv/construct.h",
+    "reader/spirv/entry_point_info.cc",
+    "reader/spirv/entry_point_info.h",
+    "reader/spirv/enum_converter.cc",
+    "reader/spirv/enum_converter.h",
+    "reader/spirv/fail_stream.h",
+    "reader/spirv/function.cc",
+    "reader/spirv/function.h",
+    "reader/spirv/namer.cc",
+    "reader/spirv/namer.h",
+    "reader/spirv/parser.cc",
+    "reader/spirv/parser.h",
+    "reader/spirv/parser_impl.cc",
+    "reader/spirv/parser_impl.h",
+    "reader/spirv/parser_type.cc",
+    "reader/spirv/parser_type.h",
+    "reader/spirv/usage.cc",
+    "reader/spirv/usage.h",
+  ]
+
+  public_deps = [
+    ":libtint_core_src",
+    "${tint_spirv_tools_dir}/:spvtools_opt",
+  ]
+
+  public_configs = [ "${tint_spirv_tools_dir}/:spvtools_internal_config" ]
+}
+
+libtint_source_set("libtint_spv_writer_src") {
+  sources = [
+    "writer/spirv/binary_writer.cc",
+    "writer/spirv/binary_writer.h",
+    "writer/spirv/builder.cc",
+    "writer/spirv/builder.h",
+    "writer/spirv/function.cc",
+    "writer/spirv/function.h",
+    "writer/spirv/generator.cc",
+    "writer/spirv/generator.h",
+    "writer/spirv/instruction.cc",
+    "writer/spirv/instruction.h",
+    "writer/spirv/operand.cc",
+    "writer/spirv/operand.h",
+    "writer/spirv/scalar_constant.h",
+  ]
+
+  public_deps = [ ":libtint_core_src" ]
+}
+
+libtint_source_set("libtint_wgsl_reader_src") {
+  sources = [
+    "reader/wgsl/lexer.cc",
+    "reader/wgsl/lexer.h",
+    "reader/wgsl/parser.cc",
+    "reader/wgsl/parser.h",
+    "reader/wgsl/parser_impl.cc",
+    "reader/wgsl/parser_impl.h",
+    "reader/wgsl/parser_impl_detail.h",
+    "reader/wgsl/token.cc",
+    "reader/wgsl/token.h",
+  ]
+
+  public_deps = [ ":libtint_core_src" ]
+}
+
+libtint_source_set("libtint_wgsl_writer_src") {
+  sources = [
+    "writer/wgsl/generator.cc",
+    "writer/wgsl/generator.h",
+    "writer/wgsl/generator_impl.cc",
+    "writer/wgsl/generator_impl.h",
+  ]
+
+  public_deps = [ ":libtint_core_src" ]
+}
+
+libtint_source_set("libtint_msl_writer_src") {
+  sources = [
+    "writer/msl/generator.cc",
+    "writer/msl/generator.h",
+    "writer/msl/generator_impl.cc",
+    "writer/msl/generator_impl.h",
+  ]
+
+  public_deps = [ ":libtint_core_src" ]
+}
+
+libtint_source_set("libtint_hlsl_writer_src") {
+  sources = [
+    "writer/hlsl/generator.cc",
+    "writer/hlsl/generator.h",
+    "writer/hlsl/generator_impl.cc",
+    "writer/hlsl/generator_impl.h",
+  ]
+
+  public_deps = [ ":libtint_core_src" ]
+}
+
+libtint_source_set("libtint_glsl_writer_src") {
+  sources = [
+    "transform/glsl.cc",
+    "transform/glsl.h",
+    "writer/glsl/generator.cc",
+    "writer/glsl/generator.h",
+    "writer/glsl/generator_impl.cc",
+    "writer/glsl/generator_impl.h",
+  ]
+
+  public_deps = [ ":libtint_core_src" ]
+}
+
+source_set("libtint") {
+  public_deps = [ ":libtint_core_src" ]
+
+  if (tint_build_spv_reader) {
+    public_deps += [ ":libtint_spv_reader_src" ]
+  }
+
+  if (tint_build_spv_writer) {
+    public_deps += [ ":libtint_spv_writer_src" ]
+  }
+
+  if (tint_build_wgsl_reader) {
+    public_deps += [ ":libtint_wgsl_reader_src" ]
+  }
+
+  if (tint_build_wgsl_writer) {
+    public_deps += [ ":libtint_wgsl_writer_src" ]
+  }
+
+  if (tint_build_msl_writer) {
+    public_deps += [ ":libtint_msl_writer_src" ]
+  }
+
+  if (tint_build_hlsl_writer) {
+    public_deps += [ ":libtint_hlsl_writer_src" ]
+  }
+
+  if (tint_build_glsl_writer) {
+    public_deps += [ ":libtint_glsl_writer_src" ]
+  }
+
+  configs += [ ":tint_common_config" ]
+  public_configs = [ ":tint_public_config" ]
+
+  if (build_with_chromium) {
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+  }
+}
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
new file mode 100644
index 0000000..138dfde
--- /dev/null
+++ b/src/tint/CMakeLists.txt
@@ -0,0 +1,1239 @@
+# Copyright 2020 The Tint Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function(tint_spvtools_compile_options TARGET)
+  # We'll use the optimizer for its nice SPIR-V in-memory representation
+  target_link_libraries(${TARGET} SPIRV-Tools-opt SPIRV-Tools)
+
+  # We'll be cheating: using internal interfaces to the SPIRV-Tools
+  # optimizer.
+  target_include_directories(${TARGET} PRIVATE
+    ${spirv-tools_SOURCE_DIR}
+    ${spirv-tools_BINARY_DIR}
+  )
+
+  if (${CMAKE_CXX_COMPILER_ID} MATCHES Clang)
+    # The SPIRV-Tools code is conditioned against C++ and an older version of Clang.
+    # Suppress warnings triggered in our current compilation environment.
+    # TODO(dneto): Fix the issues upstream.
+    target_compile_options(${TARGET} PRIVATE
+      -Wno-newline-eof
+      -Wno-sign-conversion
+      -Wno-old-style-cast
+      -Wno-weak-vtables
+    )
+  endif()
+endfunction()
+
+## Tint diagnostic utilities. Used by libtint and tint_utils_io.
+add_library(tint_diagnostic_utils
+  debug.cc
+  debug.h
+  source.h
+  source.cc
+  diagnostic/diagnostic.cc
+  diagnostic/diagnostic.h
+  diagnostic/formatter.cc
+  diagnostic/formatter.h
+  diagnostic/printer.cc
+  diagnostic/printer.h
+  utils/debugger.cc
+  utils/debugger.h
+)
+tint_default_compile_options(tint_diagnostic_utils)
+
+if (TINT_ENABLE_BREAK_IN_DEBUGGER)
+  set_source_files_properties(utils/debugger.cc
+    PROPERTIES COMPILE_DEFINITIONS "TINT_ENABLE_BREAK_IN_DEBUGGER=1" )
+endif()
+
+set(TINT_LIB_SRCS
+  ../../include/tint/tint.h
+  ast/access.cc
+  ast/access.h
+  ast/attribute.cc
+  ast/attribute.h
+  ast/alias.cc
+  ast/alias.h
+  ast/index_accessor_expression.cc
+  ast/index_accessor_expression.h
+  ast/array.cc
+  ast/array.h
+  ast/assignment_statement.cc
+  ast/assignment_statement.h
+  ast/atomic.cc
+  ast/atomic.h
+  ast/binary_expression.cc
+  ast/binary_expression.h
+  ast/binding_attribute.cc
+  ast/binding_attribute.h
+  ast/bitcast_expression.cc
+  ast/bitcast_expression.h
+  ast/block_statement.cc
+  ast/block_statement.h
+  ast/bool_literal_expression.cc
+  ast/bool_literal_expression.h
+  ast/bool.cc
+  ast/bool.h
+  ast/break_statement.cc
+  ast/break_statement.h
+  ast/builtin_attribute.cc
+  ast/builtin_attribute.h
+  ast/builtin.cc
+  ast/builtin.h
+  ast/call_expression.cc
+  ast/call_expression.h
+  ast/call_statement.cc
+  ast/call_statement.h
+  ast/case_statement.cc
+  ast/case_statement.h
+  ast/compound_assignment_statement.cc
+  ast/compound_assignment_statement.h
+  ast/continue_statement.cc
+  ast/continue_statement.h
+  ast/depth_multisampled_texture.cc
+  ast/depth_multisampled_texture.h
+  ast/disable_validation_attribute.cc
+  ast/disable_validation_attribute.h
+  ast/depth_texture.cc
+  ast/depth_texture.h
+  ast/discard_statement.cc
+  ast/discard_statement.h
+  ast/else_statement.cc
+  ast/else_statement.h
+  ast/expression.cc
+  ast/expression.h
+  ast/external_texture.cc
+  ast/external_texture.h
+  ast/f32.cc
+  ast/f32.h
+  ast/fallthrough_statement.cc
+  ast/fallthrough_statement.h
+  ast/float_literal_expression.cc
+  ast/float_literal_expression.h
+  ast/for_loop_statement.cc
+  ast/for_loop_statement.h
+  ast/function.cc
+  ast/function.h
+  ast/group_attribute.cc
+  ast/group_attribute.h
+  ast/i32.cc
+  ast/i32.h
+  ast/id_attribute.cc
+  ast/id_attribute.h
+  ast/identifier_expression.cc
+  ast/identifier_expression.h
+  ast/if_statement.cc
+  ast/if_statement.h
+  ast/int_literal_expression.cc
+  ast/int_literal_expression.h
+  ast/internal_attribute.cc
+  ast/internal_attribute.h
+  ast/interpolate_attribute.cc
+  ast/interpolate_attribute.h
+  ast/invariant_attribute.cc
+  ast/invariant_attribute.h
+  ast/literal_expression.cc
+  ast/literal_expression.h
+  ast/location_attribute.cc
+  ast/location_attribute.h
+  ast/loop_statement.cc
+  ast/loop_statement.h
+  ast/matrix.cc
+  ast/matrix.h
+  ast/member_accessor_expression.cc
+  ast/member_accessor_expression.h
+  ast/module.cc
+  ast/module.h
+  ast/multisampled_texture.cc
+  ast/multisampled_texture.h
+  ast/node.cc
+  ast/node.h
+  ast/phony_expression.cc
+  ast/phony_expression.h
+  ast/pipeline_stage.cc
+  ast/pipeline_stage.h
+  ast/pointer.cc
+  ast/pointer.h
+  ast/return_statement.cc
+  ast/return_statement.h
+  ast/sampled_texture.cc
+  ast/sampled_texture.h
+  ast/sampler.cc
+  ast/sampler.h
+  ast/sint_literal_expression.cc
+  ast/sint_literal_expression.h
+  ast/stage_attribute.cc
+  ast/stage_attribute.h
+  ast/statement.cc
+  ast/statement.h
+  ast/storage_class.cc
+  ast/storage_class.h
+  ast/storage_texture.cc
+  ast/storage_texture.h
+  ast/stride_attribute.cc
+  ast/stride_attribute.h
+  ast/struct_member_align_attribute.cc
+  ast/struct_member_align_attribute.h
+  ast/struct_member_offset_attribute.cc
+  ast/struct_member_offset_attribute.h
+  ast/struct_member_size_attribute.cc
+  ast/struct_member_size_attribute.h
+  ast/struct_member.cc
+  ast/struct_member.h
+  ast/struct.cc
+  ast/struct.h
+  ast/switch_statement.cc
+  ast/switch_statement.h
+  ast/texture.cc
+  ast/texture.h
+  ast/traverse_expressions.h
+  ast/type_name.cc
+  ast/type_name.h
+  ast/ast_type.cc  # TODO(bclayton) - rename to type.cc
+  ast/type.h
+  ast/type_decl.cc
+  ast/type_decl.h
+  ast/type_name.cc
+  ast/type_name.h
+  ast/u32.cc
+  ast/u32.h
+  ast/uint_literal_expression.cc
+  ast/uint_literal_expression.h
+  ast/unary_op_expression.cc
+  ast/unary_op_expression.h
+  ast/unary_op.cc
+  ast/unary_op.h
+  ast/variable_decl_statement.cc
+  ast/variable_decl_statement.h
+  ast/variable.cc
+  ast/variable.h
+  ast/vector.cc
+  ast/vector.h
+  ast/void.cc
+  ast/void.h
+  ast/workgroup_attribute.cc
+  ast/workgroup_attribute.h
+  builtin_table.cc
+  builtin_table.h
+  builtin_table.inl
+  castable.cc
+  castable.h
+  clone_context.cc
+  clone_context.h
+  demangler.cc
+  demangler.h
+  inspector/entry_point.cc
+  inspector/entry_point.h
+  inspector/inspector.cc
+  inspector/inspector.h
+  inspector/resource_binding.cc
+  inspector/resource_binding.h
+  inspector/scalar.cc
+  inspector/scalar.h
+  program_builder.cc
+  program_builder.h
+  program_id.cc
+  program_id.h
+  program.cc
+  program.h
+  reader/reader.cc
+  reader/reader.h
+  resolver/dependency_graph.cc
+  resolver/dependency_graph.h
+  resolver/resolver.cc
+  resolver/resolver_constants.cc
+  resolver/resolver_validation.cc
+  resolver/resolver.h
+  scope_stack.h
+  sem/array.cc
+  sem/array.h
+  sem/atomic_type.cc
+  sem/atomic_type.h
+  sem/behavior.cc
+  sem/behavior.h
+  sem/binding_point.h
+  sem/block_statement.cc
+  sem/block_statement.h
+  sem/builtin_type.cc
+  sem/builtin_type.h
+  sem/builtin.cc
+  sem/builtin.h
+  sem/call_target.cc
+  sem/call_target.h
+  sem/call.cc
+  sem/call.h
+  sem/constant.cc
+  sem/constant.h
+  sem/depth_multisampled_texture_type.cc
+  sem/depth_multisampled_texture_type.h
+  sem/expression.cc
+  sem/expression.h
+  sem/function.cc
+  sem/info.cc
+  sem/info.h
+  sem/member_accessor_expression.cc
+  sem/parameter_usage.cc
+  sem/parameter_usage.h
+  sem/pipeline_stage_set.h
+  sem/node.cc
+  sem/node.h
+  sem/module.cc
+  sem/module.h
+  sem/sampler_texture_pair.h
+  sem/statement.cc
+  sem/struct.cc
+  sem/type_mappings.h
+  sem/variable.cc
+  symbol_table.cc
+  symbol_table.h
+  symbol.cc
+  symbol.h
+  text/unicode.cc
+  text/unicode.h
+  traits.h
+  transform/add_empty_entry_point.cc
+  transform/add_empty_entry_point.h
+  transform/add_spirv_block_attribute.cc
+  transform/add_spirv_block_attribute.h
+  transform/array_length_from_uniform.cc
+  transform/array_length_from_uniform.h
+  transform/binding_remapper.cc
+  transform/binding_remapper.h
+  transform/builtin_polyfill.cc
+  transform/builtin_polyfill.h
+  transform/calculate_array_length.cc
+  transform/calculate_array_length.h
+  transform/combine_samplers.cc
+  transform/combine_samplers.h
+  transform/canonicalize_entry_point_io.cc
+  transform/canonicalize_entry_point_io.h
+  transform/decompose_memory_access.cc
+  transform/decompose_memory_access.h
+  transform/decompose_strided_array.cc
+  transform/decompose_strided_array.h
+  transform/decompose_strided_matrix.cc
+  transform/decompose_strided_matrix.h
+  transform/first_index_offset.cc
+  transform/first_index_offset.h
+  transform/fold_constants.cc
+  transform/fold_constants.h
+  transform/fold_trivial_single_use_lets.cc
+  transform/fold_trivial_single_use_lets.h
+  transform/localize_struct_array_assignment.cc
+  transform/localize_struct_array_assignment.h
+  transform/for_loop_to_loop.cc
+  transform/for_loop_to_loop.h
+  transform/expand_compound_assignment.cc
+  transform/expand_compound_assignment.h
+  transform/glsl.cc
+  transform/glsl.h
+  transform/loop_to_for_loop.cc
+  transform/loop_to_for_loop.h
+  transform/manager.cc
+  transform/manager.h
+  transform/module_scope_var_to_entry_point_param.cc
+  transform/module_scope_var_to_entry_point_param.h
+  transform/multiplanar_external_texture.cc
+  transform/multiplanar_external_texture.h
+  transform/num_workgroups_from_uniform.cc
+  transform/num_workgroups_from_uniform.h
+  transform/promote_initializers_to_const_var.cc
+  transform/promote_initializers_to_const_var.h
+  transform/promote_side_effects_to_decl.cc
+  transform/promote_side_effects_to_decl.h
+  transform/remove_phonies.cc
+  transform/remove_phonies.h
+  transform/remove_continue_in_switch.cc
+  transform/remove_continue_in_switch.h
+  transform/remove_unreachable_statements.cc
+  transform/remove_unreachable_statements.h
+  transform/renamer.cc
+  transform/renamer.h
+  transform/robustness.cc
+  transform/robustness.h
+  transform/simplify_pointers.cc
+  transform/simplify_pointers.h
+  transform/single_entry_point.cc
+  transform/single_entry_point.h
+  transform/transform.cc
+  transform/transform.h
+  transform/unshadow.cc
+  transform/unshadow.h
+  transform/unwind_discard_functions.cc
+  transform/unwind_discard_functions.h
+  transform/vectorize_scalar_matrix_constructors.cc
+  transform/vectorize_scalar_matrix_constructors.h
+  transform/var_for_dynamic_index.cc
+  transform/var_for_dynamic_index.h
+  transform/vertex_pulling.cc
+  transform/vertex_pulling.h
+  transform/wrap_arrays_in_structs.cc
+  transform/wrap_arrays_in_structs.h
+  transform/zero_init_workgroup_memory.cc
+  transform/zero_init_workgroup_memory.h
+  transform/utils/get_insertion_point.cc
+  transform/utils/get_insertion_point.h
+  transform/utils/hoist_to_decl_before.cc
+  transform/utils/hoist_to_decl_before.h
+  sem/bool_type.cc
+  sem/bool_type.h
+  sem/depth_texture_type.cc
+  sem/depth_texture_type.h
+  sem/external_texture_type.cc
+  sem/external_texture_type.h
+  sem/f32_type.cc
+  sem/f32_type.h
+  sem/for_loop_statement.cc
+  sem/for_loop_statement.h
+  sem/i32_type.cc
+  sem/i32_type.h
+  sem/if_statement.cc
+  sem/if_statement.h
+  sem/loop_statement.cc
+  sem/loop_statement.h
+  sem/matrix_type.cc
+  sem/matrix_type.h
+  sem/multisampled_texture_type.cc
+  sem/multisampled_texture_type.h
+  sem/pointer_type.cc
+  sem/pointer_type.h
+  sem/reference_type.cc
+  sem/reference_type.h
+  sem/sampled_texture_type.cc
+  sem/sampled_texture_type.h
+  sem/sampler_type.cc
+  sem/sampler_type.h
+  sem/storage_texture_type.cc
+  sem/storage_texture_type.h
+  sem/switch_statement.cc
+  sem/switch_statement.h
+  sem/texture_type.cc
+  sem/texture_type.h
+  sem/type_constructor.cc
+  sem/type_constructor.h
+  sem/type_conversion.cc
+  sem/type_conversion.h
+  sem/type.cc
+  sem/type.h
+  sem/type_manager.cc
+  sem/type_manager.h
+  sem/u32_type.cc
+  sem/u32_type.h
+  sem/vector_type.cc
+  sem/vector_type.h
+  sem/void_type.cc
+  sem/void_type.h
+  utils/block_allocator.h
+  utils/crc32.h
+  utils/enum_set.h
+  utils/hash.h
+  utils/map.h
+  utils/math.h
+  utils/scoped_assignment.h
+  utils/string.h
+  utils/unique_allocator.h
+  utils/unique_vector.h
+  writer/append_vector.cc
+  writer/append_vector.h
+  writer/array_length_from_uniform_options.cc
+  writer/array_length_from_uniform_options.h
+  writer/float_to_string.cc
+  writer/float_to_string.h
+  writer/generate_external_texture_bindings.cc
+  writer/generate_external_texture_bindings.h
+  writer/text_generator.cc
+  writer/text_generator.h
+  writer/text.cc
+  writer/text.h
+  writer/writer.cc
+  writer/writer.h
+)
+
+if(UNIX)
+  list(APPEND TINT_LIB_SRCS diagnostic/printer_linux.cc)
+elseif(WIN32)
+  list(APPEND TINT_LIB_SRCS diagnostic/printer_windows.cc)
+else()
+  list(APPEND TINT_LIB_SRCS diagnostic/printer_other.cc)
+endif()
+
+if(${TINT_BUILD_SPV_READER})
+  list(APPEND TINT_LIB_SRCS
+    reader/spirv/construct.h
+    reader/spirv/construct.cc
+    reader/spirv/entry_point_info.h
+    reader/spirv/entry_point_info.cc
+    reader/spirv/enum_converter.h
+    reader/spirv/enum_converter.cc
+    reader/spirv/fail_stream.h
+    reader/spirv/function.cc
+    reader/spirv/function.h
+    reader/spirv/namer.cc
+    reader/spirv/namer.h
+    reader/spirv/parser_type.cc
+    reader/spirv/parser_type.h
+    reader/spirv/parser.cc
+    reader/spirv/parser.h
+    reader/spirv/parser_impl.cc
+    reader/spirv/parser_impl.h
+    reader/spirv/usage.cc
+    reader/spirv/usage.h
+  )
+endif()
+
+if(${TINT_BUILD_WGSL_READER})
+  list(APPEND TINT_LIB_SRCS
+    reader/wgsl/lexer.cc
+    reader/wgsl/lexer.h
+    reader/wgsl/parser.cc
+    reader/wgsl/parser.h
+    reader/wgsl/parser_impl.cc
+    reader/wgsl/parser_impl.h
+    reader/wgsl/parser_impl_detail.h
+    reader/wgsl/token.cc
+    reader/wgsl/token.h
+  )
+endif()
+
+if(${TINT_BUILD_SPV_WRITER})
+  list(APPEND TINT_LIB_SRCS
+    writer/spirv/binary_writer.cc
+    writer/spirv/binary_writer.h
+    writer/spirv/builder.cc
+    writer/spirv/builder.h
+    writer/spirv/function.cc
+    writer/spirv/function.h
+    writer/spirv/generator.cc
+    writer/spirv/generator.h
+    writer/spirv/instruction.cc
+    writer/spirv/instruction.h
+    writer/spirv/operand.cc
+    writer/spirv/operand.h
+    writer/spirv/scalar_constant.h
+  )
+endif()
+
+if(${TINT_BUILD_WGSL_WRITER})
+  list(APPEND TINT_LIB_SRCS
+    writer/wgsl/generator.cc
+    writer/wgsl/generator.h
+    writer/wgsl/generator_impl.cc
+    writer/wgsl/generator_impl.h
+  )
+endif()
+
+if(${TINT_BUILD_MSL_WRITER})
+  list(APPEND TINT_LIB_SRCS
+    writer/msl/generator.cc
+    writer/msl/generator.h
+    writer/msl/generator_impl.cc
+    writer/msl/generator_impl.h
+  )
+endif()
+
+if(${TINT_BUILD_GLSL_WRITER})
+  list(APPEND TINT_LIB_SRCS
+    writer/glsl/generator.cc
+    writer/glsl/generator.h
+    writer/glsl/generator_impl.cc
+    writer/glsl/generator_impl.h
+    writer/glsl/version.h
+  )
+endif()
+
+if(${TINT_BUILD_HLSL_WRITER})
+  list(APPEND TINT_LIB_SRCS
+    writer/hlsl/generator.cc
+    writer/hlsl/generator.h
+    writer/hlsl/generator_impl.cc
+    writer/hlsl/generator_impl.h
+  )
+endif()
+
+if(MSVC)
+  list(APPEND TINT_LIB_SRCS
+    tint.natvis
+  )
+endif()
+
+## Tint IO utilities. Used by tint_val.
+add_library(tint_utils_io
+  utils/io/command_${TINT_OS_CC_SUFFIX}.cc
+  utils/io/command.h
+  utils/io/tmpfile_${TINT_OS_CC_SUFFIX}.cc
+  utils/io/tmpfile.h
+)
+tint_default_compile_options(tint_utils_io)
+target_link_libraries(tint_utils_io tint_diagnostic_utils)
+
+## Tint validation utilities. Used by tests and the tint executable.
+add_library(tint_val
+  val/hlsl.cc
+  val/msl.cc
+  val/val.h
+)
+
+# If we're building on mac / ios and we have CoreGraphics, then we can use the
+# metal API to validate our shaders. This is roughly 4x faster than invoking
+# the metal shader compiler executable.
+if(APPLE)
+  find_library(LIB_CORE_GRAPHICS CoreGraphics)
+  if(LIB_CORE_GRAPHICS)
+    target_sources(tint_val PRIVATE "val/msl_metal.mm")
+    target_compile_definitions(tint_val PUBLIC "-DTINT_ENABLE_MSL_VALIDATION_USING_METAL_API=1")
+    target_compile_options(tint_val PRIVATE "-fmodules" "-fcxx-modules")
+    target_link_options(tint_val PUBLIC "-framework" "CoreGraphics")
+  endif()
+endif()
+
+tint_default_compile_options(tint_val)
+target_link_libraries(tint_val tint_utils_io)
+
+## Tint library
+add_library(libtint ${TINT_LIB_SRCS})
+tint_default_compile_options(libtint)
+target_link_libraries(libtint tint_diagnostic_utils)
+if (${COMPILER_IS_LIKE_GNU})
+  target_compile_options(libtint PRIVATE -fvisibility=hidden)
+endif()
+if (${TINT_SYMBOL_STORE_DEBUG_NAME})
+    target_compile_definitions(libtint PUBLIC "TINT_SYMBOL_STORE_DEBUG_NAME=1")
+endif()
+set_target_properties(libtint PROPERTIES OUTPUT_NAME "tint")
+
+if (${TINT_BUILD_FUZZERS})
+  # Tint library with fuzzer instrumentation
+  add_library(libtint-fuzz ${TINT_LIB_SRCS})
+  tint_default_compile_options(libtint-fuzz)
+  target_link_libraries(libtint-fuzz tint_diagnostic_utils)
+  if (${COMPILER_IS_LIKE_GNU})
+    target_compile_options(libtint-fuzz PRIVATE -fvisibility=hidden)
+  endif()
+
+  if (NOT ${TINT_LIB_FUZZING_ENGINE_LINK_OPTIONS} STREQUAL "")
+    # This is set when the fuzzers are being built by OSS-Fuzz. In this case the
+    # variable provides the necessary linker flags, and OSS-Fuzz will take care
+    # of passing suitable compiler flags.
+    target_link_options(libtint-fuzz PUBLIC ${TINT_LIB_FUZZING_ENGINE_LINK_OPTIONS})
+  else()
+    # When the fuzzers are being built outside of OSS-Fuzz, specific libFuzzer
+    # arguments to enable fuzzing are used.
+    target_compile_options(libtint-fuzz PUBLIC -fsanitize=fuzzer -fsanitize-coverage=trace-cmp)
+    target_link_options(libtint-fuzz PUBLIC -fsanitize=fuzzer -fsanitize-coverage=trace-cmp)
+  endif()
+endif()
+
+if(${TINT_BUILD_SPV_READER} OR ${TINT_BUILD_SPV_WRITER})
+  tint_spvtools_compile_options(libtint)
+  if (${TINT_BUILD_FUZZERS})
+    tint_spvtools_compile_options(libtint-fuzz)
+  endif()
+endif()
+
+################################################################################
+# Tests
+################################################################################
+if(TINT_BUILD_TESTS)
+  set(TINT_TEST_SRCS
+    ast/alias_test.cc
+    ast/array_test.cc
+    ast/assignment_statement_test.cc
+    ast/atomic_test.cc
+    ast/binary_expression_test.cc
+    ast/binding_attribute_test.cc
+    ast/bitcast_expression_test.cc
+    ast/block_statement_test.cc
+    ast/bool_literal_expression_test.cc
+    ast/bool_test.cc
+    ast/break_statement_test.cc
+    ast/builtin_attribute_test.cc
+    ast/builtin_texture_helper_test.cc
+    ast/builtin_texture_helper_test.h
+    ast/call_expression_test.cc
+    ast/call_statement_test.cc
+    ast/case_statement_test.cc
+    ast/compound_assignment_statement_test.cc
+    ast/continue_statement_test.cc
+    ast/depth_multisampled_texture_test.cc
+    ast/depth_texture_test.cc
+    ast/discard_statement_test.cc
+    ast/else_statement_test.cc
+    ast/external_texture_test.cc
+    ast/f32_test.cc
+    ast/fallthrough_statement_test.cc
+    ast/float_literal_expression_test.cc
+    ast/for_loop_statement_test.cc
+    ast/function_test.cc
+    ast/group_attribute_test.cc
+    ast/i32_test.cc
+    ast/id_attribute_test.cc
+    ast/identifier_expression_test.cc
+    ast/if_statement_test.cc
+    ast/index_accessor_expression_test.cc
+    ast/int_literal_expression_test.cc
+    ast/interpolate_attribute_test.cc
+    ast/invariant_attribute_test.cc
+    ast/location_attribute_test.cc
+    ast/loop_statement_test.cc
+    ast/matrix_test.cc
+    ast/member_accessor_expression_test.cc
+    ast/module_clone_test.cc
+    ast/module_test.cc
+    ast/multisampled_texture_test.cc
+    ast/phony_expression_test.cc
+    ast/pointer_test.cc
+    ast/return_statement_test.cc
+    ast/sampled_texture_test.cc
+    ast/sampler_test.cc
+    ast/sint_literal_expression_test.cc
+    ast/stage_attribute_test.cc
+    ast/storage_texture_test.cc
+    ast/stride_attribute_test.cc
+    ast/struct_member_align_attribute_test.cc
+    ast/struct_member_offset_attribute_test.cc
+    ast/struct_member_size_attribute_test.cc
+    ast/struct_member_test.cc
+    ast/struct_test.cc
+    ast/switch_statement_test.cc
+    ast/test_helper.h
+    ast/texture_test.cc
+    ast/traverse_expressions_test.cc
+    ast/u32_test.cc
+    ast/uint_literal_expression_test.cc
+    ast/unary_op_expression_test.cc
+    ast/variable_decl_statement_test.cc
+    ast/variable_test.cc
+    ast/vector_test.cc
+    ast/workgroup_attribute_test.cc
+    builtin_table_test.cc
+    castable_test.cc
+    clone_context_test.cc
+    debug_test.cc
+    demangler_test.cc
+    diagnostic/diagnostic_test.cc
+    diagnostic/formatter_test.cc
+    diagnostic/printer_test.cc
+    program_test.cc
+    resolver/array_accessor_test.cc
+    resolver/assignment_validation_test.cc
+    resolver/atomics_test.cc
+    resolver/atomics_validation_test.cc
+    resolver/bitcast_validation_test.cc
+    resolver/builtins_validation_test.cc
+    resolver/builtin_test.cc
+    resolver/builtin_validation_test.cc
+    resolver/call_test.cc
+    resolver/call_validation_test.cc
+    resolver/compound_assignment_validation_test.cc
+    resolver/compound_statement_test.cc
+    resolver/control_block_validation_test.cc
+    resolver/attribute_validation_test.cc
+    resolver/dependency_graph_test.cc
+    resolver/entry_point_validation_test.cc
+    resolver/function_validation_test.cc
+    resolver/host_shareable_validation_test.cc
+    resolver/inferred_type_test.cc
+    resolver/is_host_shareable_test.cc
+    resolver/is_storeable_test.cc
+    resolver/pipeline_overridable_constant_test.cc
+    resolver/ptr_ref_test.cc
+    resolver/ptr_ref_validation_test.cc
+    resolver/resolver_behavior_test.cc
+    resolver/resolver_constants_test.cc
+    resolver/resolver_test_helper.cc
+    resolver/resolver_test_helper.h
+    resolver/resolver_test.cc
+    resolver/side_effects_test.cc
+    resolver/storage_class_layout_validation_test.cc
+    resolver/storage_class_validation_test.cc
+    resolver/struct_layout_test.cc
+    resolver/struct_pipeline_stage_use_test.cc
+    resolver/struct_storage_class_use_test.cc
+    resolver/type_constructor_validation_test.cc
+    resolver/type_validation_test.cc
+    resolver/validation_test.cc
+    resolver/var_let_test.cc
+    resolver/var_let_validation_test.cc
+    scope_stack_test.cc
+    sem/atomic_type_test.cc
+    sem/bool_type_test.cc
+    sem/builtin_test.cc
+    sem/depth_multisampled_texture_type_test.cc
+    sem/depth_texture_type_test.cc
+    sem/external_texture_type_test.cc
+    sem/f32_type_test.cc
+    sem/i32_type_test.cc
+    sem/matrix_type_test.cc
+    sem/multisampled_texture_type_test.cc
+    sem/pointer_type_test.cc
+    sem/reference_type_test.cc
+    sem/sampled_texture_type_test.cc
+    sem/sampler_type_test.cc
+    sem/sem_array_test.cc
+    sem/sem_struct_test.cc
+    sem/storage_texture_type_test.cc
+    sem/texture_type_test.cc
+    sem/type_manager_test.cc
+    sem/u32_type_test.cc
+    sem/vector_type_test.cc
+    source_test.cc
+    symbol_table_test.cc
+    symbol_test.cc
+    test_main.cc
+    text/unicode_test.cc
+    traits_test.cc
+    transform/transform_test.cc
+    utils/block_allocator_test.cc
+    utils/crc32_test.cc
+    utils/defer_test.cc
+    utils/enum_set_test.cc
+    utils/hash_test.cc
+    utils/io/command_test.cc
+    utils/io/tmpfile_test.cc
+    utils/map_test.cc
+    utils/math_test.cc
+    utils/reverse_test.cc
+    utils/scoped_assignment_test.cc
+    utils/string_test.cc
+    utils/transform_test.cc
+    utils/unique_allocator_test.cc
+    utils/unique_vector_test.cc
+    writer/append_vector_test.cc
+    writer/float_to_string_test.cc
+    writer/generate_external_texture_bindings_test.cc
+    writer/text_generator_test.cc
+  )
+
+  # Inspector tests depend on WGSL reader
+  if(${TINT_BUILD_WGSL_READER})
+    list(APPEND TINT_TEST_SRCS
+      inspector/inspector_test.cc
+      inspector/test_inspector_builder.cc
+      inspector/test_inspector_builder.h
+      inspector/test_inspector_runner.cc
+      inspector/test_inspector_runner.h
+    )
+  endif()
+
+  if(${TINT_BUILD_SPV_READER} AND ${TINT_BUILD_WGSL_WRITER})
+    list(APPEND TINT_TEST_SRCS
+      reader/spirv/enum_converter_test.cc
+      reader/spirv/fail_stream_test.cc
+      reader/spirv/function_arithmetic_test.cc
+      reader/spirv/function_bit_test.cc
+      reader/spirv/function_cfg_test.cc
+      reader/spirv/function_call_test.cc
+      reader/spirv/function_composite_test.cc
+      reader/spirv/function_conversion_test.cc
+      reader/spirv/function_decl_test.cc
+      reader/spirv/function_glsl_std_450_test.cc
+      reader/spirv/function_logical_test.cc
+      reader/spirv/function_memory_test.cc
+      reader/spirv/function_misc_test.cc
+      reader/spirv/function_var_test.cc
+      reader/spirv/namer_test.cc
+      reader/spirv/parser_impl_barrier_test.cc
+      reader/spirv/parser_impl_convert_member_decoration_test.cc
+      reader/spirv/parser_impl_convert_type_test.cc
+      reader/spirv/parser_impl_function_decl_test.cc
+      reader/spirv/parser_impl_get_decorations_test.cc
+      reader/spirv/parser_impl_handle_test.cc
+      reader/spirv/parser_impl_import_test.cc
+      reader/spirv/parser_impl_module_var_test.cc
+      reader/spirv/parser_impl_named_types_test.cc
+      reader/spirv/parser_impl_test_helper.cc
+      reader/spirv/parser_impl_test_helper.h
+      reader/spirv/parser_impl_test.cc
+      reader/spirv/parser_impl_user_name_test.cc
+      reader/spirv/parser_type_test.cc
+      reader/spirv/parser_test.cc
+      reader/spirv/spirv_tools_helpers_test.cc
+      reader/spirv/spirv_tools_helpers_test.h
+      reader/spirv/usage_test.cc
+    )
+  endif()
+
+  if(${TINT_BUILD_WGSL_READER})
+    list(APPEND TINT_TEST_SRCS
+      reader/wgsl/lexer_test.cc
+      reader/wgsl/parser_test.cc
+      reader/wgsl/parser_impl_additive_expression_test.cc
+      reader/wgsl/parser_impl_and_expression_test.cc
+      reader/wgsl/parser_impl_argument_expression_list_test.cc
+      reader/wgsl/parser_impl_assignment_stmt_test.cc
+      reader/wgsl/parser_impl_body_stmt_test.cc
+      reader/wgsl/parser_impl_break_stmt_test.cc
+      reader/wgsl/parser_impl_bug_cases_test.cc
+      reader/wgsl/parser_impl_call_stmt_test.cc
+      reader/wgsl/parser_impl_case_body_test.cc
+      reader/wgsl/parser_impl_const_expr_test.cc
+      reader/wgsl/parser_impl_const_literal_test.cc
+      reader/wgsl/parser_impl_continue_stmt_test.cc
+      reader/wgsl/parser_impl_continuing_stmt_test.cc
+      reader/wgsl/parser_impl_depth_texture_type_test.cc
+      reader/wgsl/parser_impl_external_texture_type_test.cc
+      reader/wgsl/parser_impl_elseif_stmt_test.cc
+      reader/wgsl/parser_impl_equality_expression_test.cc
+      reader/wgsl/parser_impl_error_msg_test.cc
+      reader/wgsl/parser_impl_error_resync_test.cc
+      reader/wgsl/parser_impl_exclusive_or_expression_test.cc
+      reader/wgsl/parser_impl_for_stmt_test.cc
+      reader/wgsl/parser_impl_function_decl_test.cc
+      reader/wgsl/parser_impl_function_attribute_list_test.cc
+      reader/wgsl/parser_impl_function_attribute_test.cc
+      reader/wgsl/parser_impl_function_header_test.cc
+      reader/wgsl/parser_impl_global_constant_decl_test.cc
+      reader/wgsl/parser_impl_global_decl_test.cc
+      reader/wgsl/parser_impl_global_variable_decl_test.cc
+      reader/wgsl/parser_impl_if_stmt_test.cc
+      reader/wgsl/parser_impl_inclusive_or_expression_test.cc
+      reader/wgsl/parser_impl_logical_and_expression_test.cc
+      reader/wgsl/parser_impl_logical_or_expression_test.cc
+      reader/wgsl/parser_impl_loop_stmt_test.cc
+      reader/wgsl/parser_impl_multiplicative_expression_test.cc
+      reader/wgsl/parser_impl_param_list_test.cc
+      reader/wgsl/parser_impl_paren_rhs_stmt_test.cc
+      reader/wgsl/parser_impl_pipeline_stage_test.cc
+      reader/wgsl/parser_impl_primary_expression_test.cc
+      reader/wgsl/parser_impl_relational_expression_test.cc
+      reader/wgsl/parser_impl_reserved_keyword_test.cc
+      reader/wgsl/parser_impl_sampled_texture_type_test.cc
+      reader/wgsl/parser_impl_sampler_type_test.cc
+      reader/wgsl/parser_impl_shift_expression_test.cc
+      reader/wgsl/parser_impl_singular_expression_test.cc
+      reader/wgsl/parser_impl_statement_test.cc
+      reader/wgsl/parser_impl_statements_test.cc
+      reader/wgsl/parser_impl_storage_class_test.cc
+      reader/wgsl/parser_impl_storage_texture_type_test.cc
+      reader/wgsl/parser_impl_struct_body_decl_test.cc
+      reader/wgsl/parser_impl_struct_decl_test.cc
+      reader/wgsl/parser_impl_struct_attribute_decl_test.cc
+      reader/wgsl/parser_impl_struct_member_attribute_decl_test.cc
+      reader/wgsl/parser_impl_struct_member_attribute_test.cc
+      reader/wgsl/parser_impl_struct_member_test.cc
+      reader/wgsl/parser_impl_switch_body_test.cc
+      reader/wgsl/parser_impl_switch_stmt_test.cc
+      reader/wgsl/parser_impl_test.cc
+      reader/wgsl/parser_impl_test_helper.cc
+      reader/wgsl/parser_impl_test_helper.h
+      reader/wgsl/parser_impl_texel_format_test.cc
+      reader/wgsl/parser_impl_texture_sampler_types_test.cc
+      reader/wgsl/parser_impl_type_alias_test.cc
+      reader/wgsl/parser_impl_type_decl_test.cc
+      reader/wgsl/parser_impl_unary_expression_test.cc
+      reader/wgsl/parser_impl_variable_decl_test.cc
+      reader/wgsl/parser_impl_variable_attribute_list_test.cc
+      reader/wgsl/parser_impl_variable_attribute_test.cc
+      reader/wgsl/parser_impl_variable_ident_decl_test.cc
+      reader/wgsl/parser_impl_variable_stmt_test.cc
+      reader/wgsl/parser_impl_variable_qualifier_test.cc
+      reader/wgsl/token_test.cc
+    )
+  endif()
+
+  if(${TINT_BUILD_SPV_WRITER})
+    list(APPEND TINT_TEST_SRCS
+      writer/spirv/binary_writer_test.cc
+      writer/spirv/builder_accessor_expression_test.cc
+      writer/spirv/builder_assign_test.cc
+      writer/spirv/builder_binary_expression_test.cc
+      writer/spirv/builder_bitcast_expression_test.cc
+      writer/spirv/builder_block_test.cc
+      writer/spirv/builder_builtin_test.cc
+      writer/spirv/builder_builtin_texture_test.cc
+      writer/spirv/builder_call_test.cc
+      writer/spirv/builder_constructor_expression_test.cc
+      writer/spirv/builder_discard_test.cc
+      writer/spirv/builder_entry_point_test.cc
+      writer/spirv/builder_format_conversion_test.cc
+      writer/spirv/builder_function_attribute_test.cc
+      writer/spirv/builder_function_test.cc
+      writer/spirv/builder_function_variable_test.cc
+      writer/spirv/builder_global_variable_test.cc
+      writer/spirv/builder_ident_expression_test.cc
+      writer/spirv/builder_if_test.cc
+      writer/spirv/builder_literal_test.cc
+      writer/spirv/builder_loop_test.cc
+      writer/spirv/builder_return_test.cc
+      writer/spirv/builder_switch_test.cc
+      writer/spirv/builder_test.cc
+      writer/spirv/builder_type_test.cc
+      writer/spirv/builder_unary_op_expression_test.cc
+      writer/spirv/instruction_test.cc
+      writer/spirv/operand_test.cc
+      writer/spirv/scalar_constant_test.cc
+      writer/spirv/spv_dump.cc
+      writer/spirv/spv_dump.h
+      writer/spirv/test_helper.h
+    )
+  endif()
+
+  if(${TINT_BUILD_WGSL_WRITER})
+    list(APPEND TINT_TEST_SRCS
+      writer/wgsl/generator_impl_test.cc
+      writer/wgsl/generator_impl_alias_type_test.cc
+      writer/wgsl/generator_impl_array_accessor_test.cc
+      writer/wgsl/generator_impl_assign_test.cc
+      writer/wgsl/generator_impl_binary_test.cc
+      writer/wgsl/generator_impl_bitcast_test.cc
+      writer/wgsl/generator_impl_block_test.cc
+      writer/wgsl/generator_impl_break_test.cc
+      writer/wgsl/generator_impl_call_test.cc
+      writer/wgsl/generator_impl_case_test.cc
+      writer/wgsl/generator_impl_cast_test.cc
+      writer/wgsl/generator_impl_constructor_test.cc
+      writer/wgsl/generator_impl_continue_test.cc
+      writer/wgsl/generator_impl_discard_test.cc
+      writer/wgsl/generator_impl_fallthrough_test.cc
+      writer/wgsl/generator_impl_function_test.cc
+      writer/wgsl/generator_impl_global_decl_test.cc
+      writer/wgsl/generator_impl_identifier_test.cc
+      writer/wgsl/generator_impl_if_test.cc
+      writer/wgsl/generator_impl_loop_test.cc
+      writer/wgsl/generator_impl_literal_test.cc
+      writer/wgsl/generator_impl_member_accessor_test.cc
+      writer/wgsl/generator_impl_return_test.cc
+      writer/wgsl/generator_impl_switch_test.cc
+      writer/wgsl/generator_impl_type_test.cc
+      writer/wgsl/generator_impl_unary_op_test.cc
+      writer/wgsl/generator_impl_variable_decl_statement_test.cc
+      writer/wgsl/generator_impl_variable_test.cc
+      writer/wgsl/test_helper.h
+    )
+  endif()
+
+  if(${TINT_BUILD_WGSL_READER} AND ${TINT_BUILD_WGSL_WRITER})
+    list(APPEND TINT_TEST_SRCS
+      transform/add_empty_entry_point_test.cc
+      transform/add_spirv_block_attribute_test.cc
+      transform/array_length_from_uniform_test.cc
+      transform/binding_remapper_test.cc
+      transform/builtin_polyfill_test.cc
+      transform/calculate_array_length_test.cc
+      transform/canonicalize_entry_point_io_test.cc
+      transform/combine_samplers_test.cc
+      transform/decompose_memory_access_test.cc
+      transform/decompose_strided_array_test.cc
+      transform/decompose_strided_matrix_test.cc
+      transform/first_index_offset_test.cc
+      transform/fold_constants_test.cc
+      transform/fold_trivial_single_use_lets_test.cc
+      transform/for_loop_to_loop_test.cc
+      transform/expand_compound_assignment.cc
+      transform/localize_struct_array_assignment_test.cc
+      transform/loop_to_for_loop_test.cc
+      transform/module_scope_var_to_entry_point_param_test.cc
+      transform/multiplanar_external_texture_test.cc
+      transform/num_workgroups_from_uniform_test.cc
+      transform/promote_initializers_to_const_var_test.cc
+      transform/promote_side_effects_to_decl_test.cc
+      transform/remove_continue_in_switch_test.cc
+      transform/remove_phonies_test.cc
+      transform/remove_unreachable_statements_test.cc
+      transform/renamer_test.cc
+      transform/robustness_test.cc
+      transform/simplify_pointers_test.cc
+      transform/single_entry_point_test.cc
+      transform/test_helper.h
+      transform/unshadow_test.cc
+      transform/unwind_discard_functions_test.cc
+      transform/var_for_dynamic_index_test.cc
+      transform/vectorize_scalar_matrix_constructors_test.cc
+      transform/vertex_pulling_test.cc
+      transform/wrap_arrays_in_structs_test.cc
+      transform/zero_init_workgroup_memory_test.cc
+      transform/utils/get_insertion_point_test.cc
+      transform/utils/hoist_to_decl_before_test.cc
+    )
+  endif()
+
+  if(${TINT_BUILD_MSL_WRITER})
+    list(APPEND TINT_TEST_SRCS
+      writer/msl/generator_impl_array_accessor_test.cc
+      writer/msl/generator_impl_assign_test.cc
+      writer/msl/generator_impl_binary_test.cc
+      writer/msl/generator_impl_bitcast_test.cc
+      writer/msl/generator_impl_block_test.cc
+      writer/msl/generator_impl_break_test.cc
+      writer/msl/generator_impl_builtin_test.cc
+      writer/msl/generator_impl_builtin_texture_test.cc
+      writer/msl/generator_impl_call_test.cc
+      writer/msl/generator_impl_case_test.cc
+      writer/msl/generator_impl_cast_test.cc
+      writer/msl/generator_impl_constructor_test.cc
+      writer/msl/generator_impl_continue_test.cc
+      writer/msl/generator_impl_discard_test.cc
+      writer/msl/generator_impl_function_test.cc
+      writer/msl/generator_impl_identifier_test.cc
+      writer/msl/generator_impl_if_test.cc
+      writer/msl/generator_impl_import_test.cc
+      writer/msl/generator_impl_loop_test.cc
+      writer/msl/generator_impl_member_accessor_test.cc
+      writer/msl/generator_impl_module_constant_test.cc
+      writer/msl/generator_impl_return_test.cc
+      writer/msl/generator_impl_sanitizer_test.cc
+      writer/msl/generator_impl_switch_test.cc
+      writer/msl/generator_impl_test.cc
+      writer/msl/generator_impl_type_test.cc
+      writer/msl/generator_impl_unary_op_test.cc
+      writer/msl/generator_impl_variable_decl_statement_test.cc
+      writer/msl/test_helper.h
+    )
+  endif()
+
+  if (${TINT_BUILD_GLSL_WRITER})
+    list(APPEND TINT_TEST_SRCS
+      writer/glsl/generator_impl_array_accessor_test.cc
+      writer/glsl/generator_impl_assign_test.cc
+      writer/glsl/generator_impl_binary_test.cc
+      writer/glsl/generator_impl_bitcast_test.cc
+      writer/glsl/generator_impl_block_test.cc
+      writer/glsl/generator_impl_break_test.cc
+      writer/glsl/generator_impl_builtin_test.cc
+      writer/glsl/generator_impl_builtin_texture_test.cc
+      writer/glsl/generator_impl_call_test.cc
+      writer/glsl/generator_impl_case_test.cc
+      writer/glsl/generator_impl_cast_test.cc
+      writer/glsl/generator_impl_constructor_test.cc
+      writer/glsl/generator_impl_continue_test.cc
+      writer/glsl/generator_impl_discard_test.cc
+      writer/glsl/generator_impl_function_test.cc
+      writer/glsl/generator_impl_identifier_test.cc
+      writer/glsl/generator_impl_if_test.cc
+      writer/glsl/generator_impl_import_test.cc
+      writer/glsl/generator_impl_loop_test.cc
+      writer/glsl/generator_impl_member_accessor_test.cc
+      writer/glsl/generator_impl_module_constant_test.cc
+      writer/glsl/generator_impl_return_test.cc
+      writer/glsl/generator_impl_sanitizer_test.cc
+      writer/glsl/generator_impl_storage_buffer_test.cc
+      writer/glsl/generator_impl_switch_test.cc
+      writer/glsl/generator_impl_test.cc
+      writer/glsl/generator_impl_type_test.cc
+      writer/glsl/generator_impl_unary_op_test.cc
+      writer/glsl/generator_impl_uniform_buffer_test.cc
+      writer/glsl/generator_impl_variable_decl_statement_test.cc
+      writer/glsl/generator_impl_workgroup_var_test.cc
+      writer/glsl/test_helper.h
+    )
+  endif()
+
+  if (${TINT_BUILD_HLSL_WRITER})
+    list(APPEND TINT_TEST_SRCS
+      writer/hlsl/generator_impl_array_accessor_test.cc
+      writer/hlsl/generator_impl_assign_test.cc
+      writer/hlsl/generator_impl_binary_test.cc
+      writer/hlsl/generator_impl_bitcast_test.cc
+      writer/hlsl/generator_impl_block_test.cc
+      writer/hlsl/generator_impl_break_test.cc
+      writer/hlsl/generator_impl_builtin_test.cc
+      writer/hlsl/generator_impl_builtin_texture_test.cc
+      writer/hlsl/generator_impl_call_test.cc
+      writer/hlsl/generator_impl_case_test.cc
+      writer/hlsl/generator_impl_cast_test.cc
+      writer/hlsl/generator_impl_constructor_test.cc
+      writer/hlsl/generator_impl_continue_test.cc
+      writer/hlsl/generator_impl_discard_test.cc
+      writer/hlsl/generator_impl_function_test.cc
+      writer/hlsl/generator_impl_identifier_test.cc
+      writer/hlsl/generator_impl_if_test.cc
+      writer/hlsl/generator_impl_import_test.cc
+      writer/hlsl/generator_impl_loop_test.cc
+      writer/hlsl/generator_impl_member_accessor_test.cc
+      writer/hlsl/generator_impl_module_constant_test.cc
+      writer/hlsl/generator_impl_return_test.cc
+      writer/hlsl/generator_impl_sanitizer_test.cc
+      writer/hlsl/generator_impl_switch_test.cc
+      writer/hlsl/generator_impl_test.cc
+      writer/hlsl/generator_impl_type_test.cc
+      writer/hlsl/generator_impl_unary_op_test.cc
+      writer/hlsl/generator_impl_variable_decl_statement_test.cc
+      writer/hlsl/generator_impl_workgroup_var_test.cc
+      writer/hlsl/test_helper.h
+    )
+  endif()
+
+  if (${TINT_BUILD_FUZZERS})
+    list(APPEND TINT_TEST_SRCS
+      fuzzers/mersenne_twister_engine.cc
+      fuzzers/mersenne_twister_engine.h
+      fuzzers/random_generator.cc
+      fuzzers/random_generator.h
+      fuzzers/random_generator_engine.cc
+      fuzzers/random_generator_engine.h
+      fuzzers/random_generator_test.cc
+    )
+  endif()
+
+  add_executable(tint_unittests ${TINT_TEST_SRCS})
+  set_target_properties(${target} PROPERTIES FOLDER "Tests")
+
+  if(NOT MSVC)
+    target_compile_options(tint_unittests PRIVATE
+      -Wno-global-constructors
+      -Wno-weak-vtables
+    )
+  endif()
+
+  ## Test executable
+  target_include_directories(
+      tint_unittests PRIVATE ${gmock_SOURCE_DIR}/include)
+  target_link_libraries(tint_unittests libtint gmock tint_utils_io)
+  tint_default_compile_options(tint_unittests)
+
+  if(${TINT_BUILD_SPV_READER} OR ${TINT_BUILD_SPV_WRITER})
+    tint_spvtools_compile_options(tint_unittests)
+  endif()
+
+  add_test(NAME tint_unittests COMMAND tint_unittests)
+endif(TINT_BUILD_TESTS)
+
+################################################################################
+# Benchmarks
+################################################################################
+if(TINT_BUILD_BENCHMARKS)
+  if(NOT TINT_BUILD_WGSL_READER)
+    message(FATAL_ERROR "TINT_BUILD_BENCHMARKS requires TINT_BUILD_WGSL_READER")
+  endif()
+
+  set(TINT_BENCHMARK_SRC
+    "castable_bench.cc"
+    "bench/benchmark.cc"
+    "reader/wgsl/parser_bench.cc"
+  )
+
+  if (${TINT_BUILD_GLSL_WRITER})
+    list(APPEND TINT_BENCHMARK_SRC writer/glsl/generator_bench.cc)
+  endif()
+  if (${TINT_BUILD_HLSL_WRITER})
+    list(APPEND TINT_BENCHMARK_SRC writer/hlsl/generator_bench.cc)
+  endif()
+  if (${TINT_BUILD_MSL_WRITER})
+    list(APPEND TINT_BENCHMARK_SRC writer/msl/generator_bench.cc)
+  endif()
+  if (${TINT_BUILD_SPV_WRITER})
+    list(APPEND TINT_BENCHMARK_SRC writer/spirv/generator_bench.cc)
+  endif()
+  if (${TINT_BUILD_WGSL_WRITER})
+    list(APPEND TINT_BENCHMARK_SRC writer/wgsl/generator_bench.cc)
+  endif()
+
+  add_executable(tint-benchmark ${TINT_BENCHMARK_SRC})
+  set_target_properties(${target} PROPERTIES FOLDER "Benchmarks")
+
+  tint_core_compile_options(tint-benchmark)
+
+  target_link_libraries(tint-benchmark PRIVATE benchmark::benchmark libtint)
+endif(TINT_BUILD_BENCHMARKS)
diff --git a/src/tint/ast/access.cc b/src/tint/ast/access.cc
new file mode 100644
index 0000000..cb5f864
--- /dev/null
+++ b/src/tint/ast/access.cc
@@ -0,0 +1,43 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/access.h"
+
+namespace tint {
+namespace ast {
+
+std::ostream& operator<<(std::ostream& out, Access access) {
+  switch (access) {
+    case ast::Access::kUndefined: {
+      out << "undefined";
+      break;
+    }
+    case ast::Access::kRead: {
+      out << "read";
+      break;
+    }
+    case ast::Access::kReadWrite: {
+      out << "read_write";
+      break;
+    }
+    case ast::Access::kWrite: {
+      out << "write";
+      break;
+    }
+  }
+  return out;
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/access.h b/src/tint/ast/access.h
new file mode 100644
index 0000000..67ad714
--- /dev/null
+++ b/src/tint/ast/access.h
@@ -0,0 +1,46 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_ACCESS_H_
+#define SRC_TINT_AST_ACCESS_H_
+
+#include <ostream>
+#include <string>
+
+namespace tint {
+namespace ast {
+
+/// The access control settings
+enum Access {
+  /// Not declared in the source
+  kUndefined = 0,
+  /// Read only
+  kRead,
+  /// Write only
+  kWrite,
+  /// Read write
+  kReadWrite,
+  // Last valid access mode
+  kLastValid = kReadWrite,
+};
+
+/// @param out the std::ostream to write to
+/// @param access the Access
+/// @return the std::ostream so calls can be chained
+std::ostream& operator<<(std::ostream& out, Access access);
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_ACCESS_H_
diff --git a/src/tint/ast/alias.cc b/src/tint/ast/alias.cc
new file mode 100644
index 0000000..d852667
--- /dev/null
+++ b/src/tint/ast/alias.cc
@@ -0,0 +1,45 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/alias.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::Alias);
+
+namespace tint {
+namespace ast {
+
+Alias::Alias(ProgramID pid,
+             const Source& src,
+             const Symbol& n,
+             const Type* subtype)
+    : Base(pid, src, n), type(subtype) {
+  TINT_ASSERT(AST, type);
+}
+
+Alias::Alias(Alias&&) = default;
+
+Alias::~Alias() = default;
+
+const Alias* Alias::Clone(CloneContext* ctx) const {
+  // Clone arguments outside of create() call to have deterministic ordering
+  auto src = ctx->Clone(source);
+  auto sym = ctx->Clone(name);
+  auto* ty = ctx->Clone(type);
+  return ctx->dst->create<Alias>(src, sym, ty);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/alias.h b/src/tint/ast/alias.h
new file mode 100644
index 0000000..c21b3e2
--- /dev/null
+++ b/src/tint/ast/alias.h
@@ -0,0 +1,54 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_ALIAS_H_
+#define SRC_TINT_AST_ALIAS_H_
+
+#include <string>
+
+#include "src/tint/ast/type_decl.h"
+
+namespace tint {
+namespace ast {
+
+/// A type alias type. Holds a name and pointer to another type.
+class Alias final : public Castable<Alias, TypeDecl> {
+ public:
+  /// Constructor
+  /// @param pid the identifier of the program that owns this node
+  /// @param src the source of this node
+  /// @param name the symbol for the alias
+  /// @param subtype the alias'd type
+  Alias(ProgramID pid,
+        const Source& src,
+        const Symbol& name,
+        const Type* subtype);
+  /// Move constructor
+  Alias(Alias&&);
+  /// Destructor
+  ~Alias() override;
+
+  /// Clones this type and all transitive types using the `CloneContext` `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned type
+  const Alias* Clone(CloneContext* ctx) const override;
+
+  /// the alias type
+  const Type* const type;
+};
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_ALIAS_H_
diff --git a/src/tint/ast/alias_test.cc b/src/tint/ast/alias_test.cc
new file mode 100644
index 0000000..db82082
--- /dev/null
+++ b/src/tint/ast/alias_test.cc
@@ -0,0 +1,45 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/alias.h"
+#include "src/tint/ast/access.h"
+#include "src/tint/ast/array.h"
+#include "src/tint/ast/bool.h"
+#include "src/tint/ast/f32.h"
+#include "src/tint/ast/i32.h"
+#include "src/tint/ast/matrix.h"
+#include "src/tint/ast/pointer.h"
+#include "src/tint/ast/sampler.h"
+#include "src/tint/ast/struct.h"
+#include "src/tint/ast/test_helper.h"
+#include "src/tint/ast/texture.h"
+#include "src/tint/ast/u32.h"
+#include "src/tint/ast/vector.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using AstAliasTest = TestHelper;
+
+TEST_F(AstAliasTest, Create) {
+  auto* u32 = create<U32>();
+  auto* a = Alias("a_type", u32);
+  EXPECT_EQ(a->name, Symbol(1, ID()));
+  EXPECT_EQ(a->type, u32);
+}
+
+}  // namespace
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/array.cc b/src/tint/ast/array.cc
new file mode 100644
index 0000000..99b8d56
--- /dev/null
+++ b/src/tint/ast/array.cc
@@ -0,0 +1,78 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/array.h"
+
+#include <cmath>
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::Array);
+
+namespace tint {
+namespace ast {
+
+namespace {
+// Returns the string representation of an array size expression.
+std::string SizeExprToString(const Expression* size,
+                             const SymbolTable& symbols) {
+  if (auto* ident = size->As<IdentifierExpression>()) {
+    return symbols.NameFor(ident->symbol);
+  }
+  if (auto* literal = size->As<IntLiteralExpression>()) {
+    return std::to_string(literal->ValueAsU32());
+  }
+  // This will never be exposed to the user as the Resolver will reject this
+  // expression for array size.
+  return "<invalid>";
+}
+}  // namespace
+
+Array::Array(ProgramID pid,
+             const Source& src,
+             const Type* subtype,
+             const Expression* cnt,
+             AttributeList attrs)
+    : Base(pid, src), type(subtype), count(cnt), attributes(attrs) {}
+
+Array::Array(Array&&) = default;
+
+Array::~Array() = default;
+
+std::string Array::FriendlyName(const SymbolTable& symbols) const {
+  std::ostringstream out;
+  for (auto* attr : attributes) {
+    if (auto* stride = attr->As<ast::StrideAttribute>()) {
+      out << "@stride(" << stride->stride << ") ";
+    }
+  }
+  out << "array<" << type->FriendlyName(symbols);
+  if (!IsRuntimeArray()) {
+    out << ", " << SizeExprToString(count, symbols);
+  }
+  out << ">";
+  return out.str();
+}
+
+const Array* Array::Clone(CloneContext* ctx) const {
+  // Clone arguments outside of create() call to have deterministic ordering
+  auto src = ctx->Clone(source);
+  auto* ty = ctx->Clone(type);
+  auto* cnt = ctx->Clone(count);
+  auto attrs = ctx->Clone(attributes);
+  return ctx->dst->create<Array>(src, ty, cnt, attrs);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/array.h b/src/tint/ast/array.h
new file mode 100644
index 0000000..413e4d3
--- /dev/null
+++ b/src/tint/ast/array.h
@@ -0,0 +1,75 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_ARRAY_H_
+#define SRC_TINT_AST_ARRAY_H_
+
+#include <string>
+
+#include "src/tint/ast/attribute.h"
+#include "src/tint/ast/type.h"
+
+namespace tint {
+namespace ast {
+
+// Forward declarations.
+class Expression;
+
+/// An array type. If size is zero then it is a runtime array.
+class Array final : public Castable<Array, Type> {
+ public:
+  /// Constructor
+  /// @param pid the identifier of the program that owns this node
+  /// @param src the source of this node
+  /// @param subtype the type of the array elements
+  /// @param count the number of elements in the array. nullptr represents a
+  /// runtime-sized array.
+  /// @param attributes the array attributes
+  Array(ProgramID pid,
+        const Source& src,
+        const Type* subtype,
+        const Expression* count,
+        AttributeList attributes);
+  /// Move constructor
+  Array(Array&&);
+  ~Array() override;
+
+  /// @returns true if this is a runtime array.
+  /// i.e. the size is determined at runtime
+  bool IsRuntimeArray() const { return count == nullptr; }
+
+  /// @param symbols the program's symbol table
+  /// @returns the name for this type that closely resembles how it would be
+  /// declared in WGSL.
+  std::string FriendlyName(const SymbolTable& symbols) const override;
+
+  /// Clones this type and all transitive types using the `CloneContext` `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned type
+  const Array* Clone(CloneContext* ctx) const override;
+
+  /// the array element type
+  const Type* const type;
+
+  /// the array size in elements, or nullptr for a runtime array
+  const Expression* const count;
+
+  /// the array attributes
+  const AttributeList attributes;
+};
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_ARRAY_H_
diff --git a/src/tint/ast/array_test.cc b/src/tint/ast/array_test.cc
new file mode 100644
index 0000000..ff97734
--- /dev/null
+++ b/src/tint/ast/array_test.cc
@@ -0,0 +1,71 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/array.h"
+
+#include "src/tint/ast/test_helper.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using AstArrayTest = TestHelper;
+
+TEST_F(AstArrayTest, CreateSizedArray) {
+  auto* u32 = create<U32>();
+  auto* count = Expr(3);
+  auto* arr = create<Array>(u32, count, AttributeList{});
+  EXPECT_EQ(arr->type, u32);
+  EXPECT_EQ(arr->count, count);
+  EXPECT_TRUE(arr->Is<Array>());
+  EXPECT_FALSE(arr->IsRuntimeArray());
+}
+
+TEST_F(AstArrayTest, CreateRuntimeArray) {
+  auto* u32 = create<U32>();
+  auto* arr = create<Array>(u32, nullptr, AttributeList{});
+  EXPECT_EQ(arr->type, u32);
+  EXPECT_EQ(arr->count, nullptr);
+  EXPECT_TRUE(arr->Is<Array>());
+  EXPECT_TRUE(arr->IsRuntimeArray());
+}
+
+TEST_F(AstArrayTest, FriendlyName_RuntimeSized) {
+  auto* i32 = create<I32>();
+  auto* arr = create<Array>(i32, nullptr, AttributeList{});
+  EXPECT_EQ(arr->FriendlyName(Symbols()), "array<i32>");
+}
+
+TEST_F(AstArrayTest, FriendlyName_LiteralSized) {
+  auto* i32 = create<I32>();
+  auto* arr = create<Array>(i32, Expr(5), AttributeList{});
+  EXPECT_EQ(arr->FriendlyName(Symbols()), "array<i32, 5>");
+}
+
+TEST_F(AstArrayTest, FriendlyName_ConstantSized) {
+  auto* i32 = create<I32>();
+  auto* arr = create<Array>(i32, Expr("size"), AttributeList{});
+  EXPECT_EQ(arr->FriendlyName(Symbols()), "array<i32, size>");
+}
+
+TEST_F(AstArrayTest, FriendlyName_WithStride) {
+  auto* i32 = create<I32>();
+  auto* arr =
+      create<Array>(i32, Expr(5), AttributeList{create<StrideAttribute>(32)});
+  EXPECT_EQ(arr->FriendlyName(Symbols()), "@stride(32) array<i32, 5>");
+}
+
+}  // namespace
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/assignment_statement.cc b/src/tint/ast/assignment_statement.cc
new file mode 100644
index 0000000..a2340b7
--- /dev/null
+++ b/src/tint/ast/assignment_statement.cc
@@ -0,0 +1,48 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/assignment_statement.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::AssignmentStatement);
+
+namespace tint {
+namespace ast {
+
+AssignmentStatement::AssignmentStatement(ProgramID pid,
+                                         const Source& src,
+                                         const Expression* l,
+                                         const Expression* r)
+    : Base(pid, src), lhs(l), rhs(r) {
+  TINT_ASSERT(AST, lhs);
+  TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, lhs, program_id);
+  TINT_ASSERT(AST, rhs);
+  TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, rhs, program_id);
+}
+
+AssignmentStatement::AssignmentStatement(AssignmentStatement&&) = default;
+
+AssignmentStatement::~AssignmentStatement() = default;
+
+const AssignmentStatement* AssignmentStatement::Clone(CloneContext* ctx) const {
+  // Clone arguments outside of create() call to have deterministic ordering
+  auto src = ctx->Clone(source);
+  auto* l = ctx->Clone(lhs);
+  auto* r = ctx->Clone(rhs);
+  return ctx->dst->create<AssignmentStatement>(src, l, r);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/assignment_statement.h b/src/tint/ast/assignment_statement.h
new file mode 100644
index 0000000..55e07b8
--- /dev/null
+++ b/src/tint/ast/assignment_statement.h
@@ -0,0 +1,57 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_ASSIGNMENT_STATEMENT_H_
+#define SRC_TINT_AST_ASSIGNMENT_STATEMENT_H_
+
+#include "src/tint/ast/expression.h"
+#include "src/tint/ast/statement.h"
+
+namespace tint {
+namespace ast {
+
+/// An assignment statement
+class AssignmentStatement final
+    : public Castable<AssignmentStatement, Statement> {
+ public:
+  /// Constructor
+  /// @param program_id the identifier of the program that owns this node
+  /// @param source the assignment statement source
+  /// @param lhs the left side of the expression
+  /// @param rhs the right side of the expression
+  AssignmentStatement(ProgramID program_id,
+                      const Source& source,
+                      const Expression* lhs,
+                      const Expression* rhs);
+  /// Move constructor
+  AssignmentStatement(AssignmentStatement&&);
+  ~AssignmentStatement() override;
+
+  /// Clones this node and all transitive child nodes using the `CloneContext`
+  /// `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned node
+  const AssignmentStatement* Clone(CloneContext* ctx) const override;
+
+  /// left side expression
+  const Expression* const lhs;
+
+  /// right side expression
+  const Expression* const rhs;
+};
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_ASSIGNMENT_STATEMENT_H_
diff --git a/src/tint/ast/assignment_statement_test.cc b/src/tint/ast/assignment_statement_test.cc
new file mode 100644
index 0000000..4a41a7c
--- /dev/null
+++ b/src/tint/ast/assignment_statement_test.cc
@@ -0,0 +1,94 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/assignment_statement.h"
+
+#include "gtest/gtest-spi.h"
+#include "src/tint/ast/test_helper.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using AssignmentStatementTest = TestHelper;
+
+TEST_F(AssignmentStatementTest, Creation) {
+  auto* lhs = Expr("lhs");
+  auto* rhs = Expr("rhs");
+
+  auto* stmt = create<AssignmentStatement>(lhs, rhs);
+  EXPECT_EQ(stmt->lhs, lhs);
+  EXPECT_EQ(stmt->rhs, rhs);
+}
+
+TEST_F(AssignmentStatementTest, CreationWithSource) {
+  auto* lhs = Expr("lhs");
+  auto* rhs = Expr("rhs");
+
+  auto* stmt =
+      create<AssignmentStatement>(Source{Source::Location{20, 2}}, lhs, rhs);
+  auto src = stmt->source;
+  EXPECT_EQ(src.range.begin.line, 20u);
+  EXPECT_EQ(src.range.begin.column, 2u);
+}
+
+TEST_F(AssignmentStatementTest, IsAssign) {
+  auto* lhs = Expr("lhs");
+  auto* rhs = Expr("rhs");
+
+  auto* stmt = create<AssignmentStatement>(lhs, rhs);
+  EXPECT_TRUE(stmt->Is<AssignmentStatement>());
+}
+
+TEST_F(AssignmentStatementTest, Assert_Null_LHS) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b;
+        b.create<AssignmentStatement>(nullptr, b.Expr(1));
+      },
+      "internal compiler error");
+}
+
+TEST_F(AssignmentStatementTest, Assert_Null_RHS) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b;
+        b.create<AssignmentStatement>(b.Expr(1), nullptr);
+      },
+      "internal compiler error");
+}
+
+TEST_F(AssignmentStatementTest, Assert_DifferentProgramID_LHS) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b1;
+        ProgramBuilder b2;
+        b1.create<AssignmentStatement>(b2.Expr("lhs"), b1.Expr("rhs"));
+      },
+      "internal compiler error");
+}
+
+TEST_F(AssignmentStatementTest, Assert_DifferentProgramID_RHS) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b1;
+        ProgramBuilder b2;
+        b1.create<AssignmentStatement>(b1.Expr("lhs"), b2.Expr("rhs"));
+      },
+      "internal compiler error");
+}
+
+}  // namespace
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/ast_type.cc b/src/tint/ast/ast_type.cc
new file mode 100644
index 0000000..cb01679
--- /dev/null
+++ b/src/tint/ast/ast_type.cc
@@ -0,0 +1,41 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/type.h"
+
+#include "src/tint/ast/alias.h"
+#include "src/tint/ast/bool.h"
+#include "src/tint/ast/f32.h"
+#include "src/tint/ast/i32.h"
+#include "src/tint/ast/matrix.h"
+#include "src/tint/ast/pointer.h"
+#include "src/tint/ast/sampler.h"
+#include "src/tint/ast/texture.h"
+#include "src/tint/ast/u32.h"
+#include "src/tint/ast/vector.h"
+#include "src/tint/symbol_table.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::Type);
+
+namespace tint {
+namespace ast {
+
+Type::Type(ProgramID pid, const Source& src) : Base(pid, src) {}
+
+Type::Type(Type&&) = default;
+
+Type::~Type() = default;
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/atomic.cc b/src/tint/ast/atomic.cc
new file mode 100644
index 0000000..addefee
--- /dev/null
+++ b/src/tint/ast/atomic.cc
@@ -0,0 +1,45 @@
+// Copyright 2021 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/atomic.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::Atomic);
+
+namespace tint {
+namespace ast {
+
+Atomic::Atomic(ProgramID pid, const Source& src, const Type* const subtype)
+    : Base(pid, src), type(subtype) {}
+
+std::string Atomic::FriendlyName(const SymbolTable& symbols) const {
+  std::ostringstream out;
+  out << "atomic<" << type->FriendlyName(symbols) << ">";
+  return out.str();
+}
+
+Atomic::Atomic(Atomic&&) = default;
+
+Atomic::~Atomic() = default;
+
+const Atomic* Atomic::Clone(CloneContext* ctx) const {
+  // Clone arguments outside of create() call to have deterministic ordering
+  auto src = ctx->Clone(source);
+  auto* ty = ctx->Clone(type);
+  return ctx->dst->create<Atomic>(src, ty);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/atomic.h b/src/tint/ast/atomic.h
new file mode 100644
index 0000000..d5e1b9b
--- /dev/null
+++ b/src/tint/ast/atomic.h
@@ -0,0 +1,54 @@
+// Copyright 2021 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_ATOMIC_H_
+#define SRC_TINT_AST_ATOMIC_H_
+
+#include <string>
+
+#include "src/tint/ast/type.h"
+
+namespace tint {
+namespace ast {
+
+/// An atomic type.
+class Atomic final : public Castable<Atomic, Type> {
+ public:
+  /// Constructor
+  /// @param pid the identifier of the program that owns this node
+  /// @param src the source of this node
+  /// @param subtype the pointee type
+  Atomic(ProgramID pid, const Source& src, const Type* const subtype);
+  /// Move constructor
+  Atomic(Atomic&&);
+  ~Atomic() override;
+
+  /// @param symbols the program's symbol table
+  /// @returns the name for this type that closely resembles how it would be
+  /// declared in WGSL.
+  std::string FriendlyName(const SymbolTable& symbols) const override;
+
+  /// Clones this type and all transitive types using the `CloneContext` `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned type
+  const Atomic* Clone(CloneContext* ctx) const override;
+
+  /// the pointee type
+  const Type* const type;
+};
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_ATOMIC_H_
diff --git a/src/tint/ast/atomic_test.cc b/src/tint/ast/atomic_test.cc
new file mode 100644
index 0000000..636654b
--- /dev/null
+++ b/src/tint/ast/atomic_test.cc
@@ -0,0 +1,40 @@
+// Copyright 2021 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/atomic.h"
+
+#include "src/tint/ast/i32.h"
+#include "src/tint/ast/test_helper.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using AstAtomicTest = TestHelper;
+
+TEST_F(AstAtomicTest, Creation) {
+  auto* i32 = create<I32>();
+  auto* p = create<Atomic>(i32);
+  EXPECT_EQ(p->type, i32);
+}
+
+TEST_F(AstAtomicTest, FriendlyName) {
+  auto* i32 = create<I32>();
+  auto* p = create<Atomic>(i32);
+  EXPECT_EQ(p->FriendlyName(Symbols()), "atomic<i32>");
+}
+
+}  // namespace
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/attribute.cc b/src/tint/ast/attribute.cc
new file mode 100644
index 0000000..87ad3f1
--- /dev/null
+++ b/src/tint/ast/attribute.cc
@@ -0,0 +1,25 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/attribute.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::Attribute);
+
+namespace tint {
+namespace ast {
+
+Attribute::~Attribute() = default;
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/attribute.h b/src/tint/ast/attribute.h
new file mode 100644
index 0000000..d336727
--- /dev/null
+++ b/src/tint/ast/attribute.h
@@ -0,0 +1,71 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_ATTRIBUTE_H_
+#define SRC_TINT_AST_ATTRIBUTE_H_
+
+#include <string>
+#include <vector>
+
+#include "src/tint/ast/node.h"
+
+namespace tint {
+namespace ast {
+
+/// The base class for all attributes
+class Attribute : public Castable<Attribute, Node> {
+ public:
+  ~Attribute() override;
+
+  /// @returns the WGSL name for the attribute
+  virtual std::string Name() const = 0;
+
+ protected:
+  /// Constructor
+  /// @param pid the identifier of the program that owns this node
+  /// @param src the source of this node
+  Attribute(ProgramID pid, const Source& src) : Base(pid, src) {}
+};
+
+/// A list of attributes
+using AttributeList = std::vector<const Attribute*>;
+
+/// @param attributes the list of attributes to search
+/// @returns true if `attributes` includes a attribute of type `T`
+template <typename T>
+bool HasAttribute(const AttributeList& attributes) {
+  for (auto* attr : attributes) {
+    if (attr->Is<T>()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/// @param attributes the list of attributes to search
+/// @returns a pointer to `T` from `attributes` if found, otherwise nullptr.
+template <typename T>
+const T* GetAttribute(const AttributeList& attributes) {
+  for (auto* attr : attributes) {
+    if (attr->Is<T>()) {
+      return attr->As<T>();
+    }
+  }
+  return nullptr;
+}
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_ATTRIBUTE_H_
diff --git a/src/tint/ast/binary_expression.cc b/src/tint/ast/binary_expression.cc
new file mode 100644
index 0000000..6b2be73
--- /dev/null
+++ b/src/tint/ast/binary_expression.cc
@@ -0,0 +1,50 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/binary_expression.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::BinaryExpression);
+
+namespace tint {
+namespace ast {
+
+BinaryExpression::BinaryExpression(ProgramID pid,
+                                   const Source& src,
+                                   BinaryOp o,
+                                   const Expression* l,
+                                   const Expression* r)
+    : Base(pid, src), op(o), lhs(l), rhs(r) {
+  TINT_ASSERT(AST, lhs);
+  TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, lhs, program_id);
+  TINT_ASSERT(AST, rhs);
+  TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, rhs, program_id);
+  TINT_ASSERT(AST, op != BinaryOp::kNone);
+}
+
+BinaryExpression::BinaryExpression(BinaryExpression&&) = default;
+
+BinaryExpression::~BinaryExpression() = default;
+
+const BinaryExpression* BinaryExpression::Clone(CloneContext* ctx) const {
+  // Clone arguments outside of create() call to have deterministic ordering
+  auto src = ctx->Clone(source);
+  auto* l = ctx->Clone(lhs);
+  auto* r = ctx->Clone(rhs);
+  return ctx->dst->create<BinaryExpression>(src, op, l, r);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/binary_expression.h b/src/tint/ast/binary_expression.h
new file mode 100644
index 0000000..bcacdd4
--- /dev/null
+++ b/src/tint/ast/binary_expression.h
@@ -0,0 +1,264 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_BINARY_EXPRESSION_H_
+#define SRC_TINT_AST_BINARY_EXPRESSION_H_
+
+#include "src/tint/ast/expression.h"
+
+namespace tint {
+namespace ast {
+
+/// The operator type
+enum class BinaryOp {
+  kNone = 0,
+  kAnd,  // &
+  kOr,   // |
+  kXor,
+  kLogicalAnd,  // &&
+  kLogicalOr,   // ||
+  kEqual,
+  kNotEqual,
+  kLessThan,
+  kGreaterThan,
+  kLessThanEqual,
+  kGreaterThanEqual,
+  kShiftLeft,
+  kShiftRight,
+  kAdd,
+  kSubtract,
+  kMultiply,
+  kDivide,
+  kModulo,
+};
+
+/// An binary expression
+class BinaryExpression final : public Castable<BinaryExpression, Expression> {
+ public:
+  /// Constructor
+  /// @param program_id the identifier of the program that owns this node
+  /// @param source the binary expression source
+  /// @param op the operation type
+  /// @param lhs the left side of the expression
+  /// @param rhs the right side of the expression
+  BinaryExpression(ProgramID program_id,
+                   const Source& source,
+                   BinaryOp op,
+                   const Expression* lhs,
+                   const Expression* rhs);
+  /// Move constructor
+  BinaryExpression(BinaryExpression&&);
+  ~BinaryExpression() override;
+
+  /// @returns true if the op is and
+  bool IsAnd() const { return op == BinaryOp::kAnd; }
+  /// @returns true if the op is or
+  bool IsOr() const { return op == BinaryOp::kOr; }
+  /// @returns true if the op is xor
+  bool IsXor() const { return op == BinaryOp::kXor; }
+  /// @returns true if the op is logical and
+  bool IsLogicalAnd() const { return op == BinaryOp::kLogicalAnd; }
+  /// @returns true if the op is logical or
+  bool IsLogicalOr() const { return op == BinaryOp::kLogicalOr; }
+  /// @returns true if the op is equal
+  bool IsEqual() const { return op == BinaryOp::kEqual; }
+  /// @returns true if the op is not equal
+  bool IsNotEqual() const { return op == BinaryOp::kNotEqual; }
+  /// @returns true if the op is less than
+  bool IsLessThan() const { return op == BinaryOp::kLessThan; }
+  /// @returns true if the op is greater than
+  bool IsGreaterThan() const { return op == BinaryOp::kGreaterThan; }
+  /// @returns true if the op is less than equal
+  bool IsLessThanEqual() const { return op == BinaryOp::kLessThanEqual; }
+  /// @returns true if the op is greater than equal
+  bool IsGreaterThanEqual() const { return op == BinaryOp::kGreaterThanEqual; }
+  /// @returns true if the op is shift left
+  bool IsShiftLeft() const { return op == BinaryOp::kShiftLeft; }
+  /// @returns true if the op is shift right
+  bool IsShiftRight() const { return op == BinaryOp::kShiftRight; }
+  /// @returns true if the op is add
+  bool IsAdd() const { return op == BinaryOp::kAdd; }
+  /// @returns true if the op is subtract
+  bool IsSubtract() const { return op == BinaryOp::kSubtract; }
+  /// @returns true if the op is multiply
+  bool IsMultiply() const { return op == BinaryOp::kMultiply; }
+  /// @returns true if the op is divide
+  bool IsDivide() const { return op == BinaryOp::kDivide; }
+  /// @returns true if the op is modulo
+  bool IsModulo() const { return op == BinaryOp::kModulo; }
+  /// @returns true if the op is an arithmetic operation
+  bool IsArithmetic() const;
+  /// @returns true if the op is a comparison operation
+  bool IsComparison() const;
+  /// @returns true if the op is a bitwise operation
+  bool IsBitwise() const;
+  /// @returns true if the op is a bit shift operation
+  bool IsBitshift() const;
+  /// @returns true if the op is a logical expression
+  bool IsLogical() const;
+
+  /// Clones this node and all transitive child nodes using the `CloneContext`
+  /// `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned node
+  const BinaryExpression* Clone(CloneContext* ctx) const override;
+
+  /// the binary op type
+  const BinaryOp op;
+  /// the left side expression
+  const Expression* const lhs;
+  /// the right side expression
+  const Expression* const rhs;
+};
+
+/// @param op the operator
+/// @returns true if the op is an arithmetic operation
+inline bool IsArithmetic(BinaryOp op) {
+  switch (op) {
+    case ast::BinaryOp::kAdd:
+    case ast::BinaryOp::kSubtract:
+    case ast::BinaryOp::kMultiply:
+    case ast::BinaryOp::kDivide:
+    case ast::BinaryOp::kModulo:
+      return true;
+    default:
+      return false;
+  }
+}
+
+/// @param op the operator
+/// @returns true if the op is a comparison operation
+inline bool IsComparison(BinaryOp op) {
+  switch (op) {
+    case ast::BinaryOp::kEqual:
+    case ast::BinaryOp::kNotEqual:
+    case ast::BinaryOp::kLessThan:
+    case ast::BinaryOp::kLessThanEqual:
+    case ast::BinaryOp::kGreaterThan:
+    case ast::BinaryOp::kGreaterThanEqual:
+      return true;
+    default:
+      return false;
+  }
+}
+
+/// @param op the operator
+/// @returns true if the op is a bitwise operation
+inline bool IsBitwise(BinaryOp op) {
+  switch (op) {
+    case ast::BinaryOp::kAnd:
+    case ast::BinaryOp::kOr:
+    case ast::BinaryOp::kXor:
+      return true;
+    default:
+      return false;
+  }
+}
+
+/// @param op the operator
+/// @returns true if the op is a bit shift operation
+inline bool IsBitshift(BinaryOp op) {
+  switch (op) {
+    case ast::BinaryOp::kShiftLeft:
+    case ast::BinaryOp::kShiftRight:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline bool BinaryExpression::IsLogical() const {
+  switch (op) {
+    case ast::BinaryOp::kLogicalAnd:
+    case ast::BinaryOp::kLogicalOr:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline bool BinaryExpression::IsArithmetic() const {
+  return ast::IsArithmetic(op);
+}
+
+inline bool BinaryExpression::IsComparison() const {
+  return ast::IsComparison(op);
+}
+
+inline bool BinaryExpression::IsBitwise() const {
+  return ast::IsBitwise(op);
+}
+
+inline bool BinaryExpression::IsBitshift() const {
+  return ast::IsBitshift(op);
+}
+
+/// @returns the human readable name of the given BinaryOp
+/// @param op the BinaryOp
+constexpr const char* FriendlyName(BinaryOp op) {
+  switch (op) {
+    case BinaryOp::kNone:
+      return "none";
+    case BinaryOp::kAnd:
+      return "and";
+    case BinaryOp::kOr:
+      return "or";
+    case BinaryOp::kXor:
+      return "xor";
+    case BinaryOp::kLogicalAnd:
+      return "logical_and";
+    case BinaryOp::kLogicalOr:
+      return "logical_or";
+    case BinaryOp::kEqual:
+      return "equal";
+    case BinaryOp::kNotEqual:
+      return "not_equal";
+    case BinaryOp::kLessThan:
+      return "less_than";
+    case BinaryOp::kGreaterThan:
+      return "greater_than";
+    case BinaryOp::kLessThanEqual:
+      return "less_than_equal";
+    case BinaryOp::kGreaterThanEqual:
+      return "greater_than_equal";
+    case BinaryOp::kShiftLeft:
+      return "shift_left";
+    case BinaryOp::kShiftRight:
+      return "shift_right";
+    case BinaryOp::kAdd:
+      return "add";
+    case BinaryOp::kSubtract:
+      return "subtract";
+    case BinaryOp::kMultiply:
+      return "multiply";
+    case BinaryOp::kDivide:
+      return "divide";
+    case BinaryOp::kModulo:
+      return "modulo";
+  }
+  return "INVALID";
+}
+
+/// @param out the std::ostream to write to
+/// @param op the BinaryOp
+/// @return the std::ostream so calls can be chained
+inline std::ostream& operator<<(std::ostream& out, BinaryOp op) {
+  out << FriendlyName(op);
+  return out;
+}
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_BINARY_EXPRESSION_H_
diff --git a/src/tint/ast/binary_expression_test.cc b/src/tint/ast/binary_expression_test.cc
new file mode 100644
index 0000000..20b8f8f
--- /dev/null
+++ b/src/tint/ast/binary_expression_test.cc
@@ -0,0 +1,95 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest-spi.h"
+#include "src/tint/ast/test_helper.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using BinaryExpressionTest = TestHelper;
+
+TEST_F(BinaryExpressionTest, Creation) {
+  auto* lhs = Expr("lhs");
+  auto* rhs = Expr("rhs");
+
+  auto* r = create<BinaryExpression>(BinaryOp::kEqual, lhs, rhs);
+  EXPECT_EQ(r->lhs, lhs);
+  EXPECT_EQ(r->rhs, rhs);
+  EXPECT_EQ(r->op, BinaryOp::kEqual);
+}
+
+TEST_F(BinaryExpressionTest, Creation_WithSource) {
+  auto* lhs = Expr("lhs");
+  auto* rhs = Expr("rhs");
+
+  auto* r = create<BinaryExpression>(Source{Source::Location{20, 2}},
+                                     BinaryOp::kEqual, lhs, rhs);
+  auto src = r->source;
+  EXPECT_EQ(src.range.begin.line, 20u);
+  EXPECT_EQ(src.range.begin.column, 2u);
+}
+
+TEST_F(BinaryExpressionTest, IsBinary) {
+  auto* lhs = Expr("lhs");
+  auto* rhs = Expr("rhs");
+
+  auto* r = create<BinaryExpression>(BinaryOp::kEqual, lhs, rhs);
+  EXPECT_TRUE(r->Is<BinaryExpression>());
+}
+
+TEST_F(BinaryExpressionTest, Assert_Null_LHS) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b;
+        b.create<BinaryExpression>(BinaryOp::kEqual, nullptr, b.Expr("rhs"));
+      },
+      "internal compiler error");
+}
+
+TEST_F(BinaryExpressionTest, Assert_Null_RHS) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b;
+        b.create<BinaryExpression>(BinaryOp::kEqual, b.Expr("lhs"), nullptr);
+      },
+      "internal compiler error");
+}
+
+TEST_F(BinaryExpressionTest, Assert_DifferentProgramID_LHS) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b1;
+        ProgramBuilder b2;
+        b1.create<BinaryExpression>(BinaryOp::kEqual, b2.Expr("lhs"),
+                                    b1.Expr("rhs"));
+      },
+      "internal compiler error");
+}
+
+TEST_F(BinaryExpressionTest, Assert_DifferentProgramID_RHS) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b1;
+        ProgramBuilder b2;
+        b1.create<BinaryExpression>(BinaryOp::kEqual, b1.Expr("lhs"),
+                                    b2.Expr("rhs"));
+      },
+      "internal compiler error");
+}
+
+}  // namespace
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/binding_attribute.cc b/src/tint/ast/binding_attribute.cc
new file mode 100644
index 0000000..bc1a74a
--- /dev/null
+++ b/src/tint/ast/binding_attribute.cc
@@ -0,0 +1,44 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/binding_attribute.h"
+
+#include <string>
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::BindingAttribute);
+
+namespace tint {
+namespace ast {
+
+BindingAttribute::BindingAttribute(ProgramID pid,
+                                   const Source& src,
+                                   uint32_t val)
+    : Base(pid, src), value(val) {}
+
+BindingAttribute::~BindingAttribute() = default;
+
+std::string BindingAttribute::Name() const {
+  return "binding";
+}
+
+const BindingAttribute* BindingAttribute::Clone(CloneContext* ctx) const {
+  // Clone arguments outside of create() call to have deterministic ordering
+  auto src = ctx->Clone(source);
+  return ctx->dst->create<BindingAttribute>(src, value);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/binding_attribute.h b/src/tint/ast/binding_attribute.h
new file mode 100644
index 0000000..8f3214d
--- /dev/null
+++ b/src/tint/ast/binding_attribute.h
@@ -0,0 +1,51 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_BINDING_ATTRIBUTE_H_
+#define SRC_TINT_AST_BINDING_ATTRIBUTE_H_
+
+#include <string>
+
+#include "src/tint/ast/attribute.h"
+
+namespace tint {
+namespace ast {
+
+/// A binding attribute
+class BindingAttribute final : public Castable<BindingAttribute, Attribute> {
+ public:
+  /// Constructor
+  /// @param pid the identifier of the program that owns this node
+  /// @param src the source of this node
+  /// @param value the binding value
+  BindingAttribute(ProgramID pid, const Source& src, uint32_t value);
+  ~BindingAttribute() override;
+
+  /// @returns the WGSL name for the attribute
+  std::string Name() const override;
+
+  /// Clones this node and all transitive child nodes using the `CloneContext`
+  /// `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned node
+  const BindingAttribute* Clone(CloneContext* ctx) const override;
+
+  /// the binding value
+  const uint32_t value;
+};
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_BINDING_ATTRIBUTE_H_
diff --git a/src/tint/ast/binding_attribute_test.cc b/src/tint/ast/binding_attribute_test.cc
new file mode 100644
index 0000000..c4c7e39
--- /dev/null
+++ b/src/tint/ast/binding_attribute_test.cc
@@ -0,0 +1,30 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/test_helper.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using BindingAttributeTest = TestHelper;
+
+TEST_F(BindingAttributeTest, Creation) {
+  auto* d = create<BindingAttribute>(2);
+  EXPECT_EQ(2u, d->value);
+}
+
+}  // namespace
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/bitcast_expression.cc b/src/tint/ast/bitcast_expression.cc
new file mode 100644
index 0000000..8de3afc
--- /dev/null
+++ b/src/tint/ast/bitcast_expression.cc
@@ -0,0 +1,46 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/bitcast_expression.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::BitcastExpression);
+
+namespace tint {
+namespace ast {
+
+BitcastExpression::BitcastExpression(ProgramID pid,
+                                     const Source& src,
+                                     const Type* t,
+                                     const Expression* e)
+    : Base(pid, src), type(t), expr(e) {
+  TINT_ASSERT(AST, type);
+  TINT_ASSERT(AST, expr);
+  TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, expr, program_id);
+}
+
+BitcastExpression::BitcastExpression(BitcastExpression&&) = default;
+BitcastExpression::~BitcastExpression() = default;
+
+const BitcastExpression* BitcastExpression::Clone(CloneContext* ctx) const {
+  // Clone arguments outside of create() call to have deterministic ordering
+  auto src = ctx->Clone(source);
+  auto* t = ctx->Clone(type);
+  auto* e = ctx->Clone(expr);
+  return ctx->dst->create<BitcastExpression>(src, t, e);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/bitcast_expression.h b/src/tint/ast/bitcast_expression.h
new file mode 100644
index 0000000..e16ae22
--- /dev/null
+++ b/src/tint/ast/bitcast_expression.h
@@ -0,0 +1,57 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_BITCAST_EXPRESSION_H_
+#define SRC_TINT_AST_BITCAST_EXPRESSION_H_
+
+#include "src/tint/ast/expression.h"
+
+namespace tint {
+namespace ast {
+
+// Forward declaration
+class Type;
+
+/// A bitcast expression
+class BitcastExpression final : public Castable<BitcastExpression, Expression> {
+ public:
+  /// Constructor
+  /// @param program_id the identifier of the program that owns this node
+  /// @param source the bitcast expression source
+  /// @param type the type
+  /// @param expr the expr
+  BitcastExpression(ProgramID program_id,
+                    const Source& source,
+                    const Type* type,
+                    const Expression* expr);
+  /// Move constructor
+  BitcastExpression(BitcastExpression&&);
+  ~BitcastExpression() override;
+
+  /// Clones this node and all transitive child nodes using the `CloneContext`
+  /// `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned node
+  const BitcastExpression* Clone(CloneContext* ctx) const override;
+
+  /// the target cast type
+  const Type* const type;
+  /// the expression
+  const Expression* const expr;
+};
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_BITCAST_EXPRESSION_H_
diff --git a/src/tint/ast/bitcast_expression_test.cc b/src/tint/ast/bitcast_expression_test.cc
new file mode 100644
index 0000000..5803003
--- /dev/null
+++ b/src/tint/ast/bitcast_expression_test.cc
@@ -0,0 +1,81 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/bitcast_expression.h"
+
+#include "gtest/gtest-spi.h"
+#include "src/tint/ast/test_helper.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using BitcastExpressionTest = TestHelper;
+
+TEST_F(BitcastExpressionTest, Create) {
+  auto* expr = Expr("expr");
+
+  auto* exp = create<BitcastExpression>(ty.f32(), expr);
+  EXPECT_TRUE(exp->type->Is<ast::F32>());
+  ASSERT_EQ(exp->expr, expr);
+}
+
+TEST_F(BitcastExpressionTest, CreateWithSource) {
+  auto* expr = Expr("expr");
+
+  auto* exp = create<BitcastExpression>(Source{Source::Location{20, 2}},
+                                        ty.f32(), expr);
+  auto src = exp->source;
+  EXPECT_EQ(src.range.begin.line, 20u);
+  EXPECT_EQ(src.range.begin.column, 2u);
+}
+
+TEST_F(BitcastExpressionTest, IsBitcast) {
+  auto* expr = Expr("expr");
+
+  auto* exp = create<BitcastExpression>(ty.f32(), expr);
+  EXPECT_TRUE(exp->Is<BitcastExpression>());
+}
+
+TEST_F(BitcastExpressionTest, Assert_Null_Type) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b;
+        b.create<BitcastExpression>(nullptr, b.Expr("idx"));
+      },
+      "internal compiler error");
+}
+
+TEST_F(BitcastExpressionTest, Assert_Null_Expr) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b;
+        b.create<BitcastExpression>(b.ty.f32(), nullptr);
+      },
+      "internal compiler error");
+}
+
+TEST_F(BitcastExpressionTest, Assert_DifferentProgramID_Expr) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b1;
+        ProgramBuilder b2;
+        b1.create<BitcastExpression>(b1.ty.f32(), b2.Expr("idx"));
+      },
+      "internal compiler error");
+}
+
+}  // namespace
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/block_statement.cc b/src/tint/ast/block_statement.cc
new file mode 100644
index 0000000..5a7dcba
--- /dev/null
+++ b/src/tint/ast/block_statement.cc
@@ -0,0 +1,46 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/block_statement.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::BlockStatement);
+
+namespace tint {
+namespace ast {
+
+BlockStatement::BlockStatement(ProgramID pid,
+                               const Source& src,
+                               const StatementList& stmts)
+    : Base(pid, src), statements(std::move(stmts)) {
+  for (auto* stmt : statements) {
+    TINT_ASSERT(AST, stmt);
+    TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, stmt, program_id);
+  }
+}
+
+BlockStatement::BlockStatement(BlockStatement&&) = default;
+
+BlockStatement::~BlockStatement() = default;
+
+const BlockStatement* BlockStatement::Clone(CloneContext* ctx) const {
+  // Clone arguments outside of create() call to have deterministic ordering
+  auto src = ctx->Clone(source);
+  auto stmts = ctx->Clone(statements);
+  return ctx->dst->create<BlockStatement>(src, stmts);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/block_statement.h b/src/tint/ast/block_statement.h
new file mode 100644
index 0000000..31b2ef1
--- /dev/null
+++ b/src/tint/ast/block_statement.h
@@ -0,0 +1,60 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_BLOCK_STATEMENT_H_
+#define SRC_TINT_AST_BLOCK_STATEMENT_H_
+
+#include <utility>
+
+#include "src/tint/ast/statement.h"
+
+namespace tint {
+namespace ast {
+
+/// A block statement
+class BlockStatement final : public Castable<BlockStatement, Statement> {
+ public:
+  /// Constructor
+  /// @param program_id the identifier of the program that owns this node
+  /// @param source the block statement source
+  /// @param statements the statements
+  BlockStatement(ProgramID program_id,
+                 const Source& source,
+                 const StatementList& statements);
+  /// Move constructor
+  BlockStatement(BlockStatement&&);
+  ~BlockStatement() override;
+
+  /// @returns true if the block has no statements
+  bool Empty() const { return statements.empty(); }
+
+  /// @returns the last statement in the block or nullptr if block empty
+  const Statement* Last() const {
+    return statements.empty() ? nullptr : statements.back();
+  }
+
+  /// Clones this node and all transitive child nodes using the `CloneContext`
+  /// `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned node
+  const BlockStatement* Clone(CloneContext* ctx) const override;
+
+  /// the statement list
+  const StatementList statements;
+};
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_BLOCK_STATEMENT_H_
diff --git a/src/tint/ast/block_statement_test.cc b/src/tint/ast/block_statement_test.cc
new file mode 100644
index 0000000..1cc8f38
--- /dev/null
+++ b/src/tint/ast/block_statement_test.cc
@@ -0,0 +1,71 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest-spi.h"
+#include "src/tint/ast/discard_statement.h"
+#include "src/tint/ast/if_statement.h"
+#include "src/tint/ast/test_helper.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using BlockStatementTest = TestHelper;
+
+TEST_F(BlockStatementTest, Creation) {
+  auto* d = create<DiscardStatement>();
+  auto* ptr = d;
+
+  auto* b = create<BlockStatement>(StatementList{d});
+
+  ASSERT_EQ(b->statements.size(), 1u);
+  EXPECT_EQ(b->statements[0], ptr);
+}
+
+TEST_F(BlockStatementTest, Creation_WithSource) {
+  auto* b = create<BlockStatement>(Source{Source::Location{20, 2}},
+                                   ast::StatementList{});
+  auto src = b->source;
+  EXPECT_EQ(src.range.begin.line, 20u);
+  EXPECT_EQ(src.range.begin.column, 2u);
+}
+
+TEST_F(BlockStatementTest, IsBlock) {
+  auto* b = create<BlockStatement>(ast::StatementList{});
+  EXPECT_TRUE(b->Is<BlockStatement>());
+}
+
+TEST_F(BlockStatementTest, Assert_Null_Statement) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b;
+        b.create<BlockStatement>(ast::StatementList{nullptr});
+      },
+      "internal compiler error");
+}
+
+TEST_F(BlockStatementTest, Assert_DifferentProgramID_Statement) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b1;
+        ProgramBuilder b2;
+        b1.create<BlockStatement>(
+            ast::StatementList{b2.create<DiscardStatement>()});
+      },
+      "internal compiler error");
+}
+
+}  // namespace
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/bool.cc b/src/tint/ast/bool.cc
new file mode 100644
index 0000000..79596a3
--- /dev/null
+++ b/src/tint/ast/bool.cc
@@ -0,0 +1,40 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/bool.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::Bool);
+
+namespace tint {
+namespace ast {
+
+Bool::Bool(ProgramID pid, const Source& src) : Base(pid, src) {}
+
+Bool::Bool(Bool&&) = default;
+
+Bool::~Bool() = default;
+
+std::string Bool::FriendlyName(const SymbolTable&) const {
+  return "bool";
+}
+
+const Bool* Bool::Clone(CloneContext* ctx) const {
+  auto src = ctx->Clone(source);
+  return ctx->dst->create<Bool>(src);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/bool.h b/src/tint/ast/bool.h
new file mode 100644
index 0000000..fa326da
--- /dev/null
+++ b/src/tint/ast/bool.h
@@ -0,0 +1,56 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_BOOL_H_
+#define SRC_TINT_AST_BOOL_H_
+
+#include <string>
+
+#include "src/tint/ast/type.h"
+
+// X11 likes to #define Bool leading to confusing error messages.
+// If its defined, undefine it.
+#ifdef Bool
+#undef Bool
+#endif
+
+namespace tint {
+namespace ast {
+
+/// A boolean type
+class Bool final : public Castable<Bool, Type> {
+ public:
+  /// Constructor
+  /// @param pid the identifier of the program that owns this node
+  /// @param src the source of this node
+  Bool(ProgramID pid, const Source& src);
+  /// Move constructor
+  Bool(Bool&&);
+  ~Bool() override;
+
+  /// @param symbols the program's symbol table
+  /// @returns the name for this type that closely resembles how it would be
+  /// declared in WGSL.
+  std::string FriendlyName(const SymbolTable& symbols) const override;
+
+  /// Clones this type and all transitive types using the `CloneContext` `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned type
+  const Bool* Clone(CloneContext* ctx) const override;
+};
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_BOOL_H_
diff --git a/src/tint/ast/bool_literal_expression.cc b/src/tint/ast/bool_literal_expression.cc
new file mode 100644
index 0000000..5c961b4
--- /dev/null
+++ b/src/tint/ast/bool_literal_expression.cc
@@ -0,0 +1,39 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/bool_literal_expression.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::BoolLiteralExpression);
+
+namespace tint {
+namespace ast {
+
+BoolLiteralExpression::BoolLiteralExpression(ProgramID pid,
+                                             const Source& src,
+                                             bool val)
+    : Base(pid, src), value(val) {}
+
+BoolLiteralExpression::~BoolLiteralExpression() = default;
+
+const BoolLiteralExpression* BoolLiteralExpression::Clone(
+    CloneContext* ctx) const {
+  // Clone arguments outside of create() call to have deterministic ordering
+  auto src = ctx->Clone(source);
+  return ctx->dst->create<BoolLiteralExpression>(src, value);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/bool_literal_expression.h b/src/tint/ast/bool_literal_expression.h
new file mode 100644
index 0000000..421453e
--- /dev/null
+++ b/src/tint/ast/bool_literal_expression.h
@@ -0,0 +1,49 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_BOOL_LITERAL_EXPRESSION_H_
+#define SRC_TINT_AST_BOOL_LITERAL_EXPRESSION_H_
+
+#include <string>
+
+#include "src/tint/ast/literal_expression.h"
+
+namespace tint {
+namespace ast {
+
+/// A boolean literal
+class BoolLiteralExpression final
+    : public Castable<BoolLiteralExpression, LiteralExpression> {
+ public:
+  /// Constructor
+  /// @param pid the identifier of the program that owns this node
+  /// @param src the source of this node
+  /// @param value the bool literals value
+  BoolLiteralExpression(ProgramID pid, const Source& src, bool value);
+  ~BoolLiteralExpression() override;
+
+  /// Clones this node and all transitive child nodes using the `CloneContext`
+  /// `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned node
+  const BoolLiteralExpression* Clone(CloneContext* ctx) const override;
+
+  /// The boolean literal value
+  const bool value;
+};
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_BOOL_LITERAL_EXPRESSION_H_
diff --git a/src/tint/ast/bool_literal_expression_test.cc b/src/tint/ast/bool_literal_expression_test.cc
new file mode 100644
index 0000000..78cd632
--- /dev/null
+++ b/src/tint/ast/bool_literal_expression_test.cc
@@ -0,0 +1,37 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/test_helper.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using BoolLiteralExpressionTest = TestHelper;
+
+TEST_F(BoolLiteralExpressionTest, True) {
+  auto* b = create<BoolLiteralExpression>(true);
+  ASSERT_TRUE(b->Is<BoolLiteralExpression>());
+  ASSERT_TRUE(b->value);
+}
+
+TEST_F(BoolLiteralExpressionTest, False) {
+  auto* b = create<BoolLiteralExpression>(false);
+  ASSERT_TRUE(b->Is<BoolLiteralExpression>());
+  ASSERT_FALSE(b->value);
+}
+
+}  // namespace
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/bool_test.cc b/src/tint/ast/bool_test.cc
new file mode 100644
index 0000000..3defbd4
--- /dev/null
+++ b/src/tint/ast/bool_test.cc
@@ -0,0 +1,32 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/bool.h"
+
+#include "src/tint/ast/test_helper.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using AstBoolTest = TestHelper;
+
+TEST_F(AstBoolTest, FriendlyName) {
+  auto* b = create<Bool>();
+  EXPECT_EQ(b->FriendlyName(Symbols()), "bool");
+}
+
+}  // namespace
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/break_statement.cc b/src/tint/ast/break_statement.cc
new file mode 100644
index 0000000..0c78c73
--- /dev/null
+++ b/src/tint/ast/break_statement.cc
@@ -0,0 +1,38 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/break_statement.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::BreakStatement);
+
+namespace tint {
+namespace ast {
+
+BreakStatement::BreakStatement(ProgramID pid, const Source& src)
+    : Base(pid, src) {}
+
+BreakStatement::BreakStatement(BreakStatement&&) = default;
+
+BreakStatement::~BreakStatement() = default;
+
+const BreakStatement* BreakStatement::Clone(CloneContext* ctx) const {
+  // Clone arguments outside of create() call to have deterministic ordering
+  auto src = ctx->Clone(source);
+  return ctx->dst->create<BreakStatement>(src);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/break_statement.h b/src/tint/ast/break_statement.h
new file mode 100644
index 0000000..cf50c74
--- /dev/null
+++ b/src/tint/ast/break_statement.h
@@ -0,0 +1,44 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_BREAK_STATEMENT_H_
+#define SRC_TINT_AST_BREAK_STATEMENT_H_
+
+#include "src/tint/ast/statement.h"
+
+namespace tint {
+namespace ast {
+
+/// An break statement
+class BreakStatement final : public Castable<BreakStatement, Statement> {
+ public:
+  /// Constructor
+  /// @param pid the identifier of the program that owns this node
+  /// @param src the source of this node
+  BreakStatement(ProgramID pid, const Source& src);
+  /// Move constructor
+  BreakStatement(BreakStatement&&);
+  ~BreakStatement() override;
+
+  /// Clones this node and all transitive child nodes using the `CloneContext`
+  /// `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned node
+  const BreakStatement* Clone(CloneContext* ctx) const override;
+};
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_BREAK_STATEMENT_H_
diff --git a/src/tint/ast/break_statement_test.cc b/src/tint/ast/break_statement_test.cc
new file mode 100644
index 0000000..ce419ae
--- /dev/null
+++ b/src/tint/ast/break_statement_test.cc
@@ -0,0 +1,39 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/break_statement.h"
+
+#include "src/tint/ast/test_helper.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using BreakStatementTest = TestHelper;
+
+TEST_F(BreakStatementTest, Creation_WithSource) {
+  auto* stmt = create<BreakStatement>(Source{Source::Location{20, 2}});
+  auto src = stmt->source;
+  EXPECT_EQ(src.range.begin.line, 20u);
+  EXPECT_EQ(src.range.begin.column, 2u);
+}
+
+TEST_F(BreakStatementTest, IsBreak) {
+  auto* stmt = create<BreakStatement>();
+  EXPECT_TRUE(stmt->Is<BreakStatement>());
+}
+
+}  // namespace
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/builtin.cc b/src/tint/ast/builtin.cc
new file mode 100644
index 0000000..48744e0
--- /dev/null
+++ b/src/tint/ast/builtin.cc
@@ -0,0 +1,82 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/builtin.h"
+
+namespace tint {
+namespace ast {
+
+std::ostream& operator<<(std::ostream& out, Builtin builtin) {
+  switch (builtin) {
+    case Builtin::kNone: {
+      out << "none";
+      break;
+    }
+    case Builtin::kPosition: {
+      out << "position";
+      break;
+    }
+    case Builtin::kVertexIndex: {
+      out << "vertex_index";
+      break;
+    }
+    case Builtin::kInstanceIndex: {
+      out << "instance_index";
+      break;
+    }
+    case Builtin::kFrontFacing: {
+      out << "front_facing";
+      break;
+    }
+    case Builtin::kFragDepth: {
+      out << "frag_depth";
+      break;
+    }
+    case Builtin::kLocalInvocationId: {
+      out << "local_invocation_id";
+      break;
+    }
+    case Builtin::kLocalInvocationIndex: {
+      out << "local_invocation_index";
+      break;
+    }
+    case Builtin::kGlobalInvocationId: {
+      out << "global_invocation_id";
+      break;
+    }
+    case Builtin::kWorkgroupId: {
+      out << "workgroup_id";
+      break;
+    }
+    case Builtin::kNumWorkgroups: {
+      out << "num_workgroups";
+      break;
+    }
+    case Builtin::kSampleIndex: {
+      out << "sample_index";
+      break;
+    }
+    case Builtin::kSampleMask: {
+      out << "sample_mask";
+      break;
+    }
+    case Builtin::kPointSize: {
+      out << "pointsize";
+    }
+  }
+  return out;
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/builtin.h b/src/tint/ast/builtin.h
new file mode 100644
index 0000000..913eba2
--- /dev/null
+++ b/src/tint/ast/builtin.h
@@ -0,0 +1,52 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_BUILTIN_H_
+#define SRC_TINT_AST_BUILTIN_H_
+
+#include <ostream>
+
+namespace tint {
+namespace ast {
+
+/// The builtin identifiers
+enum class Builtin {
+  kNone = -1,
+  kPosition,
+  kVertexIndex,
+  kInstanceIndex,
+  kFrontFacing,
+  kFragDepth,
+  kLocalInvocationId,
+  kLocalInvocationIndex,
+  kGlobalInvocationId,
+  kWorkgroupId,
+  kNumWorkgroups,
+  kSampleIndex,
+  kSampleMask,
+
+  // Below are not currently WGSL builtins, but are included in this enum as
+  // they are used by certain backends.
+  kPointSize,
+};
+
+/// @param out the std::ostream to write to
+/// @param builtin the Builtin
+/// @return the std::ostream so calls can be chained
+std::ostream& operator<<(std::ostream& out, Builtin builtin);
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_BUILTIN_H_
diff --git a/src/tint/ast/builtin_attribute.cc b/src/tint/ast/builtin_attribute.cc
new file mode 100644
index 0000000..0591b91
--- /dev/null
+++ b/src/tint/ast/builtin_attribute.cc
@@ -0,0 +1,42 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/builtin_attribute.h"
+
+#include <string>
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::BuiltinAttribute);
+
+namespace tint {
+namespace ast {
+
+BuiltinAttribute::BuiltinAttribute(ProgramID pid, const Source& src, Builtin b)
+    : Base(pid, src), builtin(b) {}
+
+BuiltinAttribute::~BuiltinAttribute() = default;
+
+std::string BuiltinAttribute::Name() const {
+  return "builtin";
+}
+
+const BuiltinAttribute* BuiltinAttribute::Clone(CloneContext* ctx) const {
+  // Clone arguments outside of create() call to have deterministic ordering
+  auto src = ctx->Clone(source);
+  return ctx->dst->create<BuiltinAttribute>(src, builtin);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/builtin_attribute.h b/src/tint/ast/builtin_attribute.h
new file mode 100644
index 0000000..4366739
--- /dev/null
+++ b/src/tint/ast/builtin_attribute.h
@@ -0,0 +1,52 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_BUILTIN_ATTRIBUTE_H_
+#define SRC_TINT_AST_BUILTIN_ATTRIBUTE_H_
+
+#include <string>
+
+#include "src/tint/ast/attribute.h"
+#include "src/tint/ast/builtin.h"
+
+namespace tint {
+namespace ast {
+
+/// A builtin attribute
+class BuiltinAttribute final : public Castable<BuiltinAttribute, Attribute> {
+ public:
+  /// constructor
+  /// @param pid the identifier of the program that owns this node
+  /// @param src the source of this node
+  /// @param builtin the builtin value
+  BuiltinAttribute(ProgramID pid, const Source& src, Builtin builtin);
+  ~BuiltinAttribute() override;
+
+  /// @returns the WGSL name for the attribute
+  std::string Name() const override;
+
+  /// Clones this node and all transitive child nodes using the `CloneContext`
+  /// `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned node
+  const BuiltinAttribute* Clone(CloneContext* ctx) const override;
+
+  /// The builtin value
+  const Builtin builtin;
+};
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_BUILTIN_ATTRIBUTE_H_
diff --git a/src/tint/ast/builtin_attribute_test.cc b/src/tint/ast/builtin_attribute_test.cc
new file mode 100644
index 0000000..e5a91ea
--- /dev/null
+++ b/src/tint/ast/builtin_attribute_test.cc
@@ -0,0 +1,30 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied->
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/test_helper.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using BuiltinAttributeTest = TestHelper;
+
+TEST_F(BuiltinAttributeTest, Creation) {
+  auto* d = create<BuiltinAttribute>(Builtin::kFragDepth);
+  EXPECT_EQ(Builtin::kFragDepth, d->builtin);
+}
+
+}  // namespace
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/builtin_texture_helper_test.cc b/src/tint/ast/builtin_texture_helper_test.cc
new file mode 100644
index 0000000..2f80558
--- /dev/null
+++ b/src/tint/ast/builtin_texture_helper_test.cc
@@ -0,0 +1,2286 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/builtin_texture_helper_test.h"
+
+#include "src/tint/sem/depth_texture_type.h"
+#include "src/tint/sem/multisampled_texture_type.h"
+#include "src/tint/sem/sampled_texture_type.h"
+
+namespace tint {
+namespace ast {
+namespace builtin {
+namespace test {
+
+using u32 = ProgramBuilder::u32;
+using i32 = ProgramBuilder::i32;
+using f32 = ProgramBuilder::f32;
+
+TextureOverloadCase::TextureOverloadCase(
+    ValidTextureOverload o,
+    const char* desc,
+    TextureKind tk,
+    ast::SamplerKind sk,
+    ast::TextureDimension dims,
+    TextureDataType datatype,
+    const char* f,
+    std::function<ExpressionList(ProgramBuilder*)> a)
+    : overload(o),
+      description(desc),
+      texture_kind(tk),
+      sampler_kind(sk),
+      texture_dimension(dims),
+      texture_data_type(datatype),
+      function(f),
+      args(std::move(a)) {}
+TextureOverloadCase::TextureOverloadCase(
+    ValidTextureOverload o,
+    const char* desc,
+    TextureKind tk,
+    ast::TextureDimension dims,
+    TextureDataType datatype,
+    const char* f,
+    std::function<ExpressionList(ProgramBuilder*)> a)
+    : overload(o),
+      description(desc),
+      texture_kind(tk),
+      texture_dimension(dims),
+      texture_data_type(datatype),
+      function(f),
+      args(std::move(a)) {}
+TextureOverloadCase::TextureOverloadCase(
+    ValidTextureOverload o,
+    const char* d,
+    Access acc,
+    ast::TexelFormat fmt,
+    ast::TextureDimension dims,
+    TextureDataType datatype,
+    const char* f,
+    std::function<ExpressionList(ProgramBuilder*)> a)
+    : overload(o),
+      description(d),
+      texture_kind(TextureKind::kStorage),
+      access(acc),
+      texel_format(fmt),
+      texture_dimension(dims),
+      texture_data_type(datatype),
+      function(f),
+      args(std::move(a)) {}
+TextureOverloadCase::TextureOverloadCase(const TextureOverloadCase&) = default;
+TextureOverloadCase::~TextureOverloadCase() = default;
+
+std::ostream& operator<<(std::ostream& out, const TextureKind& kind) {
+  switch (kind) {
+    case TextureKind::kRegular:
+      out << "regular";
+      break;
+    case TextureKind::kDepth:
+      out << "depth";
+      break;
+    case TextureKind::kDepthMultisampled:
+      out << "depth-multisampled";
+      break;
+    case TextureKind::kMultisampled:
+      out << "multisampled";
+      break;
+    case TextureKind::kStorage:
+      out << "storage";
+      break;
+  }
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const TextureDataType& ty) {
+  switch (ty) {
+    case TextureDataType::kF32:
+      out << "f32";
+      break;
+    case TextureDataType::kU32:
+      out << "u32";
+      break;
+    case TextureDataType::kI32:
+      out << "i32";
+      break;
+  }
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const TextureOverloadCase& data) {
+  out << "TextureOverloadCase " << static_cast<int>(data.overload) << "\n";
+  out << data.description << "\n";
+  out << "texture_kind:      " << data.texture_kind << "\n";
+  out << "sampler_kind:      ";
+  if (data.texture_kind != TextureKind::kStorage) {
+    out << data.sampler_kind;
+  } else {
+    out << "<unused>";
+  }
+  out << "\n";
+  out << "access:            " << data.access << "\n";
+  out << "texel_format:      " << data.texel_format << "\n";
+  out << "texture_dimension: " << data.texture_dimension << "\n";
+  out << "texture_data_type: " << data.texture_data_type << "\n";
+  return out;
+}
+
+const ast::Type* TextureOverloadCase::BuildResultVectorComponentType(
+    ProgramBuilder* b) const {
+  switch (texture_data_type) {
+    case ast::builtin::test::TextureDataType::kF32:
+      return b->ty.f32();
+    case ast::builtin::test::TextureDataType::kU32:
+      return b->ty.u32();
+    case ast::builtin::test::TextureDataType::kI32:
+      return b->ty.i32();
+  }
+
+  TINT_UNREACHABLE(AST, b->Diagnostics());
+  return {};
+}
+
+const ast::Variable* TextureOverloadCase::BuildTextureVariable(
+    ProgramBuilder* b) const {
+  AttributeList attrs = {
+      b->create<ast::GroupAttribute>(0),
+      b->create<ast::BindingAttribute>(0),
+  };
+  switch (texture_kind) {
+    case ast::builtin::test::TextureKind::kRegular:
+      return b->Global("texture",
+                       b->ty.sampled_texture(texture_dimension,
+                                             BuildResultVectorComponentType(b)),
+                       attrs);
+
+    case ast::builtin::test::TextureKind::kDepth:
+      return b->Global("texture", b->ty.depth_texture(texture_dimension),
+                       attrs);
+
+    case ast::builtin::test::TextureKind::kDepthMultisampled:
+      return b->Global("texture",
+                       b->ty.depth_multisampled_texture(texture_dimension),
+                       attrs);
+
+    case ast::builtin::test::TextureKind::kMultisampled:
+      return b->Global(
+          "texture",
+          b->ty.multisampled_texture(texture_dimension,
+                                     BuildResultVectorComponentType(b)),
+          attrs);
+
+    case ast::builtin::test::TextureKind::kStorage: {
+      auto* st = b->ty.storage_texture(texture_dimension, texel_format, access);
+      return b->Global("texture", st, attrs);
+    }
+  }
+
+  TINT_UNREACHABLE(AST, b->Diagnostics());
+  return nullptr;
+}
+
+const ast::Variable* TextureOverloadCase::BuildSamplerVariable(
+    ProgramBuilder* b) const {
+  AttributeList attrs = {
+      b->create<ast::GroupAttribute>(0),
+      b->create<ast::BindingAttribute>(1),
+  };
+  return b->Global("sampler", b->ty.sampler(sampler_kind), attrs);
+}
+
+std::vector<TextureOverloadCase> TextureOverloadCase::ValidCases() {
+  return {
+      {
+          ValidTextureOverload::kDimensions1d,
+          "textureDimensions(t : texture_1d<f32>) -> i32",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k1d,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensions2d,
+          "textureDimensions(t : texture_2d<f32>) -> vec2<i32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensions2dLevel,
+          "textureDimensions(t     : texture_2d<f32>,\n"
+          "                  level : i32) -> vec2<i32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture", 1); },
+      },
+      {
+          ValidTextureOverload::kDimensions2dArray,
+          "textureDimensions(t : texture_2d_array<f32>) -> vec2<i32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensions2dArrayLevel,
+          "textureDimensions(t     : texture_2d_array<f32>,\n"
+          "                  level : i32) -> vec2<i32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture", 1); },
+      },
+      {
+          ValidTextureOverload::kDimensions3d,
+          "textureDimensions(t : texture_3d<f32>) -> vec3<i32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensions3dLevel,
+          "textureDimensions(t     : texture_3d<f32>,\n"
+          "                  level : i32) -> vec3<i32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture", 1); },
+      },
+      {
+          ValidTextureOverload::kDimensionsCube,
+          "textureDimensions(t : texture_cube<f32>) -> vec2<i32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensionsCubeLevel,
+          "textureDimensions(t     : texture_cube<f32>,\n"
+          "                  level : i32) -> vec2<i32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture", 1); },
+      },
+      {
+          ValidTextureOverload::kDimensionsCubeArray,
+          "textureDimensions(t : texture_cube_array<f32>) -> vec2<i32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensionsCubeArrayLevel,
+          "textureDimensions(t     : texture_cube_array<f32>,\n"
+          "                  level : i32) -> vec2<i32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture", 1); },
+      },
+      {
+          ValidTextureOverload::kDimensionsMultisampled2d,
+          "textureDimensions(t : texture_multisampled_2d<f32>)-> vec2<i32>",
+          TextureKind::kMultisampled,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensionsDepth2d,
+          "textureDimensions(t : texture_depth_2d) -> vec2<i32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensionsDepth2dLevel,
+          "textureDimensions(t     : texture_depth_2d,\n"
+          "                  level : i32) -> vec2<i32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture", 1); },
+      },
+      {
+          ValidTextureOverload::kDimensionsDepth2dArray,
+          "textureDimensions(t : texture_depth_2d_array) -> vec2<i32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensionsDepth2dArrayLevel,
+          "textureDimensions(t     : texture_depth_2d_array,\n"
+          "                  level : i32) -> vec2<i32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture", 1); },
+      },
+      {
+          ValidTextureOverload::kDimensionsDepthCube,
+          "textureDimensions(t : texture_depth_cube) -> vec2<i32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensionsDepthCubeLevel,
+          "textureDimensions(t     : texture_depth_cube,\n"
+          "                  level : i32) -> vec2<i32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture", 1); },
+      },
+      {
+          ValidTextureOverload::kDimensionsDepthCubeArray,
+          "textureDimensions(t : texture_depth_cube_array) -> vec2<i32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensionsDepthCubeArrayLevel,
+          "textureDimensions(t     : texture_depth_cube_array,\n"
+          "                  level : i32) -> vec2<i32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture", 1); },
+      },
+      {
+          ValidTextureOverload::kDimensionsDepthMultisampled2d,
+          "textureDimensions(t : texture_depth_multisampled_2d) -> vec2<i32>",
+          TextureKind::kDepthMultisampled,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensionsStorageWO1d,
+          "textureDimensions(t : texture_storage_1d<rgba32float>) -> i32",
+          ast::Access::kWrite,
+          ast::TexelFormat::kRgba32Float,
+          ast::TextureDimension::k1d,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensionsStorageWO2d,
+          "textureDimensions(t : texture_storage_2d<rgba32float>) -> "
+          "vec2<i32>",
+          ast::Access::kWrite,
+          ast::TexelFormat::kRgba32Float,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensionsStorageWO2dArray,
+          "textureDimensions(t : texture_storage_2d_array<rgba32float>) -> "
+          "vec2<i32>",
+          ast::Access::kWrite,
+          ast::TexelFormat::kRgba32Float,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kDimensionsStorageWO3d,
+          "textureDimensions(t : texture_storage_3d<rgba32float>) -> "
+          "vec3<i32>",
+          ast::Access::kWrite,
+          ast::TexelFormat::kRgba32Float,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureDimensions",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+
+      {
+          ValidTextureOverload::kGather2dF32,
+          "textureGather(component : i32,\n"
+          "              t         : texture_2d<T>,\n"
+          "              s         : sampler,\n"
+          "              coords    : vec2<f32>) -> vec4<T>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureGather",
+          [](ProgramBuilder* b) {
+            return b->ExprList(0,                        // component
+                               "texture",                // t
+                               "sampler",                // s
+                               b->vec2<f32>(1.f, 2.f));  // coords
+          },
+      },
+      {
+          ValidTextureOverload::kGather2dOffsetF32,
+          "textureGather(component : i32,\n"
+          "              t         : texture_2d<T>,\n"
+          "              s         : sampler,\n"
+          "              coords    : vec2<f32>,\n"
+          "              offset    : vec2<i32>) -> vec4<T>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureGather",
+          [](ProgramBuilder* b) {
+            return b->ExprList(0,                       // component
+                               "texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               b->vec2<i32>(3, 4));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kGather2dArrayF32,
+          "textureGather(component   : i32,\n"
+          "              t           : texture_2d_array<T>,\n"
+          "              s           : sampler,\n"
+          "              coords      : vec2<f32>,\n"
+          "              array_index : i32) -> vec4<T>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureGather",
+          [](ProgramBuilder* b) {
+            return b->ExprList(0,                       // component
+                               "texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3);                      // array index
+          },
+      },
+      {
+          ValidTextureOverload::kGather2dArrayOffsetF32,
+          "textureGather(component   : i32,\n"
+          "              t           : texture_2d_array<T>,\n"
+          "              s           : sampler,\n"
+          "              coords      : vec2<f32>,\n"
+          "              array_index : i32,\n"
+          "              offset      : vec2<i32>) -> vec4<T>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureGather",
+          [](ProgramBuilder* b) {
+            return b->ExprList(0,                       // component
+                               "texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3,                       // array_index
+                               b->vec2<i32>(4, 5));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kGatherCubeF32,
+          "textureGather(component : i32,\n"
+          "              t         : texture_cube<T>,\n"
+          "              s         : sampler,\n"
+          "              coords    : vec3<f32>) -> vec4<T>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureGather",
+          [](ProgramBuilder* b) {
+            return b->ExprList(0,                             // component
+                               "texture",                     // t
+                               "sampler",                     // s
+                               b->vec3<f32>(1.f, 2.f, 3.f));  // coords
+          },
+      },
+      {
+          ValidTextureOverload::kGatherCubeArrayF32,
+          "textureGather(component   : i32,\n"
+          "              t           : texture_cube_array<T>,\n"
+          "              s           : sampler,\n"
+          "              coords      : vec3<f32>,\n"
+          "              array_index : i32) -> vec4<T>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureGather",
+          [](ProgramBuilder* b) {
+            return b->ExprList(0,                            // component
+                               "texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4);                           // array_index
+          },
+      },
+      {
+          ValidTextureOverload::kGatherDepth2dF32,
+          "textureGather(t      : texture_depth_2d,\n"
+          "              s      : sampler,\n"
+          "              coords : vec2<f32>) -> vec4<f32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureGather",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                // t
+                               "sampler",                // s
+                               b->vec2<f32>(1.f, 2.f));  // coords
+          },
+      },
+      {
+          ValidTextureOverload::kGatherDepth2dOffsetF32,
+          "textureGather(t      : texture_depth_2d,\n"
+          "              s      : sampler,\n"
+          "              coords : vec2<f32>,\n"
+          "              offset : vec2<i32>) -> vec4<f32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureGather",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               b->vec2<i32>(3, 4));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kGatherDepth2dArrayF32,
+          "textureGather(t           : texture_depth_2d_array,\n"
+          "              s           : sampler,\n"
+          "              coords      : vec2<f32>,\n"
+          "              array_index : i32) -> vec4<f32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureGather",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3);                      // array_index
+          },
+      },
+      {
+          ValidTextureOverload::kGatherDepth2dArrayOffsetF32,
+          "textureGather(t           : texture_depth_2d_array,\n"
+          "              s           : sampler,\n"
+          "              coords      : vec2<f32>,\n"
+          "              array_index : i32,\n"
+          "              offset      : vec2<i32>) -> vec4<f32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureGather",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3,                       // array_index
+                               b->vec2<i32>(4, 5));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kGatherDepthCubeF32,
+          "textureGather(t      : texture_depth_cube,\n"
+          "              s      : sampler,\n"
+          "              coords : vec3<f32>) -> vec4<f32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureGather",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                     // t
+                               "sampler",                     // s
+                               b->vec3<f32>(1.f, 2.f, 3.f));  // coords
+          },
+      },
+      {
+          ValidTextureOverload::kGatherDepthCubeArrayF32,
+          "textureGather(t           : texture_depth_cube_array,\n"
+          "              s           : sampler,\n"
+          "              coords      : vec3<f32>,\n"
+          "              array_index : i32) -> vec4<f32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureGather",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4);                           // array_index
+          },
+      },
+      {
+          ValidTextureOverload::kGatherCompareDepth2dF32,
+          "textureGatherCompare(t         : texture_depth_2d,\n"
+          "                     s         : sampler_comparison,\n"
+          "                     coords    : vec2<f32>,\n"
+          "                     depth_ref : f32) -> vec4<f32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kComparisonSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureGatherCompare",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3.f);                    // depth_ref
+          },
+      },
+      {
+          ValidTextureOverload::kGatherCompareDepth2dOffsetF32,
+          "textureGatherCompare(t         : texture_depth_2d,\n"
+          "                     s         : sampler_comparison,\n"
+          "                     coords    : vec2<f32>,\n"
+          "                     depth_ref : f32,\n"
+          "                     offset    : vec2<i32>) -> vec4<f32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kComparisonSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureGatherCompare",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3.f,                     // depth_ref
+                               b->vec2<i32>(4, 5));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kGatherCompareDepth2dArrayF32,
+          "textureGatherCompare(t           : texture_depth_2d_array,\n"
+          "                     s           : sampler_comparison,\n"
+          "                     coords      : vec2<f32>,\n"
+          "                     array_index : i32,\n"
+          "                     depth_ref   : f32) -> vec4<f32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kComparisonSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureGatherCompare",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3,                       // array_index
+                               4.f);                    // depth_ref
+          },
+      },
+      {
+          ValidTextureOverload::kGatherCompareDepth2dArrayOffsetF32,
+          "textureGatherCompare(t           : texture_depth_2d_array,\n"
+          "                     s           : sampler_comparison,\n"
+          "                     coords      : vec2<f32>,\n"
+          "                     array_index : i32,\n"
+          "                     depth_ref   : f32,\n"
+          "                     offset      : vec2<i32>) -> vec4<f32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kComparisonSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureGatherCompare",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3,                       // array_index
+                               4.f,                     // depth_ref
+                               b->vec2<i32>(5, 6));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kGatherCompareDepthCubeF32,
+          "textureGatherCompare(t         : texture_depth_cube,\n"
+          "                     s         : sampler_comparison,\n"
+          "                     coords    : vec3<f32>,\n"
+          "                     depth_ref : f32) -> vec4<f32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kComparisonSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureGatherCompare",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4.f);                         // depth_ref
+          },
+      },
+      {
+          ValidTextureOverload::kGatherCompareDepthCubeArrayF32,
+          "textureGatherCompare(t           : texture_depth_cube_array,\n"
+          "                     s           : sampler_comparison,\n"
+          "                     coords      : vec3<f32>,\n"
+          "                     array_index : i32,\n"
+          "                     depth_ref   : f32) -> vec4<f32>",
+          TextureKind::kDepth,
+          ast::SamplerKind::kComparisonSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureGatherCompare",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4,                            // array_index
+                               5.f);                         // depth_ref
+          },
+      },
+      {
+          ValidTextureOverload::kNumLayers2dArray,
+          "textureNumLayers(t : texture_2d_array<f32>) -> i32",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureNumLayers",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLayersCubeArray,
+          "textureNumLayers(t : texture_cube_array<f32>) -> i32",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureNumLayers",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLayersDepth2dArray,
+          "textureNumLayers(t : texture_depth_2d_array) -> i32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureNumLayers",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLayersDepthCubeArray,
+          "textureNumLayers(t : texture_depth_cube_array) -> i32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureNumLayers",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLayersStorageWO2dArray,
+          "textureNumLayers(t : texture_storage_2d_array<rgba32float>) -> i32",
+          ast::Access::kWrite,
+          ast::TexelFormat::kRgba32Float,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureNumLayers",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLevels2d,
+          "textureNumLevels(t : texture_2d<f32>) -> i32",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureNumLevels",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLevels2dArray,
+          "textureNumLevels(t : texture_2d_array<f32>) -> i32",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureNumLevels",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLevels3d,
+          "textureNumLevels(t : texture_3d<f32>) -> i32",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureNumLevels",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLevelsCube,
+          "textureNumLevels(t : texture_cube<f32>) -> i32",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureNumLevels",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLevelsCubeArray,
+          "textureNumLevels(t : texture_cube_array<f32>) -> i32",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureNumLevels",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLevelsDepth2d,
+          "textureNumLevels(t : texture_depth_2d) -> i32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureNumLevels",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLevelsDepth2dArray,
+          "textureNumLevels(t : texture_depth_2d_array) -> i32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureNumLevels",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLevelsDepthCube,
+          "textureNumLevels(t : texture_depth_cube) -> i32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureNumLevels",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumLevelsDepthCubeArray,
+          "textureNumLevels(t : texture_depth_cube_array) -> i32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureNumLevels",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumSamplesMultisampled2d,
+          "textureNumSamples(t : texture_multisampled_2d<f32>) -> i32",
+          TextureKind::kMultisampled,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureNumSamples",
+          [](ProgramBuilder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kSample1dF32,
+          "textureSample(t      : texture_1d<f32>,\n"
+          "              s      : sampler,\n"
+          "              coords : f32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k1d,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",  // t
+                               "sampler",  // s
+                               1.0f);      // coords
+          },
+      },
+      {
+          ValidTextureOverload::kSample2dF32,
+          "textureSample(t      : texture_2d<f32>,\n"
+          "              s      : sampler,\n"
+          "              coords : vec2<f32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                // t
+                               "sampler",                // s
+                               b->vec2<f32>(1.f, 2.f));  // coords
+          },
+      },
+      {
+          ValidTextureOverload::kSample2dOffsetF32,
+          "textureSample(t      : texture_2d<f32>,\n"
+          "              s      : sampler,\n"
+          "              coords : vec2<f32>\n"
+          "              offset : vec2<i32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               b->vec2<i32>(3, 4));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSample2dArrayF32,
+          "textureSample(t           : texture_2d_array<f32>,\n"
+          "              s           : sampler,\n"
+          "              coords      : vec2<f32>,\n"
+          "              array_index : i32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3);                      // array_index
+          },
+      },
+      {
+          ValidTextureOverload::kSample2dArrayOffsetF32,
+          "textureSample(t           : texture_2d_array<f32>,\n"
+          "              s           : sampler,\n"
+          "              coords      : vec2<f32>,\n"
+          "              array_index : i32\n"
+          "              offset      : vec2<i32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3,                       // array_index
+                               b->vec2<i32>(4, 5));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSample3dF32,
+          "textureSample(t      : texture_3d<f32>,\n"
+          "              s      : sampler,\n"
+          "              coords : vec3<f32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                     // t
+                               "sampler",                     // s
+                               b->vec3<f32>(1.f, 2.f, 3.f));  // coords
+          },
+      },
+      {
+          ValidTextureOverload::kSample3dOffsetF32,
+          "textureSample(t      : texture_3d<f32>,\n"
+          "              s      : sampler,\n"
+          "              coords : vec3<f32>\n"
+          "              offset : vec3<i32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               b->vec3<i32>(4, 5, 6));       // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleCubeF32,
+          "textureSample(t      : texture_cube<f32>,\n"
+          "              s      : sampler,\n"
+          "              coords : vec3<f32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                     // t
+                               "sampler",                     // s
+                               b->vec3<f32>(1.f, 2.f, 3.f));  // coords
+          },
+      },
+      {
+          ValidTextureOverload::kSampleCubeArrayF32,
+          "textureSample(t           : texture_cube_array<f32>,\n"
+          "              s           : sampler,\n"
+          "              coords      : vec3<f32>,\n"
+          "              array_index : i32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4);                           // array_index
+          },
+      },
+      {
+          ValidTextureOverload::kSampleDepth2dF32,
+          "textureSample(t      : texture_depth_2d,\n"
+          "              s      : sampler,\n"
+          "              coords : vec2<f32>) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                // t
+                               "sampler",                // s
+                               b->vec2<f32>(1.f, 2.f));  // coords
+          },
+      },
+      {
+          ValidTextureOverload::kSampleDepth2dOffsetF32,
+          "textureSample(t      : texture_depth_2d,\n"
+          "              s      : sampler,\n"
+          "              coords : vec2<f32>\n"
+          "              offset : vec2<i32>) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               b->vec2<i32>(3, 4));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleDepth2dArrayF32,
+          "textureSample(t           : texture_depth_2d_array,\n"
+          "              s           : sampler,\n"
+          "              coords      : vec2<f32>,\n"
+          "              array_index : i32) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3);                      // array_index
+          },
+      },
+      {
+          ValidTextureOverload::kSampleDepth2dArrayOffsetF32,
+          "textureSample(t           : texture_depth_2d_array,\n"
+          "              s           : sampler,\n"
+          "              coords      : vec2<f32>,\n"
+          "              array_index : i32\n"
+          "              offset      : vec2<i32>) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3,                       // array_index
+                               b->vec2<i32>(4, 5));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleDepthCubeF32,
+          "textureSample(t      : texture_depth_cube,\n"
+          "              s      : sampler,\n"
+          "              coords : vec3<f32>) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                     // t
+                               "sampler",                     // s
+                               b->vec3<f32>(1.f, 2.f, 3.f));  // coords
+          },
+      },
+      {
+          ValidTextureOverload::kSampleDepthCubeArrayF32,
+          "textureSample(t           : texture_depth_cube_array,\n"
+          "              s           : sampler,\n"
+          "              coords      : vec3<f32>,\n"
+          "              array_index : i32) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureSample",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4);                           // array_index
+          },
+      },
+      {
+          ValidTextureOverload::kSampleBias2dF32,
+          "textureSampleBias(t      : texture_2d<f32>,\n"
+          "                  s      : sampler,\n"
+          "                  coords : vec2<f32>,\n"
+          "                  bias   : f32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSampleBias",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3.f);                    // bias
+          },
+      },
+      {
+          ValidTextureOverload::kSampleBias2dOffsetF32,
+          "textureSampleBias(t      : texture_2d<f32>,\n"
+          "                  s      : sampler,\n"
+          "                  coords : vec2<f32>,\n"
+          "                  bias   : f32,\n"
+          "                  offset : vec2<i32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSampleBias",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3.f,                     // bias
+                               b->vec2<i32>(4, 5));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleBias2dArrayF32,
+          "textureSampleBias(t           : texture_2d_array<f32>,\n"
+          "                  s           : sampler,\n"
+          "                  coords      : vec2<f32>,\n"
+          "                  array_index : i32,\n"
+          "                  bias        : f32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSampleBias",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               4,                       // array_index
+                               3.f);                    // bias
+          },
+      },
+      {
+          ValidTextureOverload::kSampleBias2dArrayOffsetF32,
+          "textureSampleBias(t           : texture_2d_array<f32>,\n"
+          "                  s           : sampler,\n"
+          "                  coords      : vec2<f32>,\n"
+          "                  array_index : i32,\n"
+          "                  bias        : f32,\n"
+          "                  offset      : vec2<i32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSampleBias",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3,                       // array_index
+                               4.f,                     // bias
+                               b->vec2<i32>(5, 6));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleBias3dF32,
+          "textureSampleBias(t      : texture_3d<f32>,\n"
+          "                  s      : sampler,\n"
+          "                  coords : vec3<f32>,\n"
+          "                  bias   : f32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureSampleBias",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4.f);                         // bias
+          },
+      },
+      {
+          ValidTextureOverload::kSampleBias3dOffsetF32,
+          "textureSampleBias(t      : texture_3d<f32>,\n"
+          "                  s      : sampler,\n"
+          "                  coords : vec3<f32>,\n"
+          "                  bias   : f32,\n"
+          "                  offset : vec3<i32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureSampleBias",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4.f,                          // bias
+                               b->vec3<i32>(5, 6, 7));       // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleBiasCubeF32,
+          "textureSampleBias(t      : texture_cube<f32>,\n"
+          "                  s      : sampler,\n"
+          "                  coords : vec3<f32>,\n"
+          "                  bias   : f32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureSampleBias",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4.f);                         // bias
+          },
+      },
+      {
+          ValidTextureOverload::kSampleBiasCubeArrayF32,
+          "textureSampleBias(t           : texture_cube_array<f32>,\n"
+          "                  s           : sampler,\n"
+          "                  coords      : vec3<f32>,\n"
+          "                  array_index : i32,\n"
+          "                  bias        : f32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureSampleBias",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               3,                            // array_index
+                               4.f);                         // bias
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevel2dF32,
+          "textureSampleLevel(t      : texture_2d<f32>,\n"
+          "                   s      : sampler,\n"
+          "                   coords : vec2<f32>,\n"
+          "                   level  : f32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3.f);                    // level
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevel2dOffsetF32,
+          "textureSampleLevel(t      : texture_2d<f32>,\n"
+          "                   s      : sampler,\n"
+          "                   coords : vec2<f32>,\n"
+          "                   level  : f32,\n"
+          "                   offset : vec2<i32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3.f,                     // level
+                               b->vec2<i32>(4, 5));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevel2dArrayF32,
+          "textureSampleLevel(t           : texture_2d_array<f32>,\n"
+          "                   s           : sampler,\n"
+          "                   coords      : vec2<f32>,\n"
+          "                   array_index : i32,\n"
+          "                   level       : f32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3,                       // array_index
+                               4.f);                    // level
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevel2dArrayOffsetF32,
+          "textureSampleLevel(t           : texture_2d_array<f32>,\n"
+          "                   s           : sampler,\n"
+          "                   coords      : vec2<f32>,\n"
+          "                   array_index : i32,\n"
+          "                   level       : f32,\n"
+          "                   offset      : vec2<i32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3,                       // array_index
+                               4.f,                     // level
+                               b->vec2<i32>(5, 6));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevel3dF32,
+          "textureSampleLevel(t      : texture_3d<f32>,\n"
+          "                   s      : sampler,\n"
+          "                   coords : vec3<f32>,\n"
+          "                   level  : f32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4.f);                         // level
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevel3dOffsetF32,
+          "textureSampleLevel(t      : texture_3d<f32>,\n"
+          "                   s      : sampler,\n"
+          "                   coords : vec3<f32>,\n"
+          "                   level  : f32,\n"
+          "                   offset : vec3<i32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4.f,                          // level
+                               b->vec3<i32>(5, 6, 7));       // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevelCubeF32,
+          "textureSampleLevel(t      : texture_cube<f32>,\n"
+          "                   s      : sampler,\n"
+          "                   coords : vec3<f32>,\n"
+          "                   level  : f32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4.f);                         // level
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevelCubeArrayF32,
+          "textureSampleLevel(t           : texture_cube_array<f32>,\n"
+          "                   s           : sampler,\n"
+          "                   coords      : vec3<f32>,\n"
+          "                   array_index : i32,\n"
+          "                   level       : f32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4,                            // array_index
+                               5.f);                         // level
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevelDepth2dF32,
+          "textureSampleLevel(t      : texture_depth_2d,\n"
+          "                   s      : sampler,\n"
+          "                   coords : vec2<f32>,\n"
+          "                   level  : i32) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3);                      // level
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevelDepth2dOffsetF32,
+          "textureSampleLevel(t      : texture_depth_2d,\n"
+          "                   s      : sampler,\n"
+          "                   coords : vec2<f32>,\n"
+          "                   level  : i32,\n"
+          "                   offset : vec2<i32>) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3,                       // level
+                               b->vec2<i32>(4, 5));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevelDepth2dArrayF32,
+          "textureSampleLevel(t           : texture_depth_2d_array,\n"
+          "                   s           : sampler,\n"
+          "                   coords      : vec2<f32>,\n"
+          "                   array_index : i32,\n"
+          "                   level       : i32) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3,                       // array_index
+                               4);                      // level
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevelDepth2dArrayOffsetF32,
+          "textureSampleLevel(t           : texture_depth_2d_array,\n"
+          "                   s           : sampler,\n"
+          "                   coords      : vec2<f32>,\n"
+          "                   array_index : i32,\n"
+          "                   level       : i32,\n"
+          "                   offset      : vec2<i32>) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3,                       // array_index
+                               4,                       // level
+                               b->vec2<i32>(5, 6));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevelDepthCubeF32,
+          "textureSampleLevel(t      : texture_depth_cube,\n"
+          "                   s      : sampler,\n"
+          "                   coords : vec3<f32>,\n"
+          "                   level  : i32) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4);                           // level
+          },
+      },
+      {
+          ValidTextureOverload::kSampleLevelDepthCubeArrayF32,
+          "textureSampleLevel(t           : texture_depth_cube_array,\n"
+          "                   s           : sampler,\n"
+          "                   coords      : vec3<f32>,\n"
+          "                   array_index : i32,\n"
+          "                   level       : i32) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureSampleLevel",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4,                            // array_index
+                               5);                           // level
+          },
+      },
+      {
+          ValidTextureOverload::kSampleGrad2dF32,
+          "textureSampleGrad(t      : texture_2d<f32>,\n"
+          "                  s      : sampler,\n"
+          "                  coords : vec2<f32>\n"
+          "                  ddx    : vec2<f32>,\n"
+          "                  ddy    : vec2<f32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSampleGrad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                  // t
+                               "sampler",                  // s
+                               b->vec2<f32>(1.0f, 2.0f),   // coords
+                               b->vec2<f32>(3.0f, 4.0f),   // ddx
+                               b->vec2<f32>(5.0f, 6.0f));  // ddy
+          },
+      },
+      {
+          ValidTextureOverload::kSampleGrad2dOffsetF32,
+          "textureSampleGrad(t      : texture_2d<f32>,\n"
+          "                  s      : sampler,\n"
+          "                  coords : vec2<f32>,\n"
+          "                  ddx    : vec2<f32>,\n"
+          "                  ddy    : vec2<f32>,\n"
+          "                  offset : vec2<i32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSampleGrad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               b->vec2<f32>(3.f, 4.f),  // ddx
+                               b->vec2<f32>(5.f, 6.f),  // ddy
+                               b->vec2<i32>(7, 7));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleGrad2dArrayF32,
+          "textureSampleGrad(t           : texture_2d_array<f32>,\n"
+          "                  s           : sampler,\n"
+          "                  coords      : vec2<f32>,\n"
+          "                  array_index : i32,\n"
+          "                  ddx         : vec2<f32>,\n"
+          "                  ddy         : vec2<f32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSampleGrad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                // t
+                               "sampler",                // s
+                               b->vec2<f32>(1.f, 2.f),   // coords
+                               3,                        // array_index
+                               b->vec2<f32>(4.f, 5.f),   // ddx
+                               b->vec2<f32>(6.f, 7.f));  // ddy
+          },
+      },
+      {
+          ValidTextureOverload::kSampleGrad2dArrayOffsetF32,
+          "textureSampleGrad(t           : texture_2d_array<f32>,\n"
+          "                  s           : sampler,\n"
+          "                  coords      : vec2<f32>,\n"
+          "                  array_index : i32,\n"
+          "                  ddx         : vec2<f32>,\n"
+          "                  ddy         : vec2<f32>,\n"
+          "                  offset      : vec2<i32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSampleGrad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3,                       // array_index
+                               b->vec2<f32>(4.f, 5.f),  // ddx
+                               b->vec2<f32>(6.f, 7.f),  // ddy
+                               b->vec2<i32>(6, 7));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleGrad3dF32,
+          "textureSampleGrad(t      : texture_3d<f32>,\n"
+          "                  s      : sampler,\n"
+          "                  coords : vec3<f32>,\n"
+          "                  ddx    : vec3<f32>,\n"
+          "                  ddy    : vec3<f32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureSampleGrad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                     // t
+                               "sampler",                     // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),   // coords
+                               b->vec3<f32>(4.f, 5.f, 6.f),   // ddx
+                               b->vec3<f32>(7.f, 8.f, 9.f));  // ddy
+          },
+      },
+      {
+          ValidTextureOverload::kSampleGrad3dOffsetF32,
+          "textureSampleGrad(t      : texture_3d<f32>,\n"
+          "                  s      : sampler,\n"
+          "                  coords : vec3<f32>,\n"
+          "                  ddx    : vec3<f32>,\n"
+          "                  ddy    : vec3<f32>,\n"
+          "                  offset : vec3<i32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureSampleGrad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               b->vec3<f32>(4.f, 5.f, 6.f),  // ddx
+                               b->vec3<f32>(7.f, 8.f, 9.f),  // ddy
+                               b->vec3<i32>(0, 1, 2));       // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleGradCubeF32,
+          "textureSampleGrad(t      : texture_cube<f32>,\n"
+          "                  s      : sampler,\n"
+          "                  coords : vec3<f32>,\n"
+          "                  ddx    : vec3<f32>,\n"
+          "                  ddy    : vec3<f32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureSampleGrad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                     // t
+                               "sampler",                     // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),   // coords
+                               b->vec3<f32>(4.f, 5.f, 6.f),   // ddx
+                               b->vec3<f32>(7.f, 8.f, 9.f));  // ddy
+          },
+      },
+      {
+          ValidTextureOverload::kSampleGradCubeArrayF32,
+          "textureSampleGrad(t           : texture_cube_array<f32>,\n"
+          "                  s           : sampler,\n"
+          "                  coords      : vec3<f32>,\n"
+          "                  array_index : i32,\n"
+          "                  ddx         : vec3<f32>,\n"
+          "                  ddy         : vec3<f32>) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::SamplerKind::kSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureSampleGrad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                      // t
+                               "sampler",                      // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),    // coords
+                               4,                              // array_index
+                               b->vec3<f32>(5.f, 6.f, 7.f),    // ddx
+                               b->vec3<f32>(8.f, 9.f, 10.f));  // ddy
+          },
+      },
+      {
+          ValidTextureOverload::kSampleCompareDepth2dF32,
+          "textureSampleCompare(t         : texture_depth_2d,\n"
+          "                     s         : sampler_comparison,\n"
+          "                     coords    : vec2<f32>,\n"
+          "                     depth_ref : f32) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kComparisonSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSampleCompare",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3.f);                    // depth_ref
+          },
+      },
+      {
+          ValidTextureOverload::kSampleCompareDepth2dOffsetF32,
+          "textureSampleCompare(t         : texture_depth_2d,\n"
+          "                     s         : sampler_comparison,\n"
+          "                     coords    : vec2<f32>,\n"
+          "                     depth_ref : f32,\n"
+          "                     offset    : vec2<i32>) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kComparisonSampler,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureSampleCompare",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               3.f,                     // depth_ref
+                               b->vec2<i32>(4, 5));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleCompareDepth2dArrayF32,
+          "textureSampleCompare(t           : texture_depth_2d_array,\n"
+          "                     s           : sampler_comparison,\n"
+          "                     coords      : vec2<f32>,\n"
+          "                     array_index : i32,\n"
+          "                     depth_ref   : f32) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kComparisonSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSampleCompare",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               4,                       // array_index
+                               3.f);                    // depth_ref
+          },
+      },
+      {
+          ValidTextureOverload::kSampleCompareDepth2dArrayOffsetF32,
+          "textureSampleCompare(t           : texture_depth_2d_array,\n"
+          "                     s           : sampler_comparison,\n"
+          "                     coords      : vec2<f32>,\n"
+          "                     array_index : i32,\n"
+          "                     depth_ref   : f32,\n"
+          "                     offset      : vec2<i32>) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kComparisonSampler,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureSampleCompare",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",               // t
+                               "sampler",               // s
+                               b->vec2<f32>(1.f, 2.f),  // coords
+                               4,                       // array_index
+                               3.f,                     // depth_ref
+                               b->vec2<i32>(5, 6));     // offset
+          },
+      },
+      {
+          ValidTextureOverload::kSampleCompareDepthCubeF32,
+          "textureSampleCompare(t         : texture_depth_cube,\n"
+          "                     s         : sampler_comparison,\n"
+          "                     coords    : vec3<f32>,\n"
+          "                     depth_ref : f32) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kComparisonSampler,
+          ast::TextureDimension::kCube,
+          TextureDataType::kF32,
+          "textureSampleCompare",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4.f);                         // depth_ref
+          },
+      },
+      {
+          ValidTextureOverload::kSampleCompareDepthCubeArrayF32,
+          "textureSampleCompare(t           : texture_depth_cube_array,\n"
+          "                     s           : sampler_comparison,\n"
+          "                     coords      : vec3<f32>,\n"
+          "                     array_index : i32,\n"
+          "                     depth_ref   : f32) -> f32",
+          TextureKind::kDepth,
+          ast::SamplerKind::kComparisonSampler,
+          ast::TextureDimension::kCubeArray,
+          TextureDataType::kF32,
+          "textureSampleCompare",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                    // t
+                               "sampler",                    // s
+                               b->vec3<f32>(1.f, 2.f, 3.f),  // coords
+                               4,                            // array_index
+                               5.f);                         // depth_ref
+          },
+      },
+      {
+          ValidTextureOverload::kLoad1dLevelF32,
+          "textureLoad(t      : texture_1d<f32>,\n"
+          "            coords : i32,\n"
+          "            level  : i32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::TextureDimension::k1d,
+          TextureDataType::kF32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",  // t
+                               1,          // coords
+                               3);         // level
+          },
+      },
+      {
+          ValidTextureOverload::kLoad1dLevelU32,
+          "textureLoad(t      : texture_1d<u32>,\n"
+          "            coords : i32,\n"
+          "            level  : i32) -> vec4<u32>",
+          TextureKind::kRegular,
+          ast::TextureDimension::k1d,
+          TextureDataType::kU32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",  // t
+                               1,          // coords
+                               3);         // level
+          },
+      },
+      {
+          ValidTextureOverload::kLoad1dLevelI32,
+          "textureLoad(t      : texture_1d<i32>,\n"
+          "            coords : i32,\n"
+          "            level  : i32) -> vec4<i32>",
+          TextureKind::kRegular,
+          ast::TextureDimension::k1d,
+          TextureDataType::kI32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",  // t
+                               1,          // coords
+                               3);         // level
+          },
+      },
+      {
+          ValidTextureOverload::kLoad2dLevelF32,
+          "textureLoad(t      : texture_2d<f32>,\n"
+          "            coords : vec2<i32>,\n"
+          "            level  : i32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",           // t
+                               b->vec2<i32>(1, 2),  // coords
+                               3);                  // level
+          },
+      },
+      {
+          ValidTextureOverload::kLoad2dLevelU32,
+          "textureLoad(t      : texture_2d<u32>,\n"
+          "            coords : vec2<i32>,\n"
+          "            level  : i32) -> vec4<u32>",
+          TextureKind::kRegular,
+          ast::TextureDimension::k2d,
+          TextureDataType::kU32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",           // t
+                               b->vec2<i32>(1, 2),  // coords
+                               3);                  // level
+          },
+      },
+      {
+          ValidTextureOverload::kLoad2dLevelI32,
+          "textureLoad(t      : texture_2d<i32>,\n"
+          "            coords : vec2<i32>,\n"
+          "            level  : i32) -> vec4<i32>",
+          TextureKind::kRegular,
+          ast::TextureDimension::k2d,
+          TextureDataType::kI32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",           // t
+                               b->vec2<i32>(1, 2),  // coords
+                               3);                  // level
+          },
+      },
+      {
+          ValidTextureOverload::kLoad2dArrayLevelF32,
+          "textureLoad(t           : texture_2d_array<f32>,\n"
+          "            coords      : vec2<i32>,\n"
+          "            array_index : i32,\n"
+          "            level       : i32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",           // t
+                               b->vec2<i32>(1, 2),  // coords
+                               3,                   // array_index
+                               4);                  // level
+          },
+      },
+      {
+          ValidTextureOverload::kLoad2dArrayLevelU32,
+          "textureLoad(t           : texture_2d_array<u32>,\n"
+          "            coords      : vec2<i32>,\n"
+          "            array_index : i32,\n"
+          "            level       : i32) -> vec4<u32>",
+          TextureKind::kRegular,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kU32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",           // t
+                               b->vec2<i32>(1, 2),  // coords
+                               3,                   // array_index
+                               4);                  // level
+          },
+      },
+      {
+          ValidTextureOverload::kLoad2dArrayLevelI32,
+          "textureLoad(t           : texture_2d_array<i32>,\n"
+          "            coords      : vec2<i32>,\n"
+          "            array_index : i32,\n"
+          "            level       : i32) -> vec4<i32>",
+          TextureKind::kRegular,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kI32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",           // t
+                               b->vec2<i32>(1, 2),  // coords
+                               3,                   // array_index
+                               4);                  // level
+          },
+      },
+      {
+          ValidTextureOverload::kLoad3dLevelF32,
+          "textureLoad(t      : texture_3d<f32>,\n"
+          "            coords : vec3<i32>,\n"
+          "            level  : i32) -> vec4<f32>",
+          TextureKind::kRegular,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",              // t
+                               b->vec3<i32>(1, 2, 3),  // coords
+                               4);                     // level
+          },
+      },
+      {
+          ValidTextureOverload::kLoad3dLevelU32,
+          "textureLoad(t      : texture_3d<u32>,\n"
+          "            coords : vec3<i32>,\n"
+          "            level  : i32) -> vec4<u32>",
+          TextureKind::kRegular,
+          ast::TextureDimension::k3d,
+          TextureDataType::kU32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",              // t
+                               b->vec3<i32>(1, 2, 3),  // coords
+                               4);                     // level
+          },
+      },
+      {
+          ValidTextureOverload::kLoad3dLevelI32,
+          "textureLoad(t      : texture_3d<i32>,\n"
+          "            coords : vec3<i32>,\n"
+          "            level  : i32) -> vec4<i32>",
+          TextureKind::kRegular,
+          ast::TextureDimension::k3d,
+          TextureDataType::kI32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",              // t
+                               b->vec3<i32>(1, 2, 3),  // coords
+                               4);                     // level
+          },
+      },
+      {
+          ValidTextureOverload::kLoadMultisampled2dF32,
+          "textureLoad(t            : texture_multisampled_2d<f32>,\n"
+          "            coords       : vec2<i32>,\n"
+          "            sample_index : i32) -> vec4<f32>",
+          TextureKind::kMultisampled,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",           // t
+                               b->vec2<i32>(1, 2),  // coords
+                               3);                  // sample_index
+          },
+      },
+      {
+          ValidTextureOverload::kLoadMultisampled2dU32,
+          "textureLoad(t            : texture_multisampled_2d<u32>,\n"
+          "            coords       : vec2<i32>,\n"
+          "            sample_index : i32) -> vec4<u32>",
+          TextureKind::kMultisampled,
+          ast::TextureDimension::k2d,
+          TextureDataType::kU32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",           // t
+                               b->vec2<i32>(1, 2),  // coords
+                               3);                  // sample_index
+          },
+      },
+      {
+          ValidTextureOverload::kLoadMultisampled2dI32,
+          "textureLoad(t            : texture_multisampled_2d<i32>,\n"
+          "            coords       : vec2<i32>,\n"
+          "            sample_index : i32) -> vec4<i32>",
+          TextureKind::kMultisampled,
+          ast::TextureDimension::k2d,
+          TextureDataType::kI32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",           // t
+                               b->vec2<i32>(1, 2),  // coords
+                               3);                  // sample_index
+          },
+      },
+      {
+          ValidTextureOverload::kLoadDepth2dLevelF32,
+          "textureLoad(t      : texture_depth_2d,\n"
+          "            coords : vec2<i32>,\n"
+          "            level  : i32) -> f32",
+          TextureKind::kDepth,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",           // t
+                               b->vec2<i32>(1, 2),  // coords
+                               3);                  // level
+          },
+      },
+      {
+          ValidTextureOverload::kLoadDepth2dArrayLevelF32,
+          "textureLoad(t           : texture_depth_2d_array,\n"
+          "            coords      : vec2<i32>,\n"
+          "            array_index : i32,\n"
+          "            level       : i32) -> f32",
+          TextureKind::kDepth,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureLoad",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",           // t
+                               b->vec2<i32>(1, 2),  // coords
+                               3,                   // array_index
+                               4);                  // level
+          },
+      },
+      {
+          ValidTextureOverload::kStoreWO1dRgba32float,
+          "textureStore(t      : texture_storage_1d<rgba32float>,\n"
+          "             coords : i32,\n"
+          "             value  : vec4<T>)",
+          ast::Access::kWrite,
+          ast::TexelFormat::kRgba32Float,
+          ast::TextureDimension::k1d,
+          TextureDataType::kF32,
+          "textureStore",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                          // t
+                               1,                                  // coords
+                               b->vec4<f32>(2.f, 3.f, 4.f, 5.f));  // value
+          },
+      },
+      {
+          ValidTextureOverload::kStoreWO2dRgba32float,
+          "textureStore(t      : texture_storage_2d<rgba32float>,\n"
+          "             coords : vec2<i32>,\n"
+          "             value  : vec4<T>)",
+          ast::Access::kWrite,
+          ast::TexelFormat::kRgba32Float,
+          ast::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureStore",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                          // t
+                               b->vec2<i32>(1, 2),                 // coords
+                               b->vec4<f32>(3.f, 4.f, 5.f, 6.f));  // value
+          },
+      },
+      {
+          ValidTextureOverload::kStoreWO2dArrayRgba32float,
+          "textureStore(t           : texture_storage_2d_array<rgba32float>,\n"
+          "             coords      : vec2<i32>,\n"
+          "             array_index : i32,\n"
+          "             value       : vec4<T>)",
+          ast::Access::kWrite,
+          ast::TexelFormat::kRgba32Float,
+          ast::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureStore",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",           // t
+                               b->vec2<i32>(1, 2),  // coords
+                               3,                   // array_index
+                               b->vec4<f32>(4.f, 5.f, 6.f, 7.f));  // value
+          },
+      },
+      {
+          ValidTextureOverload::kStoreWO3dRgba32float,
+          "textureStore(t      : texture_storage_3d<rgba32float>,\n"
+          "             coords : vec3<i32>,\n"
+          "             value  : vec4<T>)",
+          ast::Access::kWrite,
+          ast::TexelFormat::kRgba32Float,
+          ast::TextureDimension::k3d,
+          TextureDataType::kF32,
+          "textureStore",
+          [](ProgramBuilder* b) {
+            return b->ExprList("texture",                          // t
+                               b->vec3<i32>(1, 2, 3),              // coords
+                               b->vec4<f32>(4.f, 5.f, 6.f, 7.f));  // value
+          },
+      },
+  };
+}
+
+bool ReturnsVoid(ValidTextureOverload texture_overload) {
+  switch (texture_overload) {
+    case ValidTextureOverload::kStoreWO1dRgba32float:
+    case ValidTextureOverload::kStoreWO2dRgba32float:
+    case ValidTextureOverload::kStoreWO2dArrayRgba32float:
+    case ValidTextureOverload::kStoreWO3dRgba32float:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace test
+}  // namespace builtin
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/builtin_texture_helper_test.h b/src/tint/ast/builtin_texture_helper_test.h
new file mode 100644
index 0000000..751f51b
--- /dev/null
+++ b/src/tint/ast/builtin_texture_helper_test.h
@@ -0,0 +1,269 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_BUILTIN_TEXTURE_HELPER_TEST_H_
+#define SRC_TINT_AST_BUILTIN_TEXTURE_HELPER_TEST_H_
+
+#include <vector>
+
+#include "src/tint/ast/access.h"
+#include "src/tint/program_builder.h"
+#include "src/tint/sem/storage_texture_type.h"
+
+namespace tint {
+namespace ast {
+namespace builtin {
+namespace test {
+
+enum class TextureKind {
+  kRegular,
+  kDepth,
+  kDepthMultisampled,
+  kMultisampled,
+  kStorage
+};
+enum class TextureDataType { kF32, kU32, kI32 };
+
+std::ostream& operator<<(std::ostream& out, const TextureKind& kind);
+std::ostream& operator<<(std::ostream& out, const TextureDataType& ty);
+
+/// Non-exhaustive list of valid texture overloads
+enum class ValidTextureOverload {
+  kDimensions1d,
+  kDimensions2d,
+  kDimensions2dLevel,
+  kDimensions2dArray,
+  kDimensions2dArrayLevel,
+  kDimensions3d,
+  kDimensions3dLevel,
+  kDimensionsCube,
+  kDimensionsCubeLevel,
+  kDimensionsCubeArray,
+  kDimensionsCubeArrayLevel,
+  kDimensionsMultisampled2d,
+  kDimensionsDepth2d,
+  kDimensionsDepth2dLevel,
+  kDimensionsDepth2dArray,
+  kDimensionsDepth2dArrayLevel,
+  kDimensionsDepthCube,
+  kDimensionsDepthCubeLevel,
+  kDimensionsDepthCubeArray,
+  kDimensionsDepthCubeArrayLevel,
+  kDimensionsDepthMultisampled2d,
+  kDimensionsStorageWO1d,
+  kDimensionsStorageWO2d,
+  kDimensionsStorageWO2dArray,
+  kDimensionsStorageWO3d,
+  kGather2dF32,
+  kGather2dOffsetF32,
+  kGather2dArrayF32,
+  kGather2dArrayOffsetF32,
+  kGatherCubeF32,
+  kGatherCubeArrayF32,
+  kGatherDepth2dF32,
+  kGatherDepth2dOffsetF32,
+  kGatherDepth2dArrayF32,
+  kGatherDepth2dArrayOffsetF32,
+  kGatherDepthCubeF32,
+  kGatherDepthCubeArrayF32,
+  kGatherCompareDepth2dF32,
+  kGatherCompareDepth2dOffsetF32,
+  kGatherCompareDepth2dArrayF32,
+  kGatherCompareDepth2dArrayOffsetF32,
+  kGatherCompareDepthCubeF32,
+  kGatherCompareDepthCubeArrayF32,
+  kNumLayers2dArray,
+  kNumLayersCubeArray,
+  kNumLayersDepth2dArray,
+  kNumLayersDepthCubeArray,
+  kNumLayersStorageWO2dArray,
+  kNumLevels2d,
+  kNumLevels2dArray,
+  kNumLevels3d,
+  kNumLevelsCube,
+  kNumLevelsCubeArray,
+  kNumLevelsDepth2d,
+  kNumLevelsDepth2dArray,
+  kNumLevelsDepthCube,
+  kNumLevelsDepthCubeArray,
+  kNumSamplesMultisampled2d,
+  kNumSamplesDepthMultisampled2d,
+  kSample1dF32,
+  kSample2dF32,
+  kSample2dOffsetF32,
+  kSample2dArrayF32,
+  kSample2dArrayOffsetF32,
+  kSample3dF32,
+  kSample3dOffsetF32,
+  kSampleCubeF32,
+  kSampleCubeArrayF32,
+  kSampleDepth2dF32,
+  kSampleDepth2dOffsetF32,
+  kSampleDepth2dArrayF32,
+  kSampleDepth2dArrayOffsetF32,
+  kSampleDepthCubeF32,
+  kSampleDepthCubeArrayF32,
+  kSampleBias2dF32,
+  kSampleBias2dOffsetF32,
+  kSampleBias2dArrayF32,
+  kSampleBias2dArrayOffsetF32,
+  kSampleBias3dF32,
+  kSampleBias3dOffsetF32,
+  kSampleBiasCubeF32,
+  kSampleBiasCubeArrayF32,
+  kSampleLevel2dF32,
+  kSampleLevel2dOffsetF32,
+  kSampleLevel2dArrayF32,
+  kSampleLevel2dArrayOffsetF32,
+  kSampleLevel3dF32,
+  kSampleLevel3dOffsetF32,
+  kSampleLevelCubeF32,
+  kSampleLevelCubeArrayF32,
+  kSampleLevelDepth2dF32,
+  kSampleLevelDepth2dOffsetF32,
+  kSampleLevelDepth2dArrayF32,
+  kSampleLevelDepth2dArrayOffsetF32,
+  kSampleLevelDepthCubeF32,
+  kSampleLevelDepthCubeArrayF32,
+  kSampleGrad2dF32,
+  kSampleGrad2dOffsetF32,
+  kSampleGrad2dArrayF32,
+  kSampleGrad2dArrayOffsetF32,
+  kSampleGrad3dF32,
+  kSampleGrad3dOffsetF32,
+  kSampleGradCubeF32,
+  kSampleGradCubeArrayF32,
+  kSampleCompareDepth2dF32,
+  kSampleCompareDepth2dOffsetF32,
+  kSampleCompareDepth2dArrayF32,
+  kSampleCompareDepth2dArrayOffsetF32,
+  kSampleCompareDepthCubeF32,
+  kSampleCompareDepthCubeArrayF32,
+  kSampleCompareLevelDepth2dF32,
+  kSampleCompareLevelDepth2dOffsetF32,
+  kSampleCompareLevelDepth2dArrayF32,
+  kSampleCompareLevelDepth2dArrayOffsetF32,
+  kSampleCompareLevelDepthCubeF32,
+  kSampleCompareLevelDepthCubeArrayF32,
+  kLoad1dLevelF32,
+  kLoad1dLevelU32,
+  kLoad1dLevelI32,
+  kLoad2dLevelF32,
+  kLoad2dLevelU32,
+  kLoad2dLevelI32,
+  kLoad2dArrayLevelF32,
+  kLoad2dArrayLevelU32,
+  kLoad2dArrayLevelI32,
+  kLoad3dLevelF32,
+  kLoad3dLevelU32,
+  kLoad3dLevelI32,
+  kLoadMultisampled2dF32,
+  kLoadMultisampled2dU32,
+  kLoadMultisampled2dI32,
+  kLoadDepth2dLevelF32,
+  kLoadDepth2dArrayLevelF32,
+  kLoadDepthMultisampled2dF32,
+  kStoreWO1dRgba32float,       // Not permutated for all texel formats
+  kStoreWO2dRgba32float,       // Not permutated for all texel formats
+  kStoreWO2dArrayRgba32float,  // Not permutated for all texel formats
+  kStoreWO3dRgba32float,       // Not permutated for all texel formats
+};
+
+/// @param texture_overload the ValidTextureOverload
+/// @returns true if the ValidTextureOverload builtin returns no value.
+bool ReturnsVoid(ValidTextureOverload texture_overload);
+
+/// Describes a texture builtin overload
+struct TextureOverloadCase {
+  /// Constructor for textureSample...() functions
+  TextureOverloadCase(ValidTextureOverload,
+                      const char*,
+                      TextureKind,
+                      ast::SamplerKind,
+                      ast::TextureDimension,
+                      TextureDataType,
+                      const char*,
+                      std::function<ExpressionList(ProgramBuilder*)>);
+  /// Constructor for textureLoad() functions with non-storage textures
+  TextureOverloadCase(ValidTextureOverload,
+                      const char*,
+                      TextureKind,
+                      ast::TextureDimension,
+                      TextureDataType,
+                      const char*,
+                      std::function<ExpressionList(ProgramBuilder*)>);
+  /// Constructor for textureLoad() with storage textures
+  TextureOverloadCase(ValidTextureOverload,
+                      const char*,
+                      Access,
+                      ast::TexelFormat,
+                      ast::TextureDimension,
+                      TextureDataType,
+                      const char*,
+                      std::function<ExpressionList(ProgramBuilder*)>);
+  /// Copy constructor
+  TextureOverloadCase(const TextureOverloadCase&);
+  /// Destructor
+  ~TextureOverloadCase();
+
+  /// @return a vector containing a large number (non-exhaustive) of valid
+  /// texture overloads.
+  static std::vector<TextureOverloadCase> ValidCases();
+
+  /// @param builder the AST builder used for the test
+  /// @returns the vector component type of the texture function return value
+  const ast::Type* BuildResultVectorComponentType(
+      ProgramBuilder* builder) const;
+  /// @param builder the AST builder used for the test
+  /// @returns a variable holding the test texture, automatically registered as
+  /// a global variable.
+  const ast::Variable* BuildTextureVariable(ProgramBuilder* builder) const;
+  /// @param builder the AST builder used for the test
+  /// @returns a Variable holding the test sampler, automatically registered as
+  /// a global variable.
+  const ast::Variable* BuildSamplerVariable(ProgramBuilder* builder) const;
+
+  /// The enumerator for this overload
+  const ValidTextureOverload overload;
+  /// A human readable description of the overload
+  const char* const description;
+  /// The texture kind for the texture parameter
+  const TextureKind texture_kind;
+  /// The sampler kind for the sampler parameter
+  /// Used only when texture_kind is not kStorage
+  ast::SamplerKind const sampler_kind = ast::SamplerKind::kSampler;
+  /// The access control for the storage texture
+  /// Used only when texture_kind is kStorage
+  Access const access = Access::kReadWrite;
+  /// The image format for the storage texture
+  /// Used only when texture_kind is kStorage
+  ast::TexelFormat const texel_format = ast::TexelFormat::kNone;
+  /// The dimensions of the texture parameter
+  ast::TextureDimension const texture_dimension;
+  /// The data type of the texture parameter
+  const TextureDataType texture_data_type;
+  /// Name of the function. e.g. `textureSample`, `textureSampleGrad`, etc
+  const char* const function;
+  /// A function that builds the AST arguments for the overload
+  std::function<ExpressionList(ProgramBuilder*)> const args;
+};
+
+std::ostream& operator<<(std::ostream& out, const TextureOverloadCase& data);
+
+}  // namespace test
+}  // namespace builtin
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_BUILTIN_TEXTURE_HELPER_TEST_H_
diff --git a/src/tint/ast/call_expression.cc b/src/tint/ast/call_expression.cc
new file mode 100644
index 0000000..7abf4d7
--- /dev/null
+++ b/src/tint/ast/call_expression.cc
@@ -0,0 +1,78 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/call_expression.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::CallExpression);
+
+namespace tint {
+namespace ast {
+
+namespace {
+CallExpression::Target ToTarget(const IdentifierExpression* name) {
+  CallExpression::Target target;
+  target.name = name;
+  return target;
+}
+CallExpression::Target ToTarget(const Type* type) {
+  CallExpression::Target target;
+  target.type = type;
+  return target;
+}
+}  // namespace
+
+CallExpression::CallExpression(ProgramID pid,
+                               const Source& src,
+                               const IdentifierExpression* name,
+                               ExpressionList a)
+    : Base(pid, src), target(ToTarget(name)), args(a) {
+  TINT_ASSERT(AST, name);
+  TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, name, program_id);
+  for (auto* arg : args) {
+    TINT_ASSERT(AST, arg);
+    TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, arg, program_id);
+  }
+}
+
+CallExpression::CallExpression(ProgramID pid,
+                               const Source& src,
+                               const Type* type,
+                               ExpressionList a)
+    : Base(pid, src), target(ToTarget(type)), args(a) {
+  TINT_ASSERT(AST, type);
+  TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, type, program_id);
+  for (auto* arg : args) {
+    TINT_ASSERT(AST, arg);
+    TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, arg, program_id);
+  }
+}
+
+CallExpression::CallExpression(CallExpression&&) = default;
+
+CallExpression::~CallExpression() = default;
+
+const CallExpression* CallExpression::Clone(CloneContext* ctx) const {
+  // Clone arguments outside of create() call to have deterministic ordering
+  auto src = ctx->Clone(source);
+  auto p = ctx->Clone(args);
+  return target.name
+             ? ctx->dst->create<CallExpression>(src, ctx->Clone(target.name), p)
+             : ctx->dst->create<CallExpression>(src, ctx->Clone(target.type),
+                                                p);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/call_expression.h b/src/tint/ast/call_expression.h
new file mode 100644
index 0000000..efb1841
--- /dev/null
+++ b/src/tint/ast/call_expression.h
@@ -0,0 +1,84 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_CALL_EXPRESSION_H_
+#define SRC_TINT_AST_CALL_EXPRESSION_H_
+
+#include "src/tint/ast/expression.h"
+
+namespace tint {
+namespace ast {
+
+// Forward declarations.
+class Type;
+class IdentifierExpression;
+
+/// A call expression - represents either a:
+/// * sem::Function
+/// * sem::Builtin
+/// * sem::TypeConstructor
+/// * sem::TypeConversion
+class CallExpression final : public Castable<CallExpression, Expression> {
+ public:
+  /// Constructor
+  /// @param program_id the identifier of the program that owns this node
+  /// @param source the call expression source
+  /// @param name the function or type name
+  /// @param args the arguments
+  CallExpression(ProgramID program_id,
+                 const Source& source,
+                 const IdentifierExpression* name,
+                 ExpressionList args);
+
+  /// Constructor
+  /// @param program_id the identifier of the program that owns this node
+  /// @param source the call expression source
+  /// @param type the type
+  /// @param args the arguments
+  CallExpression(ProgramID program_id,
+                 const Source& source,
+                 const Type* type,
+                 ExpressionList args);
+
+  /// Move constructor
+  CallExpression(CallExpression&&);
+  ~CallExpression() override;
+
+  /// Clones this node and all transitive child nodes using the `CloneContext`
+  /// `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned node
+  const CallExpression* Clone(CloneContext* ctx) const override;
+
+  /// Target is either an identifier, or a Type.
+  /// One of these must be nullptr and the other a non-nullptr.
+  struct Target {
+    /// name is a function or builtin to call, or type name to construct or
+    /// cast-to
+    const IdentifierExpression* name = nullptr;
+    /// type to construct or cast-to
+    const Type* type = nullptr;
+  };
+
+  /// The target function
+  const Target target;
+
+  /// The arguments
+  const ExpressionList args;
+};
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_CALL_EXPRESSION_H_
diff --git a/src/tint/ast/call_expression_test.cc b/src/tint/ast/call_expression_test.cc
new file mode 100644
index 0000000..a150af6
--- /dev/null
+++ b/src/tint/ast/call_expression_test.cc
@@ -0,0 +1,149 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest-spi.h"
+#include "src/tint/ast/test_helper.h"
+
+namespace tint {
+namespace ast {
+namespace {
+
+using CallExpressionTest = TestHelper;
+
+TEST_F(CallExpressionTest, CreationIdentifier) {
+  auto* func = Expr("func");
+  ExpressionList params;
+  params.push_back(Expr("param1"));
+  params.push_back(Expr("param2"));
+
+  auto* stmt = create<CallExpression>(func, params);
+  EXPECT_EQ(stmt->target.name, func);
+  EXPECT_EQ(stmt->target.type, nullptr);
+
+  const auto& vec = stmt->args;
+  ASSERT_EQ(vec.size(), 2u);
+  EXPECT_EQ(vec[0], params[0]);
+  EXPECT_EQ(vec[1], params[1]);
+}
+
+TEST_F(CallExpressionTest, CreationIdentifier_WithSource) {
+  auto* func = Expr("func");
+  auto* stmt = create<CallExpression>(Source{{20, 2}}, func, ExpressionList{});
+  EXPECT_EQ(stmt->target.name, func);
+  EXPECT_EQ(stmt->target.type, nullptr);
+
+  auto src = stmt->source;
+  EXPECT_EQ(src.range.begin.line, 20u);
+  EXPECT_EQ(src.range.begin.column, 2u);
+}
+
+TEST_F(CallExpressionTest, CreationType) {
+  auto* type = ty.f32();
+  ExpressionList params;
+  params.push_back(Expr("param1"));
+  params.push_back(Expr("param2"));
+
+  auto* stmt = create<CallExpression>(type, params);
+  EXPECT_EQ(stmt->target.name, nullptr);
+  EXPECT_EQ(stmt->target.type, type);
+
+  const auto& vec = stmt->args;
+  ASSERT_EQ(vec.size(), 2u);
+  EXPECT_EQ(vec[0], params[0]);
+  EXPECT_EQ(vec[1], params[1]);
+}
+
+TEST_F(CallExpressionTest, CreationType_WithSource) {
+  auto* type = ty.f32();
+  auto* stmt = create<CallExpression>(Source{{20, 2}}, type, ExpressionList{});
+  EXPECT_EQ(stmt->target.name, nullptr);
+  EXPECT_EQ(stmt->target.type, type);
+
+  auto src = stmt->source;
+  EXPECT_EQ(src.range.begin.line, 20u);
+  EXPECT_EQ(src.range.begin.column, 2u);
+}
+
+TEST_F(CallExpressionTest, IsCall) {
+  auto* func = Expr("func");
+  auto* stmt = create<CallExpression>(func, ExpressionList{});
+  EXPECT_TRUE(stmt->Is<CallExpression>());
+}
+
+TEST_F(CallExpressionTest, Assert_Null_Identifier) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b;
+        b.create<CallExpression>(static_cast<IdentifierExpression*>(nullptr),
+                                 ExpressionList{});
+      },
+      "internal compiler error");
+}
+
+TEST_F(CallExpressionTest, Assert_Null_Type) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b;
+        b.create<CallExpression>(static_cast<Type*>(nullptr), ExpressionList{});
+      },
+      "internal compiler error");
+}
+
+TEST_F(CallExpressionTest, Assert_Null_Param) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b;
+        ExpressionList params;
+        params.push_back(b.Expr("param1"));
+        params.push_back(nullptr);
+        params.push_back(b.Expr("param2"));
+        b.create<CallExpression>(b.Expr("func"), params);
+      },
+      "internal compiler error");
+}
+
+TEST_F(CallExpressionTest, Assert_DifferentProgramID_Identifier) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b1;
+        ProgramBuilder b2;
+        b1.create<CallExpression>(b2.Expr("func"), ExpressionList{});
+      },
+      "internal compiler error");
+}
+
+TEST_F(CallExpressionTest, Assert_DifferentProgramID_Type) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b1;
+        ProgramBuilder b2;
+        b1.create<CallExpression>(b2.ty.f32(), ExpressionList{});
+      },
+      "internal compiler error");
+}
+
+TEST_F(CallExpressionTest, Assert_DifferentProgramID_Param) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b1;
+        ProgramBuilder b2;
+        b1.create<CallExpression>(b1.Expr("func"),
+                                  ExpressionList{b2.Expr("param1")});
+      },
+      "internal compiler error");
+}
+
+}  // namespace
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/call_statement.cc b/src/tint/ast/call_statement.cc
new file mode 100644
index 0000000..be2e97c
--- /dev/null
+++ b/src/tint/ast/call_statement.cc
@@ -0,0 +1,44 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/call_statement.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::CallStatement);
+
+namespace tint {
+namespace ast {
+
+CallStatement::CallStatement(ProgramID pid,
+                             const Source& src,
+                             const CallExpression* call)
+    : Base(pid, src), expr(call) {
+  TINT_ASSERT(AST, expr);
+  TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, expr, program_id);
+}
+
+CallStatement::CallStatement(CallStatement&&) = default;
+
+CallStatement::~CallStatement() = default;
+
+const CallStatement* CallStatement::Clone(CloneContext* ctx) const {
+  // Clone arguments outside of create() call to have deterministic ordering
+  auto src = ctx->Clone(source);
+  auto* call = ctx->Clone(expr);
+  return ctx->dst->create<CallStatement>(src, call);
+}
+
+}  // namespace ast
+}  // namespace tint
diff --git a/src/tint/ast/call_statement.h b/src/tint/ast/call_statement.h
new file mode 100644
index 0000000..b9e0c4c
--- /dev/null
+++ b/src/tint/ast/call_statement.h
@@ -0,0 +1,49 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_AST_CALL_STATEMENT_H_
+#define SRC_TINT_AST_CALL_STATEMENT_H_
+
+#include "src/tint/ast/call_expression.h"
+#include "src/tint/ast/statement.h"
+
+namespace tint {
+namespace ast {
+
+/// A call expression
+class CallStatement final : public Castable<CallStatement, Statement> {
+ public:
+  /// Constructor
+  /// @param pid the identifier of the program that owns this node
+  /// @param src the source of this node for the statement
+  /// @param call the function
+  CallStatement(ProgramID pid, const Source& src, const CallExpression* call);
+  /// Move constructor
+  CallStatement(CallStatement&&);
+  ~CallStatement() override;
+
+  /// Clones this node and all transitive child nodes using the `CloneContext`
+  /// `ctx`.
+  /// @param ctx the clone context
+  /// @return the newly cloned node
+  const CallStatement* Clone(CloneContext* ctx) const override;
+
+  /// The call expression
+  const CallExpression* const expr;
+};
+
+}  // namespace ast
+}  // namespace tint
+
+#endif  // SRC_TINT_AST_CALL_STATEMENT_H_
diff --git a/src/tint/ast/call_statement_test.cc b/src/tint/ast/call_statement_test.cc
new file mode 100644
index 0000000..1267a6d
--- /dev/null
+++ b/src/tint/ast/call_statement_test.cc
@@ -0,0 +1,60 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/ast/call_statement.h"
+
<