[hlsl] Start `struct` type emission.
This CL adds the start of struct type emission to the HLSL IR backend.
Bug: 42251045
Change-Id: I3b6be8a1de2ab67cc2ef065ca47e715c36863f49
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/193980
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/hlsl/writer/constant_test.cc b/src/tint/lang/hlsl/writer/constant_test.cc
index 9755198..b0c7646 100644
--- a/src/tint/lang/hlsl/writer/constant_test.cc
+++ b/src/tint/lang/hlsl/writer/constant_test.cc
@@ -38,7 +38,8 @@
f->Block()->Append(b.Return(f, false));
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(bool a() {
+ EXPECT_EQ(output_.hlsl, R"(
+bool a() {
return false;
}
@@ -54,7 +55,8 @@
f->Block()->Append(b.Return(f, true));
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(bool a() {
+ EXPECT_EQ(output_.hlsl, R"(
+bool a() {
return true;
}
@@ -70,7 +72,8 @@
f->Block()->Append(b.Return(f, -12345_i));
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(int a() {
+ EXPECT_EQ(output_.hlsl, R"(
+int a() {
return -12345;
}
@@ -86,7 +89,8 @@
f->Block()->Append(b.Return(f, 56779_u));
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(uint a() {
+ EXPECT_EQ(output_.hlsl, R"(
+uint a() {
return 56779u;
}
@@ -103,7 +107,8 @@
f->Block()->Append(b.Return(f, f32((1 << 30) - 4)));
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(float a() {
+ EXPECT_EQ(output_.hlsl, R"(
+float a() {
return 1073741824.0f;
}
@@ -120,7 +125,8 @@
f->Block()->Append(b.Return(f, f16((1 << 15) - 8)));
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(float16_t a() {
+ EXPECT_EQ(output_.hlsl, R"(
+float16_t a() {
return float16_t(32752.0h);
}
@@ -136,7 +142,8 @@
b.Append(f->Block(), [&] { b.Return(f, b.Composite(ty.vec3<f32>(), 1_f, 2_f, 3_f)); });
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(float3 a() {
+ EXPECT_EQ(output_.hlsl, R"(
+float3 a() {
return float3(1.0f, 2.0f, 3.0f);
}
@@ -152,7 +159,8 @@
b.Append(f->Block(), [&] { b.Return(f, b.Composite(ty.vec3<f16>(), 1_h, 2_h, 3_h)); });
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(vector<float16_t, 3> a() {
+ EXPECT_EQ(output_.hlsl, R"(
+vector<float16_t, 3> a() {
return vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h));
}
@@ -168,7 +176,8 @@
b.Append(f->Block(), [&] { b.Return(f, b.Zero<vec3<f32>>()); });
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(float3 a() {
+ EXPECT_EQ(output_.hlsl, R"(
+float3 a() {
return (0.0f).xxx;
}
@@ -184,7 +193,8 @@
b.Append(f->Block(), [&] { b.Return(f, b.Zero<vec3<f16>>()); });
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(vector<float16_t, 3> a() {
+ EXPECT_EQ(output_.hlsl, R"(
+vector<float16_t, 3> a() {
return (float16_t(0.0h)).xxx;
}
@@ -200,7 +210,8 @@
b.Append(f->Block(), [&] { b.Return(f, b.Splat(ty.vec3<f32>(), 2_f)); });
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(float3 a() {
+ EXPECT_EQ(output_.hlsl, R"(
+float3 a() {
return (2.0f).xxx;
}
@@ -216,7 +227,8 @@
b.Append(f->Block(), [&] { b.Return(f, b.Splat(ty.vec3<f16>(), 2_h)); });
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(vector<float16_t, 3> a() {
+ EXPECT_EQ(output_.hlsl, R"(
+vector<float16_t, 3> a() {
return (float16_t(2.0h)).xxx;
}
@@ -274,7 +286,8 @@
b.Append(f->Block(), [&] { b.Return(f, b.Splat(ty.vec3<bool>(), true)); });
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(bool3 a() {
+ EXPECT_EQ(output_.hlsl, R"(
+bool3 a() {
return (true).xxx;
}
@@ -311,7 +324,8 @@
b.Append(f->Block(), [&] { b.Return(f, b.Splat(ty.vec3<i32>(), 2_i)); });
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(int3 a() {
+ EXPECT_EQ(output_.hlsl, R"(
+int3 a() {
return (2).xxx;
}
@@ -327,7 +341,8 @@
b.Append(f->Block(), [&] { b.Return(f, b.Splat(ty.vec3<u32>(), 2_u)); });
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(uint3 a() {
+ EXPECT_EQ(output_.hlsl, R"(
+uint3 a() {
return (2u).xxx;
}
@@ -346,7 +361,8 @@
});
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(float2x3 a() {
+ EXPECT_EQ(output_.hlsl, R"(
+float2x3 a() {
return float2x3(float3(1.0f, 2.0f, 3.0f), float3(3.0f, 4.0f, 5.0f));
}
@@ -365,7 +381,8 @@
});
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(matrix<float16_t, 2, 3> a() {
+ EXPECT_EQ(output_.hlsl, R"(
+matrix<float16_t, 2, 3> a() {
return matrix<float16_t, 2, 3>(vector<float16_t, 3>(float16_t(1.0h), float16_t(2.0h), float16_t(3.0h)), vector<float16_t, 3>(float16_t(3.0h), float16_t(4.0h), float16_t(5.0h)));
}
@@ -391,7 +408,8 @@
});
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(float4x4 a() {
+ EXPECT_EQ(output_.hlsl, R"(
+float4x4 a() {
return float4x4(float4(2.0f, 3.0f, 4.0f, 8.0f), (0.0f).xxxx, (7.0f).xxxx, float4(42.0f, 21.0f, 6.0f, -5.0f));
}
@@ -417,7 +435,8 @@
});
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(matrix<float16_t, 4, 4> a() {
+ EXPECT_EQ(output_.hlsl, R"(
+matrix<float16_t, 4, 4> a() {
return matrix<float16_t, 4, 4>(vector<float16_t, 4>(float16_t(2.0h), float16_t(3.0h), float16_t(4.0h), float16_t(8.0h)), (float16_t(0.0h)).xxxx, (float16_t(7.0h)).xxxx, vector<float16_t, 4>(float16_t(42.0h), float16_t(21.0h), float16_t(6.0h), float16_t(-5.0h)));
}
@@ -433,7 +452,8 @@
b.Append(f->Block(), [&] { b.Return(f, b.Zero<mat2x3<f32>>()); });
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(float2x3 a() {
+ EXPECT_EQ(output_.hlsl, R"(
+float2x3 a() {
return float2x3((0.0f).xxx, (0.0f).xxx);
}
@@ -449,7 +469,8 @@
b.Append(f->Block(), [&] { b.Return(f, b.Zero<mat2x3<f16>>()); });
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(matrix<float16_t, 2, 3> a() {
+ EXPECT_EQ(output_.hlsl, R"(
+matrix<float16_t, 2, 3> a() {
return matrix<float16_t, 2, 3>((float16_t(0.0h)).xxx, (float16_t(0.0h)).xxx);
}
@@ -540,7 +561,8 @@
b.Append(f->Block(), [&] { b.Return(f, b.Zero<array<vec3<f32>, 3>>()); });
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(float3[3] a() {
+ EXPECT_EQ(output_.hlsl, R"(
+float3[3] a() {
return (float3[3])0;
}
@@ -586,8 +608,7 @@
)");
}
-// TODO(dsinclair): Needs `struct` constant emission
-TEST_F(HlslWriterTest, DISABLED_ConstantTypeStructEmpty) {
+TEST_F(HlslWriterTest, ConstantTypeStructEmpty) {
Vector members{
ty.Get<core::type::StructMember>(b.ir.symbols.New("a"), ty.i32(), 0u, 0u, 4u, 4u,
core::type::StructMemberAttributes{}),
@@ -602,11 +623,11 @@
b.Append(f->Block(), [&] { b.Return(f, b.Zero(strct)); });
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(struct S
+ EXPECT_EQ(output_.hlsl, R"(struct S {
int a;
float b;
int3 c;
-}
+};
S a() {
return (S)0;
@@ -619,7 +640,7 @@
)");
}
-// TODO(dsinclair): Needs `struct` constant emission
+// TODO(dsinclair): Needs `construct` emission
TEST_F(HlslWriterTest, DISABLED_ConstantTypeStructStatic) {
Vector members{
ty.Get<core::type::StructMember>(b.ir.symbols.New("a"), ty.i32(), 0u, 0u, 4u, 4u,
diff --git a/src/tint/lang/hlsl/writer/function_test.cc b/src/tint/lang/hlsl/writer/function_test.cc
index 929c3d9..40af080 100644
--- a/src/tint/lang/hlsl/writer/function_test.cc
+++ b/src/tint/lang/hlsl/writer/function_test.cc
@@ -47,7 +47,8 @@
func->Block()->Append(b.Return(func));
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"(void foo() {
+ EXPECT_EQ(output_.hlsl, R"(
+void foo() {
}
[numthreads(1, 1, 1)]
@@ -80,7 +81,8 @@
func->Block()->Append(b.Return(func));
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"([numthreads(1, 1, 1)]
+ EXPECT_EQ(output_.hlsl, R"(
+[numthreads(1, 1, 1)]
void main() {
}
@@ -728,7 +730,8 @@
func->Block()->Append(b.Return(func));
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"([numthreads(1, 1, 1)]
+ EXPECT_EQ(output_.hlsl, R"(
+[numthreads(1, 1, 1)]
void main() {
}
@@ -743,7 +746,8 @@
func->Block()->Append(b.Return(func));
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"([numthreads(2, 4, 6)]
+ EXPECT_EQ(output_.hlsl, R"(
+[numthreads(2, 4, 6)]
void main() {
}
diff --git a/src/tint/lang/hlsl/writer/printer/printer.cc b/src/tint/lang/hlsl/writer/printer/printer.cc
index 6ea7666..813937f 100644
--- a/src/tint/lang/hlsl/writer/printer/printer.cc
+++ b/src/tint/lang/hlsl/writer/printer/printer.cc
@@ -30,15 +30,20 @@
#include <cmath>
#include <cstddef>
#include <cstdint>
+#include <string>
#include <unordered_map>
+#include <unordered_set>
#include <utility>
#include <vector>
#include "src/tint/lang/core/access.h"
#include "src/tint/lang/core/address_space.h"
+#include "src/tint/lang/core/builtin_value.h"
#include "src/tint/lang/core/constant/splat.h"
#include "src/tint/lang/core/constant/value.h"
#include "src/tint/lang/core/fluent_types.h"
+#include "src/tint/lang/core/interpolation_sampling.h"
+#include "src/tint/lang/core/interpolation_type.h"
#include "src/tint/lang/core/ir/access.h"
#include "src/tint/lang/core/ir/bitcast.h"
#include "src/tint/lang/core/ir/block.h"
@@ -93,6 +98,7 @@
#include "src/tint/utils/rtti/switch.h"
#include "src/tint/utils/strconv/float_to_string.h"
#include "src/tint/utils/text/string.h"
+#include "src/tint/utils/text/string_stream.h"
using namespace tint::core::fluent_types; // NOLINT
@@ -120,7 +126,10 @@
EmitFunction(func);
}
- result_.hlsl = main_buffer_.String();
+ StringStream ss;
+ ss << preamble_buffer_.String() << "\n" << main_buffer_.String();
+ result_.hlsl = ss.str();
+
return std::move(result_);
}
@@ -130,10 +139,15 @@
core::ir::Module& ir_;
+ /// The buffer holding preamble text
+ TextBuffer preamble_buffer_;
+
/// A hashmap of value to name
Hashmap<const core::ir::Value*, std::string, 32> names_;
/// Map of builtin structure to unique generated name
std::unordered_map<const core::type::Struct*, std::string> builtin_struct_names_;
+ /// Set of structs which have been emitted already
+ std::unordered_set<const core::type::Struct*> emitted_structs_;
/// The current function being emitted
const core::ir::Function* current_function_ = nullptr;
@@ -208,20 +222,15 @@
}
void EmitVar(const core::ir::Var* var) {
- auto out = Line();
-
- // TODO(dsinclair): This isn't right, as some types contain their names
- EmitType(out, var->Result(0)->Type());
- out << " ";
- out << NameOf(var->Result(0));
-
- out << " = ";
-
auto* ptr = var->Result(0)->Type()->As<core::type::Pointer>();
TINT_ASSERT(ptr);
auto space = ptr->AddressSpace();
+ auto out = Line();
+ EmitTypeAndName(out, var->Result(0)->Type(), space, ptr->Access(), NameOf(var->Result(0)));
+ out << " = ";
+
if (var->Initializer()) {
EmitValue(out, var->Initializer());
} else if (space == core::AddressSpace::kPrivate ||
@@ -242,12 +251,10 @@
void EmitLet(const core::ir::Let* l) {
auto out = Line();
- // TODO(dsinclair): This isn't right, as some types contain their names.
// TODO(dsinclair): Investigate using `const` here as well, the AST printer doesn't emit
// const with a let, but we should be able to.
- EmitType(out, l->Result(0)->Type());
- out << " ";
- out << NameOf(l->Result(0));
+ EmitTypeAndName(out, l->Result(0)->Type(), core::AddressSpace::kUndefined,
+ core::Access::kUndefined, NameOf(l->Result(0)));
out << " = ";
EmitValue(out, l->Value());
out << ";";
@@ -497,7 +504,8 @@
[&](const core::type::U32*) { out << c->ValueAs<AInt>() << "u"; },
[&](const core::type::Array* a) { EmitConstantArray(out, c, a); },
[&](const core::type::Vector* v) { EmitConstantVector(out, c, v); },
- [&](const core::type::Matrix* m) { EmitConstantMatrix(out, c, m); }, //
+ [&](const core::type::Matrix* m) { EmitConstantMatrix(out, c, m); },
+ [&](const core::type::Struct* s) { EmitConstantStruct(out, c, s); }, //
TINT_ICE_ON_NO_MATCH);
}
@@ -573,6 +581,17 @@
}
}
+ void EmitConstantStruct(StringStream& out,
+ const core::constant::Value* c,
+ const core::type::Struct* s) {
+ EmitStructType(&preamble_buffer_, s);
+
+ if (c->AllZero()) {
+ out << "(" << StructName(s) << ")0";
+ return;
+ }
+ }
+
void EmitType(StringStream& out,
const core::type::Type* ty,
core::AddressSpace address_space = core::AddressSpace::kUndefined,
@@ -627,6 +646,19 @@
TINT_ICE_ON_NO_MATCH);
}
+ void EmitTypeAndName(StringStream& out,
+ const core::type::Type* type,
+ core::AddressSpace address_space,
+ core::Access access,
+ const std::string& name) {
+ bool name_printed = false;
+ EmitType(out, type, address_space, access, name, &name_printed);
+
+ if (!name.empty() && !name_printed) {
+ out << " " << name;
+ }
+ }
+
void EmitArrayType(StringStream& out,
const core::type::Array* ary,
core::AddressSpace address_space,
@@ -768,6 +800,140 @@
out << "State";
}
+ void EmitStructType(TextBuffer* b, const core::type::Struct* str) {
+ auto it = emitted_structs_.emplace(str);
+ if (!it.second) {
+ return;
+ }
+
+ Line(b) << "struct " << StructName(str) << " {";
+ {
+ const ScopedIndent si(b);
+ for (auto* mem : str->Members()) {
+ auto mem_name = mem->Name().Name();
+ auto* ty = mem->Type();
+ auto out = Line(b);
+ std::string pre, post;
+
+ auto& attributes = mem->Attributes();
+
+ if (auto location = attributes.location) {
+ auto& pipeline_stage_uses = str->PipelineStageUses();
+ if (TINT_UNLIKELY(pipeline_stage_uses.Count() != 1)) {
+ TINT_ICE() << "invalid entry point IO struct uses";
+ }
+ if (pipeline_stage_uses.Contains(
+ core::type::PipelineStageUsage::kVertexInput)) {
+ post += " : TEXCOORD" + std::to_string(location.value());
+ } else if (pipeline_stage_uses.Contains(
+ core::type::PipelineStageUsage::kVertexOutput)) {
+ post += " : TEXCOORD" + std::to_string(location.value());
+ } else if (pipeline_stage_uses.Contains(
+ core::type::PipelineStageUsage::kFragmentInput)) {
+ post += " : TEXCOORD" + std::to_string(location.value());
+ } else if (TINT_LIKELY(pipeline_stage_uses.Contains(
+ core::type::PipelineStageUsage::kFragmentOutput))) {
+ if (auto blend_src = attributes.blend_src) {
+ post += " : SV_Target" +
+ std::to_string(location.value() + blend_src.value());
+ } else {
+ post += " : SV_Target" + std::to_string(location.value());
+ }
+
+ } else {
+ TINT_ICE() << "invalid use of location attribute";
+ }
+ }
+ if (auto builtin = attributes.builtin) {
+ auto name = builtin_to_attribute(builtin.value());
+ TINT_ASSERT(!name.empty());
+
+ post += " : " + name;
+ }
+ if (auto interpolation = attributes.interpolation) {
+ auto mod =
+ interpolation_to_modifiers(interpolation->type, interpolation->sampling);
+ TINT_ASSERT(!mod.empty());
+
+ pre += mod;
+ }
+ if (attributes.invariant) {
+ // Note: `precise` is not exactly the same as `invariant`, but is
+ // stricter and therefore provides the necessary guarantees.
+ // See discussion here: https://github.com/gpuweb/gpuweb/issues/893
+ pre += "precise ";
+ }
+
+ out << pre;
+ EmitTypeAndName(out, ty, core::AddressSpace::kUndefined, core::Access::kReadWrite,
+ mem_name);
+ out << post << ";";
+ }
+ }
+
+ Line(b) << "};";
+ }
+
+ std::string builtin_to_attribute(core::BuiltinValue builtin) const {
+ switch (builtin) {
+ case core::BuiltinValue::kPosition:
+ return "SV_Position";
+ case core::BuiltinValue::kVertexIndex:
+ return "SV_VertexID";
+ case core::BuiltinValue::kInstanceIndex:
+ return "SV_InstanceID";
+ case core::BuiltinValue::kFrontFacing:
+ return "SV_IsFrontFace";
+ case core::BuiltinValue::kFragDepth:
+ return "SV_Depth";
+ case core::BuiltinValue::kLocalInvocationId:
+ return "SV_GroupThreadID";
+ case core::BuiltinValue::kLocalInvocationIndex:
+ return "SV_GroupIndex";
+ case core::BuiltinValue::kGlobalInvocationId:
+ return "SV_DispatchThreadID";
+ case core::BuiltinValue::kWorkgroupId:
+ return "SV_GroupID";
+ case core::BuiltinValue::kSampleIndex:
+ return "SV_SampleIndex";
+ case core::BuiltinValue::kSampleMask:
+ return "SV_Coverage";
+ default:
+ break;
+ }
+ return "";
+ }
+
+ std::string interpolation_to_modifiers(core::InterpolationType type,
+ core::InterpolationSampling sampling) const {
+ std::string modifiers;
+ switch (type) {
+ case core::InterpolationType::kPerspective:
+ modifiers += "linear ";
+ break;
+ case core::InterpolationType::kLinear:
+ modifiers += "noperspective ";
+ break;
+ case core::InterpolationType::kFlat:
+ modifiers += "nointerpolation ";
+ break;
+ case core::InterpolationType::kUndefined:
+ break;
+ }
+ switch (sampling) {
+ case core::InterpolationSampling::kCentroid:
+ modifiers += "centroid ";
+ break;
+ case core::InterpolationSampling::kSample:
+ modifiers += "sample ";
+ break;
+ case core::InterpolationSampling::kCenter:
+ case core::InterpolationSampling::kUndefined:
+ break;
+ }
+ return modifiers;
+ }
+
/// @returns the name of the given value, creating a new unique name if the value is unnamed in
/// the module.
std::string NameOf(const core::ir::Value* value) {
diff --git a/src/tint/lang/hlsl/writer/var_let_test.cc b/src/tint/lang/hlsl/writer/var_let_test.cc
index a0374f5..8677eb1 100644
--- a/src/tint/lang/hlsl/writer/var_let_test.cc
+++ b/src/tint/lang/hlsl/writer/var_let_test.cc
@@ -45,7 +45,8 @@
});
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"([numthreads(1, 1, 1)]
+ EXPECT_EQ(output_.hlsl, R"(
+[numthreads(1, 1, 1)]
void main() {
uint a = 1u;
}
@@ -62,7 +63,8 @@
});
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"([numthreads(1, 1, 1)]
+ EXPECT_EQ(output_.hlsl, R"(
+[numthreads(1, 1, 1)]
void main() {
float a = 0.0f;
}
@@ -79,7 +81,8 @@
});
ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
- EXPECT_EQ(output_.hlsl, R"([numthreads(1, 1, 1)]
+ EXPECT_EQ(output_.hlsl, R"(
+[numthreads(1, 1, 1)]
void main() {
float a = 2.0f;
}