Update builtin methods.

This CL removes the import mechanism, the identifier paths and updates
all of the standard library methods to be builtins.

Bug: tint:242
Change-Id: If09b98a155ae49ced3986ba2c9b517a060693006
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/28720
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 084ee6f..e522f12 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -269,8 +269,6 @@
     "src/ast/identifier_expression.h",
     "src/ast/if_statement.cc",
     "src/ast/if_statement.h",
-    "src/ast/import.cc",
-    "src/ast/import.h",
     "src/ast/int_literal.cc",
     "src/ast/int_literal.h",
     "src/ast/intrinsic.cc",
@@ -710,7 +708,6 @@
     "src/ast/function_test.cc",
     "src/ast/identifier_expression_test.cc",
     "src/ast/if_statement_test.cc",
-    "src/ast/import_test.cc",
     "src/ast/int_literal_test.cc",
     "src/ast/location_decoration_test.cc",
     "src/ast/loop_statement_test.cc",
@@ -908,7 +905,6 @@
     "src/reader/wgsl/parser_impl_global_variable_decl_test.cc",
     "src/reader/wgsl/parser_impl_if_stmt_test.cc",
     "src/reader/wgsl/parser_impl_image_storage_type_test.cc",
-    "src/reader/wgsl/parser_impl_import_decl_test.cc",
     "src/reader/wgsl/parser_impl_inclusive_or_expression_test.cc",
     "src/reader/wgsl/parser_impl_logical_and_expression_test.cc",
     "src/reader/wgsl/parser_impl_logical_or_expression_test.cc",
@@ -991,7 +987,6 @@
     "src/writer/wgsl/generator_impl_function_test.cc",
     "src/writer/wgsl/generator_impl_identifier_test.cc",
     "src/writer/wgsl/generator_impl_if_test.cc",
-    "src/writer/wgsl/generator_impl_import_test.cc",
     "src/writer/wgsl/generator_impl_loop_test.cc",
     "src/writer/wgsl/generator_impl_member_accessor_test.cc",
     "src/writer/wgsl/generator_impl_return_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 15c84cd..58129fb 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -90,8 +90,6 @@
   ast/identifier_expression.h
   ast/if_statement.cc
   ast/if_statement.h
-  ast/import.cc
-  ast/import.h
   ast/int_literal.cc
   ast/int_literal.h
   ast/intrinsic.cc
@@ -319,7 +317,6 @@
   ast/function_test.cc
   ast/identifier_expression_test.cc
   ast/if_statement_test.cc
-  ast/import_test.cc
   ast/int_literal_test.cc
   ast/location_decoration_test.cc
   ast/loop_statement_test.cc
@@ -439,7 +436,6 @@
     reader/wgsl/parser_impl_global_variable_decl_test.cc
     reader/wgsl/parser_impl_if_stmt_test.cc
     reader/wgsl/parser_impl_image_storage_type_test.cc
-    reader/wgsl/parser_impl_import_decl_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
@@ -538,7 +534,6 @@
     writer/wgsl/generator_impl_function_test.cc
     writer/wgsl/generator_impl_identifier_test.cc
     writer/wgsl/generator_impl_if_test.cc
-    writer/wgsl/generator_impl_import_test.cc
     writer/wgsl/generator_impl_loop_test.cc
     writer/wgsl/generator_impl_member_accessor_test.cc
     writer/wgsl/generator_impl_return_test.cc
diff --git a/src/ast/identifier_expression.cc b/src/ast/identifier_expression.cc
index d14d5f4..375387e 100644
--- a/src/ast/identifier_expression.cc
+++ b/src/ast/identifier_expression.cc
@@ -18,62 +18,27 @@
 namespace ast {
 
 IdentifierExpression::IdentifierExpression(const std::string& name)
-    : Expression(), segments_({name}) {}
+    : Expression(), name_(name) {}
 
 IdentifierExpression::IdentifierExpression(const Source& source,
                                            const std::string& name)
-    : Expression(source), segments_({name}) {}
-
-IdentifierExpression::IdentifierExpression(std::vector<std::string> segments)
-    : Expression(), segments_(std::move(segments)) {}
-
-IdentifierExpression::IdentifierExpression(const Source& source,
-                                           std::vector<std::string> segments)
-    : Expression(source), segments_(std::move(segments)) {}
+    : Expression(source), name_(name) {}
 
 IdentifierExpression::IdentifierExpression(IdentifierExpression&&) = default;
 
 IdentifierExpression::~IdentifierExpression() = default;
 
-std::string IdentifierExpression::path() const {
-  if (segments_.size() < 2) {
-    return "";
-  }
-
-  std::string path = "";
-  // We skip the last segment as that's the name, not part of the path
-  for (size_t i = 0; i < segments_.size() - 1; ++i) {
-    if (i > 0) {
-      path += "::";
-    }
-    path += segments_[i];
-  }
-  return path;
-}
-
 bool IdentifierExpression::IsIdentifier() const {
   return true;
 }
 
 bool IdentifierExpression::IsValid() const {
-  if (segments_.size() == 0)
-    return false;
-
-  for (const auto& segment : segments_) {
-    if (segment.size() == 0)
-      return false;
-  }
-  return true;
+  return !name_.empty();
 }
 
 void IdentifierExpression::to_str(std::ostream& out, size_t indent) const {
   make_indent(out, indent);
-  out << "Identifier{";
-  if (has_path()) {
-    out << path() << "::";
-  }
-  out << name();
-  out << "}" << std::endl;
+  out << "Identifier{" << name_ << "}" << std::endl;
 }
 
 }  // namespace ast
diff --git a/src/ast/identifier_expression.h b/src/ast/identifier_expression.h
index e45cd8f..af08b69 100644
--- a/src/ast/identifier_expression.h
+++ b/src/ast/identifier_expression.h
@@ -16,10 +16,9 @@
 #define SRC_AST_IDENTIFIER_EXPRESSION_H_
 
 #include <string>
-#include <utility>
-#include <vector>
 
 #include "src/ast/expression.h"
+#include "src/ast/intrinsic.h"
 
 namespace tint {
 namespace ast {
@@ -34,31 +33,20 @@
   /// @param source the source
   /// @param name the name
   IdentifierExpression(const Source& source, const std::string& name);
-  /// Constructor
-  /// @param segments the name segments
-  explicit IdentifierExpression(std::vector<std::string> segments);
-  /// Constructor
-  /// @param source the identifier expression source
-  /// @param segments the name segments
-  IdentifierExpression(const Source& source, std::vector<std::string> segments);
   /// Move constructor
   IdentifierExpression(IdentifierExpression&&);
   ~IdentifierExpression() override;
 
-  /// Sets the name segments
-  /// @param segments the name segments
-  void set_segments(std::vector<std::string> segments) {
-    segments_ = std::move(segments);
-  }
-  /// @returns the name
-  std::vector<std::string> segments() const { return segments_; }
-
-  /// @returns true if this identifier has a path component
-  bool has_path() const { return segments_.size() > 1; }
-  /// @returns the path part of the identifier or blank if no path
-  std::string path() const;
   /// @returns the name part of the identifier
-  std::string name() const { return segments_.back(); }
+  std::string name() const { return name_; }
+
+  /// Sets the intrinsic for this identifier
+  /// @param i the intrinsic to set
+  void set_intrinsic(Intrinsic i) { intrinsic_ = i; }
+  /// @returns the intrinsic this identifier represents
+  Intrinsic intrinsic() const { return intrinsic_; }
+  /// @returns true if this identifier is for an intrinsic
+  bool IsIntrinsic() const { return intrinsic_ != Intrinsic::kNone; }
 
   /// @returns true if this is an identifier expression
   bool IsIdentifier() const override;
@@ -74,7 +62,8 @@
  private:
   IdentifierExpression(const IdentifierExpression&) = delete;
 
-  std::vector<std::string> segments_;
+  Intrinsic intrinsic_ = Intrinsic::kNone;
+  std::string name_;
 };
 
 }  // namespace ast
diff --git a/src/ast/identifier_expression_test.cc b/src/ast/identifier_expression_test.cc
index 0886953..ad0ff98 100644
--- a/src/ast/identifier_expression_test.cc
+++ b/src/ast/identifier_expression_test.cc
@@ -24,19 +24,11 @@
 
 TEST_F(IdentifierExpressionTest, Creation) {
   IdentifierExpression i("ident");
-  ASSERT_EQ(i.segments().size(), 1u);
-  EXPECT_EQ(i.segments()[0], "ident");
-  EXPECT_EQ(i.path(), "");
   EXPECT_EQ(i.name(), "ident");
 }
 
 TEST_F(IdentifierExpressionTest, Creation_WithSource) {
-  IdentifierExpression i(Source{20, 2}, {"ns1", "ns2", "ident"});
-  ASSERT_EQ(i.segments().size(), 3u);
-  EXPECT_EQ(i.segments()[0], "ns1");
-  EXPECT_EQ(i.segments()[1], "ns2");
-  EXPECT_EQ(i.segments()[2], "ident");
-  EXPECT_EQ(i.path(), "ns1::ns2");
+  IdentifierExpression i(Source{20, 2}, "ident");
   EXPECT_EQ(i.name(), "ident");
 
   auto src = i.source();
@@ -54,21 +46,11 @@
   EXPECT_TRUE(i.IsValid());
 }
 
-TEST_F(IdentifierExpressionTest, IsValid_WithNamespaces) {
-  IdentifierExpression i({"ns1", "n2", "ident"});
-  EXPECT_TRUE(i.IsValid());
-}
-
 TEST_F(IdentifierExpressionTest, IsValid_BlankName) {
   IdentifierExpression i("");
   EXPECT_FALSE(i.IsValid());
 }
 
-TEST_F(IdentifierExpressionTest, IsValid_BlankNamespace) {
-  IdentifierExpression i({"ns1", "", "ident"});
-  EXPECT_FALSE(i.IsValid());
-}
-
 TEST_F(IdentifierExpressionTest, ToStr) {
   IdentifierExpression i("ident");
   std::ostringstream out;
@@ -77,14 +59,6 @@
 )");
 }
 
-TEST_F(IdentifierExpressionTest, ToStr_WithNamespace) {
-  IdentifierExpression i({"ns1", "ns2", "ident"});
-  std::ostringstream out;
-  i.to_str(out, 2);
-  EXPECT_EQ(out.str(), R"(  Identifier{ns1::ns2::ident}
-)");
-}
-
 }  // namespace
 }  // namespace ast
 }  // namespace tint
diff --git a/src/ast/import.cc b/src/ast/import.cc
deleted file mode 100644
index b86c6b1..0000000
--- a/src/ast/import.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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/ast/import.h"
-
-#include <cctype>
-
-namespace tint {
-namespace ast {
-
-Import::Import() = default;
-
-Import::Import(const std::string& path, const std::string& name)
-    : Node(), path_(path), name_(name) {}
-
-Import::Import(const Source& source,
-               const std::string& path,
-               const std::string& name)
-    : Node(source), path_(path), name_(name) {}
-
-Import::Import(Import&&) = default;
-
-Import::~Import() = default;
-
-bool Import::IsValid() const {
-  if (path_.length() == 0) {
-    return false;
-  }
-
-  auto len = name_.length();
-  if (len == 0) {
-    return false;
-  }
-
-  // Verify the import name ends in a character, number or _
-  if (len > 2 && !std::isalnum(name_[len - 1]) && name_[len] != '_') {
-    return false;
-  }
-
-  return true;
-}
-
-void Import::to_str(std::ostream& out, size_t indent) const {
-  make_indent(out, indent);
-  out << R"(Import{")" + path_ + R"(" as )" + name_ + "}" << std::endl;
-}
-
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/import.h b/src/ast/import.h
deleted file mode 100644
index 5f40188..0000000
--- a/src/ast/import.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// 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_AST_IMPORT_H_
-#define SRC_AST_IMPORT_H_
-
-#include <memory>
-#include <ostream>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "src/ast/node.h"
-
-namespace tint {
-namespace ast {
-
-/// An import statement.
-class Import : public Node {
- public:
-  /// Create a new empty import statement
-  Import();
-  /// Create a new import statement
-  /// @param path The import path e.g. GLSL.std.450
-  /// @param name The import reference name e.g. std
-  Import(const std::string& path, const std::string& name);
-  /// Create a new import statement
-  /// @param source The input source for the import statement
-  /// @param path The import path e.g. GLSL.std.430
-  /// @param name The import reference name e.g. std
-  Import(const Source& source,
-         const std::string& path,
-         const std::string& name);
-  /// Move constructor
-  Import(Import&&);
-
-  ~Import() override;
-
-  /// Sets the import path
-  /// @param path the path to set
-  void set_path(const std::string& path) { path_ = path; }
-  /// @returns the import path
-  const std::string& path() const { return path_; }
-  /// Sets the import name
-  /// @param name the name to set
-  void set_name(const std::string& name) { name_ = name; }
-  /// @returns the import name
-  const std::string& name() const { return name_; }
-
-  /// Add the given |name| to map to the given |id|
-  /// @param name the name to map
-  /// @param id the id to map too
-  void AddMethodId(const std::string& name, uint32_t id) {
-    method_to_id_map_[name] = id;
-  }
-
-  /// Retrieves the id for a given name
-  /// @param name the name to lookup
-  /// @returns the id for the given name or 0 on failure
-  uint32_t GetIdForMethod(const std::string& name) const {
-    auto val = method_to_id_map_.find(name);
-    if (val == method_to_id_map_.end()) {
-      return 0;
-    }
-    return val->second;
-  }
-
-  /// @returns true if the name and path are both present
-  bool IsValid() const override;
-
-  /// Writes a representation of the node to the output stream
-  /// @param out the stream to write to
-  /// @param indent number of spaces to indent the node when writing
-  void to_str(std::ostream& out, size_t indent) const override;
-
- private:
-  Import(const Import&) = delete;
-
-  std::string path_;
-  std::string name_;
-  std::unordered_map<std::string, uint32_t> method_to_id_map_;
-};
-
-/// A list of unique imports
-using ImportList = std::vector<std::unique_ptr<Import>>;
-
-}  // namespace ast
-}  // namespace tint
-
-#endif  // SRC_AST_IMPORT_H_
diff --git a/src/ast/import_test.cc b/src/ast/import_test.cc
deleted file mode 100644
index af77bab..0000000
--- a/src/ast/import_test.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// 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/ast/import.h"
-
-#include <sstream>
-
-#include "gtest/gtest.h"
-
-namespace tint {
-namespace ast {
-namespace {
-
-using ImportTest = testing::Test;
-
-TEST_F(ImportTest, Creation) {
-  Import i("GLSL.std.430", "std::glsl");
-
-  EXPECT_EQ(i.path(), "GLSL.std.430");
-  EXPECT_EQ(i.name(), "std::glsl");
-  EXPECT_EQ(i.line(), 0u);
-  EXPECT_EQ(i.column(), 0u);
-}
-
-TEST_F(ImportTest, CreationWithSource) {
-  Source s{27, 4};
-  Import i(s, "GLSL.std.430", "std::glsl");
-
-  EXPECT_EQ(i.path(), "GLSL.std.430");
-  EXPECT_EQ(i.name(), "std::glsl");
-  EXPECT_EQ(i.line(), 27u);
-  EXPECT_EQ(i.column(), 4u);
-}
-
-TEST_F(ImportTest, CreationEmpty) {
-  Source s{27, 4};
-  Import i;
-  i.set_source(s);
-  i.set_path("GLSL.std.430");
-  i.set_name("std::glsl");
-
-  EXPECT_EQ(i.path(), "GLSL.std.430");
-  EXPECT_EQ(i.name(), "std::glsl");
-  EXPECT_EQ(i.line(), 27u);
-  EXPECT_EQ(i.column(), 4u);
-}
-
-TEST_F(ImportTest, to_str) {
-  Import i{"GLSL.std.430", "std::glsl"};
-  std::ostringstream out;
-  i.to_str(out, 2);
-  EXPECT_EQ(out.str(), "  Import{\"GLSL.std.430\" as std::glsl}\n");
-}
-
-TEST_F(ImportTest, IsValid) {
-  Import i{"GLSL.std.430", "std::glsl"};
-  EXPECT_TRUE(i.IsValid());
-}
-
-TEST_F(ImportTest, IsValid_MissingPath) {
-  Import i{"", "std::glsl"};
-  EXPECT_FALSE(i.IsValid());
-}
-
-TEST_F(ImportTest, IsValid_MissingName) {
-  Import i{"GLSL.std.430", ""};
-  EXPECT_FALSE(i.IsValid());
-}
-
-TEST_F(ImportTest, IsValid_MissingBoth) {
-  Import i;
-  EXPECT_FALSE(i.IsValid());
-}
-
-TEST_F(ImportTest, IsValid_InvalidEndingCharacter) {
-  Import i{"GLSL.std.430", "std::glsl::"};
-  EXPECT_FALSE(i.IsValid());
-}
-
-}  // namespace
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/intrinsic.cc b/src/ast/intrinsic.cc
index da5a855..6006bcb 100644
--- a/src/ast/intrinsic.cc
+++ b/src/ast/intrinsic.cc
@@ -16,37 +16,242 @@
 
 namespace tint {
 namespace ast {
+
+std::ostream& operator<<(std::ostream& out, Intrinsic i) {
+  switch (i) {
+    case Intrinsic::kAbs:
+      out << "abs";
+      break;
+    case Intrinsic::kAcos:
+      out << "acos";
+      break;
+    case Intrinsic::kAll:
+      out << "all";
+      break;
+    case Intrinsic::kAny:
+      out << "any";
+      break;
+    case Intrinsic::kAsin:
+      out << "asin";
+      break;
+    case Intrinsic::kAtan:
+      out << "atan";
+      break;
+    case Intrinsic::kAtan2:
+      out << "atan2";
+      break;
+    case Intrinsic::kCeil:
+      out << "ceil";
+      break;
+    case Intrinsic::kClamp:
+      out << "clamp";
+      break;
+    case Intrinsic::kCos:
+      out << "cos";
+      break;
+    case Intrinsic::kCosh:
+      out << "cosh";
+      break;
+    case Intrinsic::kCountOneBits:
+      out << "countOneBits";
+      break;
+    case Intrinsic::kCross:
+      out << "cross";
+      break;
+    case Intrinsic::kDeterminant:
+      out << "determinant";
+      break;
+    case Intrinsic::kDistance:
+      out << "distance";
+      break;
+    case Intrinsic::kDot:
+      out << "dot";
+      break;
+    case Intrinsic::kDpdx:
+      out << "dpdx";
+      break;
+    case Intrinsic::kDpdxCoarse:
+      out << "dpdxCoarse";
+      break;
+    case Intrinsic::kDpdxFine:
+      out << "dpdxFine";
+      break;
+    case Intrinsic::kDpdy:
+      out << "dpdy";
+      break;
+    case Intrinsic::kDpdyCoarse:
+      out << "dpdyCoarse";
+      break;
+    case Intrinsic::kDpdyFine:
+      out << "dpdyFine";
+      break;
+    case Intrinsic::kExp:
+      out << "exp";
+      break;
+    case Intrinsic::kExp2:
+      out << "exp2";
+      break;
+    case Intrinsic::kFaceForward:
+      out << "faceForward";
+      break;
+    case Intrinsic::kFloor:
+      out << "floor";
+      break;
+    case Intrinsic::kFma:
+      out << "fma";
+      break;
+    case Intrinsic::kFract:
+      out << "fract";
+      break;
+    case Intrinsic::kFrexp:
+      out << "frexp";
+      break;
+    case Intrinsic::kFwidth:
+      out << "fwidth";
+      break;
+    case Intrinsic::kFwidthCoarse:
+      out << "fwidthCoarse";
+      break;
+    case Intrinsic::kFwidthFine:
+      out << "fwidthFine";
+      break;
+    case Intrinsic::kInverseSqrt:
+      out << "inverseSqrt";
+      break;
+    case Intrinsic::kIsFinite:
+      out << "isFinite";
+      break;
+    case Intrinsic::kIsInf:
+      out << "isInf";
+      break;
+    case Intrinsic::kIsNan:
+      out << "isNan";
+      break;
+    case Intrinsic::kIsNormal:
+      out << "isNormal";
+      break;
+    case Intrinsic::kLdexp:
+      out << "ldexp";
+      break;
+    case Intrinsic::kLength:
+      out << "length";
+      break;
+    case Intrinsic::kLog:
+      out << "log";
+      break;
+    case Intrinsic::kLog2:
+      out << "log2";
+      break;
+    case Intrinsic::kMax:
+      out << "max";
+      break;
+    case Intrinsic::kMin:
+      out << "min";
+      break;
+    case Intrinsic::kMix:
+      out << "mix";
+      break;
+    case Intrinsic::kModf:
+      out << "modf";
+      break;
+    case Intrinsic::kNormalize:
+      out << "normalize";
+      break;
+    case Intrinsic::kOuterProduct:
+      out << "outerProduct";
+      break;
+    case Intrinsic::kPow:
+      out << "pow";
+      break;
+    case Intrinsic::kReflect:
+      out << "reflect";
+      break;
+    case Intrinsic::kReverseBits:
+      out << "reverseBits";
+      break;
+    case Intrinsic::kRound:
+      out << "round";
+      break;
+    case Intrinsic::kSelect:
+      out << "select";
+      break;
+    case Intrinsic::kSign:
+      out << "sign";
+      break;
+    case Intrinsic::kSin:
+      out << "sin";
+      break;
+    case Intrinsic::kSinh:
+      out << "sinh";
+      break;
+    case Intrinsic::kSmoothStep:
+      out << "smoothStep";
+      break;
+    case Intrinsic::kSqrt:
+      out << "sqrt";
+      break;
+    case Intrinsic::kStep:
+      out << "step";
+      break;
+    case Intrinsic::kTan:
+      out << "tan";
+      break;
+    case Intrinsic::kTanh:
+      out << "tanh";
+      break;
+    case Intrinsic::kTextureLoad:
+      out << "textureLoad";
+      break;
+    case Intrinsic::kTextureSample:
+      out << "textureSample";
+      break;
+    case Intrinsic::kTextureSampleBias:
+      out << "textureSampleBias";
+      break;
+    case Intrinsic::kTextureSampleCompare:
+      out << "textureSampleCompare";
+      break;
+    case Intrinsic::kTextureSampleLevel:
+      out << "textureSampleLevel";
+      break;
+    case Intrinsic::kTrunc:
+      out << "trunc";
+      break;
+    default:
+      out << "Unknown";
+      break;
+  }
+  return out;
+}
+
 namespace intrinsic {
 
-bool IsCoarseDerivative(const std::string& name) {
-  return name == "dpdxCoarse" || name == "dpdyCoarse" || name == "fwidthCoarse";
+bool IsCoarseDerivative(ast::Intrinsic i) {
+  return i == Intrinsic::kDpdxCoarse ||
+         i == Intrinsic::kDpdyCoarse | i == Intrinsic::kFwidthCoarse;
 }
 
-bool IsFineDerivative(const std::string& name) {
-  return name == "dpdxFine" || name == "dpdyFine" || name == "fwidthFine";
+bool IsFineDerivative(ast::Intrinsic i) {
+  return i == Intrinsic::kDpdxFine || i == Intrinsic::kDpdyFine ||
+         i == Intrinsic::kFwidthFine;
 }
 
-bool IsDerivative(const std::string& name) {
-  return name == "dpdx" || name == "dpdy" || name == "fwidth" ||
-         IsCoarseDerivative(name) || IsFineDerivative(name);
+bool IsDerivative(ast::Intrinsic i) {
+  return i == Intrinsic::kDpdx || i == Intrinsic::kDpdy ||
+         i == Intrinsic::kFwidth || IsCoarseDerivative(i) ||
+         IsFineDerivative(i);
 }
 
-bool IsFloatClassificationIntrinsic(const std::string& name) {
-  return name == "isFinite" || name == "isInf" || name == "isNan" ||
-         name == "isNormal";
+bool IsFloatClassificationIntrinsic(ast::Intrinsic i) {
+  return i == Intrinsic::kIsFinite || i == Intrinsic::kIsInf ||
+         i == Intrinsic::kIsNan || i == Intrinsic::kIsNormal;
 }
 
-bool IsTextureOperationIntrinsic(const std::string& name) {
-  return name == "textureLoad" || name == "textureSample" ||
-         name == "textureSampleLevel" || name == "textureSampleBias" ||
-         name == "textureSampleCompare";
-}
-
-bool IsIntrinsic(const std::string& name) {
-  return IsDerivative(name) || name == "all" || name == "any" ||
-         IsFloatClassificationIntrinsic(name) ||
-         IsTextureOperationIntrinsic(name) || name == "dot" ||
-         name == "outerProduct" || name == "select";
+bool IsTextureIntrinsic(ast::Intrinsic i) {
+  return i == Intrinsic::kTextureLoad || i == Intrinsic::kTextureSample ||
+         i == Intrinsic::kTextureSampleLevel ||
+         i == Intrinsic::kTextureSampleBias ||
+         i == Intrinsic::kTextureSampleCompare;
 }
 
 }  // namespace intrinsic
diff --git a/src/ast/intrinsic.h b/src/ast/intrinsic.h
index 1196db9..c66f749 100644
--- a/src/ast/intrinsic.h
+++ b/src/ast/intrinsic.h
@@ -15,41 +15,111 @@
 #ifndef SRC_AST_INTRINSIC_H_
 #define SRC_AST_INTRINSIC_H_
 
+#include <ostream>
 #include <string>
 
 namespace tint {
 namespace ast {
+
+enum class Intrinsic {
+  kNone = -1,
+
+  kAbs,
+  kAcos,
+  kAll,
+  kAny,
+  kAsin,
+  kAtan,
+  kAtan2,
+  kCeil,
+  kClamp,
+  kCos,
+  kCosh,
+  kCountOneBits,
+  kCross,
+  kDeterminant,
+  kDistance,
+  kDot,
+  kDpdx,
+  kDpdxCoarse,
+  kDpdxFine,
+  kDpdy,
+  kDpdyCoarse,
+  kDpdyFine,
+  kExp,
+  kExp2,
+  kFaceForward,
+  kFloor,
+  kFma,
+  kFract,
+  kFrexp,
+  kFwidth,
+  kFwidthCoarse,
+  kFwidthFine,
+  kInverseSqrt,
+  kIsFinite,
+  kIsInf,
+  kIsNan,
+  kIsNormal,
+  kLdexp,
+  kLength,
+  kLog,
+  kLog2,
+  kMax,
+  kMin,
+  kMix,
+  kModf,
+  kNormalize,
+  kOuterProduct,
+  kPow,
+  kReflect,
+  kReverseBits,
+  kRound,
+  kSelect,
+  kSign,
+  kSin,
+  kSinh,
+  kSmoothStep,
+  kSqrt,
+  kStep,
+  kTan,
+  kTanh,
+  kTextureLoad,
+  kTextureSample,
+  kTextureSampleBias,
+  kTextureSampleCompare,
+  kTextureSampleLevel,
+  kTrunc
+};
+
+std::ostream& operator<<(std::ostream& out, Intrinsic i);
+
 namespace intrinsic {
 
 /// Determines if the given |name| is a coarse derivative
-/// @param name the name to check
+/// @param i the intrinsic
 /// @returns true if the given derivative is coarse.
-bool IsCoarseDerivative(const std::string& name);
+bool IsCoarseDerivative(ast::Intrinsic i);
 
 /// Determines if the given |name| is a fine derivative
-/// @param name the name to check
+/// @param i the intrinsic
 /// @returns true if the given derivative is fine.
-bool IsFineDerivative(const std::string& name);
+bool IsFineDerivative(ast::Intrinsic i);
 
 /// Determine if the given |name| is a derivative intrinsic
-/// @param name the name to check
+/// @param i the intrinsic
 /// @returns true if the given |name| is a derivative intrinsic
-bool IsDerivative(const std::string& name);
+bool IsDerivative(ast::Intrinsic i);
 
 /// Determines if the given |name| is a float classification intrinsic
-/// @param name the name to check
+/// @param i the intrinsic
 /// @returns true if the given |name| is a float intrinsic
-bool IsFloatClassificationIntrinsic(const std::string& name);
+bool IsFloatClassificationIntrinsic(ast::Intrinsic i);
 
 /// Determines if the given |name| is a texture operation intrinsic
-/// @param name the name to check
+/// @param i the intrinsic
 /// @returns true if the given |name| is a texture operation intrinsic
-bool IsTextureOperationIntrinsic(const std::string& name);
-
-/// Determines if the given |name| is an intrinsic
-/// @param name the name to check
-/// @returns true if the given |name| is an intrinsic
-bool IsIntrinsic(const std::string& name);
+bool IsTextureIntrinsic(ast::Intrinsic i);
 
 }  // namespace intrinsic
 }  // namespace ast
diff --git a/src/ast/module.cc b/src/ast/module.cc
index b650a78..b595aa7 100644
--- a/src/ast/module.cc
+++ b/src/ast/module.cc
@@ -27,15 +27,6 @@
 
 Module::~Module() = default;
 
-Import* Module::FindImportByName(const std::string& name) const {
-  for (const auto& import : imports_) {
-    if (import->name() == name) {
-      return import.get();
-    }
-  }
-  return nullptr;
-}
-
 Function* Module::FindFunctionByName(const std::string& name) const {
   for (const auto& func : functions_) {
     if (func->name() == name) {
@@ -56,11 +47,6 @@
 }
 
 bool Module::IsValid() const {
-  for (const auto& import : imports_) {
-    if (import == nullptr || !import->IsValid()) {
-      return false;
-    }
-  }
   for (const auto& var : global_variables_) {
     if (var == nullptr || !var->IsValid()) {
       return false;
@@ -84,9 +70,6 @@
 
   out << "Module{" << std::endl;
   const auto indent = 2;
-  for (const auto& import : imports_) {
-    import->to_str(out, indent);
-  }
   for (const auto& var : global_variables_) {
     var->to_str(out, indent);
   }
diff --git a/src/ast/module.h b/src/ast/module.h
index cb8f13a..134e762 100644
--- a/src/ast/module.h
+++ b/src/ast/module.h
@@ -21,7 +21,6 @@
 #include <vector>
 
 #include "src/ast/function.h"
-#include "src/ast/import.h"
 #include "src/ast/type/alias_type.h"
 #include "src/ast/variable.h"
 
@@ -36,18 +35,6 @@
   Module(Module&&);
   ~Module();
 
-  /// Add the given import to the module
-  /// @param import The import to add.
-  void AddImport(std::unique_ptr<Import> import) {
-    imports_.push_back(std::move(import));
-  }
-  /// @returns the imports for this module
-  const ImportList& imports() const { return imports_; }
-  /// Find the import of the given name
-  /// @param name The import name to search for
-  /// @returns the import with the given name if found, nullptr otherwise.
-  Import* FindImportByName(const std::string& name) const;
-
   /// Add a global variable to the module
   /// @param var the variable to add
   void AddGlobalVariable(std::unique_ptr<Variable> var) {
@@ -94,7 +81,6 @@
  private:
   Module(const Module&) = delete;
 
-  ImportList imports_;
   VariableList global_variables_;
   // The alias types are owned by the type manager
   std::vector<type::AliasType*> alias_types_;
diff --git a/src/ast/module_test.cc b/src/ast/module_test.cc
index e1cec96..50c3982 100644
--- a/src/ast/module_test.cc
+++ b/src/ast/module_test.cc
@@ -19,7 +19,6 @@
 
 #include "gmock/gmock.h"
 #include "src/ast/function.h"
-#include "src/ast/import.h"
 #include "src/ast/type/f32_type.h"
 #include "src/ast/variable.h"
 
@@ -32,7 +31,7 @@
 TEST_F(ModuleTest, Creation) {
   Module m;
 
-  EXPECT_EQ(m.imports().size(), 0u);
+  EXPECT_EQ(m.functions().size(), 0u);
 }
 
 TEST_F(ModuleTest, ToStrEmitsPreambleAndPostamble) {
@@ -42,44 +41,6 @@
   EXPECT_EQ(str, expected);
 }
 
-TEST_F(ModuleTest, Imports) {
-  Module m;
-
-  m.AddImport(std::make_unique<Import>("GLSL.std.430", "std::glsl"));
-  m.AddImport(std::make_unique<Import>("OpenCL.debug.100", "std::debug"));
-
-  EXPECT_EQ(2u, m.imports().size());
-  EXPECT_EQ("std::glsl", m.imports()[0]->name());
-}
-
-TEST_F(ModuleTest, ToStrWithImport) {
-  Module m;
-  m.AddImport(std::make_unique<Import>("GLSL.std.430", "std::glsl"));
-  const auto str = m.to_str();
-  EXPECT_EQ(str, R"(Module{
-  Import{"GLSL.std.430" as std::glsl}
-}
-)");
-}
-
-TEST_F(ModuleTest, LookupImport) {
-  Module m;
-
-  auto i = std::make_unique<Import>("GLSL.std.430", "std::glsl");
-  m.AddImport(std::move(i));
-  m.AddImport(std::make_unique<Import>("OpenCL.debug.100", "std::debug"));
-
-  auto* import = m.FindImportByName("std::glsl");
-  ASSERT_NE(nullptr, import);
-  EXPECT_EQ(import->path(), "GLSL.std.430");
-  EXPECT_EQ(import->name(), "std::glsl");
-}
-
-TEST_F(ModuleTest, LookupImportMissing) {
-  Module m;
-  EXPECT_EQ(nullptr, m.FindImportByName("Missing"));
-}
-
 TEST_F(ModuleTest, LookupFunction) {
   type::F32Type f32;
   Module m;
@@ -100,24 +61,6 @@
   EXPECT_TRUE(m.IsValid());
 }
 
-TEST_F(ModuleTest, IsValid_Import) {
-  Module m;
-  m.AddImport(std::make_unique<Import>("GLSL.std.430", "std::glsl"));
-  EXPECT_TRUE(m.IsValid());
-}
-
-TEST_F(ModuleTest, IsValid_Null_Import) {
-  Module m;
-  m.AddImport(nullptr);
-  EXPECT_FALSE(m.IsValid());
-}
-
-TEST_F(ModuleTest, IsValid_Invalid_Import) {
-  Module m;
-  m.AddImport(std::make_unique<Import>());
-  EXPECT_FALSE(m.IsValid());
-}
-
 TEST_F(ModuleTest, IsValid_GlobalVariable) {
   type::F32Type f32;
   auto var = std::make_unique<Variable>("var", StorageClass::kInput, &f32);
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 23052e2..320dd08 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -327,7 +327,7 @@
     case GLSLstd450Normalize:
       return "normalize";
     case GLSLstd450FClamp:
-      return "fclamp";
+      return "clamp";
     case GLSLstd450Length:
       return "length";
     default:
@@ -2805,8 +2805,8 @@
     Fail() << "unhandled GLSL.std.450 instruction " << ext_opcode;
     return {};
   }
-  auto func = std::make_unique<ast::IdentifierExpression>(
-      std::vector<std::string>{parser_impl_.GlslStd450Prefix(), name});
+
+  auto func = std::make_unique<ast::IdentifierExpression>(name);
   ast::ExpressionList operands;
   // All parameters to GLSL.std.450 extended instructions are IDs.
   for (uint32_t iarg = 2; iarg < inst.NumInOperands(); ++iarg) {
diff --git a/src/reader/spirv/function_glsl_std_450_test.cc b/src/reader/spirv/function_glsl_std_450_test.cc
index a73187e..0f38e75 100644
--- a/src/reader/spirv/function_glsl_std_450_test.cc
+++ b/src/reader/spirv/function_glsl_std_450_test.cc
@@ -434,30 +434,28 @@
 
 INSTANTIATE_TEST_SUITE_P(Samples,
                          SpvParserTest_GlslStd450_Float_Floating,
-                         ::testing::Values(GlslStd450Case{
-                             "Length", "std::glsl::length"}));
+                         ::testing::Values(GlslStd450Case{"Length", "length"}));
 
 INSTANTIATE_TEST_SUITE_P(Samples,
                          SpvParserTest_GlslStd450_Float_FloatingFloating,
-                         ::testing::Values(GlslStd450Case{
-                             "Distance", "std::glsl::distance"}));
+                         ::testing::Values(GlslStd450Case{"Distance",
+                                                          "distance"}));
 
-INSTANTIATE_TEST_SUITE_P(
-    Samples,
-    SpvParserTest_GlslStd450_Floating_Floating,
-    ::testing::Values(GlslStd450Case{"Sin", "std::glsl::sin"},
-                      GlslStd450Case{"Cos", "std::glsl::cos"},
-                      GlslStd450Case{"Normalize", "std::glsl::normalize"}));
+INSTANTIATE_TEST_SUITE_P(Samples,
+                         SpvParserTest_GlslStd450_Floating_Floating,
+                         ::testing::Values(GlslStd450Case{"Sin", "sin"},
+                                           GlslStd450Case{"Cos", "cos"},
+                                           GlslStd450Case{"Normalize",
+                                                          "normalize"}));
 
 INSTANTIATE_TEST_SUITE_P(Samples,
                          SpvParserTest_GlslStd450_Floating_FloatingFloating,
-                         ::testing::Values(GlslStd450Case{"Atan2",
-                                                          "std::glsl::atan2"}));
+                         ::testing::Values(GlslStd450Case{"Atan2", "atan2"}));
 
 INSTANTIATE_TEST_SUITE_P(
     Samples,
     SpvParserTest_GlslStd450_Floating_FloatingFloatingFloating,
-    ::testing::Values(GlslStd450Case{"FClamp", "std::glsl::fclamp"}));
+    ::testing::Values(GlslStd450Case{"FClamp", "clamp"}));
 
 }  // namespace
 }  // namespace spirv
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index cb05f29..9c6c87d 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -453,7 +453,6 @@
   type_mgr_ = nullptr;
   deco_mgr_ = nullptr;
 
-  import_map_.clear();
   glsl_std_450_imports_.clear();
 }
 
@@ -544,14 +543,6 @@
         reinterpret_cast<const char*>(import.GetInOperand(0).words.data()));
     // TODO(dneto): Handle other extended instruction sets when needed.
     if (name == "GLSL.std.450") {
-      // Only create the AST import once, so we can use import name 'std::glsl'.
-      // This is a canonicalization.
-      if (glsl_std_450_imports_.empty()) {
-        auto ast_import =
-            std::make_unique<tint::ast::Import>(name, GlslStd450Prefix());
-        import_map_[import.result_id()] = ast_import.get();
-        ast_module_.AddImport(std::move(ast_import));
-      }
       glsl_std_450_imports_.insert(import.result_id());
     } else {
       return Fail() << "Unrecognized extended instruction set: " << name;
diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h
index 8f6abfa..555c77f 100644
--- a/src/reader/spirv/parser_impl.h
+++ b/src/reader/spirv/parser_impl.h
@@ -32,7 +32,6 @@
 #include "source/opt/types.h"
 #include "spirv-tools/libspirv.hpp"
 #include "src/ast/expression.h"
-#include "src/ast/import.h"
 #include "src/ast/module.h"
 #include "src/ast/struct_member_decoration.h"
 #include "src/ast/type/alias_type.h"
@@ -136,9 +135,6 @@
     return glsl_std_450_imports_;
   }
 
-  /// @returns the import prefix to use for the GLSL.std.450 import.
-  std::string GlslStd450Prefix() const { return "std::glsl"; }
-
   /// Converts a SPIR-V type to a Tint type, and saves it for fast lookup.
   /// If the type is only used for builtins, then register that specially,
   /// and return null.
@@ -460,8 +456,6 @@
   // instruction is line 1.
   std::unordered_map<const spvtools::opt::Instruction*, Source> inst_source_;
 
-  /// Maps a SPIR-V ID for an external instruction import to an AST import
-  std::unordered_map<uint32_t, ast::Import*> import_map_;
   // The set of IDs that are imports of the GLSL.std.450 extended instruction
   // sets.
   std::unordered_set<uint32_t> glsl_std_450_imports_;
diff --git a/src/reader/spirv/parser_impl_import_test.cc b/src/reader/spirv/parser_impl_import_test.cc
index afa78a3..b72445b 100644
--- a/src/reader/spirv/parser_impl_import_test.cc
+++ b/src/reader/spirv/parser_impl_import_test.cc
@@ -44,23 +44,6 @@
   EXPECT_TRUE(p->BuildAndParseInternalModule());
   EXPECT_TRUE(p->error().empty());
   EXPECT_THAT(p->glsl_std_450_imports(), ElementsAre(1));
-  const auto module_ast = p->module().to_str();
-  EXPECT_THAT(module_ast, HasSubstr(R"(Import{"GLSL.std.450" as std::glsl})"));
-}
-
-TEST_F(SpvParserTest, Import_ImportGlslStd450Twice) {
-  auto* p = parser(test::Assemble(R"(
-    %1  = OpExtInstImport "GLSL.std.450"
-    %42 = OpExtInstImport "GLSL.std.450"
-  )"));
-  EXPECT_TRUE(p->BuildAndParseInternalModule());
-  EXPECT_TRUE(p->error().empty());
-  EXPECT_THAT(p->glsl_std_450_imports(), UnorderedElementsAre(1, 42));
-  const auto module = p->module();
-  EXPECT_EQ(module.imports().size(), 1u);
-  const auto module_ast = module.to_str();
-  // TODO(dneto): Use a matcher to show there is only one import.
-  EXPECT_THAT(module_ast, HasSubstr(R"(Import{"GLSL.std.450" as std::glsl})"));
 }
 
 // TODO(dneto): We don't currently support other kinds of extended instruction
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index 6a41627..64a0649 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -203,7 +203,6 @@
 
 // global_decl
 //  : SEMICOLON
-//  | import_decl SEMICOLON
 //  | global_variable_decl SEMICLON
 //  | global_constant_decl SEMICOLON
 //  | type_alias SEMICOLON
@@ -218,19 +217,6 @@
     return;
   }
 
-  auto import = import_decl();
-  if (has_error())
-    return;
-  if (import != nullptr) {
-    t = next();
-    if (!t.IsSemicolon()) {
-      set_error(t, "missing ';' for import");
-      return;
-    }
-    module_.AddImport(std::move(import));
-    return;
-  }
-
   auto gv = global_variable_decl();
   if (has_error())
     return;
@@ -281,69 +267,6 @@
   set_error(t);
 }
 
-// import_decl
-//  : IMPORT STRING_LITERAL AS (IDENT NAMESPACE)* IDENT
-std::unique_ptr<ast::Import> ParserImpl::import_decl() {
-  auto t = peek();
-  if (!t.IsImport())
-    return {};
-
-  auto source = t.source();
-  next();  // consume the import token
-
-  t = next();
-  if (!t.IsStringLiteral()) {
-    set_error(t, "missing path for import");
-    return {};
-  }
-  auto path = t.to_str();
-  if (path.length() == 0) {
-    set_error(t, "import path must not be empty");
-    return {};
-  }
-
-  t = next();
-  if (!t.IsAs()) {
-    set_error(t, "missing 'as' for import");
-    return {};
-  }
-
-  std::string name = "";
-  for (;;) {
-    t = peek();
-    if (!t.IsIdentifier()) {
-      break;
-    }
-    next();  // consume the peek
-
-    name += t.to_str();
-
-    t = peek();
-    if (!t.IsNamespace()) {
-      break;
-    }
-    next();  // consume the peek
-
-    name += "::";
-  }
-  if (name.length() == 0) {
-    if (t.IsEof() || t.IsSemicolon()) {
-      set_error(t, "missing name for import");
-    } else {
-      set_error(t, "invalid name for import");
-    }
-    return {};
-  }
-  if (name.length() > 2) {
-    auto end = name.length() - 1;
-    if (name[end] == ':' && name[end - 1] == ':') {
-      set_error(t, "invalid name for import");
-      return {};
-    }
-  }
-  return std::make_unique<ast::Import>(source, path, name);
-}
-
 // global_variable_decl
 //  : variable_decoration_list variable_decl
 //  | variable_decoration_list variable_decl EQUAL const_expr
@@ -2840,7 +2763,7 @@
 }
 
 // primary_expression
-//   : (IDENT NAMESPACE)* IDENT
+//   : IDENT
 //   | type_decl PAREN_LEFT argument_expression_list* PAREN_RIGHT
 //   | const_literal
 //   | paren_rhs_stmt
@@ -2911,24 +2834,7 @@
   } else if (t.IsIdentifier()) {
     next();  // Consume the peek
 
-    std::vector<std::string> ident;
-    ident.push_back(t.to_str());
-    for (;;) {
-      t = peek();
-      if (!t.IsNamespace())
-        break;
-
-      next();  // Consume the peek
-      t = next();
-      if (!t.IsIdentifier()) {
-        set_error(t, "identifier expected");
-        return nullptr;
-      }
-
-      ident.push_back(t.to_str());
-    }
-    return std::make_unique<ast::IdentifierExpression>(source,
-                                                       std::move(ident));
+    return std::make_unique<ast::IdentifierExpression>(source, t.to_str());
   }
 
   auto* type = type_decl();
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 2574958..5cc5248 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -28,7 +28,6 @@
 #include "src/ast/constructor_expression.h"
 #include "src/ast/else_statement.h"
 #include "src/ast/function.h"
-#include "src/ast/import.h"
 #include "src/ast/literal.h"
 #include "src/ast/loop_statement.h"
 #include "src/ast/module.h"
@@ -123,9 +122,6 @@
   void translation_unit();
   /// Parses the `global_decl` grammar element
   void global_decl();
-  /// Parses the `import_decl grammar element
-  /// @returns the import object or nullptr if an error was encountered
-  std::unique_ptr<ast::Import> import_decl();
   /// Parses a `global_variable_decl` grammar element
   /// @returns the variable parsed or nullptr
   std::unique_ptr<ast::Variable> global_variable_decl();
diff --git a/src/reader/wgsl/parser_impl_global_decl_test.cc b/src/reader/wgsl/parser_impl_global_decl_test.cc
index 432f2e1..404f559 100644
--- a/src/reader/wgsl/parser_impl_global_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_global_decl_test.cc
@@ -27,35 +27,6 @@
   ASSERT_FALSE(p->has_error()) << p->error();
 }
 
-TEST_F(ParserImplTest, GlobalDecl_Import) {
-  auto* p = parser(R"(import "GLSL.std.430" as glsl;)");
-  p->global_decl();
-  ASSERT_FALSE(p->has_error()) << p->error();
-
-  auto m = p->module();
-  ASSERT_EQ(1u, m.imports().size());
-
-  const auto& import = m.imports()[0];
-  EXPECT_EQ("GLSL.std.430", import->path());
-  EXPECT_EQ("glsl", import->name());
-}
-
-TEST_F(ParserImplTest, GlobalDecl_Import_Invalid) {
-  auto* p = parser(R"(import as glsl;)");
-  p->global_decl();
-
-  ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:8: missing path for import");
-}
-
-TEST_F(ParserImplTest, GlobalDecl_Import_Invalid_MissingSemicolon) {
-  auto* p = parser(R"(import "GLSL.std.430" as glsl)");
-  p->global_decl();
-
-  ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:30: missing ';' for import");
-}
-
 TEST_F(ParserImplTest, GlobalDecl_GlobalVariable) {
   auto* p = parser("var<out> a : vec2<i32> = vec2<i32>(1, 2);");
   p->global_decl();
diff --git a/src/reader/wgsl/parser_impl_import_decl_test.cc b/src/reader/wgsl/parser_impl_import_decl_test.cc
deleted file mode 100644
index abf4582..0000000
--- a/src/reader/wgsl/parser_impl_import_decl_test.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-// 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.h"
-#include "src/reader/wgsl/parser_impl.h"
-#include "src/reader/wgsl/parser_impl_test_helper.h"
-
-namespace tint {
-namespace reader {
-namespace wgsl {
-namespace {
-
-TEST_F(ParserImplTest, ImportDecl_Import) {
-  auto* p = parser(R"(import "GLSL.std.450" as glsl)");
-
-  auto import = p->import_decl();
-  ASSERT_NE(import, nullptr);
-  ASSERT_FALSE(p->has_error()) << p->error();
-
-  EXPECT_EQ("GLSL.std.450", import->path());
-  EXPECT_EQ("glsl", import->name());
-  EXPECT_EQ(1u, import->line());
-  EXPECT_EQ(1u, import->column());
-}
-
-TEST_F(ParserImplTest, ImportDecl_Import_WithNamespace) {
-  auto* p = parser(R"(import "GLSL.std.450" as std::glsl)");
-  auto import = p->import_decl();
-  ASSERT_NE(import, nullptr);
-  ASSERT_FALSE(p->has_error()) << p->error();
-  EXPECT_EQ("std::glsl", import->name());
-}
-
-TEST_F(ParserImplTest, ImportDecl_Invalid_MissingPath) {
-  auto* p = parser(R"(import as glsl)");
-  auto import = p->import_decl();
-  ASSERT_EQ(import, nullptr);
-  ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:8: missing path for import");
-}
-
-TEST_F(ParserImplTest, ImportDecl_Invalid_EmptyPath) {
-  auto* p = parser(R"(import "" as glsl)");
-  auto import = p->import_decl();
-  ASSERT_EQ(import, nullptr);
-  ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:8: import path must not be empty");
-}
-
-TEST_F(ParserImplTest, ImportDecl_Invalid_NameMissingTerminatingIdentifier) {
-  auto* p = parser(R"(import "GLSL.std.450" as glsl::)");
-  auto import = p->import_decl();
-  ASSERT_EQ(import, nullptr);
-  ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:32: invalid name for import");
-}
-
-TEST_F(ParserImplTest, ImportDecl_Invalid_NameInvalid) {
-  auto* p = parser(R"(import "GLSL.std.450" as 12glsl)");
-  auto import = p->import_decl();
-  ASSERT_EQ(import, nullptr);
-  ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:26: invalid name for import");
-}
-
-TEST_F(ParserImplTest, ImportDecl_Invalid_MissingName) {
-  auto* p = parser(R"(import "GLSL.std.450" as)");
-  auto import = p->import_decl();
-  ASSERT_EQ(import, nullptr);
-  ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:25: missing name for import");
-}
-
-TEST_F(ParserImplTest, ImportDecl_Invalid_MissingAs) {
-  auto* p = parser(R"(import "GLSL.std.450" glsl)");
-  auto import = p->import_decl();
-  ASSERT_EQ(import, nullptr);
-  ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:23: missing 'as' for import");
-}
-
-}  // namespace
-}  // namespace wgsl
-}  // namespace reader
-}  // namespace tint
diff --git a/src/reader/wgsl/parser_impl_postfix_expression_test.cc b/src/reader/wgsl/parser_impl_postfix_expression_test.cc
index 9baf8a6..4ec04b2 100644
--- a/src/reader/wgsl/parser_impl_postfix_expression_test.cc
+++ b/src/reader/wgsl/parser_impl_postfix_expression_test.cc
@@ -105,7 +105,7 @@
 }
 
 TEST_F(ParserImplTest, PostfixExpression_Call_WithArgs) {
-  auto* p = parser("std::test(1, b, 2 + 3 / b)");
+  auto* p = parser("test(1, b, 2 + 3 / b)");
   auto e = p->postfix_expression();
   ASSERT_FALSE(p->has_error()) << p->error();
   ASSERT_NE(e, nullptr);
@@ -115,7 +115,6 @@
 
   ASSERT_TRUE(c->func()->IsIdentifier());
   auto* func = c->func()->AsIdentifier();
-  EXPECT_EQ(func->path(), "std");
   EXPECT_EQ(func->name(), "test");
 
   EXPECT_EQ(c->params().size(), 3u);
diff --git a/src/reader/wgsl/parser_impl_primary_expression_test.cc b/src/reader/wgsl/parser_impl_primary_expression_test.cc
index c2558e9..e6a4265 100644
--- a/src/reader/wgsl/parser_impl_primary_expression_test.cc
+++ b/src/reader/wgsl/parser_impl_primary_expression_test.cc
@@ -43,25 +43,6 @@
   EXPECT_EQ(ident->name(), "a");
 }
 
-TEST_F(ParserImplTest, PrimaryExpression_Ident_WithNamespace) {
-  auto* p = parser("a::b::c::d");
-  auto e = p->primary_expression();
-  ASSERT_FALSE(p->has_error()) << p->error();
-  ASSERT_NE(e, nullptr);
-  ASSERT_TRUE(e->IsIdentifier());
-  auto* ident = e->AsIdentifier();
-  EXPECT_EQ(ident->path(), "a::b::c");
-  EXPECT_EQ(ident->name(), "d");
-}
-
-TEST_F(ParserImplTest, PrimaryExpression_Ident_MissingIdent) {
-  auto* p = parser("a::");
-  auto e = p->primary_expression();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:4: identifier expected");
-}
-
 TEST_F(ParserImplTest, PrimaryExpression_TypeDecl) {
   auto* p = parser("vec4<i32>(1, 2, 3, 4))");
   auto e = p->primary_expression();
diff --git a/src/reader/wgsl/parser_impl_test.cc b/src/reader/wgsl/parser_impl_test.cc
index 7b6a5a0..a9fb584 100644
--- a/src/reader/wgsl/parser_impl_test.cc
+++ b/src/reader/wgsl/parser_impl_test.cc
@@ -30,8 +30,6 @@
 
 TEST_F(ParserImplTest, Parses) {
   auto* p = parser(R"(
-import "GLSL.std.430" as glsl;
-
 [[location(0)]] var<out> gl_FragColor : vec4<f32>;
 
 [[stage(vertex)]]
@@ -42,22 +40,19 @@
   ASSERT_TRUE(p->Parse()) << p->error();
 
   auto m = p->module();
-  ASSERT_EQ(1u, m.imports().size());
   ASSERT_EQ(1u, m.functions().size());
   ASSERT_EQ(1u, m.global_variables().size());
 }
 
 TEST_F(ParserImplTest, HandlesError) {
   auto* p = parser(R"(
-import "GLSL.std.430" as glsl;
-
 fn main() ->  {  # missing return type
   return;
 })");
 
   ASSERT_FALSE(p->Parse());
   ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "4:15: unable to determine function return type");
+  EXPECT_EQ(p->error(), "2:15: unable to determine function return type");
 }
 
 TEST_F(ParserImplTest, GetRegisteredType) {
diff --git a/src/reader/wgsl/parser_test.cc b/src/reader/wgsl/parser_test.cc
index a67751e..363da93 100644
--- a/src/reader/wgsl/parser_test.cc
+++ b/src/reader/wgsl/parser_test.cc
@@ -34,8 +34,6 @@
   Context ctx;
 
   Parser p(&ctx, R"(
-import "GLSL.std.430" as glsl;
-
 [[location(0)]] var<out> gl_FragColor : vec4<f32>;
 
 [[stage(vertex)]]
@@ -46,7 +44,6 @@
   ASSERT_TRUE(p.Parse()) << p.error();
 
   auto m = p.module();
-  ASSERT_EQ(1u, m.imports().size());
   ASSERT_EQ(1u, m.functions().size());
   ASSERT_EQ(1u, m.global_variables().size());
 }
@@ -54,15 +51,13 @@
 TEST_F(ParserTest, HandlesError) {
   Context ctx;
   Parser p(&ctx, R"(
-import "GLSL.std.430" as glsl;
-
 fn main() ->  {  # missing return type
   return;
 })");
 
   ASSERT_FALSE(p.Parse());
   ASSERT_TRUE(p.has_error());
-  EXPECT_EQ(p.error(), "4:15: unable to determine function return type");
+  EXPECT_EQ(p.error(), "2:15: unable to determine function return type");
 }
 
 }  // namespace
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index 7dbfaf1..0c31e5a 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -17,7 +17,6 @@
 #include <memory>
 #include <vector>
 
-#include "spirv/unified1/GLSL.std.450.h"
 #include "src/ast/array_accessor_expression.h"
 #include "src/ast/as_expression.h"
 #include "src/ast/assignment_statement.h"
@@ -56,104 +55,6 @@
 #include "src/ast/variable_decl_statement.h"
 
 namespace tint {
-namespace {
-
-// Most of these are floating-point general except the below which are only
-// FP16 and FP32. We only have FP32 at this point so the below works, if we
-// get FP64 support or otherwise we'll need to differentiate.
-//   * radians
-//   * degrees
-//   * sin, cos, tan
-//   * asin, acos, atan
-//   * sinh, cosh, tanh
-//   * asinh, acosh, atanh
-//   * exp, exp2
-//   * log, log2
-enum class GlslDataType {
-  kFloatScalarOrVector,
-  kIntScalarOrVector,
-  kFloatVector,
-  kMatrix
-};
-struct GlslData {
-  const char* name;
-  uint8_t param_count;
-  uint32_t op_id;
-  GlslDataType type;
-  uint8_t vector_count;
-};
-
-constexpr const GlslData kGlslData[] = {
-    {"acos", 1, GLSLstd450Acos, GlslDataType::kFloatScalarOrVector, 0},
-    {"acosh", 1, GLSLstd450Acosh, GlslDataType::kFloatScalarOrVector, 0},
-    {"asin", 1, GLSLstd450Asin, GlslDataType::kFloatScalarOrVector, 0},
-    {"asinh", 1, GLSLstd450Asinh, GlslDataType::kFloatScalarOrVector, 0},
-    {"atan", 1, GLSLstd450Atan, GlslDataType::kFloatScalarOrVector, 0},
-    {"atan2", 2, GLSLstd450Atan2, GlslDataType::kFloatScalarOrVector, 0},
-    {"atanh", 1, GLSLstd450Atanh, GlslDataType::kFloatScalarOrVector, 0},
-    {"ceil", 1, GLSLstd450Ceil, GlslDataType::kFloatScalarOrVector, 0},
-    {"cos", 1, GLSLstd450Cos, GlslDataType::kFloatScalarOrVector, 0},
-    {"cosh", 1, GLSLstd450Cosh, GlslDataType::kFloatScalarOrVector, 0},
-    {"cross", 2, GLSLstd450Cross, GlslDataType::kFloatVector, 3},
-    {"degrees", 1, GLSLstd450Degrees, GlslDataType::kFloatScalarOrVector, 0},
-    {"determinant", 1, GLSLstd450Determinant, GlslDataType::kMatrix, 0},
-    {"distance", 2, GLSLstd450Distance, GlslDataType::kFloatScalarOrVector, 0},
-    {"exp", 1, GLSLstd450Exp, GlslDataType::kFloatScalarOrVector, 0},
-    {"exp2", 1, GLSLstd450Exp2, GlslDataType::kFloatScalarOrVector, 0},
-    {"fabs", 1, GLSLstd450FAbs, GlslDataType::kFloatScalarOrVector, 0},
-    {"faceforward", 3, GLSLstd450FaceForward,
-     GlslDataType::kFloatScalarOrVector, 0},
-    {"fclamp", 3, GLSLstd450FClamp, GlslDataType::kFloatScalarOrVector, 0},
-    {"findilsb", 1, GLSLstd450FindILsb, GlslDataType::kIntScalarOrVector, 0},
-    {"findumsb", 1, GLSLstd450FindUMsb, GlslDataType::kIntScalarOrVector, 0},
-    {"findsmsb", 1, GLSLstd450FindSMsb, GlslDataType::kIntScalarOrVector, 0},
-    {"floor", 1, GLSLstd450Floor, GlslDataType::kFloatScalarOrVector, 0},
-    {"fma", 3, GLSLstd450Fma, GlslDataType::kFloatScalarOrVector, 0},
-    {"fmax", 2, GLSLstd450FMax, GlslDataType::kFloatScalarOrVector, 0},
-    {"fmin", 2, GLSLstd450FMin, GlslDataType::kFloatScalarOrVector, 0},
-    {"fmix", 3, GLSLstd450FMix, GlslDataType::kFloatScalarOrVector, 0},
-    {"fract", 1, GLSLstd450Fract, GlslDataType::kFloatScalarOrVector, 0},
-    {"fsign", 1, GLSLstd450FSign, GlslDataType::kFloatScalarOrVector, 0},
-    {"interpolateatcentroid", 1, GLSLstd450InterpolateAtCentroid,
-     GlslDataType::kFloatScalarOrVector, 0},
-    {"inversesqrt", 1, GLSLstd450InverseSqrt,
-     GlslDataType::kFloatScalarOrVector, 0},
-    {"length", 1, GLSLstd450Length, GlslDataType::kFloatScalarOrVector, 0},
-    {"log", 1, GLSLstd450Log, GlslDataType::kFloatScalarOrVector, 0},
-    {"log2", 1, GLSLstd450Log2, GlslDataType::kFloatScalarOrVector, 0},
-    {"matrixinverse", 1, GLSLstd450MatrixInverse, GlslDataType::kMatrix, 0},
-    {"nclamp", 3, GLSLstd450NClamp, GlslDataType::kFloatScalarOrVector, 0},
-    {"nmax", 2, GLSLstd450NMax, GlslDataType::kFloatScalarOrVector, 0},
-    {"nmin", 2, GLSLstd450NMin, GlslDataType::kFloatScalarOrVector, 0},
-    {"normalize", 1, GLSLstd450Normalize, GlslDataType::kFloatScalarOrVector,
-     0},
-    {"pow", 2, GLSLstd450Pow, GlslDataType::kFloatScalarOrVector, 0},
-    {"radians", 1, GLSLstd450Radians, GlslDataType::kFloatScalarOrVector, 0},
-    {"reflect", 2, GLSLstd450Reflect, GlslDataType::kFloatScalarOrVector, 0},
-    {"round", 1, GLSLstd450Round, GlslDataType::kFloatScalarOrVector, 0},
-    {"roundeven", 1, GLSLstd450RoundEven, GlslDataType::kFloatScalarOrVector,
-     0},
-    {"sabs", 1, GLSLstd450SAbs, GlslDataType::kIntScalarOrVector, 0},
-    {"sclamp", 3, GLSLstd450SClamp, GlslDataType::kIntScalarOrVector, 0},
-    {"sin", 1, GLSLstd450Sin, GlslDataType::kFloatScalarOrVector, 0},
-    {"sinh", 1, GLSLstd450Sinh, GlslDataType::kFloatScalarOrVector, 0},
-    {"smax", 2, GLSLstd450SMax, GlslDataType::kIntScalarOrVector, 0},
-    {"smin", 2, GLSLstd450SMin, GlslDataType::kIntScalarOrVector, 0},
-    {"smoothstep", 3, GLSLstd450SmoothStep, GlslDataType::kFloatScalarOrVector,
-     0},
-    {"sqrt", 1, GLSLstd450Sqrt, GlslDataType::kFloatScalarOrVector, 0},
-    {"ssign", 1, GLSLstd450SSign, GlslDataType::kIntScalarOrVector, 0},
-    {"step", 2, GLSLstd450Step, GlslDataType::kFloatScalarOrVector, 0},
-    {"tan", 1, GLSLstd450Tan, GlslDataType::kFloatScalarOrVector, 0},
-    {"tanh", 1, GLSLstd450Tanh, GlslDataType::kFloatScalarOrVector, 0},
-    {"trunc", 1, GLSLstd450Trunc, GlslDataType::kFloatScalarOrVector, 0},
-    {"uclamp", 3, GLSLstd450UClamp, GlslDataType::kIntScalarOrVector, 0},
-    {"umax", 2, GLSLstd450UMax, GlslDataType::kIntScalarOrVector, 0},
-    {"umin", 2, GLSLstd450UMin, GlslDataType::kIntScalarOrVector, 0},
-};
-constexpr const uint32_t kGlslDataCount = sizeof(kGlslData) / sizeof(GlslData);
-
-}  // namespace
 
 TypeDeterminer::TypeDeterminer(Context* ctx, ast::Module* mod)
     : ctx_(*ctx), mod_(mod) {}
@@ -471,6 +372,9 @@
 }
 
 bool TypeDeterminer::DetermineCall(ast::CallExpression* expr) {
+  if (!DetermineResultType(expr->func())) {
+    return false;
+  }
   if (!DetermineResultType(expr->params())) {
     return false;
   }
@@ -481,31 +385,10 @@
   if (expr->func()->IsIdentifier()) {
     auto* ident = expr->func()->AsIdentifier();
 
-    if (ast::intrinsic::IsIntrinsic(ident->name())) {
-      if (!DetermineIntrinsic(ident->name(), expr))
-        return false;
-
-    } else if (ident->has_path()) {
-      auto* imp = mod_->FindImportByName(ident->path());
-      if (imp == nullptr) {
-        set_error(expr->source(), "Unable to find import for " + ident->name());
+    if (ident->IsIntrinsic()) {
+      if (!DetermineIntrinsic(ident, expr)) {
         return false;
       }
-
-      uint32_t ext_id = 0;
-      auto* result_type = GetImportData(expr->source(), imp->path(),
-                                        ident->name(), expr->params(), &ext_id);
-      if (result_type == nullptr) {
-        if (error_.empty()) {
-          set_error(expr->source(),
-                    "Unable to determine result type for GLSL expression " +
-                        ident->name());
-        }
-        return false;
-      }
-
-      imp->AddMethodId(ident->name(), ext_id);
-      expr->func()->set_result_type(result_type);
     } else {
       if (current_function_) {
         caller_to_callee_[current_function_->name()].push_back(ident->name());
@@ -547,41 +430,111 @@
   return true;
 }
 
-bool TypeDeterminer::DetermineIntrinsic(const std::string& name,
+namespace {
+
+enum class IntrinsicDataType {
+  kFloatOrIntScalarOrVector,
+  kFloatScalarOrVector,
+  kIntScalarOrVector,
+  kFloatVector,
+  kMatrix,
+};
+struct IntrinsicData {
+  ast::Intrinsic intrinsic;
+  uint8_t param_count;
+  IntrinsicDataType data_type;
+  uint8_t vector_size;
+};
+
+// Note, this isn't all the intrinsics. Some are handled specially before
+// we get to the generic code. See the DetermineIntrinsic code below.
+constexpr const IntrinsicData kIntrinsicData[] = {
+    {ast::Intrinsic::kAbs, 1, IntrinsicDataType::kFloatOrIntScalarOrVector, 0},
+    {ast::Intrinsic::kAcos, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kAsin, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kAtan, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kAtan2, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kCeil, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kClamp, 3, IntrinsicDataType::kFloatOrIntScalarOrVector,
+     0},
+    {ast::Intrinsic::kCos, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kCosh, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kCountOneBits, 1, IntrinsicDataType::kIntScalarOrVector,
+     0},
+    {ast::Intrinsic::kCross, 2, IntrinsicDataType::kFloatVector, 3},
+    {ast::Intrinsic::kDeterminant, 1, IntrinsicDataType::kMatrix, 0},
+    {ast::Intrinsic::kDistance, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kExp, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kExp2, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kFaceForward, 3, IntrinsicDataType::kFloatScalarOrVector,
+     0},
+    {ast::Intrinsic::kFloor, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kFma, 3, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kFract, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kFrexp, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kInverseSqrt, 1, IntrinsicDataType::kFloatScalarOrVector,
+     0},
+    {ast::Intrinsic::kLdexp, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kLength, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kLog, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kLog2, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kMax, 2, IntrinsicDataType::kFloatOrIntScalarOrVector, 0},
+    {ast::Intrinsic::kMin, 2, IntrinsicDataType::kFloatOrIntScalarOrVector, 0},
+    {ast::Intrinsic::kMix, 3, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kModf, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kNormalize, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kPow, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kReflect, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kReverseBits, 1, IntrinsicDataType::kIntScalarOrVector, 0},
+    {ast::Intrinsic::kRound, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kSign, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kSin, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kSinh, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kSmoothStep, 3, IntrinsicDataType::kFloatScalarOrVector,
+     0},
+    {ast::Intrinsic::kSqrt, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kStep, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kTan, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kTanh, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+    {ast::Intrinsic::kTrunc, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+};
+
+constexpr const uint32_t kIntrinsicDataCount =
+    sizeof(kIntrinsicData) / sizeof(IntrinsicData);
+
+}  // namespace
+
+bool TypeDeterminer::DetermineIntrinsic(ast::IdentifierExpression* ident,
                                         ast::CallExpression* expr) {
-  if (ast::intrinsic::IsDerivative(name)) {
+  if (ast::intrinsic::IsDerivative(ident->intrinsic())) {
     if (expr->params().size() != 1) {
-      set_error(expr->source(), "incorrect number of parameters for " + name);
+      set_error(expr->source(),
+                "incorrect number of parameters for " + ident->name());
       return false;
     }
 
     // The result type must be the same as the type of the parameter.
-    auto& param = expr->params()[0];
-    if (!DetermineResultType(param.get())) {
-      return false;
-    }
-    expr->func()->set_result_type(param->result_type()->UnwrapPtrIfNeeded());
+    auto* param_type = expr->params()[0]->result_type()->UnwrapPtrIfNeeded();
+    expr->func()->set_result_type(param_type);
     return true;
   }
-  if (name == "any" || name == "all") {
+  if (ident->intrinsic() == ast::Intrinsic::kAny ||
+      ident->intrinsic() == ast::Intrinsic::kAll) {
     expr->func()->set_result_type(
         ctx_.type_mgr().Get(std::make_unique<ast::type::BoolType>()));
     return true;
   }
-  if (ast::intrinsic::IsFloatClassificationIntrinsic(name)) {
+  if (ast::intrinsic::IsFloatClassificationIntrinsic(ident->intrinsic())) {
     if (expr->params().size() != 1) {
-      set_error(expr->source(), "incorrect number of parameters for " + name);
+      set_error(expr->source(),
+                "incorrect number of parameters for " + ident->name());
       return false;
     }
 
     auto* bool_type =
         ctx_.type_mgr().Get(std::make_unique<ast::type::BoolType>());
 
-    auto& param = expr->params()[0];
-    if (!DetermineResultType(param.get())) {
-      return false;
-    }
-    auto* param_type = param->result_type()->UnwrapPtrIfNeeded();
+    auto* param_type = expr->params()[0]->result_type()->UnwrapPtrIfNeeded();
     if (param_type->IsVector()) {
       expr->func()->set_result_type(
           ctx_.type_mgr().Get(std::make_unique<ast::type::VectorType>(
@@ -591,31 +544,31 @@
     }
     return true;
   }
-  if (ast::intrinsic::IsTextureOperationIntrinsic(name)) {
+  if (ast::intrinsic::IsTextureIntrinsic(ident->intrinsic())) {
     // TODO: Remove the LOD param from textureLoad on storage textures when
     // https://github.com/gpuweb/gpuweb/pull/1032 gets merged.
     uint32_t num_of_params =
-        (name == "textureLoad" || name == "textureSample") ? 3 : 4;
+        (ident->intrinsic() == ast::Intrinsic::kTextureLoad ||
+         ident->intrinsic() == ast::Intrinsic::kTextureSample)
+            ? 3
+            : 4;
     if (expr->params().size() != num_of_params) {
       set_error(expr->source(),
-                "incorrect number of parameters for " + name + ", got " +
-                    std::to_string(expr->params().size()) + " and expected " +
-                    std::to_string(num_of_params));
+                "incorrect number of parameters for " + ident->name() +
+                    ", got " + std::to_string(expr->params().size()) +
+                    " and expected " + std::to_string(num_of_params));
       return false;
     }
 
-    if (name == "textureSampleCompare") {
+    if (ident->intrinsic() == ast::Intrinsic::kTextureSampleCompare) {
       expr->func()->set_result_type(
           ctx_.type_mgr().Get(std::make_unique<ast::type::F32Type>()));
       return true;
     }
 
     auto& texture_param = expr->params()[0];
-    if (!DetermineResultType(texture_param.get())) {
-      return false;
-    }
     if (!texture_param->result_type()->UnwrapPtrIfNeeded()->IsTexture()) {
-      set_error(expr->source(), "invalid first argument for " + name);
+      set_error(expr->source(), "invalid first argument for " + ident->name());
       return false;
     }
     ast::type::TextureType* texture =
@@ -623,7 +576,7 @@
 
     if (!texture->IsStorage() &&
         !(texture->IsSampled() || texture->IsMultisampled())) {
-      set_error(expr->source(), "invalid texture for " + name);
+      set_error(expr->source(), "invalid texture for " + ident->name());
       return false;
     }
 
@@ -642,29 +595,22 @@
         ctx_.type_mgr().Get(std::make_unique<ast::type::VectorType>(type, 4)));
     return true;
   }
-  if (name == "dot") {
+  if (ident->intrinsic() == ast::Intrinsic::kDot) {
     expr->func()->set_result_type(
         ctx_.type_mgr().Get(std::make_unique<ast::type::F32Type>()));
     return true;
   }
-  if (name == "outerProduct") {
+  if (ident->intrinsic() == ast::Intrinsic::kOuterProduct) {
     if (expr->params().size() != 2) {
       set_error(expr->source(),
-                "incorrect number of parameters for outer_product");
+                "incorrect number of parameters for " + ident->name());
       return false;
     }
 
-    auto& param0 = expr->params()[0];
-    auto& param1 = expr->params()[1];
-    if (!DetermineResultType(param0.get()) ||
-        !DetermineResultType(param1.get())) {
-      return false;
-    }
-
-    auto* param0_type = param0->result_type()->UnwrapPtrIfNeeded();
-    auto* param1_type = param1->result_type()->UnwrapPtrIfNeeded();
+    auto* param0_type = expr->params()[0]->result_type()->UnwrapPtrIfNeeded();
+    auto* param1_type = expr->params()[1]->result_type()->UnwrapPtrIfNeeded();
     if (!param0_type->IsVector() || !param1_type->IsVector()) {
-      set_error(expr->source(), "invalid parameter type for outer_product");
+      set_error(expr->source(), "invalid parameter type for " + ident->name());
       return false;
     }
 
@@ -674,24 +620,121 @@
             param0_type->AsVector()->size(), param1_type->AsVector()->size())));
     return true;
   }
-  if (name == "select") {
+  if (ident->intrinsic() == ast::Intrinsic::kSelect) {
     if (expr->params().size() != 3) {
-      set_error(expr->source(),
-                "incorrect number of parameters for select expected 3 got " +
-                    std::to_string(expr->params().size()));
+      set_error(expr->source(), "incorrect number of parameters for " +
+                                    ident->name() + " expected 3 got " +
+                                    std::to_string(expr->params().size()));
       return false;
     }
 
     // The result type must be the same as the type of the parameter.
-    auto& param = expr->params()[0];
-    if (!DetermineResultType(param.get())) {
-      return false;
-    }
-    expr->func()->set_result_type(param->result_type()->UnwrapPtrIfNeeded());
+    auto* param_type = expr->params()[0]->result_type()->UnwrapPtrIfNeeded();
+    expr->func()->set_result_type(param_type);
     return true;
   }
 
-  return false;
+  const IntrinsicData* data = nullptr;
+  for (uint32_t i = 0; i < kIntrinsicDataCount; ++i) {
+    if (ident->intrinsic() == kIntrinsicData[i].intrinsic) {
+      data = &kIntrinsicData[i];
+      break;
+    }
+  }
+  if (data == nullptr) {
+    return false;
+  }
+
+  if (expr->params().size() != data->param_count) {
+    set_error(expr->source(), "incorrect number of parameters for " +
+                                  ident->name() + ". Expected " +
+                                  std::to_string(data->param_count) + " got " +
+                                  std::to_string(expr->params().size()));
+    return false;
+  }
+
+  std::vector<ast::type::Type*> result_types;
+  for (uint32_t i = 0; i < data->param_count; ++i) {
+    result_types.push_back(
+        expr->params()[i]->result_type()->UnwrapPtrIfNeeded());
+
+    switch (data->data_type) {
+      case IntrinsicDataType::kFloatOrIntScalarOrVector:
+        if (!result_types.back()->is_float_scalar_or_vector() &&
+            !result_types.back()->is_integer_scalar_or_vector()) {
+          set_error(expr->source(),
+                    "incorrect type for " + ident->name() + ". " +
+                        "Requires float or int, scalar or vector values");
+          return false;
+        }
+        break;
+      case IntrinsicDataType::kFloatScalarOrVector:
+        if (!result_types.back()->is_float_scalar_or_vector()) {
+          set_error(expr->source(),
+                    "incorrect type for " + ident->name() + ". " +
+                        "Requires float scalar or float vector values");
+          return false;
+        }
+
+        break;
+      case IntrinsicDataType::kIntScalarOrVector:
+        if (!result_types.back()->is_integer_scalar_or_vector()) {
+          set_error(expr->source(),
+                    "incorrect type for " + ident->name() + ". " +
+                        "Requires integer scalar or integer vector values");
+          return false;
+        }
+        break;
+      case IntrinsicDataType::kFloatVector:
+        if (!result_types.back()->is_float_vector()) {
+          set_error(expr->source(), "incorrect type for " + ident->name() +
+                                        ". " + "Requires float vector values");
+          return false;
+        }
+        if (data->vector_size > 0 &&
+            result_types.back()->AsVector()->size() != data->vector_size) {
+          set_error(expr->source(), "incorrect vector size for " +
+                                        ident->name() + ". " + "Requires " +
+                                        std::to_string(data->vector_size) +
+                                        " elements");
+          return false;
+        }
+        break;
+      case IntrinsicDataType::kMatrix:
+        if (!result_types.back()->IsMatrix()) {
+          set_error(expr->source(), "incorrect type for " + ident->name() +
+                                        ". Requires matrix value");
+          return false;
+        }
+        break;
+    }
+  }
+
+  // Verify all the parameter types match
+  for (size_t i = 1; i < data->param_count; ++i) {
+    if (result_types[0] != result_types[i]) {
+      set_error(expr->source(),
+                "mismatched parameter types for " + ident->name());
+      return false;
+    }
+  }
+
+  // Handle functions which aways return the type, even if a vector is
+  // provided.
+  if (ident->intrinsic() == ast::Intrinsic::kLength ||
+      ident->intrinsic() == ast::Intrinsic::kDistance) {
+    expr->func()->set_result_type(result_types[0]->is_float_scalar()
+                                      ? result_types[0]
+                                      : result_types[0]->AsVector()->type());
+    return true;
+  }
+  // The determinant returns the component type of the columns
+  if (ident->intrinsic() == ast::Intrinsic::kDeterminant) {
+    expr->func()->set_result_type(result_types[0]->AsMatrix()->type());
+    return true;
+  }
+  expr->func()->set_result_type(result_types[0]);
+  return true;
 }
 
 bool TypeDeterminer::DetermineCast(ast::CastExpression* expr) {
@@ -719,12 +762,6 @@
 }
 
 bool TypeDeterminer::DetermineIdentifier(ast::IdentifierExpression* expr) {
-  if (expr->has_path()) {
-    set_error(expr->source(),
-              "determine identifier should not be called with imports");
-    return false;
-  }
-
   auto name = expr->name();
   ast::Variable* var;
   if (variable_stack_.get(name, &var)) {
@@ -750,9 +787,147 @@
     return true;
   }
 
+  SetIntrinsicIfNeeded(expr);
+
   return true;
 }
 
+void TypeDeterminer::SetIntrinsicIfNeeded(ast::IdentifierExpression* ident) {
+  if (ident->name() == "abs") {
+    ident->set_intrinsic(ast::Intrinsic::kAbs);
+  } else if (ident->name() == "acos") {
+    ident->set_intrinsic(ast::Intrinsic::kAcos);
+  } else if (ident->name() == "all") {
+    ident->set_intrinsic(ast::Intrinsic::kAll);
+  } else if (ident->name() == "any") {
+    ident->set_intrinsic(ast::Intrinsic::kAny);
+  } else if (ident->name() == "asin") {
+    ident->set_intrinsic(ast::Intrinsic::kAsin);
+  } else if (ident->name() == "atan") {
+    ident->set_intrinsic(ast::Intrinsic::kAtan);
+  } else if (ident->name() == "atan2") {
+    ident->set_intrinsic(ast::Intrinsic::kAtan2);
+  } else if (ident->name() == "ceil") {
+    ident->set_intrinsic(ast::Intrinsic::kCeil);
+  } else if (ident->name() == "clamp") {
+    ident->set_intrinsic(ast::Intrinsic::kClamp);
+  } else if (ident->name() == "cos") {
+    ident->set_intrinsic(ast::Intrinsic::kCos);
+  } else if (ident->name() == "cosh") {
+    ident->set_intrinsic(ast::Intrinsic::kCosh);
+  } else if (ident->name() == "countOneBits") {
+    ident->set_intrinsic(ast::Intrinsic::kCountOneBits);
+  } else if (ident->name() == "cross") {
+    ident->set_intrinsic(ast::Intrinsic::kCross);
+  } else if (ident->name() == "determinant") {
+    ident->set_intrinsic(ast::Intrinsic::kDeterminant);
+  } else if (ident->name() == "distance") {
+    ident->set_intrinsic(ast::Intrinsic::kDistance);
+  } else if (ident->name() == "dot") {
+    ident->set_intrinsic(ast::Intrinsic::kDot);
+  } else if (ident->name() == "dpdx") {
+    ident->set_intrinsic(ast::Intrinsic::kDpdx);
+  } else if (ident->name() == "dpdxCoarse") {
+    ident->set_intrinsic(ast::Intrinsic::kDpdxCoarse);
+  } else if (ident->name() == "dpdxFine") {
+    ident->set_intrinsic(ast::Intrinsic::kDpdxFine);
+  } else if (ident->name() == "dpdy") {
+    ident->set_intrinsic(ast::Intrinsic::kDpdy);
+  } else if (ident->name() == "dpdyCoarse") {
+    ident->set_intrinsic(ast::Intrinsic::kDpdyCoarse);
+  } else if (ident->name() == "dpdyFine") {
+    ident->set_intrinsic(ast::Intrinsic::kDpdyFine);
+  } else if (ident->name() == "exp") {
+    ident->set_intrinsic(ast::Intrinsic::kExp);
+  } else if (ident->name() == "exp2") {
+    ident->set_intrinsic(ast::Intrinsic::kExp2);
+  } else if (ident->name() == "faceForward") {
+    ident->set_intrinsic(ast::Intrinsic::kFaceForward);
+  } else if (ident->name() == "floor") {
+    ident->set_intrinsic(ast::Intrinsic::kFloor);
+  } else if (ident->name() == "fma") {
+    ident->set_intrinsic(ast::Intrinsic::kFma);
+  } else if (ident->name() == "fract") {
+    ident->set_intrinsic(ast::Intrinsic::kFract);
+  } else if (ident->name() == "frexp") {
+    ident->set_intrinsic(ast::Intrinsic::kFrexp);
+  } else if (ident->name() == "fwidth") {
+    ident->set_intrinsic(ast::Intrinsic::kFwidth);
+  } else if (ident->name() == "fwidthCoarse") {
+    ident->set_intrinsic(ast::Intrinsic::kFwidthCoarse);
+  } else if (ident->name() == "fwidthFine") {
+    ident->set_intrinsic(ast::Intrinsic::kFwidthFine);
+  } else if (ident->name() == "inverseSqrt") {
+    ident->set_intrinsic(ast::Intrinsic::kInverseSqrt);
+  } else if (ident->name() == "isFinite") {
+    ident->set_intrinsic(ast::Intrinsic::kIsFinite);
+  } else if (ident->name() == "isInf") {
+    ident->set_intrinsic(ast::Intrinsic::kIsInf);
+  } else if (ident->name() == "isNan") {
+    ident->set_intrinsic(ast::Intrinsic::kIsNan);
+  } else if (ident->name() == "isNormal") {
+    ident->set_intrinsic(ast::Intrinsic::kIsNormal);
+  } else if (ident->name() == "ldexp") {
+    ident->set_intrinsic(ast::Intrinsic::kLdexp);
+  } else if (ident->name() == "length") {
+    ident->set_intrinsic(ast::Intrinsic::kLength);
+  } else if (ident->name() == "log") {
+    ident->set_intrinsic(ast::Intrinsic::kLog);
+  } else if (ident->name() == "log2") {
+    ident->set_intrinsic(ast::Intrinsic::kLog2);
+  } else if (ident->name() == "max") {
+    ident->set_intrinsic(ast::Intrinsic::kMax);
+  } else if (ident->name() == "min") {
+    ident->set_intrinsic(ast::Intrinsic::kMin);
+  } else if (ident->name() == "mix") {
+    ident->set_intrinsic(ast::Intrinsic::kMix);
+  } else if (ident->name() == "modf") {
+    ident->set_intrinsic(ast::Intrinsic::kModf);
+  } else if (ident->name() == "normalize") {
+    ident->set_intrinsic(ast::Intrinsic::kNormalize);
+  } else if (ident->name() == "outerProduct") {
+    ident->set_intrinsic(ast::Intrinsic::kOuterProduct);
+  } else if (ident->name() == "pow") {
+    ident->set_intrinsic(ast::Intrinsic::kPow);
+  } else if (ident->name() == "reflect") {
+    ident->set_intrinsic(ast::Intrinsic::kReflect);
+  } else if (ident->name() == "reverseBits") {
+    ident->set_intrinsic(ast::Intrinsic::kReverseBits);
+  } else if (ident->name() == "round") {
+    ident->set_intrinsic(ast::Intrinsic::kRound);
+  } else if (ident->name() == "select") {
+    ident->set_intrinsic(ast::Intrinsic::kSelect);
+  } else if (ident->name() == "sign") {
+    ident->set_intrinsic(ast::Intrinsic::kSign);
+  } else if (ident->name() == "sin") {
+    ident->set_intrinsic(ast::Intrinsic::kSin);
+  } else if (ident->name() == "sinh") {
+    ident->set_intrinsic(ast::Intrinsic::kSinh);
+  } else if (ident->name() == "smoothStep") {
+    ident->set_intrinsic(ast::Intrinsic::kSmoothStep);
+  } else if (ident->name() == "sqrt") {
+    ident->set_intrinsic(ast::Intrinsic::kSqrt);
+  } else if (ident->name() == "step") {
+    ident->set_intrinsic(ast::Intrinsic::kStep);
+  } else if (ident->name() == "tan") {
+    ident->set_intrinsic(ast::Intrinsic::kTan);
+  } else if (ident->name() == "tanh") {
+    ident->set_intrinsic(ast::Intrinsic::kTanh);
+  } else if (ident->name() == "textureLoad") {
+    ident->set_intrinsic(ast::Intrinsic::kTextureLoad);
+  } else if (ident->name() == "textureSample") {
+    ident->set_intrinsic(ast::Intrinsic::kTextureSample);
+  } else if (ident->name() == "textureSampleBias") {
+    ident->set_intrinsic(ast::Intrinsic::kTextureSampleBias);
+  } else if (ident->name() == "textureSampleCompare") {
+    ident->set_intrinsic(ast::Intrinsic::kTextureSampleCompare);
+  } else if (ident->name() == "textureSampleLevel") {
+    ident->set_intrinsic(ast::Intrinsic::kTextureSampleLevel);
+  } else if (ident->name() == "trunc") {
+    ident->set_intrinsic(ast::Intrinsic::kTrunc);
+  }
+}
+
 bool TypeDeterminer::DetermineMemberAccessor(
     ast::MemberAccessorExpression* expr) {
   if (!DetermineResultType(expr->structure())) {
@@ -962,101 +1137,4 @@
   return false;
 }
 
-ast::type::Type* TypeDeterminer::GetImportData(
-    const Source& source,
-    const std::string& path,
-    const std::string& name,
-    const ast::ExpressionList& params,
-    uint32_t* id) {
-  if (path != "GLSL.std.450") {
-    set_error(source, "unknown import path " + path);
-    return nullptr;
-  }
-
-  const GlslData* data = nullptr;
-  for (uint32_t i = 0; i < kGlslDataCount; ++i) {
-    if (name == kGlslData[i].name) {
-      data = &kGlslData[i];
-      break;
-    }
-  }
-  if (data == nullptr) {
-    return nullptr;
-  }
-
-  if (params.size() != data->param_count) {
-    set_error(source, "incorrect number of parameters for " + name +
-                          ". Expected " + std::to_string(data->param_count) +
-                          " got " + std::to_string(params.size()));
-    return nullptr;
-  }
-
-  std::vector<ast::type::Type*> result_types;
-  for (uint32_t i = 0; i < data->param_count; ++i) {
-    result_types.push_back(params[i]->result_type()->UnwrapPtrIfNeeded());
-
-    switch (data->type) {
-      case GlslDataType::kFloatScalarOrVector:
-        if (!result_types.back()->is_float_scalar_or_vector()) {
-          set_error(source, "incorrect type for " + name + ". " +
-                                "Requires float scalar or float vector values");
-          return nullptr;
-        }
-
-        break;
-      case GlslDataType::kIntScalarOrVector:
-        if (!result_types.back()->is_integer_scalar_or_vector()) {
-          set_error(source,
-                    "incorrect type for " + name + ". " +
-                        "Requires integer scalar or integer vector values");
-          return nullptr;
-        }
-        break;
-      case GlslDataType::kFloatVector:
-        if (!result_types.back()->is_float_vector()) {
-          set_error(source, "incorrect type for " + name + ". " +
-                                "Requires float vector values");
-          return nullptr;
-        }
-        if (data->vector_count > 0 &&
-            result_types.back()->AsVector()->size() != data->vector_count) {
-          set_error(source,
-                    "incorrect vector size for " + name + ". " + "Requires " +
-                        std::to_string(data->vector_count) + " elements");
-          return nullptr;
-        }
-        break;
-      case GlslDataType::kMatrix:
-        if (!result_types.back()->IsMatrix()) {
-          set_error(source,
-                    "incorrect type for " + name + ". Requires matrix value");
-          return nullptr;
-        }
-        break;
-    }
-  }
-
-  // Verify all the parameter types match
-  for (size_t i = 1; i < data->param_count; ++i) {
-    if (result_types[0] != result_types[i]) {
-      error_ = "mismatched parameter types for " + name;
-      return nullptr;
-    }
-  }
-
-  *id = data->op_id;
-
-  // Handle functions which aways return the type, even if a vector is provided.
-  if (name == "length" || name == "distance") {
-    return result_types[0]->is_float_scalar()
-               ? result_types[0]
-               : result_types[0]->AsVector()->type();
-  }
-  // The determinant returns the component type of the columns
-  if (name == "determinant") {
-    return result_types[0]->AsMatrix()->type();
-  }
-  return result_types[0];
-}
-
 }  // namespace tint
diff --git a/src/type_determiner.h b/src/type_determiner.h
index d094259..a7b689d 100644
--- a/src/type_determiner.h
+++ b/src/type_determiner.h
@@ -107,6 +107,10 @@
                                  const ast::ExpressionList& params,
                                  uint32_t* id);
 
+  /// Sets the intrinsic data information for the identifier if needed
+  /// @param ident the identifier expression
+  void SetIntrinsicIfNeeded(ast::IdentifierExpression* ident);
+
  private:
   void set_error(const Source& src, const std::string& msg);
   void set_referenced_from_function_if_needed(ast::Variable* var);
@@ -119,7 +123,8 @@
   bool DetermineCast(ast::CastExpression* expr);
   bool DetermineConstructor(ast::ConstructorExpression* expr);
   bool DetermineIdentifier(ast::IdentifierExpression* expr);
-  bool DetermineIntrinsic(const std::string& name, ast::CallExpression* expr);
+  bool DetermineIntrinsic(ast::IdentifierExpression* name,
+                          ast::CallExpression* expr);
   bool DetermineMemberAccessor(ast::MemberAccessorExpression* expr);
   bool DetermineUnaryOp(ast::UnaryOpExpression* expr);
 
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index f2ce9d6..21c29d4 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -19,12 +19,12 @@
 #include <vector>
 
 #include "gtest/gtest.h"
-#include "spirv/unified1/GLSL.std.450.h"
 #include "src/ast/array_accessor_expression.h"
 #include "src/ast/as_expression.h"
 #include "src/ast/assignment_statement.h"
 #include "src/ast/binary_expression.h"
 #include "src/ast/block_statement.h"
+#include "src/ast/bool_literal.h"
 #include "src/ast/break_statement.h"
 #include "src/ast/call_expression.h"
 #include "src/ast/call_statement.h"
@@ -666,11 +666,9 @@
   EXPECT_TRUE(param_ptr->result_type()->IsF32());
 }
 
-TEST_F(TypeDeterminerTest, Expr_Call_GLSLImport) {
+TEST_F(TypeDeterminerTest, Expr_Call_Intrinsic) {
   ast::type::F32Type f32;
 
-  mod()->AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
-
   // Register the function
   EXPECT_TRUE(td()->Determine());
 
@@ -678,8 +676,7 @@
   call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 2.4)));
 
-  std::vector<std::string> name{"std", "round"};
-  ast::CallExpression call(std::make_unique<ast::IdentifierExpression>(name),
+  ast::CallExpression call(std::make_unique<ast::IdentifierExpression>("round"),
                            std::move(call_params));
 
   EXPECT_TRUE(td()->DetermineResultType(&call));
@@ -2351,7 +2348,7 @@
   // Register the variable
   EXPECT_TRUE(td()->Determine());
   EXPECT_FALSE(td()->DetermineResultType(&expr));
-  EXPECT_EQ(td()->error(), "incorrect number of parameters for outer_product");
+  EXPECT_EQ(td()->error(), "incorrect number of parameters for outerProduct");
 }
 
 TEST_F(TypeDeterminerTest, Intrinsic_OuterProduct_TooManyParams) {
@@ -2375,7 +2372,7 @@
   // Register the variable
   EXPECT_TRUE(td()->Determine());
   EXPECT_FALSE(td()->DetermineResultType(&expr));
-  EXPECT_EQ(td()->error(), "incorrect number of parameters for outer_product");
+  EXPECT_EQ(td()->error(), "incorrect number of parameters for outerProduct");
 }
 
 using UnaryOpExpressionTest = TypeDeterminerTestWithParam<ast::UnaryOp>;
@@ -2470,16 +2467,104 @@
             "function variable has a non-function storage class");
 }
 
-struct GLSLData {
+struct IntrinsicData {
   const char* name;
-  uint32_t value;
+  ast::Intrinsic intrinsic;
 };
-inline std::ostream& operator<<(std::ostream& out, GLSLData data) {
+inline std::ostream& operator<<(std::ostream& out, IntrinsicData data) {
   out << data.name;
   return out;
 }
-using ImportData_SingleParamTest = TypeDeterminerTestWithParam<GLSLData>;
+using IntrinsicDataTest = TypeDeterminerTestWithParam<IntrinsicData>;
+TEST_P(IntrinsicDataTest, Lookup) {
+  auto param = GetParam();
 
+  ast::IdentifierExpression ident(param.name);
+  td()->SetIntrinsicIfNeeded(&ident);
+  EXPECT_EQ(ident.intrinsic(), param.intrinsic);
+  EXPECT_TRUE(ident.IsIntrinsic());
+}
+INSTANTIATE_TEST_SUITE_P(
+    TypeDeterminerTest,
+    IntrinsicDataTest,
+    testing::Values(
+        IntrinsicData{"abs", ast::Intrinsic::kAbs},
+        IntrinsicData{"acos", ast::Intrinsic::kAcos},
+        IntrinsicData{"all", ast::Intrinsic::kAll},
+        IntrinsicData{"any", ast::Intrinsic::kAny},
+        IntrinsicData{"asin", ast::Intrinsic::kAsin},
+        IntrinsicData{"atan", ast::Intrinsic::kAtan},
+        IntrinsicData{"atan2", ast::Intrinsic::kAtan2},
+        IntrinsicData{"ceil", ast::Intrinsic::kCeil},
+        IntrinsicData{"clamp", ast::Intrinsic::kClamp},
+        IntrinsicData{"cos", ast::Intrinsic::kCos},
+        IntrinsicData{"cosh", ast::Intrinsic::kCosh},
+        IntrinsicData{"countOneBits", ast::Intrinsic::kCountOneBits},
+        IntrinsicData{"cross", ast::Intrinsic::kCross},
+        IntrinsicData{"determinant", ast::Intrinsic::kDeterminant},
+        IntrinsicData{"distance", ast::Intrinsic::kDistance},
+        IntrinsicData{"dot", ast::Intrinsic::kDot},
+        IntrinsicData{"dpdx", ast::Intrinsic::kDpdx},
+        IntrinsicData{"dpdxCoarse", ast::Intrinsic::kDpdxCoarse},
+        IntrinsicData{"dpdxFine", ast::Intrinsic::kDpdxFine},
+        IntrinsicData{"dpdy", ast::Intrinsic::kDpdy},
+        IntrinsicData{"dpdyCoarse", ast::Intrinsic::kDpdyCoarse},
+        IntrinsicData{"dpdyFine", ast::Intrinsic::kDpdyFine},
+        IntrinsicData{"exp", ast::Intrinsic::kExp},
+        IntrinsicData{"exp2", ast::Intrinsic::kExp2},
+        IntrinsicData{"faceForward", ast::Intrinsic::kFaceForward},
+        IntrinsicData{"floor", ast::Intrinsic::kFloor},
+        IntrinsicData{"fma", ast::Intrinsic::kFma},
+        IntrinsicData{"fract", ast::Intrinsic::kFract},
+        IntrinsicData{"frexp", ast::Intrinsic::kFrexp},
+        IntrinsicData{"fwidth", ast::Intrinsic::kFwidth},
+        IntrinsicData{"fwidthCoarse", ast::Intrinsic::kFwidthCoarse},
+        IntrinsicData{"fwidthFine", ast::Intrinsic::kFwidthFine},
+        IntrinsicData{"inverseSqrt", ast::Intrinsic::kInverseSqrt},
+        IntrinsicData{"isFinite", ast::Intrinsic::kIsFinite},
+        IntrinsicData{"isInf", ast::Intrinsic::kIsInf},
+        IntrinsicData{"isNan", ast::Intrinsic::kIsNan},
+        IntrinsicData{"isNormal", ast::Intrinsic::kIsNormal},
+        IntrinsicData{"ldexp", ast::Intrinsic::kLdexp},
+        IntrinsicData{"length", ast::Intrinsic::kLength},
+        IntrinsicData{"log", ast::Intrinsic::kLog},
+        IntrinsicData{"log2", ast::Intrinsic::kLog2},
+        IntrinsicData{"max", ast::Intrinsic::kMax},
+        IntrinsicData{"min", ast::Intrinsic::kMin},
+        IntrinsicData{"mix", ast::Intrinsic::kMix},
+        IntrinsicData{"modf", ast::Intrinsic::kModf},
+        IntrinsicData{"normalize", ast::Intrinsic::kNormalize},
+        IntrinsicData{"outerProduct", ast::Intrinsic::kOuterProduct},
+        IntrinsicData{"pow", ast::Intrinsic::kPow},
+        IntrinsicData{"reflect", ast::Intrinsic::kReflect},
+        IntrinsicData{"reverseBits", ast::Intrinsic::kReverseBits},
+        IntrinsicData{"round", ast::Intrinsic::kRound},
+        IntrinsicData{"select", ast::Intrinsic::kSelect},
+        IntrinsicData{"sign", ast::Intrinsic::kSign},
+        IntrinsicData{"sin", ast::Intrinsic::kSin},
+        IntrinsicData{"sinh", ast::Intrinsic::kSinh},
+        IntrinsicData{"smoothStep", ast::Intrinsic::kSmoothStep},
+        IntrinsicData{"sqrt", ast::Intrinsic::kSqrt},
+        IntrinsicData{"step", ast::Intrinsic::kStep},
+        IntrinsicData{"tan", ast::Intrinsic::kTan},
+        IntrinsicData{"tanh", ast::Intrinsic::kTanh},
+        IntrinsicData{"textureLoad", ast::Intrinsic::kTextureLoad},
+        IntrinsicData{"textureSample", ast::Intrinsic::kTextureSample},
+        IntrinsicData{"textureSampleBias", ast::Intrinsic::kTextureSampleBias},
+        IntrinsicData{"textureSampleCompare",
+                      ast::Intrinsic::kTextureSampleCompare},
+        IntrinsicData{"textureSampleLevel",
+                      ast::Intrinsic::kTextureSampleLevel},
+        IntrinsicData{"trunc", ast::Intrinsic::kTrunc}));
+
+TEST_F(TypeDeterminerTest, IntrinsicNotSetIfNotMatched) {
+  ast::IdentifierExpression ident("not_intrinsic");
+  td()->SetIntrinsicIfNeeded(&ident);
+  EXPECT_EQ(ident.intrinsic(), ast::Intrinsic::kNone);
+  EXPECT_FALSE(ident.IsIntrinsic());
+}
+
+using ImportData_SingleParamTest = TypeDeterminerTestWithParam<IntrinsicData>;
 TEST_P(ImportData_SingleParamTest, Scalar) {
   auto param = GetParam();
 
@@ -2489,14 +2574,13 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_float_scalar());
-  EXPECT_EQ(id, param.value);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_scalar());
 }
 
 TEST_P(ImportData_SingleParamTest, Vector) {
@@ -2517,15 +2601,14 @@
   params.push_back(
       std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_float_vector());
-  EXPECT_EQ(type->AsVector()->size(), 3u);
-  EXPECT_EQ(id, param.value);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
 }
 
 TEST_P(ImportData_SingleParamTest, Error_Integer) {
@@ -2537,12 +2620,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 1)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             std::string("incorrect type for ") + param.name +
                 ". Requires float scalar or float vector values");
@@ -2552,10 +2633,11 @@
   auto param = GetParam();
 
   ast::ExpressionList params;
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 1 got 0");
 }
@@ -2572,12 +2654,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 1 got 3");
 }
@@ -2585,37 +2665,224 @@
 INSTANTIATE_TEST_SUITE_P(
     TypeDeterminerTest,
     ImportData_SingleParamTest,
-    testing::Values(GLSLData{"round", GLSLstd450Round},
-                    GLSLData{"roundeven", GLSLstd450RoundEven},
-                    GLSLData{"trunc", GLSLstd450Trunc},
-                    GLSLData{"fabs", GLSLstd450FAbs},
-                    GLSLData{"fsign", GLSLstd450FSign},
-                    GLSLData{"floor", GLSLstd450Floor},
-                    GLSLData{"ceil", GLSLstd450Ceil},
-                    GLSLData{"fract", GLSLstd450Fract},
-                    GLSLData{"radians", GLSLstd450Radians},
-                    GLSLData{"degrees", GLSLstd450Degrees},
-                    GLSLData{"sin", GLSLstd450Sin},
-                    GLSLData{"cos", GLSLstd450Cos},
-                    GLSLData{"tan", GLSLstd450Tan},
-                    GLSLData{"asin", GLSLstd450Asin},
-                    GLSLData{"acos", GLSLstd450Acos},
-                    GLSLData{"atan", GLSLstd450Atan},
-                    GLSLData{"sinh", GLSLstd450Sinh},
-                    GLSLData{"cosh", GLSLstd450Cosh},
-                    GLSLData{"tanh", GLSLstd450Tanh},
-                    GLSLData{"asinh", GLSLstd450Asinh},
-                    GLSLData{"acosh", GLSLstd450Acosh},
-                    GLSLData{"atanh", GLSLstd450Atanh},
-                    GLSLData{"exp", GLSLstd450Exp},
-                    GLSLData{"log", GLSLstd450Log},
-                    GLSLData{"exp2", GLSLstd450Exp2},
-                    GLSLData{"log2", GLSLstd450Log2},
-                    GLSLData{"sqrt", GLSLstd450Sqrt},
-                    GLSLData{"inversesqrt", GLSLstd450InverseSqrt},
-                    GLSLData{"normalize", GLSLstd450Normalize},
-                    GLSLData{"interpolateatcentroid",
-                             GLSLstd450InterpolateAtCentroid}));
+    testing::Values(IntrinsicData{"acos", ast::Intrinsic::kAcos},
+                    IntrinsicData{"asin", ast::Intrinsic::kAsin},
+                    IntrinsicData{"atan", ast::Intrinsic::kAtan},
+                    IntrinsicData{"ceil", ast::Intrinsic::kCeil},
+                    IntrinsicData{"cos", ast::Intrinsic::kCos},
+                    IntrinsicData{"cosh", ast::Intrinsic::kCosh},
+                    IntrinsicData{"exp", ast::Intrinsic::kExp},
+                    IntrinsicData{"exp2", ast::Intrinsic::kExp2},
+                    IntrinsicData{"floor", ast::Intrinsic::kFloor},
+                    IntrinsicData{"fract", ast::Intrinsic::kFract},
+                    IntrinsicData{"inverseSqrt", ast::Intrinsic::kInverseSqrt},
+                    IntrinsicData{"log", ast::Intrinsic::kLog},
+                    IntrinsicData{"log2", ast::Intrinsic::kLog2},
+                    IntrinsicData{"normalize", ast::Intrinsic::kNormalize},
+                    IntrinsicData{"round", ast::Intrinsic::kRound},
+                    IntrinsicData{"sign", ast::Intrinsic::kSign},
+                    IntrinsicData{"sin", ast::Intrinsic::kSin},
+                    IntrinsicData{"sinh", ast::Intrinsic::kSinh},
+                    IntrinsicData{"sqrt", ast::Intrinsic::kSqrt},
+                    IntrinsicData{"tan", ast::Intrinsic::kTan},
+                    IntrinsicData{"tanh", ast::Intrinsic::kTanh},
+                    IntrinsicData{"trunc", ast::Intrinsic::kTrunc}));
+
+using ImportData_SingleParam_FloatOrInt_Test =
+    TypeDeterminerTestWithParam<IntrinsicData>;
+TEST_P(ImportData_SingleParam_FloatOrInt_Test, Float_Scalar) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_scalar());
+}
+
+TEST_P(ImportData_SingleParam_FloatOrInt_Test, Float_Vector) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 3);
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 3.0f)));
+
+  ast::ExpressionList params;
+  params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
+}
+
+TEST_P(ImportData_SingleParam_FloatOrInt_Test, Sint_Scalar) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, -11)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->IsI32());
+}
+
+TEST_P(ImportData_SingleParam_FloatOrInt_Test, Sint_Vector) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+  ast::type::VectorType vec(&i32, 3);
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 3)));
+
+  ast::ExpressionList params;
+  params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_signed_integer_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
+}
+
+TEST_P(ImportData_SingleParam_FloatOrInt_Test, Uint_Scalar) {
+  auto param = GetParam();
+
+  ast::type::U32Type u32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->IsU32());
+}
+
+TEST_P(ImportData_SingleParam_FloatOrInt_Test, Uint_Vector) {
+  auto param = GetParam();
+
+  ast::type::U32Type u32;
+  ast::type::VectorType vec(&u32, 3);
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1.0f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1.0f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 3.0f)));
+
+  ast::ExpressionList params;
+  params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_unsigned_integer_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
+}
+
+TEST_P(ImportData_SingleParam_FloatOrInt_Test, Error_Bool) {
+  auto param = GetParam();
+
+  ast::type::BoolType bool_type;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::BoolLiteral>(&bool_type, false)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
+  EXPECT_EQ(td()->error(),
+            std::string("incorrect type for ") + param.name +
+                ". Requires float or int, scalar or vector values");
+}
+
+TEST_P(ImportData_SingleParam_FloatOrInt_Test, Error_NoParams) {
+  auto param = GetParam();
+
+  ast::ExpressionList params;
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
+  EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
+                               param.name + ". Expected 1 got 0");
+}
+
+TEST_P(ImportData_SingleParam_FloatOrInt_Test, Error_MultipleParams) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
+  EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
+                               param.name + ". Expected 1 got 3");
+}
+
+INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
+                         ImportData_SingleParam_FloatOrInt_Test,
+                         testing::Values(IntrinsicData{"abs",
+                                                       ast::Intrinsic::kAbs}));
 
 TEST_F(TypeDeterminerTest, ImportData_Length_Scalar) {
   ast::type::F32Type f32;
@@ -2626,12 +2893,13 @@
 
   ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "length", params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_float_scalar());
-  EXPECT_EQ(id, GLSLstd450Length);
+  auto ident = std::make_unique<ast::IdentifierExpression>("length");
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_scalar());
 }
 
 TEST_F(TypeDeterminerTest, ImportData_Length_FloatVector) {
@@ -2650,14 +2918,13 @@
   params.push_back(
       std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("length");
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "length", params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_float_scalar());
-  EXPECT_EQ(id, GLSLstd450Length);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_scalar());
 }
 
 TEST_F(TypeDeterminerTest, ImportData_Length_Error_Integer) {
@@ -2667,12 +2934,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 1)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("length");
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "length", params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             "incorrect type for length. Requires float scalar or float vector "
             "values");
@@ -2680,10 +2945,11 @@
 
 TEST_F(TypeDeterminerTest, ImportData_Length_Error_NoParams) {
   ast::ExpressionList params;
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "length", params, &id);
-  ASSERT_EQ(type, nullptr);
+
+  auto ident = std::make_unique<ast::IdentifierExpression>("length");
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             "incorrect number of parameters for length. Expected 1 got 0");
 }
@@ -2698,18 +2964,15 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("length");
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "length", params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             "incorrect number of parameters for length. Expected 1 got 3");
 }
 
-using ImportData_TwoParamTest = TypeDeterminerTestWithParam<GLSLData>;
-
+using ImportData_TwoParamTest = TypeDeterminerTestWithParam<IntrinsicData>;
 TEST_P(ImportData_TwoParamTest, Scalar) {
   auto param = GetParam();
 
@@ -2721,14 +2984,13 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_float_scalar());
-  EXPECT_EQ(id, param.value);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_scalar());
 }
 
 TEST_P(ImportData_TwoParamTest, Vector) {
@@ -2759,15 +3021,14 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec, std::move(vals_2)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_float_vector());
-  EXPECT_EQ(type->AsVector()->size(), 3u);
-  EXPECT_EQ(id, param.value);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
 }
 
 TEST_P(ImportData_TwoParamTest, Error_Integer) {
@@ -2781,12 +3042,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 2)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             std::string("incorrect type for ") + param.name +
                 ". Requires float scalar or float vector values");
@@ -2796,10 +3055,11 @@
   auto param = GetParam();
 
   ast::ExpressionList params;
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 2 got 0");
 }
@@ -2812,12 +3072,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 2 got 1");
 }
@@ -2849,12 +3107,10 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec3, std::move(vals_2)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             std::string("mismatched parameter types for ") + param.name);
 }
@@ -2879,12 +3135,10 @@
   params.push_back(
       std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             std::string("mismatched parameter types for ") + param.name);
 }
@@ -2901,26 +3155,21 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 2 got 3");
 }
 
-INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
-                         ImportData_TwoParamTest,
-                         testing::Values(GLSLData{"atan2", GLSLstd450Atan2},
-                                         GLSLData{"pow", GLSLstd450Pow},
-                                         GLSLData{"fmin", GLSLstd450FMin},
-                                         GLSLData{"fmax", GLSLstd450FMax},
-                                         GLSLData{"step", GLSLstd450Step},
-                                         GLSLData{"reflect", GLSLstd450Reflect},
-                                         GLSLData{"nmin", GLSLstd450NMin},
-                                         GLSLData{"nmax", GLSLstd450NMax}));
+INSTANTIATE_TEST_SUITE_P(
+    TypeDeterminerTest,
+    ImportData_TwoParamTest,
+    testing::Values(IntrinsicData{"atan2", ast::Intrinsic::kAtan2},
+                    IntrinsicData{"pow", ast::Intrinsic::kPow},
+                    IntrinsicData{"step", ast::Intrinsic::kStep},
+                    IntrinsicData{"reflect", ast::Intrinsic::kReflect}));
 
 TEST_F(TypeDeterminerTest, ImportData_Distance_Scalar) {
   ast::type::F32Type f32;
@@ -2931,14 +3180,13 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("distance");
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "distance", params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_float_scalar());
-  EXPECT_EQ(id, GLSLstd450Distance);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_scalar());
 }
 
 TEST_F(TypeDeterminerTest, ImportData_Distance_Vector) {
@@ -2967,14 +3215,13 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec, std::move(vals_2)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("distance");
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "distance", params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->IsF32());
-  EXPECT_EQ(id, GLSLstd450Distance);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->IsF32());
 }
 
 TEST_F(TypeDeterminerTest, ImportData_Distance_Error_Integer) {
@@ -2986,12 +3233,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 2)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("distance");
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "distance", params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             "incorrect type for distance. Requires float scalar or float "
             "vector values");
@@ -2999,10 +3244,11 @@
 
 TEST_F(TypeDeterminerTest, ImportData_Distance_Error_NoParams) {
   ast::ExpressionList params;
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "distance", params, &id);
-  ASSERT_EQ(type, nullptr);
+
+  auto ident = std::make_unique<ast::IdentifierExpression>("distance");
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             "incorrect number of parameters for distance. Expected 2 got 0");
 }
@@ -3013,12 +3259,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("distance");
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "distance", params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             "incorrect number of parameters for distance. Expected 2 got 1");
 }
@@ -3048,12 +3292,10 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec3, std::move(vals_2)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("distance");
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "distance", params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), "mismatched parameter types for distance");
 }
 
@@ -3075,12 +3317,10 @@
   params.push_back(
       std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("distance");
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "distance", params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), "mismatched parameter types for distance");
 }
 
@@ -3094,12 +3334,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("distance");
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "distance", params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             "incorrect number of parameters for distance. Expected 2 got 3");
 }
@@ -3130,15 +3368,14 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec, std::move(vals_2)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("cross");
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "cross", params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_float_vector());
-  EXPECT_EQ(type->AsVector()->size(), 3u);
-  EXPECT_EQ(id, GLSLstd450Cross);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
 }
 
 TEST_F(TypeDeterminerTest, ImportData_Cross_Error_Scalar) {
@@ -3150,12 +3387,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("cross");
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "cross", params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             "incorrect type for cross. Requires float vector values");
 }
@@ -3186,27 +3421,20 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec, std::move(vals_2)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("cross");
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "cross", params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             "incorrect type for cross. Requires float vector values");
 }
 
 TEST_F(TypeDeterminerTest, ImportData_Cross_Error_MissingParams) {
-  ast::type::F32Type f32;
-  ast::type::VectorType vec(&f32, 3);
-
   ast::ExpressionList params;
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("cross");
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "cross", params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             "incorrect number of parameters for cross. Expected 2 got 0");
 }
@@ -3227,12 +3455,10 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec, std::move(vals_1)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("cross");
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "cross", params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             "incorrect number of parameters for cross. Expected 2 got 1");
 }
@@ -3273,17 +3499,15 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec, std::move(vals_3)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("cross");
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "cross", params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             "incorrect number of parameters for cross. Expected 2 got 3");
 }
 
-using ImportData_ThreeParamTest = TypeDeterminerTestWithParam<GLSLData>;
+using ImportData_ThreeParamTest = TypeDeterminerTestWithParam<IntrinsicData>;
 TEST_P(ImportData_ThreeParamTest, Scalar) {
   auto param = GetParam();
 
@@ -3297,14 +3521,13 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_float_scalar());
-  EXPECT_EQ(id, param.value);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_scalar());
 }
 
 TEST_P(ImportData_ThreeParamTest, Vector) {
@@ -3345,15 +3568,14 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec, std::move(vals_3)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_float_vector());
-  EXPECT_EQ(type->AsVector()->size(), 3u);
-  EXPECT_EQ(id, param.value);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
 }
 
 TEST_P(ImportData_ThreeParamTest, Error_Integer) {
@@ -3369,12 +3591,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 3)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             std::string("incorrect type for ") + param.name +
                 ". Requires float scalar or float vector values");
@@ -3384,10 +3604,11 @@
   auto param = GetParam();
 
   ast::ExpressionList params;
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 3 got 0");
 }
@@ -3400,12 +3621,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 3 got 1");
 }
@@ -3420,12 +3639,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 3 got 2");
 }
@@ -3467,12 +3684,10 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec3, std::move(vals_3)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             std::string("mismatched parameter types for ") + param.name);
 }
@@ -3499,12 +3714,10 @@
   params.push_back(
       std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             std::string("mismatched parameter types for ") + param.name);
 }
@@ -3523,12 +3736,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 3 got 4");
 }
@@ -3536,14 +3747,397 @@
 INSTANTIATE_TEST_SUITE_P(
     TypeDeterminerTest,
     ImportData_ThreeParamTest,
-    testing::Values(GLSLData{"fclamp", GLSLstd450FClamp},
-                    GLSLData{"fmix", GLSLstd450FMix},
-                    GLSLData{"smoothstep", GLSLstd450SmoothStep},
-                    GLSLData{"fma", GLSLstd450Fma},
-                    GLSLData{"faceforward", GLSLstd450FaceForward},
-                    GLSLData{"nclamp", GLSLstd450NClamp}));
+    testing::Values(IntrinsicData{"mix", ast::Intrinsic::kMix},
+                    IntrinsicData{"smoothStep", ast::Intrinsic::kSmoothStep},
+                    IntrinsicData{"fma", ast::Intrinsic::kFma},
+                    IntrinsicData{"faceForward",
+                                  ast::Intrinsic::kFaceForward}));
 
-using ImportData_Int_SingleParamTest = TypeDeterminerTestWithParam<GLSLData>;
+using ImportData_ThreeParam_FloatOrInt_Test =
+    TypeDeterminerTestWithParam<IntrinsicData>;
+TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Float_Scalar) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_scalar());
+}
+
+TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Float_Vector) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 3);
+
+  ast::ExpressionList vals_1;
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 3.0f)));
+
+  ast::ExpressionList vals_2;
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 3.0f)));
+
+  ast::ExpressionList vals_3;
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 3.0f)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_1)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_2)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_3)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
+}
+
+TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Sint_Scalar) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->IsI32());
+}
+
+TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Sint_Vector) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+  ast::type::VectorType vec(&i32, 3);
+
+  ast::ExpressionList vals_1;
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 3)));
+
+  ast::ExpressionList vals_2;
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 3)));
+
+  ast::ExpressionList vals_3;
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 3)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_1)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_2)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_3)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_signed_integer_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
+}
+
+TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Uint_Scalar) {
+  auto param = GetParam();
+
+  ast::type::U32Type u32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->IsU32());
+}
+
+TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Uint_Vector) {
+  auto param = GetParam();
+
+  ast::type::U32Type u32;
+  ast::type::VectorType vec(&u32, 3);
+
+  ast::ExpressionList vals_1;
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 3)));
+
+  ast::ExpressionList vals_2;
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 3)));
+
+  ast::ExpressionList vals_3;
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 3)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_1)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_2)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_3)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_unsigned_integer_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
+}
+
+TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_Bool) {
+  auto param = GetParam();
+
+  ast::type::BoolType bool_type;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::BoolLiteral>(&bool_type, true)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::BoolLiteral>(&bool_type, false)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::BoolLiteral>(&bool_type, true)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
+  EXPECT_EQ(td()->error(),
+            std::string("incorrect type for ") + param.name +
+                ". Requires float or int, scalar or vector values");
+}
+
+TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_NoParams) {
+  auto param = GetParam();
+
+  ast::ExpressionList params;
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
+  EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
+                               param.name + ". Expected 3 got 0");
+}
+
+TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_OneParam) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
+  EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
+                               param.name + ". Expected 3 got 1");
+}
+
+TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_TwoParams) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
+  EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
+                               param.name + ". Expected 3 got 2");
+}
+
+TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_MismatchedParamCount) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::VectorType vec3(&f32, 3);
+
+  ast::ExpressionList vals_1;
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+
+  ast::ExpressionList vals_2;
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 3.0f)));
+
+  ast::ExpressionList vals_3;
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 3.0f)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec2, std::move(vals_1)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec3, std::move(vals_2)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec3, std::move(vals_3)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
+  EXPECT_EQ(td()->error(),
+            std::string("mismatched parameter types for ") + param.name);
+}
+
+TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_MismatchedParamType) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 3);
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 3.0f)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
+  params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
+  EXPECT_EQ(td()->error(),
+            std::string("mismatched parameter types for ") + param.name);
+}
+
+TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_TooManyParams) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
+  EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
+                               param.name + ". Expected 3 got 4");
+}
+
+INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
+                         ImportData_ThreeParam_FloatOrInt_Test,
+                         testing::Values(IntrinsicData{
+                             "clamp", ast::Intrinsic::kClamp}));
+
+using ImportData_Int_SingleParamTest =
+    TypeDeterminerTestWithParam<IntrinsicData>;
 TEST_P(ImportData_Int_SingleParamTest, Scalar) {
   auto param = GetParam();
 
@@ -3553,14 +4147,13 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 1)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_integer_scalar());
-  EXPECT_EQ(id, param.value);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_integer_scalar());
 }
 
 TEST_P(ImportData_Int_SingleParamTest, Vector) {
@@ -3581,15 +4174,14 @@
   params.push_back(
       std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_signed_integer_vector());
-  EXPECT_EQ(type->AsVector()->size(), 3u);
-  EXPECT_EQ(id, param.value);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_signed_integer_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
 }
 
 TEST_P(ImportData_Int_SingleParamTest, Error_Float) {
@@ -3601,12 +4193,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             std::string("incorrect type for ") + param.name +
                 ". Requires integer scalar or integer vector values");
@@ -3616,10 +4206,11 @@
   auto param = GetParam();
 
   ast::ExpressionList params;
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 1 got 0");
 }
@@ -3636,12 +4227,10 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 1)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 1 got 3");
 }
@@ -3649,14 +4238,13 @@
 INSTANTIATE_TEST_SUITE_P(
     TypeDeterminerTest,
     ImportData_Int_SingleParamTest,
-    testing::Values(GLSLData{"sabs", GLSLstd450SAbs},
-                    GLSLData{"ssign", GLSLstd450SSign},
-                    GLSLData{"findilsb", GLSLstd450FindILsb},
-                    GLSLData{"findumsb", GLSLstd450FindUMsb},
-                    GLSLData{"findsmsb", GLSLstd450FindSMsb}));
+    testing::Values(
+        IntrinsicData{"countOneBits", ast::Intrinsic::kCountOneBits},
+        IntrinsicData{"reverseBits", ast::Intrinsic::kReverseBits}));
 
-using ImportData_Int_TwoParamTest = TypeDeterminerTestWithParam<GLSLData>;
-TEST_P(ImportData_Int_TwoParamTest, Scalar_Signed) {
+using ImportData_FloatOrInt_TwoParamTest =
+    TypeDeterminerTestWithParam<IntrinsicData>;
+TEST_P(ImportData_FloatOrInt_TwoParamTest, Scalar_Signed) {
   auto param = GetParam();
 
   ast::type::I32Type i32;
@@ -3667,17 +4255,16 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 1)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->IsI32());
-  EXPECT_EQ(id, param.value);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->IsI32());
 }
 
-TEST_P(ImportData_Int_TwoParamTest, Scalar_Unsigned) {
+TEST_P(ImportData_FloatOrInt_TwoParamTest, Scalar_Unsigned) {
   auto param = GetParam();
 
   ast::type::U32Type u32;
@@ -3688,17 +4275,36 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::UintLiteral>(&u32, 1)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->IsU32());
-  EXPECT_EQ(id, param.value);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->IsU32());
 }
 
-TEST_P(ImportData_Int_TwoParamTest, Vector_Signed) {
+TEST_P(ImportData_FloatOrInt_TwoParamTest, Scalar_Float) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->IsF32());
+}
+
+TEST_P(ImportData_FloatOrInt_TwoParamTest, Vector_Signed) {
   auto param = GetParam();
 
   ast::type::I32Type i32;
@@ -3726,18 +4332,17 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec, std::move(vals_2)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_signed_integer_vector());
-  EXPECT_EQ(type->AsVector()->size(), 3u);
-  EXPECT_EQ(id, param.value);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_signed_integer_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
 }
 
-TEST_P(ImportData_Int_TwoParamTest, Vector_Unsigned) {
+TEST_P(ImportData_FloatOrInt_TwoParamTest, Vector_Unsigned) {
   auto param = GetParam();
 
   ast::type::U32Type u32;
@@ -3765,52 +4370,88 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec, std::move(vals_2)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_unsigned_integer_vector());
-  EXPECT_EQ(type->AsVector()->size(), 3u);
-  EXPECT_EQ(id, param.value);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_unsigned_integer_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
 }
 
-TEST_P(ImportData_Int_TwoParamTest, Error_Float) {
+TEST_P(ImportData_FloatOrInt_TwoParamTest, Vector_Float) {
   auto param = GetParam();
 
   ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 3);
+
+  ast::ExpressionList vals_1;
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 3)));
+
+  ast::ExpressionList vals_2;
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 3)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_1)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_2)));
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->is_float_vector());
+  EXPECT_EQ(ident_ptr->result_type()->AsVector()->size(), 3u);
+}
+
+TEST_P(ImportData_FloatOrInt_TwoParamTest, Error_Bool) {
+  auto param = GetParam();
+
+  ast::type::BoolType bool_type;
 
   ast::ExpressionList params;
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+      std::make_unique<ast::BoolLiteral>(&bool_type, true)));
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 2.f)));
+      std::make_unique<ast::BoolLiteral>(&bool_type, false)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             std::string("incorrect type for ") + param.name +
-                ". Requires integer scalar or integer vector values");
+                ". Requires float or int, scalar or vector values");
 }
 
-TEST_P(ImportData_Int_TwoParamTest, Error_NoParams) {
+TEST_P(ImportData_FloatOrInt_TwoParamTest, Error_NoParams) {
   auto param = GetParam();
 
   ast::ExpressionList params;
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 2 got 0");
 }
 
-TEST_P(ImportData_Int_TwoParamTest, Error_OneParam) {
+TEST_P(ImportData_FloatOrInt_TwoParamTest, Error_OneParam) {
   auto param = GetParam();
 
   ast::type::I32Type i32;
@@ -3818,17 +4459,15 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 1)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 2 got 1");
 }
 
-TEST_P(ImportData_Int_TwoParamTest, Error_MismatchedParamCount) {
+TEST_P(ImportData_FloatOrInt_TwoParamTest, Error_MismatchedParamCount) {
   auto param = GetParam();
 
   ast::type::I32Type i32;
@@ -3855,17 +4494,15 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec3, std::move(vals_2)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             std::string("mismatched parameter types for ") + param.name);
 }
 
-TEST_P(ImportData_Int_TwoParamTest, Error_MismatchedParamType) {
+TEST_P(ImportData_FloatOrInt_TwoParamTest, Error_MismatchedParamType) {
   auto param = GetParam();
 
   ast::type::I32Type i32;
@@ -3885,17 +4522,15 @@
   params.push_back(
       std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(),
             std::string("mismatched parameter types for ") + param.name);
 }
 
-TEST_P(ImportData_Int_TwoParamTest, Error_TooManyParams) {
+TEST_P(ImportData_FloatOrInt_TwoParamTest, Error_TooManyParams) {
   auto param = GetParam();
 
   ast::type::I32Type i32;
@@ -3907,22 +4542,19 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 1)));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 2 got 3");
 }
 
-INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
-                         ImportData_Int_TwoParamTest,
-                         testing::Values(GLSLData{"umin", GLSLstd450UMin},
-                                         GLSLData{"smin", GLSLstd450SMin},
-                                         GLSLData{"umax", GLSLstd450UMax},
-                                         GLSLData{"smax", GLSLstd450SMax}));
+INSTANTIATE_TEST_SUITE_P(
+    TypeDeterminerTest,
+    ImportData_FloatOrInt_TwoParamTest,
+    testing::Values(IntrinsicData{"min", ast::Intrinsic::kMin},
+                    IntrinsicData{"max", ast::Intrinsic::kMax}));
 
 TEST_F(TypeDeterminerTest, ImportData_GLSL_Determinant) {
   ast::type::F32Type f32;
@@ -3938,44 +4570,17 @@
   ast::ExpressionList params;
   params.push_back(std::make_unique<ast::IdentifierExpression>("var"));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>("determinant");
+  auto* ident_ptr = ident.get();
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "determinant", params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->IsF32());
-  EXPECT_EQ(id, GLSLstd450Determinant);
+  EXPECT_TRUE(td()->DetermineResultType(&call)) << td()->error();
+  ASSERT_NE(ident_ptr->result_type(), nullptr);
+  EXPECT_TRUE(ident_ptr->result_type()->IsF32());
 }
 
-TEST_F(TypeDeterminerTest, ImportData_GLSL_MatrixInverse) {
-  ast::type::F32Type f32;
-  ast::type::MatrixType mat(&f32, 3, 3);
-
-  auto var = std::make_unique<ast::Variable>(
-      "var", ast::StorageClass::kFunction, &mat);
-  mod()->AddGlobalVariable(std::move(var));
-
-  // Register the global
-  ASSERT_TRUE(td()->Determine()) << td()->error();
-
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::IdentifierExpression>("var"));
-
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
-
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", "matrixinverse", params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->IsMatrix());
-  EXPECT_TRUE(type->AsMatrix()->type()->IsF32());
-  EXPECT_EQ(type->AsMatrix()->rows(), 3u);
-  EXPECT_EQ(type->AsMatrix()->columns(), 3u);
-  EXPECT_EQ(id, GLSLstd450MatrixInverse);
-}
-
-using ImportData_Matrix_OneParam_Test = TypeDeterminerTestWithParam<GLSLData>;
+using ImportData_Matrix_OneParam_Test =
+    TypeDeterminerTestWithParam<IntrinsicData>;
 TEST_P(ImportData_Matrix_OneParam_Test, Error_Float) {
   auto param = GetParam();
 
@@ -3991,12 +4596,10 @@
   ast::ExpressionList params;
   params.push_back(std::make_unique<ast::IdentifierExpression>("var"));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect type for ") + param.name +
                                ". Requires matrix value");
 }
@@ -4006,10 +4609,10 @@
 
   ast::ExpressionList params;
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
+
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 1 got 0");
 }
@@ -4031,347 +4634,17 @@
   params.push_back(std::make_unique<ast::IdentifierExpression>("var"));
   params.push_back(std::make_unique<ast::IdentifierExpression>("var"));
 
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression call(std::move(ident), std::move(params));
 
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
+  EXPECT_FALSE(td()->DetermineResultType(&call));
   EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
                                param.name + ". Expected 1 got 2");
 }
-INSTANTIATE_TEST_SUITE_P(
-    TypeDeterminerTest,
-    ImportData_Matrix_OneParam_Test,
-    testing::Values(GLSLData{"determinant", GLSLstd450Determinant},
-                    GLSLData{"matrixinverse", GLSLstd450MatrixInverse}));
-
-using ImportData_IntThreeParamTest = TypeDeterminerTestWithParam<GLSLData>;
-TEST_P(ImportData_IntThreeParamTest, Scalar_Signed) {
-  auto param = GetParam();
-
-  ast::type::I32Type i32;
-
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
-
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->IsI32());
-  EXPECT_EQ(id, param.value);
-}
-
-TEST_P(ImportData_IntThreeParamTest, Scalar_Unsigned) {
-  auto param = GetParam();
-
-  ast::type::U32Type u32;
-
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::UintLiteral>(&u32, 1u)));
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::UintLiteral>(&u32, 1u)));
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::UintLiteral>(&u32, 1u)));
-
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
-
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->IsU32());
-  EXPECT_EQ(id, param.value);
-}
-
-TEST_P(ImportData_IntThreeParamTest, Vector_Signed) {
-  auto param = GetParam();
-
-  ast::type::I32Type i32;
-  ast::type::VectorType vec(&i32, 3);
-
-  ast::ExpressionList vals_1;
-  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 3)));
-
-  ast::ExpressionList vals_2;
-  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 3)));
-
-  ast::ExpressionList vals_3;
-  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 3)));
-
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
-      &vec, std::move(vals_1)));
-  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
-      &vec, std::move(vals_2)));
-  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
-      &vec, std::move(vals_3)));
-
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
-
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_signed_integer_vector());
-  EXPECT_EQ(type->AsVector()->size(), 3u);
-  EXPECT_EQ(id, param.value);
-}
-
-TEST_P(ImportData_IntThreeParamTest, Vector_Unsigned) {
-  auto param = GetParam();
-
-  ast::type::U32Type u32;
-  ast::type::VectorType vec(&u32, 3);
-
-  ast::ExpressionList vals_1;
-  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::UintLiteral>(&u32, 1u)));
-  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::UintLiteral>(&u32, 1u)));
-  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::UintLiteral>(&u32, 3u)));
-
-  ast::ExpressionList vals_2;
-  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::UintLiteral>(&u32, 1u)));
-  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::UintLiteral>(&u32, 1u)));
-  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::UintLiteral>(&u32, 3u)));
-
-  ast::ExpressionList vals_3;
-  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::UintLiteral>(&u32, 1u)));
-  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::UintLiteral>(&u32, 1u)));
-  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::UintLiteral>(&u32, 3u)));
-
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
-      &vec, std::move(vals_1)));
-  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
-      &vec, std::move(vals_2)));
-  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
-      &vec, std::move(vals_3)));
-
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
-
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_NE(type, nullptr);
-  EXPECT_TRUE(type->is_unsigned_integer_vector());
-  EXPECT_EQ(type->AsVector()->size(), 3u);
-  EXPECT_EQ(id, param.value);
-}
-
-TEST_P(ImportData_IntThreeParamTest, Error_Float) {
-  auto param = GetParam();
-
-  ast::type::F32Type f32;
-
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 2.f)));
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 3.f)));
-
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
-
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
-  EXPECT_EQ(td()->error(),
-            std::string("incorrect type for ") + param.name +
-                ". Requires integer scalar or integer vector values");
-}
-
-TEST_P(ImportData_IntThreeParamTest, Error_NoParams) {
-  auto param = GetParam();
-
-  ast::ExpressionList params;
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
-  EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
-                               param.name + ". Expected 3 got 0");
-}
-
-TEST_P(ImportData_IntThreeParamTest, Error_OneParam) {
-  auto param = GetParam();
-
-  ast::type::I32Type i32;
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
-
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
-  EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
-                               param.name + ". Expected 3 got 1");
-}
-
-TEST_P(ImportData_IntThreeParamTest, Error_TwoParams) {
-  auto param = GetParam();
-
-  ast::type::I32Type i32;
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
-
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
-  EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
-                               param.name + ". Expected 3 got 2");
-}
-
-TEST_P(ImportData_IntThreeParamTest, Error_MismatchedParamCount) {
-  auto param = GetParam();
-
-  ast::type::I32Type i32;
-  ast::type::VectorType vec2(&i32, 2);
-  ast::type::VectorType vec3(&i32, 3);
-
-  ast::ExpressionList vals_1;
-  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-
-  ast::ExpressionList vals_2;
-  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 3)));
-
-  ast::ExpressionList vals_3;
-  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 3)));
-
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
-      &vec2, std::move(vals_1)));
-  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
-      &vec3, std::move(vals_2)));
-  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
-      &vec3, std::move(vals_3)));
-
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
-
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
-  EXPECT_EQ(td()->error(),
-            std::string("mismatched parameter types for ") + param.name);
-}
-
-TEST_P(ImportData_IntThreeParamTest, Error_MismatchedParamType) {
-  auto param = GetParam();
-
-  ast::type::I32Type i32;
-  ast::type::VectorType vec(&i32, 3);
-
-  ast::ExpressionList vals;
-  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 3)));
-
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  params.push_back(
-      std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
-
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
-
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
-  EXPECT_EQ(td()->error(),
-            std::string("mismatched parameter types for ") + param.name);
-}
-
-TEST_P(ImportData_IntThreeParamTest, Error_TooManyParams) {
-  auto param = GetParam();
-
-  ast::type::I32Type i32;
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::SintLiteral>(&i32, 1)));
-
-  ASSERT_TRUE(td()->DetermineResultType(params)) << td()->error();
-
-  uint32_t id = 0;
-  auto* type =
-      td()->GetImportData({0, 0}, "GLSL.std.450", param.name, params, &id);
-  ASSERT_EQ(type, nullptr);
-  EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
-                               param.name + ". Expected 3 got 4");
-}
-
 INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
-                         ImportData_IntThreeParamTest,
-                         testing::Values(GLSLData{"sclamp", GLSLstd450SClamp},
-                                         GLSLData{"uclamp", GLSLstd450UClamp}));
+                         ImportData_Matrix_OneParam_Test,
+                         testing::Values(IntrinsicData{
+                             "determinant", ast::Intrinsic::kDeterminant}));
 
 TEST_F(TypeDeterminerTest, Function_EntryPoints_StageDecoration) {
   ast::type::F32Type f32;
diff --git a/src/validator_impl.cc b/src/validator_impl.cc
index 2a8ce16..a7e58d1 100644
--- a/src/validator_impl.cc
+++ b/src/validator_impl.cc
@@ -46,9 +46,6 @@
   if (!ValidateGlobalVariables(module->global_variables())) {
     return false;
   }
-  if (!CheckImports(module)) {
-    return false;
-  }
   if (!ValidateFunctions(module->functions())) {
     return false;
   }
@@ -330,9 +327,7 @@
   if (expr->func()->IsIdentifier()) {
     auto* ident = expr->func()->AsIdentifier();
     auto func_name = ident->name();
-    if (ident->has_path()) {
-      // TODO(sarahM0): validate import statements
-    } else if (ast::intrinsic::IsIntrinsic(ident->name())) {
+    if (ident->IsIntrinsic()) {
       // TODO(sarahM0): validate intrinsics - tied with type-determiner
     } else {
       if (!function_stack_.has(func_name)) {
@@ -433,14 +428,4 @@
   return true;
 }
 
-bool ValidatorImpl::CheckImports(const ast::Module* module) {
-  for (const auto& import : module->imports()) {
-    if (import->path() != "GLSL.std.450") {
-      set_error(import->source(), "v-0001: unknown import: " + import->path());
-      return false;
-    }
-  }
-  return true;
-}
-
 }  // namespace tint
diff --git a/src/validator_impl.h b/src/validator_impl.h
index 6481292..4a5fe64 100644
--- a/src/validator_impl.h
+++ b/src/validator_impl.h
@@ -76,10 +76,6 @@
   /// @param assign the assignment to check
   /// @returns true if the validation was successful
   bool ValidateAssign(const ast::AssignmentStatement* assign);
-  /// Validates v-0001: Only allowed import is "GLSL.std.450"
-  /// @param module the modele to check imports
-  /// @returns ture if input complies with v-0001 rule
-  bool CheckImports(const ast::Module* module);
   /// Validates an expression
   /// @param expr the expression to check
   /// @return true if the expression is valid
diff --git a/src/validator_test.cc b/src/validator_test.cc
index 8658f25..8381e5c 100644
--- a/src/validator_test.cc
+++ b/src/validator_test.cc
@@ -63,33 +63,6 @@
 
 class ValidatorTest : public ValidatorTestHelper, public testing::Test {};
 
-TEST_F(ValidatorTest, Import) {
-  ast::Module m;
-  m.AddImport(std::make_unique<ast::Import>("GLSL.std.450", "glsl"));
-
-  EXPECT_TRUE(v()->CheckImports(&m));
-}
-
-TEST_F(ValidatorTest, Import_Fail_NotGLSL) {
-  ast::Module m;
-  m.AddImport(
-      std::make_unique<ast::Import>(Source{12, 34}, "not.GLSL", "glsl"));
-
-  EXPECT_FALSE(v()->CheckImports(&m));
-  ASSERT_TRUE(v()->has_error());
-  EXPECT_EQ(v()->error(), "12:34: v-0001: unknown import: not.GLSL");
-}
-
-TEST_F(ValidatorTest, Import_Fail_Typo) {
-  ast::Module m;
-  m.AddImport(
-      std::make_unique<ast::Import>(Source{12, 34}, "GLSL.std.4501", "glsl"));
-
-  EXPECT_FALSE(v()->CheckImports(&m));
-  ASSERT_TRUE(v()->has_error());
-  EXPECT_EQ(v()->error(), "12:34: v-0001: unknown import: GLSL.std.4501");
-}
-
 TEST_F(ValidatorTest, DISABLED_AssignToScalar_Fail) {
   // 1 = my_var;
   ast::type::I32Type i32;
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index 7fa9229..bd20995 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -16,7 +16,6 @@
 
 #include <sstream>
 
-#include "spirv/unified1/GLSL.std.450.h"
 #include "src/ast/array_accessor_expression.h"
 #include "src/ast/as_expression.h"
 #include "src/ast/assignment_statement.h"
@@ -31,7 +30,6 @@
 #include "src/ast/float_literal.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/if_statement.h"
-#include "src/ast/intrinsic.h"
 #include "src/ast/loop_statement.h"
 #include "src/ast/member_accessor_expression.h"
 #include "src/ast/return_statement.h"
@@ -469,44 +467,46 @@
   return true;
 }
 
-std::string GeneratorImpl::generate_intrinsic_name(const std::string& name) {
-  if (name == "any") {
+std::string GeneratorImpl::generate_intrinsic_name(ast::Intrinsic intrinsic) {
+  if (intrinsic == ast::Intrinsic::kAny) {
     return "any";
   }
-  if (name == "all") {
+  if (intrinsic == ast::Intrinsic::kAll) {
     return "all";
   }
-  if (name == "dot") {
+  if (intrinsic == ast::Intrinsic::kDot) {
     return "dot";
   }
-  if (name == "is_finite") {
+  if (intrinsic == ast::Intrinsic::kIsFinite) {
     return "isfinite";
   }
-  if (name == "is_inf") {
+  if (intrinsic == ast::Intrinsic::kIsInf) {
     return "isinf";
   }
-  if (name == "is_nan") {
+  if (intrinsic == ast::Intrinsic::kIsNan) {
     return "isnan";
   }
-  if (name == "dpdy") {
+  if (intrinsic == ast::Intrinsic::kDpdy) {
     return "ddy";
   }
-  if (name == "dpdy_fine") {
+  if (intrinsic == ast::Intrinsic::kDpdyFine) {
     return "ddy_fine";
   }
-  if (name == "dpdy_coarse") {
+  if (intrinsic == ast::Intrinsic::kDpdyCoarse) {
     return "ddy_coarse";
   }
-  if (name == "dpdx") {
+  if (intrinsic == ast::Intrinsic::kDpdx) {
     return "ddx";
   }
-  if (name == "dpdx_fine") {
+  if (intrinsic == ast::Intrinsic::kDpdxFine) {
     return "ddx_fine";
   }
-  if (name == "dpdx_coarse") {
+  if (intrinsic == ast::Intrinsic::kDpdxCoarse) {
     return "ddx_coarse";
   }
-  if (name == "fwidth" || name == "fwidth_fine" || name == "fwidth_coarse") {
+  if (intrinsic == ast::Intrinsic::kFwidth ||
+      intrinsic == ast::Intrinsic::kFwidthFine ||
+      intrinsic == ast::Intrinsic::kFwidthCoarse) {
     return "fwidth";
   }
   return "";
@@ -521,15 +521,15 @@
   }
 
   auto* ident = expr->func()->AsIdentifier();
-  if (!ident->has_path() && ast::intrinsic::IsIntrinsic(ident->name())) {
+  if (ident->IsIntrinsic()) {
     const auto& params = expr->params();
-    if (ident->name() == "select") {
+    if (ident->intrinsic() == ast::Intrinsic::kSelect) {
       error_ = "select not supported in HLSL backend yet";
       return false;
-    } else if (ident->name() == "is_normal") {
+    } else if (ident->intrinsic() == ast::Intrinsic::kIsNormal) {
       error_ = "is_normal not supported in HLSL backend yet";
       return false;
-    } else if (ident->name() == "outer_product") {
+    } else if (ident->intrinsic() == ast::Intrinsic::kOuterProduct) {
       error_ = "outer_product not supported yet";
       return false;
       // TODO(dsinclair): This gets tricky. We need to generate two variables to
@@ -582,11 +582,15 @@
 
       // out << ")";
     } else {
-      auto name = generate_intrinsic_name(ident->name());
+      auto name = generate_intrinsic_name(ident->intrinsic());
       if (name.empty()) {
-        error_ = "unable to determine intrinsic name for intrinsic: " +
-                 ident->name();
-        return false;
+        if (ast::intrinsic::IsTextureIntrinsic(ident->intrinsic())) {
+          error_ = "Textures not implemented yet";
+          return false;
+        }
+        if (!EmitBuiltinName(pre, out, expr)) {
+          return false;
+        }
       }
 
       make_indent(out);
@@ -609,162 +613,39 @@
     return true;
   }
 
-  if (!ident->has_path()) {
-    auto name = ident->name();
-    auto it = ep_func_name_remapped_.find(current_ep_name_ + "_" + name);
-    if (it != ep_func_name_remapped_.end()) {
-      name = it->second;
-    }
+  auto name = ident->name();
+  auto it = ep_func_name_remapped_.find(current_ep_name_ + "_" + name);
+  if (it != ep_func_name_remapped_.end()) {
+    name = it->second;
+  }
 
-    auto* func = module_->FindFunctionByName(ident->name());
-    if (func == nullptr) {
-      error_ = "Unable to find function: " + name;
-      return false;
-    }
+  auto* func = module_->FindFunctionByName(ident->name());
+  if (func == nullptr) {
+    error_ = "Unable to find function: " + name;
+    return false;
+  }
 
-    out << name << "(";
+  out << name << "(";
 
-    bool first = true;
-    if (has_referenced_in_var_needing_struct(func)) {
-      auto var_name = current_ep_var_name(VarType::kIn);
-      if (!var_name.empty()) {
-        out << var_name;
-        first = false;
-      }
+  bool first = true;
+  if (has_referenced_in_var_needing_struct(func)) {
+    auto var_name = current_ep_var_name(VarType::kIn);
+    if (!var_name.empty()) {
+      out << var_name;
+      first = false;
     }
-    if (has_referenced_out_var_needing_struct(func)) {
-      auto var_name = current_ep_var_name(VarType::kOut);
-      if (!var_name.empty()) {
-        if (!first) {
-          out << ", ";
-        }
-        first = false;
-        out << var_name;
-      }
-    }
-
-    const auto& params = expr->params();
-    for (const auto& param : params) {
+  }
+  if (has_referenced_out_var_needing_struct(func)) {
+    auto var_name = current_ep_var_name(VarType::kOut);
+    if (!var_name.empty()) {
       if (!first) {
         out << ", ";
       }
       first = false;
-
-      if (!EmitExpression(pre, out, param.get())) {
-        return false;
-      }
+      out << var_name;
     }
-
-    out << ")";
-  } else {
-    return EmitImportFunction(pre, out, expr);
-  }
-  return true;
-}
-
-bool GeneratorImpl::EmitImportFunction(std::ostream& pre,
-                                       std::ostream& out,
-                                       ast::CallExpression* expr) {
-  auto* ident = expr->func()->AsIdentifier();
-
-  auto* imp = module_->FindImportByName(ident->path());
-  if (imp == nullptr) {
-    error_ = "unable to find import for " + ident->path();
-    return 0;
-  }
-  auto id = imp->GetIdForMethod(ident->name());
-  if (id == 0) {
-    error_ = "unable to lookup: " + ident->name() + " in " + ident->path();
   }
 
-  switch (id) {
-    case GLSLstd450Acos:
-    case GLSLstd450Asin:
-    case GLSLstd450Atan:
-    case GLSLstd450Atan2:
-    case GLSLstd450Ceil:
-    case GLSLstd450Cos:
-    case GLSLstd450Cosh:
-    case GLSLstd450Cross:
-    case GLSLstd450Degrees:
-    case GLSLstd450Determinant:
-    case GLSLstd450Distance:
-    case GLSLstd450Exp:
-    case GLSLstd450Exp2:
-    case GLSLstd450FaceForward:
-    case GLSLstd450Floor:
-    case GLSLstd450Fma:
-    case GLSLstd450Length:
-    case GLSLstd450Log:
-    case GLSLstd450Log2:
-    case GLSLstd450Normalize:
-    case GLSLstd450Pow:
-    case GLSLstd450Radians:
-    case GLSLstd450Reflect:
-    case GLSLstd450Round:
-    case GLSLstd450Sin:
-    case GLSLstd450Sinh:
-    case GLSLstd450SmoothStep:
-    case GLSLstd450Sqrt:
-    case GLSLstd450Step:
-    case GLSLstd450Tan:
-    case GLSLstd450Tanh:
-    case GLSLstd450Trunc:
-      out << ident->name();
-      break;
-    case GLSLstd450Fract:
-      out << "frac";
-      break;
-    case GLSLstd450InterpolateAtCentroid:
-      out << "EvaluateAttributeAtCentroid";
-      break;
-    case GLSLstd450InverseSqrt:
-      out << "rsqrt";
-      break;
-    case GLSLstd450FMix:
-      out << "mix";
-      break;
-    case GLSLstd450SSign:
-    case GLSLstd450FSign:
-      out << "sign";
-      break;
-    case GLSLstd450FAbs:
-    case GLSLstd450SAbs:
-      out << "abs";
-      break;
-    case GLSLstd450FMax:
-    case GLSLstd450NMax:
-    case GLSLstd450SMax:
-    case GLSLstd450UMax:
-      out << "max";
-      break;
-    case GLSLstd450FMin:
-    case GLSLstd450NMin:
-    case GLSLstd450SMin:
-    case GLSLstd450UMin:
-      out << "min";
-      break;
-    case GLSLstd450FClamp:
-    case GLSLstd450SClamp:
-    case GLSLstd450NClamp:
-    case GLSLstd450UClamp:
-      out << "clamp";
-      break;
-    // TODO(dsinclair): Determine mappings for the following
-    case GLSLstd450Atanh:
-    case GLSLstd450Asinh:
-    case GLSLstd450Acosh:
-    case GLSLstd450FindILsb:
-    case GLSLstd450FindUMsb:
-    case GLSLstd450FindSMsb:
-    case GLSLstd450MatrixInverse:
-    case GLSLstd450RoundEven:
-      error_ = "Unknown import method: " + ident->name();
-      return false;
-  }
-
-  out << "(";
-  bool first = true;
   const auto& params = expr->params();
   for (const auto& param : params) {
     if (!first) {
@@ -776,11 +657,73 @@
       return false;
     }
   }
+
   out << ")";
 
   return true;
 }
 
+bool GeneratorImpl::EmitBuiltinName(std::ostream&,
+                                    std::ostream& out,
+                                    ast::CallExpression* expr) {
+  auto* ident = expr->func()->AsIdentifier();
+  switch (ident->intrinsic()) {
+    case ast::Intrinsic::kAcos:
+    case ast::Intrinsic::kAsin:
+    case ast::Intrinsic::kAtan:
+    case ast::Intrinsic::kAtan2:
+    case ast::Intrinsic::kCeil:
+    case ast::Intrinsic::kCos:
+    case ast::Intrinsic::kCosh:
+    case ast::Intrinsic::kCross:
+    case ast::Intrinsic::kDeterminant:
+    case ast::Intrinsic::kDistance:
+    case ast::Intrinsic::kExp:
+    case ast::Intrinsic::kExp2:
+    case ast::Intrinsic::kFloor:
+    case ast::Intrinsic::kFma:
+    case ast::Intrinsic::kLength:
+    case ast::Intrinsic::kLog:
+    case ast::Intrinsic::kLog2:
+    case ast::Intrinsic::kNormalize:
+    case ast::Intrinsic::kPow:
+    case ast::Intrinsic::kReflect:
+    case ast::Intrinsic::kRound:
+    case ast::Intrinsic::kSin:
+    case ast::Intrinsic::kSinh:
+    case ast::Intrinsic::kSqrt:
+    case ast::Intrinsic::kStep:
+    case ast::Intrinsic::kTan:
+    case ast::Intrinsic::kTanh:
+    case ast::Intrinsic::kTrunc:
+    case ast::Intrinsic::kMix:
+    case ast::Intrinsic::kSign:
+    case ast::Intrinsic::kAbs:
+    case ast::Intrinsic::kMax:
+    case ast::Intrinsic::kMin:
+    case ast::Intrinsic::kClamp:
+      out << ident->name();
+      break;
+    case ast::Intrinsic::kFaceForward:
+      out << "faceforward";
+      break;
+    case ast::Intrinsic::kFract:
+      out << "frac";
+      break;
+    case ast::Intrinsic::kInverseSqrt:
+      out << "rsqrt";
+      break;
+    case ast::Intrinsic::kSmoothStep:
+      out << "smoothstep";
+      break;
+    default:
+      error_ = "Unknown builtin method: " + ident->name();
+      return false;
+  }
+
+  return true;
+}
+
 bool GeneratorImpl::EmitCast(std::ostream& pre,
                              std::ostream& out,
                              ast::CastExpression* expr) {
@@ -957,12 +900,6 @@
                                    std::ostream& out,
                                    ast::IdentifierExpression* expr) {
   auto* ident = expr->AsIdentifier();
-  if (ident->has_path()) {
-    // TODO(dsinclair): Handle identifier with path
-    error_ = "Identifier paths not handled yet.";
-    return false;
-  }
-
   ast::Variable* var = nullptr;
   if (global_variables_.get(ident->name(), &var)) {
     if (global_is_in_struct(var)) {
@@ -1832,10 +1769,6 @@
   // Check if this is a storage buffer variable
   if (structure->IsIdentifier()) {
     auto* ident = expr->structure()->AsIdentifier();
-    if (ident->has_path()) {
-      return false;
-    }
-
     ast::Variable* var = nullptr;
     if (!global_variables_.get(ident->name(), &var)) {
       return false;
diff --git a/src/writer/hlsl/generator_impl.h b/src/writer/hlsl/generator_impl.h
index 798bf87..031cf7e 100644
--- a/src/writer/hlsl/generator_impl.h
+++ b/src/writer/hlsl/generator_impl.h
@@ -15,6 +15,7 @@
 #ifndef SRC_WRITER_HLSL_GENERATOR_IMPL_H_
 #define SRC_WRITER_HLSL_GENERATOR_IMPL_H_
 
+#include "src/ast/intrinsic.h"
 #include "src/ast/literal.h"
 #include "src/ast/module.h"
 #include "src/ast/scalar_constructor_expression.h"
@@ -206,14 +207,14 @@
   /// @param stmt the statement to emit
   /// @returns true if the statement was successfully emitted
   bool EmitIf(std::ostream& out, ast::IfStatement* stmt);
-  /// Handles genreating an import expression
+  /// Handles generating a builtin method name
   /// @param pre the preamble for the expression stream
   /// @param out the output of the expression stream
   /// @param expr the expression
-  /// @returns true if the expression was successfully emitted.
-  bool EmitImportFunction(std::ostream& pre,
-                          std::ostream& out,
-                          ast::CallExpression* expr);
+  /// @returns true if the name was successfully emitted.
+  bool EmitBuiltinName(std::ostream& pre,
+                       std::ostream& out,
+                       ast::CallExpression* expr);
   /// Handles a literal
   /// @param out the output stream
   /// @param lit the literal to emit
@@ -327,9 +328,9 @@
   /// @returns the name
   std::string generate_name(const std::string& prefix);
   /// Generates an intrinsic name from the given name
-  /// @param name the name to convert to an intrinsic
+  /// @param intrinsic the intrinsic to convert to a name
   /// @returns the intrinsic name or blank on error
-  std::string generate_intrinsic_name(const std::string& name);
+  std::string generate_intrinsic_name(ast::Intrinsic intrinsic);
   /// Converts a builtin to an attribute name
   /// @param builtin the builtin to convert
   /// @returns the string name of the builtin or blank on error
diff --git a/src/writer/hlsl/generator_impl_identifier_test.cc b/src/writer/hlsl/generator_impl_identifier_test.cc
index 56f15bb..bf38e8d 100644
--- a/src/writer/hlsl/generator_impl_identifier_test.cc
+++ b/src/writer/hlsl/generator_impl_identifier_test.cc
@@ -23,13 +23,7 @@
 
 using HlslGeneratorImplTest_Identifier = TestHelper;
 
-TEST_F(HlslGeneratorImplTest_Identifier, DISABLED_EmitExpression_Identifier) {
-  ast::IdentifierExpression i(std::vector<std::string>{"std", "glsl"});
-  ASSERT_TRUE(gen().EmitExpression(pre(), out(), &i)) << gen().error();
-  EXPECT_EQ(result(), "std::glsl");
-}
-
-TEST_F(HlslGeneratorImplTest_Identifier, EmitIdentifierExpression_Single) {
+TEST_F(HlslGeneratorImplTest_Identifier, EmitIdentifierExpression) {
   ast::IdentifierExpression i("foo");
   ASSERT_TRUE(gen().EmitExpression(pre(), out(), &i)) << gen().error();
   EXPECT_EQ(result(), "foo");
@@ -42,14 +36,6 @@
   EXPECT_EQ(result(), "virtual_tint_0");
 }
 
-// TODO(dsinclair): Handle import names
-TEST_F(HlslGeneratorImplTest_Identifier,
-       DISABLED_EmitIdentifierExpression_MultipleNames) {
-  ast::IdentifierExpression i({"std", "glsl", "init"});
-  ASSERT_TRUE(gen().EmitExpression(pre(), out(), &i)) << gen().error();
-  EXPECT_EQ(result(), "std::glsl::init");
-}
-
 }  // namespace
 }  // namespace hlsl
 }  // namespace writer
diff --git a/src/writer/hlsl/generator_impl_import_test.cc b/src/writer/hlsl/generator_impl_import_test.cc
index 8bea055..4cd8a61 100644
--- a/src/writer/hlsl/generator_impl_import_test.cc
+++ b/src/writer/hlsl/generator_impl_import_test.cc
@@ -58,59 +58,40 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", param.name}),
-                           std::move(params));
-
-  mod()->AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  ast::CallExpression expr(std::move(ident), std::move(params));
 
   ASSERT_TRUE(td().DetermineResultType(&expr)) << td().error();
-  ASSERT_TRUE(gen().EmitImportFunction(pre(), out(), &expr)) << gen().error();
+  ASSERT_TRUE(gen().EmitCall(pre(), out(), &expr)) << gen().error();
   EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1.00000000f)");
 }
-INSTANTIATE_TEST_SUITE_P(
-    HlslGeneratorImplTest_Import,
-    HlslImportData_SingleParamTest,
-    testing::Values(HlslImportData{"acos", "acos"},
-                    HlslImportData{"asin", "asin"},
-                    HlslImportData{"atan", "atan"},
-                    HlslImportData{"cos", "cos"},
-                    HlslImportData{"cosh", "cosh"},
-                    HlslImportData{"ceil", "ceil"},
-                    HlslImportData{"degrees", "degrees"},
-                    HlslImportData{"exp", "exp"},
-                    HlslImportData{"exp2", "exp2"},
-                    HlslImportData{"fabs", "abs"},
-                    HlslImportData{"floor", "floor"},
-                    HlslImportData{"fract", "frac"},
-                    HlslImportData{"interpolateatcentroid",
-                                   "EvaluateAttributeAtCentroid"},
-                    HlslImportData{"inversesqrt", "rsqrt"},
-                    HlslImportData{"length", "length"},
-                    HlslImportData{"log", "log"},
-                    HlslImportData{"log2", "log2"},
-                    HlslImportData{"normalize", "normalize"},
-                    HlslImportData{"radians", "radians"},
-                    HlslImportData{"round", "round"},
-                    HlslImportData{"fsign", "sign"},
-                    HlslImportData{"sin", "sin"},
-                    HlslImportData{"sinh", "sinh"},
-                    HlslImportData{"sqrt", "sqrt"},
-                    HlslImportData{"tan", "tan"},
-                    HlslImportData{"tanh", "tanh"},
-                    HlslImportData{"trunc", "trunc"}));
-
-TEST_F(HlslGeneratorImplTest_Import, DISABLED_HlslImportData_Acosh) {
-  FAIL();
-}
-
-TEST_F(HlslGeneratorImplTest_Import, DISABLED_HlslImportData_ASinh) {
-  FAIL();
-}
-
-TEST_F(HlslGeneratorImplTest_Import, DISABLED_HlslImportData_ATanh) {
-  FAIL();
-}
+INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
+                         HlslImportData_SingleParamTest,
+                         testing::Values(HlslImportData{"abs", "abs"},
+                                         HlslImportData{"acos", "acos"},
+                                         HlslImportData{"asin", "asin"},
+                                         HlslImportData{"atan", "atan"},
+                                         HlslImportData{"cos", "cos"},
+                                         HlslImportData{"cosh", "cosh"},
+                                         HlslImportData{"ceil", "ceil"},
+                                         HlslImportData{"exp", "exp"},
+                                         HlslImportData{"exp2", "exp2"},
+                                         HlslImportData{"floor", "floor"},
+                                         HlslImportData{"fract", "frac"},
+                                         HlslImportData{"inverseSqrt", "rsqrt"},
+                                         HlslImportData{"length", "length"},
+                                         HlslImportData{"log", "log"},
+                                         HlslImportData{"log2", "log2"},
+                                         HlslImportData{"normalize",
+                                                        "normalize"},
+                                         HlslImportData{"round", "round"},
+                                         HlslImportData{"sign", "sign"},
+                                         HlslImportData{"sin", "sin"},
+                                         HlslImportData{"sinh", "sinh"},
+                                         HlslImportData{"sqrt", "sqrt"},
+                                         HlslImportData{"tan", "tan"},
+                                         HlslImportData{"tanh", "tanh"},
+                                         HlslImportData{"trunc", "trunc"}));
 
 using HlslImportData_SingleIntParamTest =
     TestHelperBase<testing::TestWithParam<HlslImportData>>;
@@ -123,20 +104,17 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 1)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", param.name}),
-                           std::move(params));
-
-  mod()->AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
 
   ASSERT_TRUE(td().DetermineResultType(&expr)) << td().error();
-  ASSERT_TRUE(gen().EmitImportFunction(pre(), out(), &expr)) << gen().error();
+  ASSERT_TRUE(gen().EmitCall(pre(), out(), &expr)) << gen().error();
   EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
                          HlslImportData_SingleIntParamTest,
-                         testing::Values(HlslImportData{"sabs", "abs"},
-                                         HlslImportData{"ssign", "sign"}));
+                         testing::Values(HlslImportData{"abs", "abs"}));
 
 using HlslImportData_DualParamTest =
     TestHelperBase<testing::TestWithParam<HlslImportData>>;
@@ -151,14 +129,12 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 2.f)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", param.name}),
-                           std::move(params));
-
-  mod()->AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
 
   ASSERT_TRUE(td().DetermineResultType(&expr)) << td().error();
-  ASSERT_TRUE(gen().EmitImportFunction(pre(), out(), &expr)) << gen().error();
+  ASSERT_TRUE(gen().EmitCall(pre(), out(), &expr)) << gen().error();
   EXPECT_EQ(result(),
             std::string(param.hlsl_name) + "(1.00000000f, 2.00000000f)");
 }
@@ -166,10 +142,8 @@
                          HlslImportData_DualParamTest,
                          testing::Values(HlslImportData{"atan2", "atan2"},
                                          HlslImportData{"distance", "distance"},
-                                         HlslImportData{"fmax", "max"},
-                                         HlslImportData{"fmin", "min"},
-                                         HlslImportData{"nmax", "max"},
-                                         HlslImportData{"nmin", "min"},
+                                         HlslImportData{"max", "max"},
+                                         HlslImportData{"min", "min"},
                                          HlslImportData{"pow", "pow"},
                                          HlslImportData{"reflect", "reflect"},
                                          HlslImportData{"step", "step"}));
@@ -203,14 +177,12 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec, std::move(type_params)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", param.name}),
-                           std::move(params));
-
-  mod()->AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
 
   ASSERT_TRUE(td().DetermineResultType(&expr)) << td().error();
-  ASSERT_TRUE(gen().EmitImportFunction(pre(), out(), &expr)) << gen().error();
+  ASSERT_TRUE(gen().EmitCall(pre(), out(), &expr)) << gen().error();
   EXPECT_EQ(result(),
             std::string(param.hlsl_name) +
                 "(vector<float, 3>(1.00000000f, 2.00000000f, 3.00000000f), "
@@ -233,22 +205,18 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 2)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", param.name}),
-                           std::move(params));
-
-  mod()->AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
 
   ASSERT_TRUE(td().DetermineResultType(&expr)) << td().error();
-  ASSERT_TRUE(gen().EmitImportFunction(pre(), out(), &expr)) << gen().error();
+  ASSERT_TRUE(gen().EmitCall(pre(), out(), &expr)) << gen().error();
   EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1, 2)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
                          HlslImportData_DualParam_Int_Test,
-                         testing::Values(HlslImportData{"smax", "max"},
-                                         HlslImportData{"smin", "min"},
-                                         HlslImportData{"umax", "max"},
-                                         HlslImportData{"umin", "min"}));
+                         testing::Values(HlslImportData{"max", "max"},
+                                         HlslImportData{"min", "min"}));
 
 using HlslImportData_TripleParamTest =
     TestHelperBase<testing::TestWithParam<HlslImportData>>;
@@ -265,25 +233,22 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 3.f)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", param.name}),
-                           std::move(params));
-
-  mod()->AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
 
   ASSERT_TRUE(td().DetermineResultType(&expr)) << td().error();
-  ASSERT_TRUE(gen().EmitImportFunction(pre(), out(), &expr)) << gen().error();
+  ASSERT_TRUE(gen().EmitCall(pre(), out(), &expr)) << gen().error();
   EXPECT_EQ(result(), std::string(param.hlsl_name) +
                           "(1.00000000f, 2.00000000f, 3.00000000f)");
 }
 INSTANTIATE_TEST_SUITE_P(
     HlslGeneratorImplTest_Import,
     HlslImportData_TripleParamTest,
-    testing::Values(HlslImportData{"faceforward", "faceforward"},
+    testing::Values(HlslImportData{"faceForward", "faceforward"},
                     HlslImportData{"fma", "fma"},
-                    HlslImportData{"fclamp", "clamp"},
-                    HlslImportData{"nclamp", "clamp"},
-                    HlslImportData{"smoothstep", "smoothstep"}));
+                    HlslImportData{"clamp", "clamp"},
+                    HlslImportData{"smoothStep", "smoothstep"}));
 
 TEST_F(HlslGeneratorImplTest_Import, DISABLED_HlslImportData_FMix) {
   FAIL();
@@ -304,20 +269,17 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 3)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", param.name}),
-                           std::move(params));
-
-  mod()->AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
 
   ASSERT_TRUE(td().DetermineResultType(&expr)) << td().error();
-  ASSERT_TRUE(gen().EmitImportFunction(pre(), out(), &expr)) << gen().error();
+  ASSERT_TRUE(gen().EmitCall(pre(), out(), &expr)) << gen().error();
   EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1, 2, 3)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
                          HlslImportData_TripleParam_Int_Test,
-                         testing::Values(HlslImportData{"sclamp", "clamp"},
-                                         HlslImportData{"uclamp", "clamp"}));
+                         testing::Values(HlslImportData{"clamp", "clamp"}));
 
 TEST_F(HlslGeneratorImplTest_Import, HlslImportData_Determinant) {
   ast::type::F32Type f32;
@@ -329,17 +291,16 @@
   ast::ExpressionList params;
   params.push_back(std::make_unique<ast::IdentifierExpression>("var"));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", "determinant"}),
-                           std::move(params));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("determinant"),
+      std::move(params));
 
   mod()->AddGlobalVariable(std::move(var));
-  mod()->AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
 
   // Register the global
   ASSERT_TRUE(td().Determine()) << td().error();
   ASSERT_TRUE(td().DetermineResultType(&expr)) << td().error();
-  ASSERT_TRUE(gen().EmitImportFunction(pre(), out(), &expr)) << gen().error();
+  ASSERT_TRUE(gen().EmitCall(pre(), out(), &expr)) << gen().error();
   EXPECT_EQ(result(), std::string("determinant(var)"));
 }
 
diff --git a/src/writer/hlsl/generator_impl_intrinsic_test.cc b/src/writer/hlsl/generator_impl_intrinsic_test.cc
index 0455e97..8b46c7f 100644
--- a/src/writer/hlsl/generator_impl_intrinsic_test.cc
+++ b/src/writer/hlsl/generator_impl_intrinsic_test.cc
@@ -29,36 +29,36 @@
 using HlslGeneratorImplTest_Intrinsic = TestHelper;
 
 struct IntrinsicData {
-  const char* name;
+  ast::Intrinsic intrinsic;
   const char* hlsl_name;
 };
 inline std::ostream& operator<<(std::ostream& out, IntrinsicData data) {
-  out << data.name;
+  out << data.hlsl_name;
   return out;
 }
 using HlslIntrinsicTest = TestHelperBase<testing::TestWithParam<IntrinsicData>>;
 TEST_P(HlslIntrinsicTest, Emit) {
   auto param = GetParam();
-  EXPECT_EQ(gen().generate_intrinsic_name(param.name), param.hlsl_name);
+  EXPECT_EQ(gen().generate_intrinsic_name(param.intrinsic), param.hlsl_name);
 }
 INSTANTIATE_TEST_SUITE_P(
     HlslGeneratorImplTest_Intrinsic,
     HlslIntrinsicTest,
-    testing::Values(IntrinsicData{"any", "any"},
-                    IntrinsicData{"all", "all"},
-                    IntrinsicData{"dot", "dot"},
-                    IntrinsicData{"dpdx", "ddx"},
-                    IntrinsicData{"dpdx_coarse", "ddx_coarse"},
-                    IntrinsicData{"dpdx_fine", "ddx_fine"},
-                    IntrinsicData{"dpdy", "ddy"},
-                    IntrinsicData{"dpdy_coarse", "ddy_coarse"},
-                    IntrinsicData{"dpdy_fine", "ddy_fine"},
-                    IntrinsicData{"fwidth", "fwidth"},
-                    IntrinsicData{"fwidth_coarse", "fwidth"},
-                    IntrinsicData{"fwidth_fine", "fwidth"},
-                    IntrinsicData{"is_finite", "isfinite"},
-                    IntrinsicData{"is_inf", "isinf"},
-                    IntrinsicData{"is_nan", "isnan"}));
+    testing::Values(IntrinsicData{ast::Intrinsic::kAny, "any"},
+                    IntrinsicData{ast::Intrinsic::kAll, "all"},
+                    IntrinsicData{ast::Intrinsic::kDot, "dot"},
+                    IntrinsicData{ast::Intrinsic::kDpdx, "ddx"},
+                    IntrinsicData{ast::Intrinsic::kDpdxCoarse, "ddx_coarse"},
+                    IntrinsicData{ast::Intrinsic::kDpdxFine, "ddx_fine"},
+                    IntrinsicData{ast::Intrinsic::kDpdy, "ddy"},
+                    IntrinsicData{ast::Intrinsic::kDpdyCoarse, "ddy_coarse"},
+                    IntrinsicData{ast::Intrinsic::kDpdyFine, "ddy_fine"},
+                    IntrinsicData{ast::Intrinsic::kFwidth, "fwidth"},
+                    IntrinsicData{ast::Intrinsic::kFwidthCoarse, "fwidth"},
+                    IntrinsicData{ast::Intrinsic::kFwidthFine, "fwidth"},
+                    IntrinsicData{ast::Intrinsic::kIsFinite, "isfinite"},
+                    IntrinsicData{ast::Intrinsic::kIsInf, "isinf"},
+                    IntrinsicData{ast::Intrinsic::kIsNan, "isnan"}));
 
 TEST_F(HlslGeneratorImplTest_Intrinsic, DISABLED_Intrinsic_IsNormal) {
   FAIL();
@@ -101,7 +101,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Intrinsic, Intrinsic_Bad_Name) {
-  EXPECT_EQ(gen().generate_intrinsic_name("unknown name"), "");
+  EXPECT_EQ(gen().generate_intrinsic_name(ast::Intrinsic::kNone), "");
 }
 
 TEST_F(HlslGeneratorImplTest_Intrinsic, Intrinsic_Call) {
@@ -112,6 +112,8 @@
   ast::CallExpression call(std::make_unique<ast::IdentifierExpression>("dot"),
                            std::move(params));
 
+  ASSERT_TRUE(td().DetermineResultType(&call)) << td().error();
+
   gen().increment_indent();
   ASSERT_TRUE(gen().EmitExpression(pre(), out(), &call)) << gen().error();
   EXPECT_EQ(result(), "  dot(param1, param2)");
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index c6d45f7..c4f2329 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -14,7 +14,6 @@
 
 #include "src/writer/msl/generator_impl.h"
 
-#include "spirv/unified1/GLSL.std.450.h"
 #include "src/ast/array_accessor_expression.h"
 #include "src/ast/as_expression.h"
 #include "src/ast/assignment_statement.h"
@@ -33,7 +32,6 @@
 #include "src/ast/function.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/if_statement.h"
-#include "src/ast/intrinsic.h"
 #include "src/ast/location_decoration.h"
 #include "src/ast/loop_statement.h"
 #include "src/ast/member_accessor_expression.h"
@@ -409,38 +407,44 @@
   return name;
 }
 
-std::string GeneratorImpl::generate_intrinsic_name(const std::string& name) {
-  if (name == "any") {
+std::string GeneratorImpl::generate_intrinsic_name(ast::Intrinsic intrinsic) {
+  if (intrinsic == ast::Intrinsic::kAny) {
     return "any";
   }
-  if (name == "all") {
+  if (intrinsic == ast::Intrinsic::kAll) {
     return "all";
   }
-  if (name == "dot") {
+  if (intrinsic == ast::Intrinsic::kDot) {
     return "dot";
   }
-  if (name == "is_finite") {
+  if (intrinsic == ast::Intrinsic::kIsFinite) {
     return "isfinite";
   }
-  if (name == "is_inf") {
+  if (intrinsic == ast::Intrinsic::kIsInf) {
     return "isinf";
   }
-  if (name == "is_nan") {
+  if (intrinsic == ast::Intrinsic::kIsNan) {
     return "isnan";
   }
-  if (name == "is_normal") {
+  if (intrinsic == ast::Intrinsic::kIsNormal) {
     return "isnormal";
   }
-  if (name == "select") {
+  if (intrinsic == ast::Intrinsic::kSelect) {
     return "select";
   }
-  if (name == "dpdy" || name == "dpdy_fine" || name == "dpdy_coarse") {
+  if (intrinsic == ast::Intrinsic::kDpdy ||
+      intrinsic == ast::Intrinsic::kDpdyFine ||
+      intrinsic == ast::Intrinsic::kDpdyCoarse) {
     return "dfdy";
   }
-  if (name == "dpdx" || name == "dpdx_fine" || name == "dpdx_coarse") {
+  if (intrinsic == ast::Intrinsic::kDpdx ||
+      intrinsic == ast::Intrinsic::kDpdxFine ||
+      intrinsic == ast::Intrinsic::kDpdxCoarse) {
     return "dfdx";
   }
-  if (name == "fwidth" || name == "fwidth_fine" || name == "fwidth_coarse") {
+  if (intrinsic == ast::Intrinsic::kFwidth ||
+      intrinsic == ast::Intrinsic::kFwidthFine ||
+      intrinsic == ast::Intrinsic::kFwidthCoarse) {
     return "fwidth";
   }
   return "";
@@ -453,9 +457,9 @@
   }
 
   auto* ident = expr->func()->AsIdentifier();
-  if (!ident->has_path() && ast::intrinsic::IsIntrinsic(ident->name())) {
+  if (ident->IsIntrinsic()) {
     const auto& params = expr->params();
-    if (ident->name() == "outer_product") {
+    if (ident->intrinsic() == ast::Intrinsic::kOuterProduct) {
       error_ = "outer_product not supported yet";
       return false;
       // TODO(dsinclair): This gets tricky. We need to generate two variables to
@@ -508,11 +512,15 @@
 
       // out_ << ")";
     } else {
-      auto name = generate_intrinsic_name(ident->name());
+      auto name = generate_intrinsic_name(ident->intrinsic());
       if (name.empty()) {
-        error_ = "unable to determine intrinsic name for intrinsic: " +
-                 ident->name();
-        return false;
+        if (ast::intrinsic::IsTextureIntrinsic(ident->intrinsic())) {
+          error_ = "Textures not implemented yet";
+          return false;
+        }
+        if (!EmitBuiltinName(ident)) {
+          return false;
+        }
       }
 
       make_indent();
@@ -535,191 +543,69 @@
     return true;
   }
 
-  if (!ident->has_path()) {
-    auto name = ident->name();
-    auto it = ep_func_name_remapped_.find(current_ep_name_ + "_" + name);
-    if (it != ep_func_name_remapped_.end()) {
-      name = it->second;
-    }
-
-    auto* func = module_->FindFunctionByName(ident->name());
-    if (func == nullptr) {
-      error_ = "Unable to find function: " + name;
-      return false;
-    }
-
-    out_ << name << "(";
-
-    bool first = true;
-    if (has_referenced_in_var_needing_struct(func)) {
-      auto var_name = current_ep_var_name(VarType::kIn);
-      if (!var_name.empty()) {
-        out_ << var_name;
-        first = false;
-      }
-    }
-    if (has_referenced_out_var_needing_struct(func)) {
-      auto var_name = current_ep_var_name(VarType::kOut);
-      if (!var_name.empty()) {
-        if (!first) {
-          out_ << ", ";
-        }
-        first = false;
-        out_ << var_name;
-      }
-    }
-
-    for (const auto& data : func->referenced_builtin_variables()) {
-      auto* var = data.first;
-      if (var->storage_class() != ast::StorageClass::kInput) {
-        continue;
-      }
-      if (!first) {
-        out_ << ", ";
-      }
-      first = false;
-      out_ << var->name();
-    }
-
-    for (const auto& data : func->referenced_uniform_variables()) {
-      auto* var = data.first;
-      if (!first) {
-        out_ << ", ";
-      }
-      first = false;
-      out_ << var->name();
-    }
-
-    for (const auto& data : func->referenced_storagebuffer_variables()) {
-      auto* var = data.first;
-      if (!first) {
-        out_ << ", ";
-      }
-      first = false;
-      out_ << var->name();
-    }
-
-    const auto& params = expr->params();
-    for (const auto& param : params) {
-      if (!first) {
-        out_ << ", ";
-      }
-      first = false;
-
-      if (!EmitExpression(param.get())) {
-        return false;
-      }
-    }
-
-    out_ << ")";
-  } else {
-    return EmitImportFunction(expr);
-  }
-  return true;
-}
-
-bool GeneratorImpl::EmitImportFunction(ast::CallExpression* expr) {
-  auto* ident = expr->func()->AsIdentifier();
-
-  auto* imp = module_->FindImportByName(ident->path());
-  if (imp == nullptr) {
-    error_ = "unable to find import for " + ident->path();
-    return 0;
-  }
-  auto id = imp->GetIdForMethod(ident->name());
-  if (id == 0) {
-    error_ = "unable to lookup: " + ident->name() + " in " + ident->path();
+  auto name = ident->name();
+  auto it = ep_func_name_remapped_.find(current_ep_name_ + "_" + name);
+  if (it != ep_func_name_remapped_.end()) {
+    name = it->second;
   }
 
-  out_ << "metal::";
-  switch (id) {
-    case GLSLstd450Acos:
-    case GLSLstd450Acosh:
-    case GLSLstd450Asin:
-    case GLSLstd450Asinh:
-    case GLSLstd450Atan:
-    case GLSLstd450Atan2:
-    case GLSLstd450Atanh:
-    case GLSLstd450Ceil:
-    case GLSLstd450Cos:
-    case GLSLstd450Cosh:
-    case GLSLstd450Cross:
-    case GLSLstd450Determinant:
-    case GLSLstd450Distance:
-    case GLSLstd450Exp:
-    case GLSLstd450Exp2:
-    case GLSLstd450FAbs:
-    case GLSLstd450FaceForward:
-    case GLSLstd450Floor:
-    case GLSLstd450Fma:
-    case GLSLstd450Fract:
-    case GLSLstd450Length:
-    case GLSLstd450Log:
-    case GLSLstd450Log2:
-    case GLSLstd450Normalize:
-    case GLSLstd450Pow:
-    case GLSLstd450Reflect:
-    case GLSLstd450Round:
-    case GLSLstd450Sin:
-    case GLSLstd450Sinh:
-    case GLSLstd450SmoothStep:
-    case GLSLstd450Sqrt:
-    case GLSLstd450Step:
-    case GLSLstd450Tan:
-    case GLSLstd450Tanh:
-    case GLSLstd450Trunc:
-      out_ << ident->name();
-      break;
-    case GLSLstd450InverseSqrt:
-      out_ << "rsqrt";
-      break;
-    case GLSLstd450FMax:
-    case GLSLstd450NMax:
-      out_ << "fmax";
-      break;
-    case GLSLstd450FMin:
-    case GLSLstd450NMin:
-      out_ << "fmin";
-      break;
-    case GLSLstd450FMix:
-      out_ << "mix";
-      break;
-    case GLSLstd450FSign:
-      out_ << "sign";
-      break;
-    case GLSLstd450SAbs:
-      out_ << "abs";
-      break;
-    case GLSLstd450SMax:
-    case GLSLstd450UMax:
-      out_ << "max";
-      break;
-    case GLSLstd450SMin:
-    case GLSLstd450UMin:
-      out_ << "min";
-      break;
-    case GLSLstd450FClamp:
-    case GLSLstd450SClamp:
-    case GLSLstd450NClamp:
-    case GLSLstd450UClamp:
-      out_ << "clamp";
-      break;
-    // TODO(dsinclair): Determine mappings for the following
-    case GLSLstd450Degrees:
-    case GLSLstd450FindILsb:
-    case GLSLstd450FindUMsb:
-    case GLSLstd450FindSMsb:
-    case GLSLstd450InterpolateAtCentroid:
-    case GLSLstd450MatrixInverse:
-    case GLSLstd450Radians:
-    case GLSLstd450RoundEven:
-    case GLSLstd450SSign:
-      error_ = "Unknown import method: " + ident->name();
-      return false;
+  auto* func = module_->FindFunctionByName(ident->name());
+  if (func == nullptr) {
+    error_ = "Unable to find function: " + name;
+    return false;
   }
 
-  out_ << "(";
+  out_ << name << "(";
+
   bool first = true;
+  if (has_referenced_in_var_needing_struct(func)) {
+    auto var_name = current_ep_var_name(VarType::kIn);
+    if (!var_name.empty()) {
+      out_ << var_name;
+      first = false;
+    }
+  }
+  if (has_referenced_out_var_needing_struct(func)) {
+    auto var_name = current_ep_var_name(VarType::kOut);
+    if (!var_name.empty()) {
+      if (!first) {
+        out_ << ", ";
+      }
+      first = false;
+      out_ << var_name;
+    }
+  }
+
+  for (const auto& data : func->referenced_builtin_variables()) {
+    auto* var = data.first;
+    if (var->storage_class() != ast::StorageClass::kInput) {
+      continue;
+    }
+    if (!first) {
+      out_ << ", ";
+    }
+    first = false;
+    out_ << var->name();
+  }
+
+  for (const auto& data : func->referenced_uniform_variables()) {
+    auto* var = data.first;
+    if (!first) {
+      out_ << ", ";
+    }
+    first = false;
+    out_ << var->name();
+  }
+
+  for (const auto& data : func->referenced_storagebuffer_variables()) {
+    auto* var = data.first;
+    if (!first) {
+      out_ << ", ";
+    }
+    first = false;
+    out_ << var->name();
+  }
+
   const auto& params = expr->params();
   for (const auto& param : params) {
     if (!first) {
@@ -731,11 +617,89 @@
       return false;
     }
   }
+
   out_ << ")";
 
   return true;
 }
 
+bool GeneratorImpl::EmitBuiltinName(ast::IdentifierExpression* ident) {
+  out_ << "metal::";
+  switch (ident->intrinsic()) {
+    case ast::Intrinsic::kAcos:
+    case ast::Intrinsic::kAsin:
+    case ast::Intrinsic::kAtan:
+    case ast::Intrinsic::kAtan2:
+    case ast::Intrinsic::kCeil:
+    case ast::Intrinsic::kCos:
+    case ast::Intrinsic::kCosh:
+    case ast::Intrinsic::kCross:
+    case ast::Intrinsic::kDeterminant:
+    case ast::Intrinsic::kDistance:
+    case ast::Intrinsic::kExp:
+    case ast::Intrinsic::kExp2:
+    case ast::Intrinsic::kFloor:
+    case ast::Intrinsic::kFma:
+    case ast::Intrinsic::kFract:
+    case ast::Intrinsic::kLength:
+    case ast::Intrinsic::kLog:
+    case ast::Intrinsic::kLog2:
+    case ast::Intrinsic::kMix:
+    case ast::Intrinsic::kNormalize:
+    case ast::Intrinsic::kPow:
+    case ast::Intrinsic::kReflect:
+    case ast::Intrinsic::kRound:
+    case ast::Intrinsic::kSin:
+    case ast::Intrinsic::kSinh:
+    case ast::Intrinsic::kSqrt:
+    case ast::Intrinsic::kStep:
+    case ast::Intrinsic::kTan:
+    case ast::Intrinsic::kTanh:
+    case ast::Intrinsic::kTrunc:
+    case ast::Intrinsic::kSign:
+    case ast::Intrinsic::kClamp:
+      out_ << ident->name();
+      break;
+    case ast::Intrinsic::kAbs:
+      if (ident->result_type()->IsF32()) {
+        out_ << "fabs";
+      } else if (ident->result_type()->IsU32() ||
+                 ident->result_type()->IsI32()) {
+        out_ << "abs";
+      }
+      break;
+    case ast::Intrinsic::kMax:
+      if (ident->result_type()->IsF32()) {
+        out_ << "fmax";
+      } else if (ident->result_type()->IsU32() ||
+                 ident->result_type()->IsI32()) {
+        out_ << "max";
+      }
+      break;
+    case ast::Intrinsic::kMin:
+      if (ident->result_type()->IsF32()) {
+        out_ << "fmin";
+      } else if (ident->result_type()->IsU32() ||
+                 ident->result_type()->IsI32()) {
+        out_ << "min";
+      }
+      break;
+    case ast::Intrinsic::kFaceForward:
+      out_ << "faceforward";
+      break;
+    case ast::Intrinsic::kSmoothStep:
+      out_ << "smoothstep";
+      break;
+    case ast::Intrinsic::kInverseSqrt:
+      out_ << "rsqrt";
+      break;
+    default:
+      error_ = "Unknown import method: " + ident->name();
+      return false;
+  }
+  return true;
+}
+
 bool GeneratorImpl::EmitCase(ast::CaseStatement* stmt) {
   make_indent();
 
@@ -1402,12 +1366,6 @@
 
 bool GeneratorImpl::EmitIdentifier(ast::IdentifierExpression* expr) {
   auto* ident = expr->AsIdentifier();
-  if (ident->has_path()) {
-    // TODO(dsinclair): Handle identifier with path
-    error_ = "Identifier paths not handled yet.";
-    return false;
-  }
-
   ast::Variable* var = nullptr;
   if (global_variables_.get(ident->name(), &var)) {
     if (global_is_in_struct(var)) {
diff --git a/src/writer/msl/generator_impl.h b/src/writer/msl/generator_impl.h
index 459cac5..68f5574 100644
--- a/src/writer/msl/generator_impl.h
+++ b/src/writer/msl/generator_impl.h
@@ -19,6 +19,7 @@
 #include <string>
 #include <unordered_map>
 
+#include "src/ast/intrinsic.h"
 #include "src/ast/literal.h"
 #include "src/ast/module.h"
 #include "src/ast/scalar_constructor_expression.h"
@@ -150,10 +151,10 @@
   /// @param stmt the statement to emit
   /// @returns true if the statement was successfully emitted
   bool EmitIf(ast::IfStatement* stmt);
-  /// Handles genreating an import expression
-  /// @param expr the expression
-  /// @returns true if the expression was successfully emitted.
-  bool EmitImportFunction(ast::CallExpression* expr);
+  /// Handles generating a builtin name
+  /// @param ident the identifier to build the name from
+  /// @returns true if the name was successfully emitted.
+  bool EmitBuiltinName(ast::IdentifierExpression* ident);
   /// Handles a literal
   /// @param lit the literal to emit
   /// @returns true if the literal was successfully emitted
@@ -230,9 +231,9 @@
   /// @returns the name
   std::string generate_name(const std::string& prefix);
   /// Generates an intrinsic name from the given name
-  /// @param name the name to convert to an intrinsic
+  /// @param intrinsic the intrinsic to convert to an method name
   /// @returns the intrinsic name or blank on error
-  std::string generate_intrinsic_name(const std::string& name);
+  std::string generate_intrinsic_name(ast::Intrinsic intrinsic);
 
   /// Checks if the global variable is in an input or output struct
   /// @param var the variable to check
diff --git a/src/writer/msl/generator_impl_identifier_test.cc b/src/writer/msl/generator_impl_identifier_test.cc
index 7c7bc19..2e07db5 100644
--- a/src/writer/msl/generator_impl_identifier_test.cc
+++ b/src/writer/msl/generator_impl_identifier_test.cc
@@ -24,16 +24,7 @@
 
 using MslGeneratorImplTest = testing::Test;
 
-TEST_F(MslGeneratorImplTest, DISABLED_EmitExpression_Identifier) {
-  ast::IdentifierExpression i(std::vector<std::string>{"std", "glsl"});
-
-  ast::Module m;
-  GeneratorImpl g(&m);
-  ASSERT_TRUE(g.EmitExpression(&i)) << g.error();
-  EXPECT_EQ(g.result(), "std::glsl");
-}
-
-TEST_F(MslGeneratorImplTest, EmitIdentifierExpression_Single) {
+TEST_F(MslGeneratorImplTest, EmitIdentifierExpression) {
   ast::IdentifierExpression i("foo");
 
   ast::Module m;
@@ -51,16 +42,6 @@
   EXPECT_EQ(g.result(), "virtual_tint_0");
 }
 
-// TODO(dsinclair): Handle import names
-TEST_F(MslGeneratorImplTest, DISABLED_EmitIdentifierExpression_MultipleNames) {
-  ast::IdentifierExpression i({"std", "glsl", "init"});
-
-  ast::Module m;
-  GeneratorImpl g(&m);
-  ASSERT_TRUE(g.EmitExpression(&i)) << g.error();
-  EXPECT_EQ(g.result(), "std::glsl::init");
-}
-
 }  // namespace
 }  // namespace msl
 }  // namespace writer
diff --git a/src/writer/msl/generator_impl_import_test.cc b/src/writer/msl/generator_impl_import_test.cc
index fcf0d50..358838f 100644
--- a/src/writer/msl/generator_impl_import_test.cc
+++ b/src/writer/msl/generator_impl_import_test.cc
@@ -57,46 +57,41 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", param.name}),
-                           std::move(params));
+  auto ident = std::make_unique<ast::IdentifierExpression>(param.name);
+  auto* ident_ptr = ident.get();
+
+  ast::CallExpression call(std::move(ident), std::move(params));
 
   Context ctx;
   ast::Module mod;
   TypeDeterminer td(&ctx, &mod);
-  mod.AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
 
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+  // The call type determination will set the intrinsic data for the ident
+  ASSERT_TRUE(td.DetermineResultType(&call)) << td.error();
 
   GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.EmitImportFunction(&expr)) << g.error();
-  EXPECT_EQ(g.result(),
-            std::string("metal::") + param.msl_name + "(1.00000000f)");
+  ASSERT_TRUE(g.EmitBuiltinName(ident_ptr)) << g.error();
+  EXPECT_EQ(g.result(), std::string("metal::") + param.msl_name);
 }
 INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
                          MslImportData_SingleParamTest,
-                         testing::Values(MslImportData{"acos", "acos"},
-                                         MslImportData{"acosh", "acosh"},
+                         testing::Values(MslImportData{"abs", "fabs"},
+                                         MslImportData{"acos", "acos"},
                                          MslImportData{"asin", "asin"},
-                                         MslImportData{"asinh", "asinh"},
                                          MslImportData{"atan", "atan"},
-                                         MslImportData{"atanh", "atanh"},
+                                         MslImportData{"ceil", "ceil"},
                                          MslImportData{"cos", "cos"},
                                          MslImportData{"cosh", "cosh"},
-                                         MslImportData{"ceil", "ceil"},
                                          MslImportData{"exp", "exp"},
                                          MslImportData{"exp2", "exp2"},
-                                         MslImportData{"fabs", "fabs"},
                                          MslImportData{"floor", "floor"},
                                          MslImportData{"fract", "fract"},
-                                         MslImportData{"inversesqrt", "rsqrt"},
+                                         MslImportData{"inverseSqrt", "rsqrt"},
                                          MslImportData{"length", "length"},
                                          MslImportData{"log", "log"},
                                          MslImportData{"log2", "log2"},
-                                         MslImportData{"normalize",
-                                                       "normalize"},
                                          MslImportData{"round", "round"},
-                                         MslImportData{"fsign", "sign"},
+                                         MslImportData{"sign", "sign"},
                                          MslImportData{"sin", "sin"},
                                          MslImportData{"sinh", "sinh"},
                                          MslImportData{"sqrt", "sqrt"},
@@ -111,19 +106,17 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 1)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", "sabs"}),
+  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>("abs"),
                            std::move(params));
 
   Context ctx;
   ast::Module mod;
   TypeDeterminer td(&ctx, &mod);
-  mod.AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
   GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.EmitImportFunction(&expr)) << g.error();
+  ASSERT_TRUE(g.EmitCall(&expr)) << g.error();
   EXPECT_EQ(g.result(), R"(metal::abs(1))");
 }
 
@@ -139,19 +132,18 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 2.f)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", param.name}),
-                           std::move(params));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
 
   Context ctx;
   ast::Module mod;
   TypeDeterminer td(&ctx, &mod);
-  mod.AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
   GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.EmitImportFunction(&expr)) << g.error();
+  ASSERT_TRUE(g.EmitCall(&expr)) << g.error();
   EXPECT_EQ(g.result(), std::string("metal::") + param.msl_name +
                             "(1.00000000f, 2.00000000f)");
 }
@@ -159,10 +151,8 @@
                          MslImportData_DualParamTest,
                          testing::Values(MslImportData{"atan2", "atan2"},
                                          MslImportData{"distance", "distance"},
-                                         MslImportData{"fmax", "fmax"},
-                                         MslImportData{"fmin", "fmin"},
-                                         MslImportData{"nmax", "fmax"},
-                                         MslImportData{"nmin", "fmin"},
+                                         MslImportData{"max", "fmax"},
+                                         MslImportData{"min", "fmin"},
                                          MslImportData{"pow", "pow"},
                                          MslImportData{"reflect", "reflect"},
                                          MslImportData{"step", "step"}));
@@ -196,19 +186,18 @@
   params.push_back(std::make_unique<ast::TypeConstructorExpression>(
       &vec, std::move(type_params)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", param.name}),
-                           std::move(params));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
 
   Context ctx;
   ast::Module mod;
   TypeDeterminer td(&ctx, &mod);
-  mod.AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
   GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.EmitImportFunction(&expr)) << g.error();
+  ASSERT_TRUE(g.EmitCall(&expr)) << g.error();
   EXPECT_EQ(g.result(), std::string("metal::") + param.msl_name +
                             "(float3(1.00000000f, 2.00000000f, 3.00000000f), "
                             "float3(4.00000000f, 5.00000000f, 6.00000000f))");
@@ -229,27 +218,24 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 2)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", param.name}),
-                           std::move(params));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
 
   Context ctx;
   ast::Module mod;
   TypeDeterminer td(&ctx, &mod);
-  mod.AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
   GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.EmitImportFunction(&expr)) << g.error();
+  ASSERT_TRUE(g.EmitCall(&expr)) << g.error();
   EXPECT_EQ(g.result(), std::string("metal::") + param.msl_name + "(1, 2)");
 }
 INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
                          MslImportData_DualParam_Int_Test,
-                         testing::Values(MslImportData{"smax", "max"},
-                                         MslImportData{"smin", "min"},
-                                         MslImportData{"umax", "max"},
-                                         MslImportData{"umin", "min"}));
+                         testing::Values(MslImportData{"max", "max"},
+                                         MslImportData{"min", "min"}));
 
 using MslImportData_TripleParamTest = testing::TestWithParam<MslImportData>;
 TEST_P(MslImportData_TripleParamTest, FloatScalar) {
@@ -265,31 +251,29 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::FloatLiteral>(&f32, 3.f)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", param.name}),
-                           std::move(params));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
 
   Context ctx;
   ast::Module mod;
   TypeDeterminer td(&ctx, &mod);
-  mod.AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
   GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.EmitImportFunction(&expr)) << g.error();
+  ASSERT_TRUE(g.EmitCall(&expr)) << g.error();
   EXPECT_EQ(g.result(), std::string("metal::") + param.msl_name +
                             "(1.00000000f, 2.00000000f, 3.00000000f)");
 }
 INSTANTIATE_TEST_SUITE_P(
     MslGeneratorImplTest,
     MslImportData_TripleParamTest,
-    testing::Values(MslImportData{"faceforward", "faceforward"},
+    testing::Values(MslImportData{"faceForward", "faceforward"},
                     MslImportData{"fma", "fma"},
-                    MslImportData{"fclamp", "clamp"},
-                    MslImportData{"fmix", "mix"},
-                    MslImportData{"nclamp", "clamp"},
-                    MslImportData{"smoothstep", "smoothstep"}));
+                    MslImportData{"mix", "mix"},
+                    MslImportData{"clamp", "clamp"},
+                    MslImportData{"smoothStep", "smoothstep"}));
 
 using MslImportData_TripleParam_Int_Test =
     testing::TestWithParam<MslImportData>;
@@ -306,25 +290,24 @@
   params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
       std::make_unique<ast::SintLiteral>(&i32, 3)));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", param.name}),
-                           std::move(params));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
 
   Context ctx;
   ast::Module mod;
   TypeDeterminer td(&ctx, &mod);
-  mod.AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
   GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.EmitImportFunction(&expr)) << g.error();
+  ASSERT_TRUE(g.EmitCall(&expr)) << g.error();
   EXPECT_EQ(g.result(), std::string("metal::") + param.msl_name + "(1, 2, 3)");
 }
 INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
                          MslImportData_TripleParam_Int_Test,
-                         testing::Values(MslImportData{"sclamp", "clamp"},
-                                         MslImportData{"uclamp", "clamp"}));
+                         testing::Values(MslImportData{"clamp", "clamp"},
+                                         MslImportData{"clamp", "clamp"}));
 
 TEST_F(MslGeneratorImplTest, MslImportData_Determinant) {
   ast::type::F32Type f32;
@@ -336,14 +319,13 @@
   ast::ExpressionList params;
   params.push_back(std::make_unique<ast::IdentifierExpression>("var"));
 
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", "determinant"}),
-                           std::move(params));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("determinant"),
+      std::move(params));
 
   Context ctx;
   ast::Module mod;
   mod.AddGlobalVariable(std::move(var));
-  mod.AddImport(std::make_unique<ast::Import>("GLSL.std.450", "std"));
 
   TypeDeterminer td(&ctx, &mod);
   // Register the global
@@ -351,7 +333,7 @@
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
 
   GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.EmitImportFunction(&expr)) << g.error();
+  ASSERT_TRUE(g.EmitCall(&expr)) << g.error();
   EXPECT_EQ(g.result(), std::string("metal::determinant(var)"));
 }
 
diff --git a/src/writer/msl/generator_impl_intrinsic_test.cc b/src/writer/msl/generator_impl_intrinsic_test.cc
index b52955c..ed09571 100644
--- a/src/writer/msl/generator_impl_intrinsic_test.cc
+++ b/src/writer/msl/generator_impl_intrinsic_test.cc
@@ -30,11 +30,11 @@
 using MslGeneratorImplTest = testing::Test;
 
 struct IntrinsicData {
-  const char* name;
+  ast::Intrinsic intrinsic;
   const char* msl_name;
 };
 inline std::ostream& operator<<(std::ostream& out, IntrinsicData data) {
-  out << data.name;
+  out << data.msl_name;
   return out;
 }
 using MslIntrinsicTest = testing::TestWithParam<IntrinsicData>;
@@ -43,28 +43,28 @@
 
   ast::Module m;
   GeneratorImpl g(&m);
-  EXPECT_EQ(g.generate_intrinsic_name(param.name), param.msl_name);
+  EXPECT_EQ(g.generate_intrinsic_name(param.intrinsic), param.msl_name);
 }
-INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
-                         MslIntrinsicTest,
-                         testing::Values(IntrinsicData{"any", "any"},
-                                         IntrinsicData{"all", "all"},
-                                         IntrinsicData{"dot", "dot"},
-                                         IntrinsicData{"dpdx", "dfdx"},
-                                         IntrinsicData{"dpdx_coarse", "dfdx"},
-                                         IntrinsicData{"dpdx_fine", "dfdx"},
-                                         IntrinsicData{"dpdy", "dfdy"},
-                                         IntrinsicData{"dpdy_coarse", "dfdy"},
-                                         IntrinsicData{"dpdy_fine", "dfdy"},
-                                         IntrinsicData{"fwidth", "fwidth"},
-                                         IntrinsicData{"fwidth_coarse",
-                                                       "fwidth"},
-                                         IntrinsicData{"fwidth_fine", "fwidth"},
-                                         IntrinsicData{"is_finite", "isfinite"},
-                                         IntrinsicData{"is_inf", "isinf"},
-                                         IntrinsicData{"is_nan", "isnan"},
-                                         IntrinsicData{"is_normal", "isnormal"},
-                                         IntrinsicData{"select", "select"}));
+INSTANTIATE_TEST_SUITE_P(
+    MslGeneratorImplTest,
+    MslIntrinsicTest,
+    testing::Values(IntrinsicData{ast::Intrinsic::kAny, "any"},
+                    IntrinsicData{ast::Intrinsic::kAll, "all"},
+                    IntrinsicData{ast::Intrinsic::kDot, "dot"},
+                    IntrinsicData{ast::Intrinsic::kDpdx, "dfdx"},
+                    IntrinsicData{ast::Intrinsic::kDpdxCoarse, "dfdx"},
+                    IntrinsicData{ast::Intrinsic::kDpdxFine, "dfdx"},
+                    IntrinsicData{ast::Intrinsic::kDpdy, "dfdy"},
+                    IntrinsicData{ast::Intrinsic::kDpdyCoarse, "dfdy"},
+                    IntrinsicData{ast::Intrinsic::kDpdyFine, "dfdy"},
+                    IntrinsicData{ast::Intrinsic::kFwidth, "fwidth"},
+                    IntrinsicData{ast::Intrinsic::kFwidthCoarse, "fwidth"},
+                    IntrinsicData{ast::Intrinsic::kFwidthFine, "fwidth"},
+                    IntrinsicData{ast::Intrinsic::kIsFinite, "isfinite"},
+                    IntrinsicData{ast::Intrinsic::kIsInf, "isinf"},
+                    IntrinsicData{ast::Intrinsic::kIsNan, "isnan"},
+                    IntrinsicData{ast::Intrinsic::kIsNormal, "isnormal"},
+                    IntrinsicData{ast::Intrinsic::kSelect, "select"}));
 
 TEST_F(MslGeneratorImplTest, DISABLED_Intrinsic_OuterProduct) {
   ast::type::F32Type f32;
@@ -106,7 +106,7 @@
 TEST_F(MslGeneratorImplTest, Intrinsic_Bad_Name) {
   ast::Module m;
   GeneratorImpl g(&m);
-  EXPECT_EQ(g.generate_intrinsic_name("unknown name"), "");
+  EXPECT_EQ(g.generate_intrinsic_name(ast::Intrinsic::kNone), "");
 }
 
 TEST_F(MslGeneratorImplTest, Intrinsic_Call) {
@@ -117,7 +117,11 @@
   ast::CallExpression call(std::make_unique<ast::IdentifierExpression>("dot"),
                            std::move(params));
 
+  Context ctx;
   ast::Module m;
+  TypeDeterminer td(&ctx, &m);
+  ASSERT_TRUE(td.DetermineResultType(&call)) << td.error();
+
   GeneratorImpl g(&m);
   g.increment_indent();
   ASSERT_TRUE(g.EmitExpression(&call)) << g.error();
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 8bfd9a1..529286f 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -17,6 +17,7 @@
 #include <sstream>
 #include <utility>
 
+#include "spirv/unified1/GLSL.std.450.h"
 #include "spirv/unified1/spirv.h"
 #include "src/ast/array_accessor_expression.h"
 #include "src/ast/as_expression.h"
@@ -68,11 +69,15 @@
 #include "src/ast/unary_op_expression.h"
 #include "src/ast/variable_decl_statement.h"
 
+#include <iostream>
+
 namespace tint {
 namespace writer {
 namespace spirv {
 namespace {
 
+const char kGLSLstd450[] = "GLSL.std.450";
+
 uint32_t size_of(const InstructionList& instructions) {
   uint32_t size = 0;
   for (const auto& inst : instructions)
@@ -147,6 +152,119 @@
   return type->IsMatrix() ? type->AsMatrix() : nullptr;
 }
 
+uint32_t intrinsic_to_glsl_method(ast::type::Type* type,
+                                  ast::Intrinsic intrinsic) {
+  switch (intrinsic) {
+    case ast::Intrinsic::kAbs:
+      if (type->is_float_scalar_or_vector()) {
+        return GLSLstd450FAbs;
+      } else {
+        return GLSLstd450SAbs;
+      }
+    case ast::Intrinsic::kAcos:
+      return GLSLstd450Acos;
+    case ast::Intrinsic::kAsin:
+      return GLSLstd450Asin;
+    case ast::Intrinsic::kAtan:
+      return GLSLstd450Atan;
+    case ast::Intrinsic::kAtan2:
+      return GLSLstd450Atan2;
+    case ast::Intrinsic::kCeil:
+      return GLSLstd450Ceil;
+    case ast::Intrinsic::kClamp:
+      if (type->is_float_scalar_or_vector()) {
+        return GLSLstd450NClamp;
+      } else if (type->is_unsigned_scalar_or_vector()) {
+        return GLSLstd450UClamp;
+      } else {
+        return GLSLstd450SClamp;
+      }
+    case ast::Intrinsic::kCos:
+      return GLSLstd450Cos;
+    case ast::Intrinsic::kCosh:
+      return GLSLstd450Cosh;
+    case ast::Intrinsic::kCross:
+      return GLSLstd450Cross;
+    case ast::Intrinsic::kDeterminant:
+      return GLSLstd450Determinant;
+    case ast::Intrinsic::kDistance:
+      return GLSLstd450Distance;
+    case ast::Intrinsic::kExp:
+      return GLSLstd450Exp;
+    case ast::Intrinsic::kExp2:
+      return GLSLstd450Exp2;
+    case ast::Intrinsic::kFaceForward:
+      return GLSLstd450FaceForward;
+    case ast::Intrinsic::kFloor:
+      return GLSLstd450Floor;
+    case ast::Intrinsic::kFma:
+      return GLSLstd450Fma;
+    case ast::Intrinsic::kFract:
+      return GLSLstd450Fract;
+    case ast::Intrinsic::kFrexp:
+      return GLSLstd450Frexp;
+    case ast::Intrinsic::kInverseSqrt:
+      return GLSLstd450InverseSqrt;
+    case ast::Intrinsic::kLdexp:
+      return GLSLstd450Ldexp;
+    case ast::Intrinsic::kLength:
+      return GLSLstd450Length;
+    case ast::Intrinsic::kLog:
+      return GLSLstd450Log;
+    case ast::Intrinsic::kLog2:
+      return GLSLstd450Log2;
+    case ast::Intrinsic::kMax:
+      if (type->is_float_scalar_or_vector()) {
+        return GLSLstd450NMax;
+      } else if (type->is_unsigned_scalar_or_vector()) {
+        return GLSLstd450UMax;
+      } else {
+        return GLSLstd450SMax;
+      }
+    case ast::Intrinsic::kMin:
+      if (type->is_float_scalar_or_vector()) {
+        return GLSLstd450NMin;
+      } else if (type->is_unsigned_scalar_or_vector()) {
+        return GLSLstd450UMin;
+      } else {
+        return GLSLstd450SMin;
+      }
+    case ast::Intrinsic::kMix:
+      return GLSLstd450FMix;
+    case ast::Intrinsic::kModf:
+      return GLSLstd450Modf;
+    case ast::Intrinsic::kNormalize:
+      return GLSLstd450Normalize;
+    case ast::Intrinsic::kPow:
+      return GLSLstd450Pow;
+    case ast::Intrinsic::kReflect:
+      return GLSLstd450Reflect;
+    case ast::Intrinsic::kRound:
+      return GLSLstd450Round;
+    case ast::Intrinsic::kSign:
+      return GLSLstd450FSign;
+    case ast::Intrinsic::kSin:
+      return GLSLstd450Sin;
+    case ast::Intrinsic::kSinh:
+      return GLSLstd450Sinh;
+    case ast::Intrinsic::kSmoothStep:
+      return GLSLstd450SmoothStep;
+    case ast::Intrinsic::kSqrt:
+      return GLSLstd450Sqrt;
+    case ast::Intrinsic::kStep:
+      return GLSLstd450Step;
+    case ast::Intrinsic::kTan:
+      return GLSLstd450Tan;
+    case ast::Intrinsic::kTanh:
+      return GLSLstd450Tanh;
+    case ast::Intrinsic::kTrunc:
+      return GLSLstd450Trunc;
+    default:
+      break;
+  }
+  return 0;
+}
+
 }  // namespace
 
 Builder::AccessorInfo::AccessorInfo() : source_id(0), source_type(nullptr) {}
@@ -165,10 +283,6 @@
   push_preamble(spv::Op::OpExtension,
                 {Operand::String("SPV_KHR_vulkan_memory_model")});
 
-  for (const auto& imp : mod_->imports()) {
-    GenerateImport(imp.get());
-  }
-
   push_preamble(spv::Op::OpMemoryModel,
                 {Operand::Int(SpvAddressingModelLogical),
                  Operand::Int(SpvMemoryModelVulkanKHR)});
@@ -853,18 +967,6 @@
 uint32_t Builder::GenerateIdentifierExpression(
     ast::IdentifierExpression* expr) {
   uint32_t val = 0;
-  if (expr->has_path()) {
-    auto* imp = mod_->FindImportByName(expr->path());
-    if (imp == nullptr) {
-      error_ = "unable to find import for " + expr->path();
-      return 0;
-    }
-    val = imp->GetIdForMethod(expr->name());
-    if (val == 0) {
-      error_ = "unable to lookup: " + expr->name() + " in " + expr->path();
-    }
-    return val;
-  }
   if (scope_stack_.get(expr->name(), &val)) {
     return val;
   }
@@ -920,14 +1022,18 @@
   return result_id;
 }
 
-void Builder::GenerateImport(ast::Import* imp) {
+void Builder::GenerateGLSLstd450Import() {
+  if (import_name_to_id_.find(kGLSLstd450) != import_name_to_id_.end()) {
+    return;
+  }
+
   auto result = result_op();
   auto id = result.to_i();
 
   push_preamble(spv::Op::OpExtInstImport,
-                {result, Operand::String(imp->path())});
+                {result, Operand::String(kGLSLstd450)});
 
-  import_name_to_id_[imp->name()] = id;
+  import_name_to_id_[kGLSLstd450] = id;
 }
 
 uint32_t Builder::GenerateConstructorExpression(
@@ -1361,8 +1467,8 @@
 
   auto* ident = expr->func()->AsIdentifier();
 
-  if (!ident->has_path() && ast::intrinsic::IsIntrinsic(ident->name())) {
-    return GenerateIntrinsic(ident->name(), expr);
+  if (ident->IsIntrinsic()) {
+    return GenerateIntrinsic(ident, expr);
   }
 
   auto type_id = GenerateTypeIfNeeded(expr->func()->result_type());
@@ -1373,45 +1479,14 @@
   auto result = result_op();
   auto result_id = result.to_i();
 
-  spv::Op op = spv::Op::OpNop;
   OperandList ops = {Operand::Int(type_id), result};
 
-  // Handle regular function calls
-  if (!ident->has_path()) {
-    auto func_id = func_name_to_id_[ident->name()];
-    if (func_id == 0) {
-      error_ = "unable to find called function: " + ident->name();
-      return 0;
-    }
-    ops.push_back(Operand::Int(func_id));
-
-    op = spv::Op::OpFunctionCall;
-  } else {
-    // Imported function call
-    auto set_iter = import_name_to_id_.find(ident->path());
-    if (set_iter == import_name_to_id_.end()) {
-      error_ = "unknown import " + ident->path();
-      return 0;
-    }
-    auto set_id = set_iter->second;
-
-    auto* imp = mod_->FindImportByName(ident->path());
-    if (imp == nullptr) {
-      error_ = "unknown import " + ident->path();
-      return 0;
-    }
-
-    auto inst_id = imp->GetIdForMethod(ident->name());
-    if (inst_id == 0) {
-      error_ = "unknown method " + ident->name();
-      return 0;
-    }
-
-    ops.push_back(Operand::Int(set_id));
-    ops.push_back(Operand::Int(inst_id));
-
-    op = spv::Op::OpExtInst;
+  auto func_id = func_name_to_id_[ident->name()];
+  if (func_id == 0) {
+    error_ = "unable to find called function: " + ident->name();
+    return 0;
   }
+  ops.push_back(Operand::Int(func_id));
 
   for (const auto& param : expr->params()) {
     auto id = GenerateExpression(param.get());
@@ -1422,12 +1497,12 @@
     ops.push_back(Operand::Int(id));
   }
 
-  push_function_inst(op, std::move(ops));
+  push_function_inst(spv::Op::OpFunctionCall, std::move(ops));
 
   return result_id;
 }
 
-uint32_t Builder::GenerateIntrinsic(const std::string& name,
+uint32_t Builder::GenerateIntrinsic(ast::IdentifierExpression* ident,
                                     ast::CallExpression* call) {
   auto result = result_op();
   auto result_id = result.to_i();
@@ -1438,6 +1513,76 @@
   }
 
   OperandList params = {Operand::Int(result_type_id), result};
+
+  auto intrinsic = ident->intrinsic();
+  if (ast::intrinsic::IsFineDerivative(intrinsic) ||
+      ast::intrinsic::IsCoarseDerivative(intrinsic)) {
+    push_capability(SpvCapabilityDerivativeControl);
+  }
+
+  spv::Op op = spv::Op::OpNop;
+  if (intrinsic == ast::Intrinsic::kAny) {
+    op = spv::Op::OpAny;
+  } else if (intrinsic == ast::Intrinsic::kAll) {
+    op = spv::Op::OpAll;
+  } else if (intrinsic == ast::Intrinsic::kCountOneBits) {
+    op = spv::Op::OpBitCount;
+  } else if (intrinsic == ast::Intrinsic::kDot) {
+    op = spv::Op::OpDot;
+  } else if (intrinsic == ast::Intrinsic::kDpdx) {
+    op = spv::Op::OpDPdx;
+  } else if (intrinsic == ast::Intrinsic::kDpdxCoarse) {
+    op = spv::Op::OpDPdxCoarse;
+  } else if (intrinsic == ast::Intrinsic::kDpdxFine) {
+    op = spv::Op::OpDPdxFine;
+  } else if (intrinsic == ast::Intrinsic::kDpdy) {
+    op = spv::Op::OpDPdy;
+  } else if (intrinsic == ast::Intrinsic::kDpdyCoarse) {
+    op = spv::Op::OpDPdyCoarse;
+  } else if (intrinsic == ast::Intrinsic::kDpdyFine) {
+    op = spv::Op::OpDPdyFine;
+  } else if (intrinsic == ast::Intrinsic::kFwidth) {
+    op = spv::Op::OpFwidth;
+  } else if (intrinsic == ast::Intrinsic::kFwidthCoarse) {
+    op = spv::Op::OpFwidthCoarse;
+  } else if (intrinsic == ast::Intrinsic::kFwidthFine) {
+    op = spv::Op::OpFwidthFine;
+  } else if (intrinsic == ast::Intrinsic::kIsInf) {
+    op = spv::Op::OpIsInf;
+  } else if (intrinsic == ast::Intrinsic::kIsNan) {
+    op = spv::Op::OpIsNan;
+  } else if (intrinsic == ast::Intrinsic::kOuterProduct) {
+    op = spv::Op::OpOuterProduct;
+  } else if (intrinsic == ast::Intrinsic::kReverseBits) {
+    op = spv::Op::OpBitReverse;
+  } else if (intrinsic == ast::Intrinsic::kSelect) {
+    op = spv::Op::OpSelect;
+  } else if (!ast::intrinsic::IsTextureIntrinsic(intrinsic)) {
+    GenerateGLSLstd450Import();
+
+    auto set_iter = import_name_to_id_.find(kGLSLstd450);
+    if (set_iter == import_name_to_id_.end()) {
+      error_ = std::string("unknown import ") + kGLSLstd450;
+      return 0;
+    }
+    auto set_id = set_iter->second;
+    auto inst_id =
+        intrinsic_to_glsl_method(ident->result_type(), ident->intrinsic());
+    if (inst_id == 0) {
+      error_ = "unknown method " + ident->name();
+      return 0;
+    }
+
+    params.push_back(Operand::Int(set_id));
+    params.push_back(Operand::Int(inst_id));
+
+    op = spv::Op::OpExtInst;
+  }
+  if (!ast::intrinsic::IsTextureIntrinsic(intrinsic) && op == spv::Op::OpNop) {
+    error_ = "unable to determine operator for: " + ident->name();
+    return 0;
+  }
+
   for (const auto& p : call->params()) {
     auto val_id = GenerateExpression(p.get());
     if (val_id == 0) {
@@ -1448,59 +1593,16 @@
     params.push_back(Operand::Int(val_id));
   }
 
-  if (ast::intrinsic::IsTextureOperationIntrinsic(name)) {
-    return GenerateTextureIntrinsic(name, call, result_id, params);
+  if (ast::intrinsic::IsTextureIntrinsic(intrinsic)) {
+    return GenerateTextureIntrinsic(ident, call, result_id, params);
   }
 
-  if (ast::intrinsic::IsFineDerivative(name) ||
-      ast::intrinsic::IsCoarseDerivative(name)) {
-    push_capability(SpvCapabilityDerivativeControl);
-  }
-
-  spv::Op op = spv::Op::OpNop;
-  if (name == "any") {
-    op = spv::Op::OpAny;
-  } else if (name == "all") {
-    op = spv::Op::OpAll;
-  } else if (name == "dot") {
-    op = spv::Op::OpDot;
-  } else if (name == "dpdx") {
-    op = spv::Op::OpDPdx;
-  } else if (name == "dpdxCoarse") {
-    op = spv::Op::OpDPdxCoarse;
-  } else if (name == "dpdxFine") {
-    op = spv::Op::OpDPdxFine;
-  } else if (name == "dpdy") {
-    op = spv::Op::OpDPdy;
-  } else if (name == "dpdyCoarse") {
-    op = spv::Op::OpDPdyCoarse;
-  } else if (name == "dpdyFine") {
-    op = spv::Op::OpDPdyFine;
-  } else if (name == "fwidth") {
-    op = spv::Op::OpFwidth;
-  } else if (name == "fwidthCoarse") {
-    op = spv::Op::OpFwidthCoarse;
-  } else if (name == "fwidthFine") {
-    op = spv::Op::OpFwidthFine;
-  } else if (name == "isInf") {
-    op = spv::Op::OpIsInf;
-  } else if (name == "isNan") {
-    op = spv::Op::OpIsNan;
-  } else if (name == "outerProduct") {
-    op = spv::Op::OpOuterProduct;
-  } else if (name == "select") {
-    op = spv::Op::OpSelect;
-  }
-  if (op == spv::Op::OpNop) {
-    error_ = "unable to determine operator for: " + name;
-    return 0;
-  }
   push_function_inst(op, params);
 
   return result_id;
 }
 
-uint32_t Builder::GenerateTextureIntrinsic(const std::string& name,
+uint32_t Builder::GenerateTextureIntrinsic(ast::IdentifierExpression* ident,
                                            ast::CallExpression* call,
                                            uint32_t result_id,
                                            OperandList wgsl_params) {
@@ -1512,7 +1614,7 @@
 
   // TODO: Remove the LOD param from textureLoad on storage textures when
   // https://github.com/gpuweb/gpuweb/pull/1032 gets merged.
-  if (name == "textureLoad") {
+  if (ident->intrinsic() == ast::Intrinsic::kTextureLoad) {
     std::vector<Operand> spirv_params = {
         std::move(wgsl_params[0]), std::move(wgsl_params[1]),
         std::move(wgsl_params[2]), std::move(wgsl_params[3])};
@@ -1538,17 +1640,17 @@
                                         std::move(wgsl_params[3]))),
       std::move(wgsl_params[4])};
 
-  if (name == "textureSample") {
+  if (ident->intrinsic() == ast::Intrinsic::kTextureSample) {
     op = spv::Op::OpImageSampleImplicitLod;
-  } else if (name == "textureSampleLevel") {
+  } else if (ident->intrinsic() == ast::Intrinsic::kTextureSampleLevel) {
     op = spv::Op::OpImageSampleExplicitLod;
     spirv_params.push_back(Operand::Int(SpvImageOperandsLodMask));
     spirv_params.push_back(std::move(wgsl_params[5]));
-  } else if (name == "textureSampleBias") {
+  } else if (ident->intrinsic() == ast::Intrinsic::kTextureSampleBias) {
     op = spv::Op::OpImageSampleImplicitLod;
     spirv_params.push_back(Operand::Int(SpvImageOperandsBiasMask));
     spirv_params.push_back(std::move(wgsl_params[5]));
-  } else if (name == "textureSampleCompare") {
+  } else if (ident->intrinsic() == ast::Intrinsic::kTextureSampleCompare) {
     op = spv::Op::OpImageSampleDrefExplicitLod;
     spirv_params.push_back(std::move(wgsl_params[5]));
 
@@ -1558,7 +1660,7 @@
     spirv_params.push_back(Operand::Int(GenerateLiteralIfNeeded(&float_0)));
   }
   if (op == spv::Op::OpNop) {
-    error_ = "unable to determine operator for: " + name;
+    error_ = "unable to determine operator for: " + ident->name();
     return 0;
   }
   push_function_inst(op, spirv_params);
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index 2a467fc..7b809f3 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -247,8 +247,7 @@
   /// @returns true on success
   bool GenerateIfStatement(ast::IfStatement* stmt);
   /// Generates an import instruction
-  /// @param imp the import
-  void GenerateImport(ast::Import* imp);
+  void GenerateGLSLstd450Import();
   /// Generates a constructor expression
   /// @param expr the expression to generate
   /// @param is_global_init set true if this is a global variable constructor
@@ -283,19 +282,19 @@
   /// @returns the expression ID on success or 0 otherwise
   uint32_t GenerateCallExpression(ast::CallExpression* expr);
   /// Generates an intrinsic call
-  /// @param name the intrinsic name
+  /// @param ident the intrinsic expression
   /// @param call the call expression
   /// @returns the expression ID on success or 0 otherwise
-  uint32_t GenerateIntrinsic(const std::string& name,
+  uint32_t GenerateIntrinsic(ast::IdentifierExpression* ident,
                              ast::CallExpression* call);
   /// Generates a texture intrinsic call
-  /// @param name the texture intrinsic name
+  /// @param ident the texture intrinsic
   /// @param call the call expression
   /// @param result_id result ID of the texture instruction
   /// @param wgsl_params SPIR-V arguments for WGSL-specific intrinsic's call
   /// parameters
   /// @returns the expression ID on success or 0 otherwise
-  uint32_t GenerateTextureIntrinsic(const std::string& name,
+  uint32_t GenerateTextureIntrinsic(ast::IdentifierExpression* ident,
                                     ast::CallExpression* call,
                                     uint32_t result_id,
                                     OperandList wgsl_params);
diff --git a/src/writer/spirv/builder_call_test.cc b/src/writer/spirv/builder_call_test.cc
index 5b78cbf..2544a48 100644
--- a/src/writer/spirv/builder_call_test.cc
+++ b/src/writer/spirv/builder_call_test.cc
@@ -38,98 +38,6 @@
 
 using BuilderTest = testing::Test;
 
-TEST_F(BuilderTest, Call_GLSLMethod) {
-  ast::type::F32Type f32;
-  ast::type::VoidType void_type;
-
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
-
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", "round"}),
-                           std::move(params));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-
-  auto imp = std::make_unique<ast::Import>("GLSL.std.450", "std");
-  auto* glsl = imp.get();
-  mod.AddImport(std::move(imp));
-
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-
-  ast::Function func("a_func", {}, &void_type);
-
-  Builder b(&mod);
-  b.GenerateImport(glsl);
-  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
-
-  EXPECT_EQ(b.GenerateCallExpression(&expr), 7u) << b.error();
-  EXPECT_EQ(DumpBuilder(b), R"(%1 = OpExtInstImport "GLSL.std.450"
-OpName %4 "a_func"
-%3 = OpTypeVoid
-%2 = OpTypeFunction %3
-%6 = OpTypeFloat 32
-%8 = OpConstant %6 1
-%4 = OpFunction %3 None %2
-%5 = OpLabel
-%7 = OpExtInst %6 %1 Round %8
-OpFunctionEnd
-)");
-}
-
-TEST_F(BuilderTest, Call_GLSLMethod_WithLoad) {
-  ast::type::F32Type f32;
-  ast::type::VoidType void_type;
-
-  auto var = std::make_unique<ast::Variable>("ident",
-                                             ast::StorageClass::kPrivate, &f32);
-
-  ast::ExpressionList params;
-  params.push_back(std::make_unique<ast::IdentifierExpression>("ident"));
-
-  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>(
-                               std::vector<std::string>{"std", "round"}),
-                           std::move(params));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(var.get());
-
-  auto imp = std::make_unique<ast::Import>("GLSL.std.450", "std");
-  auto* glsl = imp.get();
-  mod.AddImport(std::move(imp));
-
-  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
-
-  ast::Function func("a_func", {}, &void_type);
-
-  Builder b(&mod);
-  b.GenerateImport(glsl);
-  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
-  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
-
-  EXPECT_EQ(b.GenerateCallExpression(&expr), 10u) << b.error();
-  EXPECT_EQ(DumpBuilder(b), R"(%1 = OpExtInstImport "GLSL.std.450"
-OpName %2 "ident"
-OpName %8 "a_func"
-%4 = OpTypeFloat 32
-%3 = OpTypePointer Private %4
-%5 = OpConstantNull %4
-%2 = OpVariable %3 Private %5
-%7 = OpTypeVoid
-%6 = OpTypeFunction %7
-%8 = OpFunction %7 None %6
-%9 = OpLabel
-%11 = OpLoad %4 %2
-%10 = OpExtInst %4 %1 Round %11
-OpFunctionEnd
-)");
-}
-
 TEST_F(BuilderTest, Expression_Call) {
   ast::type::F32Type f32;
   ast::type::VoidType void_type;
diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc
index 1fbc36b..f2d374b 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -30,7 +30,9 @@
 #include "src/ast/type/sampler_type.h"
 #include "src/ast/type/u32_type.h"
 #include "src/ast/type/vector_type.h"
+#include "src/ast/type/void_type.h"
 #include "src/ast/type_constructor_expression.h"
+#include "src/ast/uint_literal.h"
 #include "src/ast/variable.h"
 #include "src/context.h"
 #include "src/type_determiner.h"
@@ -181,6 +183,164 @@
                          testing::Values(IntrinsicData{"isNan", "OpIsNan"},
                                          IntrinsicData{"isInf", "OpIsInf"}));
 
+using IntrinsicIntTest = testing::TestWithParam<IntrinsicData>;
+TEST_P(IntrinsicIntTest, Call_SInt_Scalar) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+
+  auto var =
+      std::make_unique<ast::Variable>("v", ast::StorageClass::kPrivate, &i32);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("v"));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  td.RegisterVariableForTesting(var.get());
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
+%2 = OpTypePointer Private %3
+%4 = OpConstantNull %3
+%1 = OpVariable %2 Private %4
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%6 = OpLoad %3 %1
+%5 = )" + param.op +
+                " %3 %6\n");
+}
+
+TEST_P(IntrinsicIntTest, Call_SInt_Vector) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+  ast::type::VectorType vec3(&i32, 3);
+
+  auto var =
+      std::make_unique<ast::Variable>("v", ast::StorageClass::kPrivate, &vec3);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("v"));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  td.RegisterVariableForTesting(var.get());
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 6u) << b.error();
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%7 = OpLoad %3 %1
+%6 = )" + param.op +
+                " %3 %7\n");
+}
+
+TEST_P(IntrinsicIntTest, Call_UInt_Scalar) {
+  auto param = GetParam();
+
+  ast::type::U32Type u32;
+
+  auto var =
+      std::make_unique<ast::Variable>("v", ast::StorageClass::kPrivate, &u32);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("v"));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  td.RegisterVariableForTesting(var.get());
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 0
+%2 = OpTypePointer Private %3
+%4 = OpConstantNull %3
+%1 = OpVariable %2 Private %4
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%6 = OpLoad %3 %1
+%5 = )" + param.op +
+                " %3 %6\n");
+}
+
+TEST_P(IntrinsicIntTest, Call_UInt_Vector) {
+  auto param = GetParam();
+
+  ast::type::U32Type u32;
+  ast::type::VectorType vec3(&u32, 3);
+
+  auto var =
+      std::make_unique<ast::Variable>("v", ast::StorageClass::kPrivate, &vec3);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("v"));
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  td.RegisterVariableForTesting(var.get());
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  Builder b(&mod);
+  b.push_function(Function{});
+  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 6u) << b.error();
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%7 = OpLoad %3 %1
+%6 = )" + param.op +
+                " %3 %7\n");
+}
+INSTANTIATE_TEST_SUITE_P(
+    BuilderTest,
+    IntrinsicIntTest,
+    testing::Values(IntrinsicData{"countOneBits", "OpBitCount"},
+                    IntrinsicData{"reverseBits", "OpBitReverse"}));
+
 TEST_F(BuilderTest, Call_Dot) {
   ast::type::F32Type f32;
   ast::type::VectorType vec3(&f32, 3);
@@ -1215,6 +1375,1347 @@
 )");
 }
 
+TEST_F(BuilderTest, Call_GLSLMethod_WithLoad) {
+  ast::type::F32Type f32;
+  ast::type::VoidType void_type;
+
+  auto var = std::make_unique<ast::Variable>("ident",
+                                             ast::StorageClass::kPrivate, &f32);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("ident"));
+
+  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>("round"),
+                           std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  td.RegisterVariableForTesting(var.get());
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 9u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%10 = OpExtInstImport "GLSL.std.450"
+OpName %1 "ident"
+OpName %7 "a_func"
+%3 = OpTypeFloat 32
+%2 = OpTypePointer Private %3
+%4 = OpConstantNull %3
+%1 = OpVariable %2 Private %4
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
+%7 = OpFunction %6 None %5
+%8 = OpLabel
+%11 = OpLoad %3 %1
+%9 = OpExtInst %3 %10 Round %11
+OpFunctionEnd
+)");
+}
+
+using Intrinsic_Builtin_SingleParam_Float_Test =
+    testing::TestWithParam<IntrinsicData>;
+TEST_P(Intrinsic_Builtin_SingleParam_Float_Test, Call_Scalar) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%8 = OpConstant %6 1
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 )" + param.op +
+                                R"( %8
+OpFunctionEnd
+)");
+}
+
+TEST_P(Intrinsic_Builtin_SingleParam_Float_Test, Call_Vector) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 2);
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::ExpressionList params;
+  params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeFloat 32
+%6 = OpTypeVector %7 2
+%9 = OpConstant %7 1
+%10 = OpConstantComposite %6 %9 %9
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %8 )" + param.op +
+                                R"( %10
+OpFunctionEnd
+)");
+}
+INSTANTIATE_TEST_SUITE_P(BuilderTest,
+                         Intrinsic_Builtin_SingleParam_Float_Test,
+                         testing::Values(IntrinsicData{"abs", "FAbs"},
+                                         IntrinsicData{"acos", "Acos"},
+                                         IntrinsicData{"asin", "Asin"},
+                                         IntrinsicData{"atan", "Atan"},
+                                         IntrinsicData{"ceil", "Ceil"},
+                                         IntrinsicData{"cos", "Cos"},
+                                         IntrinsicData{"cosh", "Cosh"},
+                                         IntrinsicData{"exp", "Exp"},
+                                         IntrinsicData{"exp2", "Exp2"},
+                                         IntrinsicData{"floor", "Floor"},
+                                         IntrinsicData{"fract", "Fract"},
+                                         IntrinsicData{"inverseSqrt",
+                                                       "InverseSqrt"},
+                                         IntrinsicData{"log", "Log"},
+                                         IntrinsicData{"log2", "Log2"},
+                                         IntrinsicData{"round", "Round"},
+                                         IntrinsicData{"sign", "FSign"},
+                                         IntrinsicData{"sin", "Sin"},
+                                         IntrinsicData{"sinh", "Sinh"},
+                                         IntrinsicData{"sqrt", "Sqrt"},
+                                         IntrinsicData{"tan", "Tan"},
+                                         IntrinsicData{"tanh", "Tanh"},
+                                         IntrinsicData{"trunc", "Trunc"}));
+
+TEST_F(BuilderTest, Call_Length_Scalar) {
+  ast::type::F32Type f32;
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("length"), std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%8 = OpConstant %6 1
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 Length %8
+OpFunctionEnd
+)");
+}
+
+TEST_F(BuilderTest, Call_Length_Vector) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 2);
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::ExpressionList params;
+  params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("length"), std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%8 = OpTypeVector %6 2
+%9 = OpConstant %6 1
+%10 = OpConstantComposite %8 %9 %9
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 Length %10
+OpFunctionEnd
+)");
+}
+
+TEST_F(BuilderTest, Call_Normalize) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 2);
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::ExpressionList params;
+  params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("normalize"),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeFloat 32
+%6 = OpTypeVector %7 2
+%9 = OpConstant %7 1
+%10 = OpConstantComposite %6 %9 %9
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %8 Normalize %10
+OpFunctionEnd
+)");
+}
+
+using Intrinsic_Builtin_DualParam_Float_Test =
+    testing::TestWithParam<IntrinsicData>;
+TEST_P(Intrinsic_Builtin_DualParam_Float_Test, Call_Scalar) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%8 = OpConstant %6 1
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 )" + param.op +
+                                R"( %8 %8
+OpFunctionEnd
+)");
+}
+
+TEST_P(Intrinsic_Builtin_DualParam_Float_Test, Call_Vector) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 2);
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList vals_1;
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::ExpressionList vals_2;
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_1)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_2)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeFloat 32
+%6 = OpTypeVector %7 2
+%9 = OpConstant %7 1
+%10 = OpConstantComposite %6 %9 %9
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %8 )" + param.op +
+                                R"( %10 %10
+OpFunctionEnd
+)");
+}
+INSTANTIATE_TEST_SUITE_P(BuilderTest,
+                         Intrinsic_Builtin_DualParam_Float_Test,
+                         testing::Values(IntrinsicData{"atan2", "Atan2"},
+                                         IntrinsicData{"max", "NMax"},
+                                         IntrinsicData{"min", "NMin"},
+                                         IntrinsicData{"pow", "Pow"},
+                                         IntrinsicData{"reflect", "Reflect"},
+                                         IntrinsicData{"step", "Step"}));
+
+TEST_F(BuilderTest, Call_Distance_Scalar) {
+  ast::type::F32Type f32;
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("distance"),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%8 = OpConstant %6 1
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 Distance %8 %8
+OpFunctionEnd
+)");
+}
+
+TEST_F(BuilderTest, Call_Distance_Vector) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 2);
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList vals_1;
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::ExpressionList vals_2;
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_1)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_2)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("distance"),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%8 = OpTypeVector %6 2
+%9 = OpConstant %6 1
+%10 = OpConstantComposite %8 %9 %9
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 Distance %10 %10
+OpFunctionEnd
+)");
+}
+
+TEST_F(BuilderTest, Call_Cross) {
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 3);
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList vals_1;
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::ExpressionList vals_2;
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_1)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_2)));
+
+  ast::CallExpression expr(std::make_unique<ast::IdentifierExpression>("cross"),
+                           std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeFloat 32
+%6 = OpTypeVector %7 3
+%9 = OpConstant %7 1
+%10 = OpConstantComposite %6 %9 %9 %9
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %8 Cross %10 %10
+OpFunctionEnd
+)");
+}
+
+using Intrinsic_Builtin_ThreeParam_Float_Test =
+    testing::TestWithParam<IntrinsicData>;
+TEST_P(Intrinsic_Builtin_ThreeParam_Float_Test, Call_Scalar) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 32
+%8 = OpConstant %6 1
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 )" + param.op +
+                                R"( %8 %8 %8
+OpFunctionEnd
+)");
+}
+
+TEST_P(Intrinsic_Builtin_ThreeParam_Float_Test, Call_Vector) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::type::VectorType vec(&f32, 2);
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList vals_1;
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::ExpressionList vals_2;
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::ExpressionList vals_3;
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_1)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_2)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_3)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeFloat 32
+%6 = OpTypeVector %7 2
+%9 = OpConstant %7 1
+%10 = OpConstantComposite %6 %9 %9
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %8 )" + param.op +
+                                R"( %10 %10 %10
+OpFunctionEnd
+)");
+}
+INSTANTIATE_TEST_SUITE_P(
+    BuilderTest,
+    Intrinsic_Builtin_ThreeParam_Float_Test,
+    testing::Values(IntrinsicData{"clamp", "NClamp"},
+                    IntrinsicData{"faceForward", "FaceForward"},
+                    IntrinsicData{"fma", "Fma"},
+                    IntrinsicData{"mix", "FMix"},
+
+                    IntrinsicData{"smoothStep", "SmoothStep"}));
+
+using Intrinsic_Builtin_SingleParam_Sint_Test =
+    testing::TestWithParam<IntrinsicData>;
+TEST_P(Intrinsic_Builtin_SingleParam_Sint_Test, Call_Scalar) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeInt 32 1
+%8 = OpConstant %6 1
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 )" + param.op +
+                                R"( %8
+OpFunctionEnd
+)");
+}
+
+TEST_P(Intrinsic_Builtin_SingleParam_Sint_Test, Call_Vector) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+  ast::type::VectorType vec(&i32, 2);
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+
+  ast::ExpressionList params;
+  params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeInt 32 1
+%6 = OpTypeVector %7 2
+%9 = OpConstant %7 1
+%10 = OpConstantComposite %6 %9 %9
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %8 )" + param.op +
+                                R"( %10
+OpFunctionEnd
+)");
+}
+INSTANTIATE_TEST_SUITE_P(BuilderTest,
+                         Intrinsic_Builtin_SingleParam_Sint_Test,
+                         testing::Values(IntrinsicData{"abs", "SAbs"}));
+
+using Intrinsic_Builtin_SingleParam_Uint_Test =
+    testing::TestWithParam<IntrinsicData>;
+TEST_P(Intrinsic_Builtin_SingleParam_Uint_Test, Call_Scalar) {
+  auto param = GetParam();
+
+  ast::type::U32Type u32;
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeInt 32 0
+%8 = OpConstant %6 1
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 )" + param.op +
+                                R"( %8
+OpFunctionEnd
+)");
+}
+
+TEST_P(Intrinsic_Builtin_SingleParam_Uint_Test, Call_Vector) {
+  auto param = GetParam();
+
+  ast::type::U32Type u32;
+  ast::type::VectorType vec(&u32, 2);
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+
+  ast::ExpressionList params;
+  params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec, std::move(vals)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeInt 32 0
+%6 = OpTypeVector %7 2
+%9 = OpConstant %7 1
+%10 = OpConstantComposite %6 %9 %9
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %8 )" + param.op +
+                                R"( %10
+OpFunctionEnd
+)");
+}
+INSTANTIATE_TEST_SUITE_P(BuilderTest,
+                         Intrinsic_Builtin_SingleParam_Uint_Test,
+                         testing::Values(IntrinsicData{"abs", "SAbs"}));
+
+using Intrinsic_Builtin_DualParam_SInt_Test =
+    testing::TestWithParam<IntrinsicData>;
+TEST_P(Intrinsic_Builtin_DualParam_SInt_Test, Call_Scalar) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeInt 32 1
+%8 = OpConstant %6 1
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 )" + param.op +
+                                R"( %8 %8
+OpFunctionEnd
+)");
+}
+
+TEST_P(Intrinsic_Builtin_DualParam_SInt_Test, Call_Vector) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+  ast::type::VectorType vec(&i32, 2);
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList vals_1;
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+
+  ast::ExpressionList vals_2;
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_1)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_2)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeInt 32 1
+%6 = OpTypeVector %7 2
+%9 = OpConstant %7 1
+%10 = OpConstantComposite %6 %9 %9
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %8 )" + param.op +
+                                R"( %10 %10
+OpFunctionEnd
+)");
+}
+INSTANTIATE_TEST_SUITE_P(BuilderTest,
+                         Intrinsic_Builtin_DualParam_SInt_Test,
+                         testing::Values(IntrinsicData{"max", "SMax"},
+                                         IntrinsicData{"min", "SMin"}));
+
+using Intrinsic_Builtin_DualParam_UInt_Test =
+    testing::TestWithParam<IntrinsicData>;
+TEST_P(Intrinsic_Builtin_DualParam_UInt_Test, Call_Scalar) {
+  auto param = GetParam();
+
+  ast::type::U32Type u32;
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeInt 32 0
+%8 = OpConstant %6 1
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 )" + param.op +
+                                R"( %8 %8
+OpFunctionEnd
+)");
+}
+
+TEST_P(Intrinsic_Builtin_DualParam_UInt_Test, Call_Vector) {
+  auto param = GetParam();
+
+  ast::type::U32Type u32;
+  ast::type::VectorType vec(&u32, 2);
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList vals_1;
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+
+  ast::ExpressionList vals_2;
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_1)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_2)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeInt 32 0
+%6 = OpTypeVector %7 2
+%9 = OpConstant %7 1
+%10 = OpConstantComposite %6 %9 %9
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %8 )" + param.op +
+                                R"( %10 %10
+OpFunctionEnd
+)");
+}
+INSTANTIATE_TEST_SUITE_P(BuilderTest,
+                         Intrinsic_Builtin_DualParam_UInt_Test,
+                         testing::Values(IntrinsicData{"max", "UMax"},
+                                         IntrinsicData{"min", "UMin"}));
+
+using Intrinsic_Builtin_ThreeParam_Sint_Test =
+    testing::TestWithParam<IntrinsicData>;
+TEST_P(Intrinsic_Builtin_ThreeParam_Sint_Test, Call_Scalar) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeInt 32 1
+%8 = OpConstant %6 1
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 )" + param.op +
+                                R"( %8 %8 %8
+OpFunctionEnd
+)");
+}
+
+TEST_P(Intrinsic_Builtin_ThreeParam_Sint_Test, Call_Vector) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+  ast::type::VectorType vec(&i32, 2);
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList vals_1;
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+
+  ast::ExpressionList vals_2;
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+
+  ast::ExpressionList vals_3;
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 1)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_1)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_2)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_3)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeInt 32 1
+%6 = OpTypeVector %7 2
+%9 = OpConstant %7 1
+%10 = OpConstantComposite %6 %9 %9
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %8 )" + param.op +
+                                R"( %10 %10 %10
+OpFunctionEnd
+)");
+}
+INSTANTIATE_TEST_SUITE_P(BuilderTest,
+                         Intrinsic_Builtin_ThreeParam_Sint_Test,
+                         testing::Values(IntrinsicData{"clamp", "SClamp"}));
+
+using Intrinsic_Builtin_ThreeParam_Uint_Test =
+    testing::TestWithParam<IntrinsicData>;
+TEST_P(Intrinsic_Builtin_ThreeParam_Uint_Test, Call_Scalar) {
+  auto param = GetParam();
+
+  ast::type::U32Type u32;
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeInt 32 0
+%8 = OpConstant %6 1
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %7 )" + param.op +
+                                R"( %8 %8 %8
+OpFunctionEnd
+)");
+}
+
+TEST_P(Intrinsic_Builtin_ThreeParam_Uint_Test, Call_Vector) {
+  auto param = GetParam();
+
+  ast::type::U32Type u32;
+  ast::type::VectorType vec(&u32, 2);
+  ast::type::VoidType void_type;
+
+  ast::ExpressionList vals_1;
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  vals_1.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+
+  ast::ExpressionList vals_2;
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  vals_2.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+
+  ast::ExpressionList vals_3;
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+  vals_3.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::UintLiteral>(&u32, 1)));
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_1)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_2)));
+  params.push_back(std::make_unique<ast::TypeConstructorExpression>(
+      &vec, std::move(vals_3)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>(param.name),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 5u) << b.error();
+  EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%7 = OpTypeInt 32 0
+%6 = OpTypeVector %7 2
+%9 = OpConstant %7 1
+%10 = OpConstantComposite %6 %9 %9
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%5 = OpExtInst %6 %8 )" + param.op +
+                                R"( %10 %10 %10
+OpFunctionEnd
+)");
+}
+INSTANTIATE_TEST_SUITE_P(BuilderTest,
+                         Intrinsic_Builtin_ThreeParam_Uint_Test,
+                         testing::Values(IntrinsicData{"clamp", "UClamp"}));
+
+TEST_F(BuilderTest, Call_Determinant) {
+  ast::type::F32Type f32;
+  ast::type::VoidType void_type;
+  ast::type::MatrixType mat(&f32, 3, 3);
+
+  auto var =
+      std::make_unique<ast::Variable>("var", ast::StorageClass::kPrivate, &mat);
+
+  ast::ExpressionList params;
+  params.push_back(std::make_unique<ast::IdentifierExpression>("var"));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("determinant"),
+      std::move(params));
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  td.RegisterVariableForTesting(var.get());
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  ast::Function func("a_func", {}, &void_type);
+
+  Builder b(&mod);
+  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
+
+  ASSERT_TRUE(b.GenerateGlobalVariable(var.get())) << b.error();
+  EXPECT_EQ(b.GenerateCallExpression(&expr), 11u) << b.error();
+
+  EXPECT_EQ(DumpBuilder(b), R"(%12 = OpExtInstImport "GLSL.std.450"
+OpName %3 "a_func"
+OpName %5 "var"
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%9 = OpTypeFloat 32
+%8 = OpTypeVector %9 3
+%7 = OpTypeMatrix %8 3
+%6 = OpTypePointer Private %7
+%10 = OpConstantNull %7
+%5 = OpVariable %6 Private %10
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%13 = OpLoad %7 %5
+%11 = OpExtInst %9 %12 Determinant %13
+OpFunctionEnd
+)");
+}
+
 }  // namespace
 }  // namespace spirv
 }  // namespace writer
diff --git a/src/writer/spirv/builder_test.cc b/src/writer/spirv/builder_test.cc
index 6c80d8f..1e22d5f 100644
--- a/src/writer/spirv/builder_test.cc
+++ b/src/writer/spirv/builder_test.cc
@@ -19,7 +19,6 @@
 #include "gtest/gtest.h"
 #include "spirv/unified1/spirv.h"
 #include "spirv/unified1/spirv.hpp11"
-#include "src/ast/import.h"
 #include "src/ast/module.h"
 #include "src/writer/spirv/spv_dump.h"
 
@@ -30,24 +29,7 @@
 
 using BuilderTest = testing::Test;
 
-TEST_F(BuilderTest, InsertsPreambleWithImport) {
-  ast::Module m;
-  m.AddImport(std::make_unique<ast::Import>("GLSL.std.450", "glsl"));
-
-  Builder b(&m);
-  ASSERT_TRUE(b.Build());
-  ASSERT_EQ(b.capabilities().size(), 2u);
-  ASSERT_EQ(b.preamble().size(), 3u);
-
-  EXPECT_EQ(DumpBuilder(b), R"(OpCapability Shader
-OpCapability VulkanMemoryModel
-OpExtension "SPV_KHR_vulkan_memory_model"
-%1 = OpExtInstImport "GLSL.std.450"
-OpMemoryModel Logical Vulkan
-)");
-}
-
-TEST_F(BuilderTest, InsertsPreambleWithoutImport) {
+TEST_F(BuilderTest, InsertsPreamble) {
   ast::Module m;
   Builder b(&m);
   ASSERT_TRUE(b.Build());
diff --git a/src/writer/wgsl/generator_impl.cc b/src/writer/wgsl/generator_impl.cc
index da4aed2..6f0073c 100644
--- a/src/writer/wgsl/generator_impl.cc
+++ b/src/writer/wgsl/generator_impl.cc
@@ -75,15 +75,6 @@
 GeneratorImpl::~GeneratorImpl() = default;
 
 bool GeneratorImpl::Generate(const ast::Module& module) {
-  for (const auto& import : module.imports()) {
-    if (!EmitImport(import.get())) {
-      return false;
-    }
-  }
-  if (!module.imports().empty()) {
-    out_ << std::endl;
-  }
-
   for (auto* const alias : module.alias_types()) {
     if (!EmitAliasType(alias)) {
       return false;
@@ -114,16 +105,6 @@
 bool GeneratorImpl::GenerateEntryPoint(const ast::Module& module,
                                        ast::PipelineStage stage,
                                        const std::string& name) {
-  // TODO(dsinclair): We're always emitting imports even if they aren't needed.
-  for (const auto& import : module.imports()) {
-    if (!EmitImport(import.get())) {
-      return false;
-    }
-  }
-  if (!module.imports().empty()) {
-    out_ << std::endl;
-  }
-
   auto* func = module.FindFunctionByNameAndStage(name, stage);
   if (func == nullptr) {
     error_ = "Unable to find requested entry point: " + name;
@@ -365,20 +346,10 @@
 
 bool GeneratorImpl::EmitIdentifier(ast::IdentifierExpression* expr) {
   auto* ident = expr->AsIdentifier();
-  if (ident->has_path()) {
-    out_ << ident->path() << "::";
-  }
   out_ << ident->name();
   return true;
 }
 
-bool GeneratorImpl::EmitImport(const ast::Import* import) {
-  make_indent();
-  out_ << R"(import ")" << import->path() << R"(" as )" << import->name() << ";"
-       << std::endl;
-  return true;
-}
-
 bool GeneratorImpl::EmitFunction(ast::Function* func) {
   for (auto& deco : func->decorations()) {
     make_indent();
diff --git a/src/writer/wgsl/generator_impl.h b/src/writer/wgsl/generator_impl.h
index 1ae3853..43947a0 100644
--- a/src/writer/wgsl/generator_impl.h
+++ b/src/writer/wgsl/generator_impl.h
@@ -21,7 +21,6 @@
 #include "src/ast/array_accessor_expression.h"
 #include "src/ast/constructor_expression.h"
 #include "src/ast/identifier_expression.h"
-#include "src/ast/import.h"
 #include "src/ast/module.h"
 #include "src/ast/scalar_constructor_expression.h"
 #include "src/ast/type/alias_type.h"
@@ -135,10 +134,6 @@
   /// @param stmt the statement to emit
   /// @returns true if the statement was successfully emitted
   bool EmitIf(ast::IfStatement* stmt);
-  /// Handles generating an import command
-  /// @param import the import to generate
-  /// @returns true if the import was emitted
-  bool EmitImport(const ast::Import* import);
   /// Handles generating constructor expressions
   /// @param expr the constructor expression
   /// @returns true if the expression was emitted
diff --git a/src/writer/wgsl/generator_impl_identifier_test.cc b/src/writer/wgsl/generator_impl_identifier_test.cc
index eb5e9c0..503579a 100644
--- a/src/writer/wgsl/generator_impl_identifier_test.cc
+++ b/src/writer/wgsl/generator_impl_identifier_test.cc
@@ -22,14 +22,6 @@
 
 using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(WgslGeneratorImplTest, EmitExpression_Identifier) {
-  ast::IdentifierExpression i(std::vector<std::string>{"std", "glsl"});
-
-  GeneratorImpl g;
-  ASSERT_TRUE(g.EmitExpression(&i)) << g.error();
-  EXPECT_EQ(g.result(), "std::glsl");
-}
-
 TEST_F(WgslGeneratorImplTest, EmitIdentifierExpression_Single) {
   ast::IdentifierExpression i("glsl");
 
@@ -38,14 +30,6 @@
   EXPECT_EQ(g.result(), "glsl");
 }
 
-TEST_F(WgslGeneratorImplTest, EmitIdentifierExpression_MultipleNames) {
-  ast::IdentifierExpression i({"std", "glsl", "init"});
-
-  GeneratorImpl g;
-  ASSERT_TRUE(g.EmitExpression(&i)) << g.error();
-  EXPECT_EQ(g.result(), "std::glsl::init");
-}
-
 }  // namespace
 }  // namespace wgsl
 }  // namespace writer
diff --git a/src/writer/wgsl/generator_impl_import_test.cc b/src/writer/wgsl/generator_impl_import_test.cc
deleted file mode 100644
index 24d1980..0000000
--- a/src/writer/wgsl/generator_impl_import_test.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.h"
-#include "src/writer/wgsl/generator_impl.h"
-
-namespace tint {
-namespace writer {
-namespace wgsl {
-namespace {
-
-using WgslGeneratorImplTest = testing::Test;
-
-TEST_F(WgslGeneratorImplTest, EmitImport) {
-  ast::Import import("GLSL.std.450", "std::glsl");
-
-  GeneratorImpl g;
-  ASSERT_TRUE(g.EmitImport(&import)) << g.error();
-  EXPECT_EQ(g.result(), R"(import "GLSL.std.450" as std::glsl;
-)");
-}
-
-}  // namespace
-}  // namespace wgsl
-}  // namespace writer
-}  // namespace tint
diff --git a/src/writer/wgsl/generator_impl_test.cc b/src/writer/wgsl/generator_impl_test.cc
index b4420d6..675111d 100644
--- a/src/writer/wgsl/generator_impl_test.cc
+++ b/src/writer/wgsl/generator_impl_test.cc
@@ -17,6 +17,9 @@
 #include <memory>
 
 #include "gtest/gtest.h"
+#include "src/ast/function.h"
+#include "src/ast/module.h"
+#include "src/ast/type/void_type.h"
 
 namespace tint {
 namespace writer {
@@ -26,13 +29,17 @@
 using WgslGeneratorImplTest = testing::Test;
 
 TEST_F(WgslGeneratorImplTest, Generate) {
+  ast::type::VoidType void_type;
+
   ast::Module m;
-  m.AddImport(std::make_unique<ast::Import>("GLSL.std.430", "a"));
+  m.AddFunction(std::make_unique<ast::Function>("my_func", ast::VariableList{},
+                                                &void_type));
 
   GeneratorImpl g;
 
   ASSERT_TRUE(g.Generate(m)) << g.error();
-  EXPECT_EQ(g.result(), R"(import "GLSL.std.430" as a;
+  EXPECT_EQ(g.result(), R"(fn my_func() -> void {
+}
 
 )");
 }
diff --git a/test/compute_boids.wgsl b/test/compute_boids.wgsl
index 16642d2..e3f015e 100644
--- a/test/compute_boids.wgsl
+++ b/test/compute_boids.wgsl
@@ -12,8 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import "GLSL.std.450" as std;
-
 # vertex shader
 
 [[location(0)]] var<in> a_particlePos : vec2<f32>;
@@ -23,10 +21,10 @@
 
 [[stage(vertex)]]
 fn vert_main() -> void {
-  var angle : f32 = -std::atan2(a_particleVel.x, a_particleVel.y);
+  var angle : f32 = -atan2(a_particleVel.x, a_particleVel.y);
   var pos : vec2<f32> = vec2<f32>(
-      (a_pos.x * std::cos(angle)) - (a_pos.y * std::sin(angle)),
-      (a_pos.x * std::sin(angle)) + (a_pos.y * std::cos(angle)));
+      (a_pos.x * cos(angle)) - (a_pos.y * sin(angle)),
+      (a_pos.x * sin(angle)) + (a_pos.y * cos(angle)));
   gl_Position = vec4<f32>(pos + a_particlePos, 0.0, 1.0);
   return;
 }
@@ -97,14 +95,14 @@
     pos = particlesA.particles[i].pos.xy;
     vel = particlesA.particles[i].vel.xy;
 
-    if (std::distance(pos, vPos) < params.rule1Distance) {
+    if (distance(pos, vPos) < params.rule1Distance) {
       cMass = cMass + pos;
       cMassCount = cMassCount + 1;
     }
-    if (std::distance(pos, vPos) < params.rule2Distance) {
+    if (distance(pos, vPos) < params.rule2Distance) {
       colVel = colVel - (pos - vPos);
     }
-    if (std::distance(pos, vPos) < params.rule3Distance) {
+    if (distance(pos, vPos) < params.rule3Distance) {
       cVel = cVel + vel;
       cVelCount = cVelCount + 1;
     }
@@ -125,7 +123,7 @@
       (cVel * params.rule3Scale);
 
   # clamp velocity for a more pleasing simulation
-  vVel = std::normalize(vVel) * std::fclamp(std::length(vVel), 0.0, 0.1);
+  vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1);
 
   # kinematic update
   vPos = vPos + (vVel * params.deltaT);