[ir] Add a SPIRV BuiltinCall instruction
This CL adds a `BuiltinCall` instruction to SPIR-V IR and moves
`VectorTimesScalar` over to the builtin.
Bug: tint:1718
Change-Id: I4fd92aab01d32897c5bdb4add97e22b698ded377
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/150100
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/core/ir/builder.h b/src/tint/lang/core/ir/builder.h
index ae5ae69..ffa0347 100644
--- a/src/tint/lang/core/ir/builder.h
+++ b/src/tint/lang/core/ir/builder.h
@@ -620,13 +620,26 @@
InstructionResult(type), func, Values(std::forward<ARGS>(args)...)));
}
+ /// Creates a core builtin call instruction
+ /// @param type the return type of the call
+ /// @param func the builtin function to call
+ /// @param args the call arguments
+ /// @returns the instruction
+ template <typename KLASS, typename FUNC, typename... ARGS>
+ tint::traits::EnableIf<tint::traits::IsTypeOrDerived<KLASS, ir::BuiltinCall>, KLASS*>
+ Call(const core::type::Type* type, FUNC func, ARGS&&... args) {
+ return Append(ir.instructions.Create<KLASS>(InstructionResult(type), func,
+ Values(std::forward<ARGS>(args)...)));
+ }
+
/// Creates an intrinsic call instruction
/// @param type the return type of the call
/// @param kind the intrinsic function to call
/// @param args the call arguments
/// @returns the intrinsic call instruction
template <typename KLASS, typename KIND, typename... ARGS>
- ir::IntrinsicCall* Call(const core::type::Type* type, KIND kind, ARGS&&... args) {
+ tint::traits::EnableIf<tint::traits::IsTypeOrDerived<KLASS, ir::IntrinsicCall>, KLASS*>
+ Call(const core::type::Type* type, KIND kind, ARGS&&... args) {
return Append(ir.instructions.Create<KLASS>(InstructionResult(type), kind,
Values(std::forward<ARGS>(args)...)));
}
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index d712e65..182c035 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -527,7 +527,9 @@
[&](Convert*) {}, //
[&](Discard*) {}, //
[&](UserCall*) {}, //
- [&](Default) { AddError(call, InstError(call, "missing validation")); });
+ [&](Default) {
+ // Validation of custom IR instructions
+ });
}
void Validator::CheckCoreBuiltinCall(CoreBuiltinCall* call) {
diff --git a/src/tint/lang/spirv/ir/BUILD.cmake b/src/tint/lang/spirv/ir/BUILD.cmake
index 7f0e19a..ad4b8a2 100644
--- a/src/tint/lang/spirv/ir/BUILD.cmake
+++ b/src/tint/lang/spirv/ir/BUILD.cmake
@@ -26,6 +26,10 @@
# Kind: lib
################################################################################
tint_add_target(tint_lang_spirv_ir lib
+ lang/spirv/ir/builtin_call.cc
+ lang/spirv/ir/builtin_call.h
+ lang/spirv/ir/function.cc
+ lang/spirv/ir/function.h
lang/spirv/ir/intrinsic.cc
lang/spirv/ir/intrinsic.h
lang/spirv/ir/intrinsic_call.cc
diff --git a/src/tint/lang/spirv/ir/BUILD.gn b/src/tint/lang/spirv/ir/BUILD.gn
index 452982f..9938f26 100644
--- a/src/tint/lang/spirv/ir/BUILD.gn
+++ b/src/tint/lang/spirv/ir/BUILD.gn
@@ -31,6 +31,10 @@
libtint_source_set("ir") {
sources = [
+ "builtin_call.cc",
+ "builtin_call.h",
+ "function.cc",
+ "function.h",
"intrinsic.cc",
"intrinsic.h",
"intrinsic_call.cc",
diff --git a/src/tint/lang/spirv/ir/builtin_call.cc b/src/tint/lang/spirv/ir/builtin_call.cc
new file mode 100644
index 0000000..9cf3f08
--- /dev/null
+++ b/src/tint/lang/spirv/ir/builtin_call.cc
@@ -0,0 +1,34 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/lang/spirv/ir/builtin_call.h"
+
+#include <utility>
+
+#include "src/tint/utils/ice/ice.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::spirv::ir::BuiltinCall);
+
+namespace tint::spirv::ir {
+
+BuiltinCall::BuiltinCall(core::ir::InstructionResult* result,
+ Function func,
+ VectorRef<core::ir::Value*> arguments)
+ : Base(result, arguments), func_(func) {
+ TINT_ASSERT(func != Function::kNone);
+}
+
+BuiltinCall::~BuiltinCall() = default;
+
+} // namespace tint::spirv::ir
diff --git a/src/tint/lang/spirv/ir/builtin_call.h b/src/tint/lang/spirv/ir/builtin_call.h
new file mode 100644
index 0000000..9f8b888
--- /dev/null
+++ b/src/tint/lang/spirv/ir/builtin_call.h
@@ -0,0 +1,50 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_LANG_SPIRV_IR_BUILTIN_CALL_H_
+#define SRC_TINT_LANG_SPIRV_IR_BUILTIN_CALL_H_
+
+#include <string>
+
+#include "src/tint/lang/core/ir/builtin_call.h"
+#include "src/tint/lang/spirv/ir/function.h"
+#include "src/tint/utils/rtti/castable.h"
+
+namespace tint::spirv::ir {
+
+/// A spirv builtin call instruction in the IR.
+class BuiltinCall : public Castable<BuiltinCall, core::ir::BuiltinCall> {
+ public:
+ /// Constructor
+ /// @param result the result value
+ /// @param func the builtin function
+ /// @param args the conversion arguments
+ BuiltinCall(core::ir::InstructionResult* result,
+ Function func,
+ VectorRef<core::ir::Value*> args = tint::Empty);
+ ~BuiltinCall() override;
+
+ /// @returns the builtin function
+ Function Func() { return func_; }
+
+ /// @returns the friendly name for the instruction
+ std::string FriendlyName() override { return str(func_); }
+
+ private:
+ Function func_;
+};
+
+} // namespace tint::spirv::ir
+
+#endif // SRC_TINT_LANG_SPIRV_IR_BUILTIN_CALL_H_
diff --git a/src/tint/lang/spirv/ir/function.cc b/src/tint/lang/spirv/ir/function.cc
new file mode 100644
index 0000000..f7612f3
--- /dev/null
+++ b/src/tint/lang/spirv/ir/function.cc
@@ -0,0 +1,38 @@
+// Copyright 2023 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.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by 'tools/src/cmd/gen' using the template:
+// src/tint/lang/spirv/ir/function.cc.tmpl
+//
+// To regenerate run: './tools/run gen'
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#include "src/tint/lang/spirv/ir/function.h"
+
+namespace tint::spirv::ir {
+
+const char* str(Function i) {
+ switch (i) {
+ case Function::kNone:
+ return "<none>";
+ case Function::kVectorTimesScalar:
+ return "spirv.vector_times_scalar";
+ }
+ return "<unknown>";
+}
+
+} // namespace tint::spirv::ir
diff --git a/src/tint/lang/spirv/ir/function.cc.tmpl b/src/tint/lang/spirv/ir/function.cc.tmpl
new file mode 100644
index 0000000..2a301e3
--- /dev/null
+++ b/src/tint/lang/spirv/ir/function.cc.tmpl
@@ -0,0 +1,31 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate function.cc
+
+To update the generated file, run:
+ ./tools/run gen
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- $I := LoadIntrinsics "src/tint/lang/spirv/ir/spirv.def" -}}
+#include "src/tint/lang/spirv/ir/function.h"
+
+namespace tint::spirv::ir {
+
+const char* str(Function i) {
+ switch (i) {
+ case Function::kNone:
+ return "<none>";
+{{- range $I.Sem.Builtins }}
+ case Function::k{{PascalCase .Name}}:
+ return "spirv.{{.Name}}";
+{{- end }}
+ }
+ return "<unknown>";
+}
+
+} // namespace tint::spirv::ir
diff --git a/src/tint/lang/spirv/ir/function.h b/src/tint/lang/spirv/ir/function.h
new file mode 100644
index 0000000..99bf16d
--- /dev/null
+++ b/src/tint/lang/spirv/ir/function.h
@@ -0,0 +1,55 @@
+// Copyright 2023 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.
+
+////////////////////////////////////////////////////////////////////////////////
+// File generated by 'tools/src/cmd/gen' using the template:
+// src/tint/lang/spirv/ir/function.h.tmpl
+//
+// To regenerate run: './tools/run gen'
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef SRC_TINT_LANG_SPIRV_IR_FUNCTION_H_
+#define SRC_TINT_LANG_SPIRV_IR_FUNCTION_H_
+
+#include <cstdint>
+#include <string>
+
+#include "src/tint/utils/traits/traits.h"
+
+// \cond DO_NOT_DOCUMENT
+namespace tint::spirv::ir {
+
+/// Enumerator of all builtin functions
+enum class Function : uint8_t {
+ kVectorTimesScalar,
+ kNone,
+};
+
+/// @returns the name of the builtin function type. The spelling, including
+/// case, matches the name in the WGSL spec.
+const char* str(Function i);
+
+/// Emits the name of the builtin function type. The spelling, including case,
+/// matches the name in the WGSL spec.
+template <typename STREAM, typename = traits::EnableIfIsOStream<STREAM>>
+auto& operator<<(STREAM& o, Function i) {
+ return o << str(i);
+}
+
+} // namespace tint::spirv::ir
+// \endcond
+
+#endif // SRC_TINT_LANG_SPIRV_IR_FUNCTION_H_
diff --git a/src/tint/lang/spirv/ir/function.h.tmpl b/src/tint/lang/spirv/ir/function.h.tmpl
new file mode 100644
index 0000000..e983e8a
--- /dev/null
+++ b/src/tint/lang/spirv/ir/function.h.tmpl
@@ -0,0 +1,49 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate function.h
+
+To update the generated file, run:
+ ./tools/run gen
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- $I := LoadIntrinsics "src/tint/lang/spirv/ir/spirv.def" -}}
+
+#ifndef SRC_TINT_LANG_SPIRV_IR_FUNCTION_H_
+#define SRC_TINT_LANG_SPIRV_IR_FUNCTION_H_
+
+#include <cstdint>
+#include <string>
+
+#include "src/tint/utils/traits/traits.h"
+
+// \cond DO_NOT_DOCUMENT
+namespace tint::spirv::ir {
+
+/// Enumerator of all builtin functions
+enum class Function : uint8_t {
+{{- range $I.Sem.Builtins }}
+ k{{PascalCase .Name}},
+{{- end }}
+ kNone,
+};
+
+/// @returns the name of the builtin function type. The spelling, including
+/// case, matches the name in the WGSL spec.
+const char* str(Function i);
+
+/// Emits the name of the builtin function type. The spelling, including case,
+/// matches the name in the WGSL spec.
+template <typename STREAM, typename = traits::EnableIfIsOStream<STREAM>>
+auto& operator<<(STREAM& o, Function i) {
+ return o << str(i);
+}
+
+} // namespace tint::spirv::ir
+// \endcond
+
+#endif // SRC_TINT_LANG_SPIRV_IR_FUNCTION_H_
diff --git a/src/tint/lang/spirv/ir/intrinsic.cc b/src/tint/lang/spirv/ir/intrinsic.cc
index d2141ef..febe468 100644
--- a/src/tint/lang/spirv/ir/intrinsic.cc
+++ b/src/tint/lang/spirv/ir/intrinsic.cc
@@ -122,9 +122,6 @@
if (str == "vector_times_matrix") {
return Intrinsic::kVectorTimesMatrix;
}
- if (str == "vector_times_scalar") {
- return Intrinsic::kVectorTimesScalar;
- }
return Intrinsic::kUndefined;
}
@@ -194,8 +191,6 @@
return "select";
case Intrinsic::kVectorTimesMatrix:
return "vector_times_matrix";
- case Intrinsic::kVectorTimesScalar:
- return "vector_times_scalar";
}
return "<unknown>";
}
diff --git a/src/tint/lang/spirv/ir/intrinsic.h b/src/tint/lang/spirv/ir/intrinsic.h
index d94ed51..c99eb53 100644
--- a/src/tint/lang/spirv/ir/intrinsic.h
+++ b/src/tint/lang/spirv/ir/intrinsic.h
@@ -65,7 +65,6 @@
kSampledImage,
kSelect,
kVectorTimesMatrix,
- kVectorTimesScalar,
};
/// @param value the enum value
@@ -117,7 +116,6 @@
"sampled_image",
"select",
"vector_times_matrix",
- "vector_times_scalar",
};
} // namespace tint::spirv::ir
diff --git a/src/tint/lang/spirv/ir/spirv.def b/src/tint/lang/spirv/ir/spirv.def
index c395c30..754c6a5 100644
--- a/src/tint/lang/spirv/ir/spirv.def
+++ b/src/tint/lang/spirv/ir/spirv.def
@@ -16,6 +16,17 @@
// Spirv builtin definition file //
////////////////////////////////////////////////////////////////////////////////
+// TODO(crbug.com/2036): add an include facility and move these duplicate match and type lines
+// into a common file.
+match f32_f16: f32 | f16
+
+type f32
+type f16
+type vec2<T>
+type vec3<T>
+type vec4<T>
+@display("vec{N}<{T}>") type vec<N: num, T>
+
////////////////////////////////////////////////////////////////////////////////
// Enumerators //
////////////////////////////////////////////////////////////////////////////////
@@ -52,5 +63,10 @@
sampled_image
select
vector_times_matrix
- vector_times_scalar
}
+
+////////////////////////////////////////////////////////////////////////////////
+// Builtin Functions //
+////////////////////////////////////////////////////////////////////////////////
+fn vector_times_scalar<T: f32_f16, N: num>(vec<N, T>, T) -> vec<N, T>
+
diff --git a/src/tint/lang/spirv/writer/printer/printer.cc b/src/tint/lang/spirv/writer/printer/printer.cc
index 331986f..1e7dbc7 100644
--- a/src/tint/lang/spirv/writer/printer/printer.cc
+++ b/src/tint/lang/spirv/writer/printer/printer.cc
@@ -763,6 +763,7 @@
[&](core::ir::Binary* b) { EmitBinary(b); }, //
[&](core::ir::Bitcast* b) { EmitBitcast(b); }, //
[&](core::ir::CoreBuiltinCall* b) { EmitCoreBuiltinCall(b); }, //
+ [&](spirv::ir::BuiltinCall* b) { EmitSpirvBuiltinCall(b); }, //
[&](core::ir::Construct* c) { EmitConstruct(c); }, //
[&](core::ir::Convert* c) { EmitConvert(c); }, //
[&](spirv::ir::IntrinsicCall* i) { EmitIntrinsicCall(i); }, //
@@ -1086,6 +1087,29 @@
{Type(ty), Value(bitcast), Value(bitcast->Val())});
}
+void Printer::EmitSpirvBuiltinCall(spirv::ir::BuiltinCall* builtin) {
+ auto id = Value(builtin);
+
+ spv::Op op = spv::Op::Max;
+ switch (builtin->Func()) {
+ case spirv::ir::Function::kVectorTimesScalar:
+ op = spv::Op::OpVectorTimesScalar;
+ break;
+ case spirv::ir::Function::kNone:
+ TINT_ICE() << "undefined spirv ir function";
+ return;
+ }
+
+ OperandList operands;
+ if (!builtin->Result()->Type()->Is<core::type::Void>()) {
+ operands = {Type(builtin->Result()->Type()), id};
+ }
+ for (auto* arg : builtin->Args()) {
+ operands.push_back(Value(arg));
+ }
+ current_function_.push_inst(op, operands);
+}
+
void Printer::EmitCoreBuiltinCall(core::ir::CoreBuiltinCall* builtin) {
auto* result_ty = builtin->Result()->Type();
@@ -1630,9 +1654,6 @@
case spirv::ir::Intrinsic::kVectorTimesMatrix:
op = spv::Op::OpVectorTimesMatrix;
break;
- case spirv::ir::Intrinsic::kVectorTimesScalar:
- op = spv::Op::OpVectorTimesScalar;
- break;
case spirv::ir::Intrinsic::kUndefined:
TINT_ICE() << "undefined spirv intrinsic";
return;
diff --git a/src/tint/lang/spirv/writer/printer/printer.h b/src/tint/lang/spirv/writer/printer/printer.h
index 36d10a4..f991bfe 100644
--- a/src/tint/lang/spirv/writer/printer/printer.h
+++ b/src/tint/lang/spirv/writer/printer/printer.h
@@ -24,6 +24,7 @@
#include "src/tint/lang/core/ir/builder.h"
#include "src/tint/lang/core/ir/constant.h"
#include "src/tint/lang/core/texel_format.h"
+#include "src/tint/lang/spirv/ir/builtin_call.h"
#include "src/tint/lang/spirv/ir/intrinsic_call.h"
#include "src/tint/lang/spirv/writer/common/binary_writer.h"
#include "src/tint/lang/spirv/writer/common/function.h"
@@ -201,6 +202,10 @@
/// Emit a builtin function call instruction.
/// @param call the builtin call instruction to emit
+ void EmitSpirvBuiltinCall(spirv::ir::BuiltinCall* call);
+
+ /// Emit a builtin function call instruction.
+ /// @param call the builtin call instruction to emit
void EmitCoreBuiltinCall(core::ir::CoreBuiltinCall* call);
/// Emit a construct instruction.
diff --git a/src/tint/lang/spirv/writer/raise/expand_implicit_splats.cc b/src/tint/lang/spirv/writer/raise/expand_implicit_splats.cc
index 19b6e99..c222b71 100644
--- a/src/tint/lang/spirv/writer/raise/expand_implicit_splats.cc
+++ b/src/tint/lang/spirv/writer/raise/expand_implicit_splats.cc
@@ -19,7 +19,8 @@
#include "src/tint/lang/core/ir/builder.h"
#include "src/tint/lang/core/ir/module.h"
#include "src/tint/lang/core/ir/validator.h"
-#include "src/tint/lang/spirv/ir/intrinsic_call.h"
+#include "src/tint/lang/spirv/ir/builtin_call.h"
+#include "src/tint/lang/spirv/ir/function.h"
using namespace tint::core::number_suffixes; // NOLINT
@@ -89,8 +90,8 @@
auto* result_ty = binary->Result()->Type();
if (result_ty->is_float_vector() && binary->Kind() == core::ir::Binary::Kind::kMultiply) {
// Use OpVectorTimesScalar for floating point multiply.
- auto* vts = b.Call<spirv::ir::IntrinsicCall>(result_ty,
- spirv::ir::Intrinsic::kVectorTimesScalar);
+ auto* vts =
+ b.Call<spirv::ir::BuiltinCall>(result_ty, spirv::ir::Function::kVectorTimesScalar);
if (binary->LHS()->Type()->Is<core::type::Scalar>()) {
vts->AppendArg(binary->RHS());
vts->AppendArg(binary->LHS());