blob: dcad1b5d1ce0b606faef764b316d942132bf964d [file] [log] [blame]
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001/// Copyright 2020 The Tint Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "src/tint/writer/hlsl/generator_impl.h"
16
17#include <algorithm>
18#include <cmath>
19#include <functional>
20#include <iomanip>
21#include <set>
22#include <utility>
23#include <vector>
24
25#include "src/tint/ast/call_statement.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000026#include "src/tint/ast/id_attribute.h"
27#include "src/tint/ast/internal_attribute.h"
28#include "src/tint/ast/interpolate_attribute.h"
29#include "src/tint/ast/variable_decl_statement.h"
30#include "src/tint/debug.h"
31#include "src/tint/sem/array.h"
Ben Clayton01004b72022-04-28 18:49:04 +000032#include "src/tint/sem/atomic.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000033#include "src/tint/sem/block_statement.h"
34#include "src/tint/sem/call.h"
Ben Claytone9f8b092022-06-01 13:14:39 +000035#include "src/tint/sem/constant.h"
Ben Clayton01004b72022-04-28 18:49:04 +000036#include "src/tint/sem/depth_multisampled_texture.h"
37#include "src/tint/sem/depth_texture.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000038#include "src/tint/sem/function.h"
39#include "src/tint/sem/member_accessor_expression.h"
40#include "src/tint/sem/module.h"
Ben Clayton01004b72022-04-28 18:49:04 +000041#include "src/tint/sem/multisampled_texture.h"
42#include "src/tint/sem/sampled_texture.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000043#include "src/tint/sem/statement.h"
Ben Clayton01004b72022-04-28 18:49:04 +000044#include "src/tint/sem/storage_texture.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000045#include "src/tint/sem/struct.h"
dan sinclaird32fbe02022-10-19 00:43:41 +000046#include "src/tint/sem/switch_statement.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000047#include "src/tint/sem/type_conversion.h"
dan sinclair6e77b472022-10-20 13:38:28 +000048#include "src/tint/sem/type_initializer.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000049#include "src/tint/sem/variable.h"
50#include "src/tint/transform/add_empty_entry_point.h"
51#include "src/tint/transform/array_length_from_uniform.h"
Ben Clayton27aa57c2022-02-22 23:13:39 +000052#include "src/tint/transform/builtin_polyfill.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000053#include "src/tint/transform/calculate_array_length.h"
54#include "src/tint/transform/canonicalize_entry_point_io.h"
55#include "src/tint/transform/decompose_memory_access.h"
James Price744d0eb2022-11-09 19:58:59 +000056#include "src/tint/transform/demote_to_helper.h"
James Price791b4352022-05-11 13:50:33 +000057#include "src/tint/transform/disable_uniformity_analysis.h"
James Price508a9662022-03-31 22:30:10 +000058#include "src/tint/transform/expand_compound_assignment.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000059#include "src/tint/transform/localize_struct_array_assignment.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000060#include "src/tint/transform/manager.h"
61#include "src/tint/transform/num_workgroups_from_uniform.h"
Ben Clayton7ebcfc72022-06-27 20:20:25 +000062#include "src/tint/transform/promote_initializers_to_let.h"
Antonio Maiorano93baaae2022-03-15 15:35:13 +000063#include "src/tint/transform/promote_side_effects_to_decl.h"
Antonio Maioranob3497102022-03-31 15:02:25 +000064#include "src/tint/transform/remove_continue_in_switch.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000065#include "src/tint/transform/remove_phonies.h"
66#include "src/tint/transform/simplify_pointers.h"
shrekshaof9c66332022-11-22 21:36:27 +000067#include "src/tint/transform/truncate_interstage_variables.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000068#include "src/tint/transform/unshadow.h"
dan sinclair6e77b472022-10-20 13:38:28 +000069#include "src/tint/transform/vectorize_scalar_matrix_initializers.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000070#include "src/tint/transform/zero_init_workgroup_memory.h"
71#include "src/tint/utils/defer.h"
72#include "src/tint/utils/map.h"
73#include "src/tint/utils/scoped_assignment.h"
dan sinclair4abf28e2022-08-02 15:55:35 +000074#include "src/tint/utils/string.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000075#include "src/tint/writer/append_vector.h"
Ben Clayton1a567782022-10-14 13:38:27 +000076#include "src/tint/writer/check_supported_extensions.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000077#include "src/tint/writer/float_to_string.h"
Antonio Maioranoa730eb72022-04-06 13:57:54 +000078#include "src/tint/writer/generate_external_texture_bindings.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000079
Ben Clayton0ce9ab02022-05-05 20:23:40 +000080using namespace tint::number_suffixes; // NOLINT
81
dan sinclair6a5bef12022-04-07 14:30:24 +000082namespace tint::writer::hlsl {
Ryan Harrisondbc13af2022-02-21 15:19:07 +000083namespace {
84
85const char kTempNamePrefix[] = "tint_tmp";
Ryan Harrisondbc13af2022-02-21 15:19:07 +000086
87const char* image_format_to_rwtexture_type(ast::TexelFormat image_format) {
dan sinclair41e4d9a2022-05-01 14:40:55 +000088 switch (image_format) {
89 case ast::TexelFormat::kRgba8Unorm:
90 case ast::TexelFormat::kRgba8Snorm:
91 case ast::TexelFormat::kRgba16Float:
92 case ast::TexelFormat::kR32Float:
93 case ast::TexelFormat::kRg32Float:
94 case ast::TexelFormat::kRgba32Float:
95 return "float4";
96 case ast::TexelFormat::kRgba8Uint:
97 case ast::TexelFormat::kRgba16Uint:
98 case ast::TexelFormat::kR32Uint:
99 case ast::TexelFormat::kRg32Uint:
100 case ast::TexelFormat::kRgba32Uint:
101 return "uint4";
102 case ast::TexelFormat::kRgba8Sint:
103 case ast::TexelFormat::kRgba16Sint:
104 case ast::TexelFormat::kR32Sint:
105 case ast::TexelFormat::kRg32Sint:
106 case ast::TexelFormat::kRgba32Sint:
107 return "int4";
108 default:
109 return nullptr;
110 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000111}
112
Ben Claytone9f8b092022-06-01 13:14:39 +0000113void PrintF32(std::ostream& out, float value) {
Ben Claytone9f8b092022-06-01 13:14:39 +0000114 if (std::isinf(value)) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000115 out << "0.0f " << (value >= 0 ? "/* inf */" : "/* -inf */");
Ben Claytone9f8b092022-06-01 13:14:39 +0000116 } else if (std::isnan(value)) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000117 out << "0.0f /* nan */";
Ben Claytone9f8b092022-06-01 13:14:39 +0000118 } else {
119 out << FloatToString(value) << "f";
120 }
121}
122
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000123void PrintF16(std::ostream& out, float value) {
124 if (std::isinf(value)) {
125 out << "0.0h " << (value >= 0 ? "/* inf */" : "/* -inf */");
126 } else if (std::isnan(value)) {
127 out << "0.0h /* nan */";
Zhaoming Jianga5988a32022-07-11 15:43:38 +0000128 } else {
129 out << FloatToString(value) << "h";
Zhaoming Jianga5988a32022-07-11 15:43:38 +0000130 }
131}
132
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000133// Helper for writing " : register(RX, spaceY)", where R is the register, X is
134// the binding point binding value, and Y is the binding point group value.
135struct RegisterAndSpace {
dan sinclairacdf6e12022-08-24 15:47:25 +0000136 RegisterAndSpace(char r, sem::BindingPoint bp) : reg(r), binding_point(bp) {}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000137
dan sinclair41e4d9a2022-05-01 14:40:55 +0000138 const char reg;
dan sinclairacdf6e12022-08-24 15:47:25 +0000139 sem::BindingPoint const binding_point;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000140};
141
142std::ostream& operator<<(std::ostream& s, const RegisterAndSpace& rs) {
dan sinclairacdf6e12022-08-24 15:47:25 +0000143 s << " : register(" << rs.reg << rs.binding_point.binding << ", space" << rs.binding_point.group
144 << ")";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000145 return s;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000146}
147
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000148} // namespace
149
150SanitizedResult::SanitizedResult() = default;
151SanitizedResult::~SanitizedResult() = default;
152SanitizedResult::SanitizedResult(SanitizedResult&&) = default;
153
Antonio Maiorano7eaab382022-04-11 16:33:30 +0000154SanitizedResult Sanitize(const Program* in, const Options& options) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000155 transform::Manager manager;
156 transform::DataMap data;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000157
James Price791b4352022-05-11 13:50:33 +0000158 manager.Add<transform::DisableUniformityAnalysis>();
159
Ben Clayton46ee6392022-11-09 22:04:11 +0000160 // ExpandCompoundAssignment must come before BuiltinPolyfill
161 manager.Add<transform::ExpandCompoundAssignment>();
162
dan sinclair41e4d9a2022-05-01 14:40:55 +0000163 { // Builtin polyfills
164 transform::BuiltinPolyfill::Builtins polyfills;
dan sinclaird23f2962022-06-28 15:27:44 +0000165 polyfills.acosh = transform::BuiltinPolyfill::Level::kFull;
166 polyfills.asinh = true;
167 polyfills.atanh = transform::BuiltinPolyfill::Level::kFull;
Ben Clayton02f04d92022-11-03 19:15:17 +0000168 polyfills.bitshift_modulo = true;
Ben Clayton6dbb4632022-10-31 17:54:49 +0000169 polyfills.clamp_int = true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000170 // TODO(crbug.com/tint/1449): Some of these can map to HLSL's `firstbitlow`
171 // and `firstbithigh`.
172 polyfills.count_leading_zeros = true;
173 polyfills.count_trailing_zeros = true;
174 polyfills.extract_bits = transform::BuiltinPolyfill::Level::kFull;
175 polyfills.first_leading_bit = true;
176 polyfills.first_trailing_bit = true;
177 polyfills.insert_bits = transform::BuiltinPolyfill::Level::kFull;
Ben Clayton46ee6392022-11-09 22:04:11 +0000178 polyfills.int_div_mod = true;
Ben Claytonc4ebf2c2022-09-22 22:59:16 +0000179 polyfills.texture_sample_base_clamp_to_edge_2d_f32 = true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000180 data.Add<transform::BuiltinPolyfill::Config>(polyfills);
181 manager.Add<transform::BuiltinPolyfill>();
182 }
Ben Clayton27aa57c2022-02-22 23:13:39 +0000183
dan sinclair41e4d9a2022-05-01 14:40:55 +0000184 // Build the config for the internal ArrayLengthFromUniform transform.
185 auto& array_length_from_uniform = options.array_length_from_uniform;
186 transform::ArrayLengthFromUniform::Config array_length_from_uniform_cfg(
187 array_length_from_uniform.ubo_binding);
188 array_length_from_uniform_cfg.bindpoint_to_size_index =
189 array_length_from_uniform.bindpoint_to_size_index;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000190
dan sinclair41e4d9a2022-05-01 14:40:55 +0000191 if (options.generate_external_texture_bindings) {
192 auto new_bindings_map = GenerateExternalTextureBindings(in);
193 data.Add<transform::MultiplanarExternalTexture::NewBindingPoints>(new_bindings_map);
194 }
195 manager.Add<transform::MultiplanarExternalTexture>();
Antonio Maioranoa730eb72022-04-06 13:57:54 +0000196
dan sinclair41e4d9a2022-05-01 14:40:55 +0000197 manager.Add<transform::Unshadow>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000198
dan sinclair41e4d9a2022-05-01 14:40:55 +0000199 // LocalizeStructArrayAssignment must come after:
200 // * SimplifyPointers, because it assumes assignment to arrays in structs are
201 // done directly, not indirectly.
202 // TODO(crbug.com/tint/1340): See if we can get rid of the duplicate
203 // SimplifyPointers transform. Can't do it right now because
204 // LocalizeStructArrayAssignment introduces pointers.
205 manager.Add<transform::SimplifyPointers>();
206 manager.Add<transform::LocalizeStructArrayAssignment>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000207
dan sinclair41e4d9a2022-05-01 14:40:55 +0000208 if (!options.disable_workgroup_init) {
209 // ZeroInitWorkgroupMemory must come before CanonicalizeEntryPointIO as
210 // ZeroInitWorkgroupMemory may inject new builtin parameters.
211 manager.Add<transform::ZeroInitWorkgroupMemory>();
212 }
213 manager.Add<transform::CanonicalizeEntryPointIO>();
shrekshaof9c66332022-11-22 21:36:27 +0000214
215 if (options.interstage_locations.any()) {
216 // When interstage_locations is empty, it means there's no user-defined interstage variables
217 // being used in the next stage. This is treated as a special case.
218 // TruncateInterstageVariables transform is trying to solve the HLSL compiler register
219 // mismatch issue. So it is not needed if no register is assigned to any interstage
220 // variables. As a result we only add this transform when there is at least one interstage
221 // locations being used.
222
223 // TruncateInterstageVariables itself will skip when interstage_locations matches exactly
224 // with the current stage output.
225
226 // Build the config for internal TruncateInterstageVariables transform.
227 transform::TruncateInterstageVariables::Config truncate_interstage_variables_cfg;
228 truncate_interstage_variables_cfg.interstage_locations =
229 std::move(options.interstage_locations);
230 manager.Add<transform::TruncateInterstageVariables>();
231 data.Add<transform::TruncateInterstageVariables::Config>(
232 std::move(truncate_interstage_variables_cfg));
233 }
234
dan sinclair41e4d9a2022-05-01 14:40:55 +0000235 // NumWorkgroupsFromUniform must come after CanonicalizeEntryPointIO, as it
236 // assumes that num_workgroups builtins only appear as struct members and are
237 // only accessed directly via member accessors.
238 manager.Add<transform::NumWorkgroupsFromUniform>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000239 manager.Add<transform::PromoteSideEffectsToDecl>();
dan sinclair6e77b472022-10-20 13:38:28 +0000240 manager.Add<transform::VectorizeScalarMatrixInitializers>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000241 manager.Add<transform::SimplifyPointers>();
242 manager.Add<transform::RemovePhonies>();
James Price744d0eb2022-11-09 19:58:59 +0000243
244 // DemoteToHelper must come after CanonicalizeEntryPointIO, PromoteSideEffectsToDecl, and
245 // ExpandCompoundAssignment.
246 // TODO(crbug.com/tint/1752): This is only necessary when FXC is being used.
247 manager.Add<transform::DemoteToHelper>();
248
dan sinclair41e4d9a2022-05-01 14:40:55 +0000249 // ArrayLengthFromUniform must come after InlinePointerLets and Simplify, as
250 // it assumes that the form of the array length argument is &var.array.
251 manager.Add<transform::ArrayLengthFromUniform>();
252 data.Add<transform::ArrayLengthFromUniform::Config>(std::move(array_length_from_uniform_cfg));
253 // DecomposeMemoryAccess must come after:
254 // * InlinePointerLets, as we cannot take the address of calls to
255 // DecomposeMemoryAccess::Intrinsic.
256 // * Simplify, as we need to fold away the address-of and dereferences of
257 // `*(&(intrinsic_load()))` expressions.
258 // * RemovePhonies, as phonies can be assigned a pointer to a
259 // non-constructible buffer, or dynamic array, which DMA cannot cope with.
260 manager.Add<transform::DecomposeMemoryAccess>();
261 // CalculateArrayLength must come after DecomposeMemoryAccess, as
262 // DecomposeMemoryAccess special-cases the arrayLength() intrinsic, which
263 // will be transformed by CalculateArrayLength
264 manager.Add<transform::CalculateArrayLength>();
Ben Clayton7ebcfc72022-06-27 20:20:25 +0000265 manager.Add<transform::PromoteInitializersToLet>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000266
dan sinclair41e4d9a2022-05-01 14:40:55 +0000267 manager.Add<transform::RemoveContinueInSwitch>();
Antonio Maioranob3497102022-03-31 15:02:25 +0000268
dan sinclair41e4d9a2022-05-01 14:40:55 +0000269 manager.Add<transform::AddEmptyEntryPoint>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000270
dan sinclair41e4d9a2022-05-01 14:40:55 +0000271 data.Add<transform::CanonicalizeEntryPointIO::Config>(
272 transform::CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
273 data.Add<transform::NumWorkgroupsFromUniform::Config>(options.root_constant_binding_point);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000274
dan sinclair41e4d9a2022-05-01 14:40:55 +0000275 auto out = manager.Run(in, data);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000276
dan sinclair41e4d9a2022-05-01 14:40:55 +0000277 SanitizedResult result;
278 result.program = std::move(out.program);
279 if (auto* res = out.data.Get<transform::ArrayLengthFromUniform::Result>()) {
280 result.used_array_length_from_uniform_indices = std::move(res->used_size_indices);
281 }
282 return result;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000283}
284
285GeneratorImpl::GeneratorImpl(const Program* program) : TextGenerator(program) {}
286
287GeneratorImpl::~GeneratorImpl() = default;
288
289bool GeneratorImpl::Generate() {
Ben Clayton1a567782022-10-14 13:38:27 +0000290 if (!CheckSupportedExtensions("HLSL", program_->AST(), diagnostics_,
291 utils::Vector{
292 ast::Extension::kChromiumDisableUniformityAnalysis,
293 ast::Extension::kChromiumExperimentalDp4A,
294 ast::Extension::kChromiumExperimentalPushConstant,
295 ast::Extension::kF16,
296 })) {
297 return false;
298 }
299
dan sinclair41e4d9a2022-05-01 14:40:55 +0000300 const TypeInfo* last_kind = nullptr;
301 size_t last_padding_line = 0;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000302
dan sinclair41e4d9a2022-05-01 14:40:55 +0000303 auto* mod = builder_.Sem().Module();
304 for (auto* decl : mod->DependencyOrderedDeclarations()) {
Ben Claytonb4744ac2022-08-03 07:01:08 +0000305 if (decl->IsAnyOf<ast::Alias, ast::Enable, ast::StaticAssert>()) {
306 continue; // These are not emitted.
James Price791b4352022-05-11 13:50:33 +0000307 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000308
dan sinclair41e4d9a2022-05-01 14:40:55 +0000309 // Emit a new line between declarations if the type of declaration has
310 // changed, or we're about to emit a function
311 auto* kind = &decl->TypeInfo();
312 if (current_buffer_->lines.size() != last_padding_line) {
313 if (last_kind && (last_kind != kind || decl->Is<ast::Function>())) {
314 line();
315 last_padding_line = current_buffer_->lines.size();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000316 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000317 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000318 last_kind = kind;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000319
dan sinclair41e4d9a2022-05-01 14:40:55 +0000320 bool ok = Switch(
321 decl,
322 [&](const ast::Variable* global) { //
323 return EmitGlobalVariable(global);
324 },
325 [&](const ast::Struct* str) {
326 auto* ty = builder_.Sem().Get(str);
dan sinclairff7cf212022-10-03 14:05:23 +0000327 auto address_space_uses = ty->AddressSpaceUsage();
328 if (address_space_uses.size() !=
329 (address_space_uses.count(ast::AddressSpace::kStorage) +
330 address_space_uses.count(ast::AddressSpace::kUniform))) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000331 // The structure is used as something other than a storage buffer or
332 // uniform buffer, so it needs to be emitted.
333 // Storage buffer are read and written to via a ByteAddressBuffer
334 // instead of true structure.
335 // Structures used as uniform buffer are read from an array of
336 // vectors instead of true structure.
337 return EmitStructType(current_buffer_, ty);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000338 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000339 return true;
340 },
341 [&](const ast::Function* func) {
342 if (func->IsEntryPoint()) {
343 return EmitEntryPointFunction(func);
344 }
345 return EmitFunction(func);
346 },
dan sinclair41e4d9a2022-05-01 14:40:55 +0000347 [&](Default) {
348 TINT_ICE(Writer, diagnostics_)
349 << "unhandled module-scope declaration: " << decl->TypeInfo().name;
350 return false;
351 });
352
353 if (!ok) {
354 return false;
355 }
356 }
357
358 if (!helpers_.lines.empty()) {
359 current_buffer_->Insert(helpers_, 0, 0);
360 }
361
362 return true;
363}
364
365bool GeneratorImpl::EmitDynamicVectorAssignment(const ast::AssignmentStatement* stmt,
366 const sem::Vector* vec) {
367 auto name = utils::GetOrCreate(dynamic_vector_write_, vec, [&]() -> std::string {
368 std::string fn;
369 {
370 std::ostringstream ss;
Ben Claytond2e0db32022-10-12 18:49:15 +0000371 if (!EmitType(ss, vec, tint::ast::AddressSpace::kUndefined, ast::Access::kUndefined,
372 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000373 return "";
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000374 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000375 fn = UniqueIdentifier("set_" + ss.str());
376 }
377 {
378 auto out = line(&helpers_);
379 out << "void " << fn << "(inout ";
Ben Claytond2e0db32022-10-12 18:49:15 +0000380 if (!EmitTypeAndName(out, vec, ast::AddressSpace::kUndefined, ast::Access::kUndefined,
dan sinclair41e4d9a2022-05-01 14:40:55 +0000381 "vec")) {
382 return "";
383 }
384 out << ", int idx, ";
Ben Claytond2e0db32022-10-12 18:49:15 +0000385 if (!EmitTypeAndName(out, vec->type(), ast::AddressSpace::kUndefined,
386 ast::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000387 return "";
388 }
389 out << ") {";
390 }
391 {
392 ScopedIndent si(&helpers_);
393 auto out = line(&helpers_);
394 switch (vec->Width()) {
395 case 2:
396 out << "vec = (idx.xx == int2(0, 1)) ? val.xx : vec;";
397 break;
398 case 3:
399 out << "vec = (idx.xxx == int3(0, 1, 2)) ? val.xxx : vec;";
400 break;
401 case 4:
402 out << "vec = (idx.xxxx == int4(0, 1, 2, 3)) ? val.xxxx : vec;";
403 break;
404 default:
Ben Claytondcdf66e2022-06-17 12:48:51 +0000405 TINT_UNREACHABLE(Writer, diagnostics_)
dan sinclair41e4d9a2022-05-01 14:40:55 +0000406 << "invalid vector size " << vec->Width();
407 break;
408 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000409 }
410 line(&helpers_) << "}";
411 line(&helpers_);
412 return fn;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000413 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000414
dan sinclair41e4d9a2022-05-01 14:40:55 +0000415 if (name.empty()) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000416 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000417 }
418
dan sinclair41e4d9a2022-05-01 14:40:55 +0000419 auto* ast_access_expr = stmt->lhs->As<ast::IndexAccessorExpression>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000420
dan sinclair41e4d9a2022-05-01 14:40:55 +0000421 auto out = line();
422 out << name << "(";
423 if (!EmitExpression(out, ast_access_expr->object)) {
424 return false;
425 }
426 out << ", ";
427 if (!EmitExpression(out, ast_access_expr->index)) {
428 return false;
429 }
430 out << ", ";
431 if (!EmitExpression(out, stmt->rhs)) {
432 return false;
433 }
434 out << ");";
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000435
dan sinclair41e4d9a2022-05-01 14:40:55 +0000436 return true;
437}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000438
dan sinclair41e4d9a2022-05-01 14:40:55 +0000439bool GeneratorImpl::EmitDynamicMatrixVectorAssignment(const ast::AssignmentStatement* stmt,
440 const sem::Matrix* mat) {
441 auto name = utils::GetOrCreate(dynamic_matrix_vector_write_, mat, [&]() -> std::string {
442 std::string fn;
443 {
444 std::ostringstream ss;
Ben Claytond2e0db32022-10-12 18:49:15 +0000445 if (!EmitType(ss, mat, tint::ast::AddressSpace::kUndefined, ast::Access::kUndefined,
446 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000447 return "";
448 }
449 fn = UniqueIdentifier("set_vector_" + ss.str());
450 }
451 {
452 auto out = line(&helpers_);
453 out << "void " << fn << "(inout ";
Ben Claytond2e0db32022-10-12 18:49:15 +0000454 if (!EmitTypeAndName(out, mat, ast::AddressSpace::kUndefined, ast::Access::kUndefined,
dan sinclair41e4d9a2022-05-01 14:40:55 +0000455 "mat")) {
456 return "";
457 }
458 out << ", int col, ";
Ben Claytond2e0db32022-10-12 18:49:15 +0000459 if (!EmitTypeAndName(out, mat->ColumnType(), ast::AddressSpace::kUndefined,
460 ast::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000461 return "";
462 }
463 out << ") {";
464 }
465 {
466 ScopedIndent si(&helpers_);
467 line(&helpers_) << "switch (col) {";
468 {
469 ScopedIndent si2(&helpers_);
470 for (uint32_t i = 0; i < mat->columns(); ++i) {
471 line(&helpers_) << "case " << i << ": mat[" << i << "] = val; break;";
472 }
473 }
474 line(&helpers_) << "}";
475 }
476 line(&helpers_) << "}";
477 line(&helpers_);
478 return fn;
479 });
480
481 if (name.empty()) {
482 return false;
483 }
484
485 auto* ast_access_expr = stmt->lhs->As<ast::IndexAccessorExpression>();
486
487 auto out = line();
488 out << name << "(";
489 if (!EmitExpression(out, ast_access_expr->object)) {
490 return false;
491 }
492 out << ", ";
493 if (!EmitExpression(out, ast_access_expr->index)) {
494 return false;
495 }
496 out << ", ";
497 if (!EmitExpression(out, stmt->rhs)) {
498 return false;
499 }
500 out << ");";
501
502 return true;
503}
504
505bool GeneratorImpl::EmitDynamicMatrixScalarAssignment(const ast::AssignmentStatement* stmt,
506 const sem::Matrix* mat) {
507 auto* lhs_col_access = stmt->lhs->As<ast::IndexAccessorExpression>();
508 auto* lhs_row_access = lhs_col_access->object->As<ast::IndexAccessorExpression>();
509
510 auto name = utils::GetOrCreate(dynamic_matrix_scalar_write_, mat, [&]() -> std::string {
511 std::string fn;
512 {
513 std::ostringstream ss;
Ben Claytond2e0db32022-10-12 18:49:15 +0000514 if (!EmitType(ss, mat, tint::ast::AddressSpace::kUndefined, ast::Access::kUndefined,
515 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000516 return "";
517 }
518 fn = UniqueIdentifier("set_scalar_" + ss.str());
519 }
520 {
521 auto out = line(&helpers_);
522 out << "void " << fn << "(inout ";
Ben Claytond2e0db32022-10-12 18:49:15 +0000523 if (!EmitTypeAndName(out, mat, ast::AddressSpace::kUndefined, ast::Access::kUndefined,
dan sinclair41e4d9a2022-05-01 14:40:55 +0000524 "mat")) {
525 return "";
526 }
527 out << ", int col, int row, ";
Ben Claytond2e0db32022-10-12 18:49:15 +0000528 if (!EmitTypeAndName(out, mat->type(), ast::AddressSpace::kUndefined,
529 ast::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000530 return "";
531 }
532 out << ") {";
533 }
534 {
535 ScopedIndent si(&helpers_);
536 line(&helpers_) << "switch (col) {";
537 {
538 ScopedIndent si2(&helpers_);
539 auto* vec = TypeOf(lhs_row_access->object)->UnwrapRef()->As<sem::Vector>();
540 for (uint32_t i = 0; i < mat->columns(); ++i) {
541 line(&helpers_) << "case " << i << ":";
542 {
543 auto vec_name = "mat[" + std::to_string(i) + "]";
544 ScopedIndent si3(&helpers_);
545 {
546 auto out = line(&helpers_);
547 switch (mat->rows()) {
548 case 2:
549 out << vec_name
550 << " = (row.xx == int2(0, 1)) ? val.xx : " << vec_name
551 << ";";
552 break;
553 case 3:
554 out << vec_name
555 << " = (row.xxx == int3(0, 1, 2)) ? val.xxx : " << vec_name
556 << ";";
557 break;
558 case 4:
559 out << vec_name
560 << " = (row.xxxx == int4(0, 1, 2, 3)) ? val.xxxx : "
561 << vec_name << ";";
562 break;
563 default:
Ben Claytondcdf66e2022-06-17 12:48:51 +0000564 TINT_UNREACHABLE(Writer, diagnostics_)
dan sinclair41e4d9a2022-05-01 14:40:55 +0000565 << "invalid vector size " << vec->Width();
566 break;
567 }
568 }
569 line(&helpers_) << "break;";
570 }
571 }
572 }
573 line(&helpers_) << "}";
574 }
575 line(&helpers_) << "}";
576 line(&helpers_);
577 return fn;
578 });
579
580 if (name.empty()) {
581 return false;
582 }
583
584 auto out = line();
585 out << name << "(";
586 if (!EmitExpression(out, lhs_row_access->object)) {
587 return false;
588 }
589 out << ", ";
590 if (!EmitExpression(out, lhs_col_access->index)) {
591 return false;
592 }
593 out << ", ";
594 if (!EmitExpression(out, lhs_row_access->index)) {
595 return false;
596 }
597 out << ", ";
598 if (!EmitExpression(out, stmt->rhs)) {
599 return false;
600 }
601 out << ");";
602
603 return true;
604}
605
606bool GeneratorImpl::EmitIndexAccessor(std::ostream& out, const ast::IndexAccessorExpression* expr) {
607 if (!EmitExpression(out, expr->object)) {
608 return false;
609 }
610 out << "[";
611
612 if (!EmitExpression(out, expr->index)) {
613 return false;
614 }
615 out << "]";
616
617 return true;
618}
619
620bool GeneratorImpl::EmitBitcast(std::ostream& out, const ast::BitcastExpression* expr) {
621 auto* type = TypeOf(expr);
622 if (auto* vec = type->UnwrapRef()->As<sem::Vector>()) {
623 type = vec->type();
624 }
625
626 if (!type->is_integer_scalar() && !type->is_float_scalar()) {
627 diagnostics_.add_error(diag::System::Writer, "Unable to do bitcast to type " +
628 type->FriendlyName(builder_.Symbols()));
629 return false;
630 }
631
632 out << "as";
dan sinclairff7cf212022-10-03 14:05:23 +0000633 if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000634 return false;
635 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000636 out << "(";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000637 if (!EmitExpression(out, expr->expr)) {
638 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000639 }
640 out << ")";
641 return true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000642}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000643
dan sinclair41e4d9a2022-05-01 14:40:55 +0000644bool GeneratorImpl::EmitAssign(const ast::AssignmentStatement* stmt) {
645 if (auto* lhs_access = stmt->lhs->As<ast::IndexAccessorExpression>()) {
646 // BUG(crbug.com/tint/1333): work around assignment of scalar to matrices
647 // with at least one dynamic index
648 if (auto* lhs_sub_access = lhs_access->object->As<ast::IndexAccessorExpression>()) {
649 if (auto* mat = TypeOf(lhs_sub_access->object)->UnwrapRef()->As<sem::Matrix>()) {
650 auto* rhs_col_idx_sem = builder_.Sem().Get(lhs_access->index);
651 auto* rhs_row_idx_sem = builder_.Sem().Get(lhs_sub_access->index);
Ben Claytonaa037ac2022-06-29 19:07:30 +0000652 if (!rhs_col_idx_sem->ConstantValue() || !rhs_row_idx_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000653 return EmitDynamicMatrixScalarAssignment(stmt, mat);
654 }
655 }
656 }
657 // BUG(crbug.com/tint/1333): work around assignment of vector to matrices
658 // with dynamic indices
659 const auto* lhs_access_type = TypeOf(lhs_access->object)->UnwrapRef();
660 if (auto* mat = lhs_access_type->As<sem::Matrix>()) {
661 auto* lhs_index_sem = builder_.Sem().Get(lhs_access->index);
Ben Claytonaa037ac2022-06-29 19:07:30 +0000662 if (!lhs_index_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000663 return EmitDynamicMatrixVectorAssignment(stmt, mat);
664 }
665 }
666 // BUG(crbug.com/tint/534): work around assignment to vectors with dynamic
667 // indices
668 if (auto* vec = lhs_access_type->As<sem::Vector>()) {
669 auto* rhs_sem = builder_.Sem().Get(lhs_access->index);
Ben Claytonaa037ac2022-06-29 19:07:30 +0000670 if (!rhs_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000671 return EmitDynamicVectorAssignment(stmt, vec);
672 }
673 }
674 }
675
676 auto out = line();
677 if (!EmitExpression(out, stmt->lhs)) {
678 return false;
679 }
680 out << " = ";
681 if (!EmitExpression(out, stmt->rhs)) {
682 return false;
683 }
684 out << ";";
685 return true;
686}
687
dan sinclair41e4d9a2022-05-01 14:40:55 +0000688bool GeneratorImpl::EmitBinary(std::ostream& out, const ast::BinaryExpression* expr) {
689 if (expr->op == ast::BinaryOp::kLogicalAnd || expr->op == ast::BinaryOp::kLogicalOr) {
690 auto name = UniqueIdentifier(kTempNamePrefix);
691
692 {
693 auto pre = line();
694 pre << "bool " << name << " = ";
695 if (!EmitExpression(pre, expr->lhs)) {
696 return false;
697 }
698 pre << ";";
699 }
700
701 if (expr->op == ast::BinaryOp::kLogicalOr) {
702 line() << "if (!" << name << ") {";
703 } else {
704 line() << "if (" << name << ") {";
705 }
706
707 {
708 ScopedIndent si(this);
709 auto pre = line();
710 pre << name << " = ";
711 if (!EmitExpression(pre, expr->rhs)) {
712 return false;
713 }
714 pre << ";";
715 }
716
717 line() << "}";
718
719 out << "(" << name << ")";
720 return true;
721 }
722
723 auto* lhs_type = TypeOf(expr->lhs)->UnwrapRef();
724 auto* rhs_type = TypeOf(expr->rhs)->UnwrapRef();
725 // Multiplying by a matrix requires the use of `mul` in order to get the
726 // type of multiply we desire.
727 if (expr->op == ast::BinaryOp::kMultiply &&
728 ((lhs_type->Is<sem::Vector>() && rhs_type->Is<sem::Matrix>()) ||
729 (lhs_type->Is<sem::Matrix>() && rhs_type->Is<sem::Vector>()) ||
730 (lhs_type->Is<sem::Matrix>() && rhs_type->Is<sem::Matrix>()))) {
731 // Matrices are transposed, so swap LHS and RHS.
732 out << "mul(";
733 if (!EmitExpression(out, expr->rhs)) {
734 return false;
735 }
736 out << ", ";
737 if (!EmitExpression(out, expr->lhs)) {
738 return false;
739 }
740 out << ")";
741
742 return true;
743 }
744
Ben Claytone9f8b092022-06-01 13:14:39 +0000745 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000746
747 if (!EmitExpression(out, expr->lhs)) {
748 return false;
749 }
750 out << " ";
751
752 switch (expr->op) {
753 case ast::BinaryOp::kAnd:
754 out << "&";
755 break;
756 case ast::BinaryOp::kOr:
757 out << "|";
758 break;
759 case ast::BinaryOp::kXor:
760 out << "^";
761 break;
762 case ast::BinaryOp::kLogicalAnd:
763 case ast::BinaryOp::kLogicalOr: {
764 // These are both handled above.
765 TINT_UNREACHABLE(Writer, diagnostics_);
766 return false;
767 }
768 case ast::BinaryOp::kEqual:
769 out << "==";
770 break;
771 case ast::BinaryOp::kNotEqual:
772 out << "!=";
773 break;
774 case ast::BinaryOp::kLessThan:
775 out << "<";
776 break;
777 case ast::BinaryOp::kGreaterThan:
778 out << ">";
779 break;
780 case ast::BinaryOp::kLessThanEqual:
781 out << "<=";
782 break;
783 case ast::BinaryOp::kGreaterThanEqual:
784 out << ">=";
785 break;
786 case ast::BinaryOp::kShiftLeft:
787 out << "<<";
788 break;
789 case ast::BinaryOp::kShiftRight:
790 // TODO(dsinclair): MSL is based on C++14, and >> in C++14 has
791 // implementation-defined behaviour for negative LHS. We may have to
792 // generate extra code to implement WGSL-specified behaviour for negative
793 // LHS.
794 out << R"(>>)";
795 break;
796
797 case ast::BinaryOp::kAdd:
798 out << "+";
799 break;
800 case ast::BinaryOp::kSubtract:
801 out << "-";
802 break;
803 case ast::BinaryOp::kMultiply:
804 out << "*";
805 break;
806 case ast::BinaryOp::kDivide:
807 out << "/";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000808 break;
809 case ast::BinaryOp::kModulo:
810 out << "%";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000811 break;
812 case ast::BinaryOp::kNone:
813 diagnostics_.add_error(diag::System::Writer, "missing binary operation type");
814 return false;
815 }
816 out << " ";
817
818 if (!EmitExpression(out, expr->rhs)) {
819 return false;
820 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000821
822 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000823}
824
Ben Clayton783b1692022-08-02 17:03:35 +0000825bool GeneratorImpl::EmitStatements(utils::VectorRef<const ast::Statement*> stmts) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000826 for (auto* s : stmts) {
827 if (!EmitStatement(s)) {
828 return false;
829 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000830 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000831 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000832}
833
Ben Clayton783b1692022-08-02 17:03:35 +0000834bool GeneratorImpl::EmitStatementsWithIndent(utils::VectorRef<const ast::Statement*> stmts) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000835 ScopedIndent si(this);
836 return EmitStatements(stmts);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000837}
838
839bool GeneratorImpl::EmitBlock(const ast::BlockStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000840 line() << "{";
841 if (!EmitStatementsWithIndent(stmt->statements)) {
842 return false;
843 }
844 line() << "}";
845 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000846}
847
848bool GeneratorImpl::EmitBreak(const ast::BreakStatement*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000849 line() << "break;";
850 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000851}
852
dan sinclairb8b0c212022-10-20 22:45:50 +0000853bool GeneratorImpl::EmitBreakIf(const ast::BreakIfStatement* b) {
854 auto out = line();
855 out << "if (";
856 if (!EmitExpression(out, b->condition)) {
857 return false;
858 }
859 out << ") { break; }";
860 return true;
861}
862
dan sinclair41e4d9a2022-05-01 14:40:55 +0000863bool GeneratorImpl::EmitCall(std::ostream& out, const ast::CallExpression* expr) {
Ben Claytone9f8b092022-06-01 13:14:39 +0000864 auto* call = builder_.Sem().Get<sem::Call>(expr);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000865 auto* target = call->Target();
866 return Switch(
867 target, [&](const sem::Function* func) { return EmitFunctionCall(out, call, func); },
868 [&](const sem::Builtin* builtin) { return EmitBuiltinCall(out, call, builtin); },
869 [&](const sem::TypeConversion* conv) { return EmitTypeConversion(out, call, conv); },
dan sinclair6e77b472022-10-20 13:38:28 +0000870 [&](const sem::TypeInitializer* ctor) { return EmitTypeInitializer(out, call, ctor); },
dan sinclair41e4d9a2022-05-01 14:40:55 +0000871 [&](Default) {
872 TINT_ICE(Writer, diagnostics_) << "unhandled call target: " << target->TypeInfo().name;
873 return false;
874 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000875}
876
877bool GeneratorImpl::EmitFunctionCall(std::ostream& out,
878 const sem::Call* call,
879 const sem::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000880 auto* expr = call->Declaration();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000881
dan sinclair41e4d9a2022-05-01 14:40:55 +0000882 if (ast::HasAttribute<transform::CalculateArrayLength::BufferSizeIntrinsic>(
883 func->Declaration()->attributes)) {
884 // Special function generated by the CalculateArrayLength transform for
885 // calling X.GetDimensions(Y)
886 if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
887 return false;
888 }
889 out << ".GetDimensions(";
890 if (!EmitExpression(out, call->Arguments()[1]->Declaration())) {
891 return false;
892 }
893 out << ")";
894 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000895 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000896
897 if (auto* intrinsic = ast::GetAttribute<transform::DecomposeMemoryAccess::Intrinsic>(
898 func->Declaration()->attributes)) {
dan sinclairff7cf212022-10-03 14:05:23 +0000899 switch (intrinsic->address_space) {
900 case ast::AddressSpace::kUniform:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000901 return EmitUniformBufferAccess(out, expr, intrinsic);
dan sinclairff7cf212022-10-03 14:05:23 +0000902 case ast::AddressSpace::kStorage:
Antonio Maiorano08f4b552022-05-31 13:20:28 +0000903 if (!intrinsic->IsAtomic()) {
904 return EmitStorageBufferAccess(out, expr, intrinsic);
905 }
906 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000907 default:
908 TINT_UNREACHABLE(Writer, diagnostics_)
dan sinclairff7cf212022-10-03 14:05:23 +0000909 << "unsupported DecomposeMemoryAccess::Intrinsic address space:"
910 << intrinsic->address_space;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000911 return false;
912 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000913 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000914
915 out << builder_.Symbols().NameFor(func->Declaration()->symbol) << "(";
916
917 bool first = true;
918 for (auto* arg : call->Arguments()) {
919 if (!first) {
920 out << ", ";
921 }
922 first = false;
923
924 if (!EmitExpression(out, arg->Declaration())) {
925 return false;
926 }
927 }
928
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000929 out << ")";
930 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000931}
932
933bool GeneratorImpl::EmitBuiltinCall(std::ostream& out,
934 const sem::Call* call,
935 const sem::Builtin* builtin) {
Antonio Maioranoab4c0352022-05-20 01:58:40 +0000936 const auto type = builtin->Type();
937
dan sinclair41e4d9a2022-05-01 14:40:55 +0000938 auto* expr = call->Declaration();
939 if (builtin->IsTexture()) {
940 return EmitTextureCall(out, call, builtin);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000941 }
Antonio Maioranoab4c0352022-05-20 01:58:40 +0000942 if (type == sem::BuiltinType::kSelect) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000943 return EmitSelectCall(out, expr);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000944 }
Antonio Maioranoab4c0352022-05-20 01:58:40 +0000945 if (type == sem::BuiltinType::kModf) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000946 return EmitModfCall(out, expr, builtin);
947 }
Antonio Maioranoab4c0352022-05-20 01:58:40 +0000948 if (type == sem::BuiltinType::kFrexp) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000949 return EmitFrexpCall(out, expr, builtin);
950 }
Antonio Maioranoab4c0352022-05-20 01:58:40 +0000951 if (type == sem::BuiltinType::kDegrees) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000952 return EmitDegreesCall(out, expr, builtin);
953 }
Antonio Maioranoab4c0352022-05-20 01:58:40 +0000954 if (type == sem::BuiltinType::kRadians) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000955 return EmitRadiansCall(out, expr, builtin);
956 }
Ben Clayton2bea9052022-11-02 00:09:50 +0000957 if (type == sem::BuiltinType::kQuantizeToF16) {
958 return EmitQuantizeToF16Call(out, expr, builtin);
959 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000960 if (builtin->IsDataPacking()) {
961 return EmitDataPackingCall(out, expr, builtin);
962 }
963 if (builtin->IsDataUnpacking()) {
964 return EmitDataUnpackingCall(out, expr, builtin);
965 }
966 if (builtin->IsBarrier()) {
967 return EmitBarrierCall(out, builtin);
968 }
969 if (builtin->IsAtomic()) {
970 return EmitWorkgroupAtomicCall(out, expr, builtin);
971 }
Jiawei Shaoab975702022-05-13 00:09:56 +0000972 if (builtin->IsDP4a()) {
973 return EmitDP4aCall(out, expr, builtin);
974 }
Antonio Maioranoab4c0352022-05-20 01:58:40 +0000975
dan sinclair41e4d9a2022-05-01 14:40:55 +0000976 auto name = generate_builtin_name(builtin);
977 if (name.empty()) {
978 return false;
979 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000980
Antonio Maioranoab4c0352022-05-20 01:58:40 +0000981 // Handle single argument builtins that only accept and return uint (not int overload). We need
982 // to explicitly cast the return value (we also cast the arg for good measure). See
983 // crbug.com/tint/1550
984 if (type == sem::BuiltinType::kCountOneBits || type == sem::BuiltinType::kReverseBits) {
985 auto* arg = call->Arguments()[0];
986 if (arg->Type()->UnwrapRef()->is_signed_scalar_or_vector()) {
987 out << "asint(" << name << "(asuint(";
988 if (!EmitExpression(out, arg->Declaration())) {
989 return false;
990 }
991 out << ")))";
992 return true;
993 }
994 }
995
dan sinclair41e4d9a2022-05-01 14:40:55 +0000996 out << name << "(";
997
998 bool first = true;
999 for (auto* arg : call->Arguments()) {
1000 if (!first) {
1001 out << ", ";
1002 }
1003 first = false;
1004
1005 if (!EmitExpression(out, arg->Declaration())) {
1006 return false;
1007 }
1008 }
1009
1010 out << ")";
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001011
dan sinclair41e4d9a2022-05-01 14:40:55 +00001012 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001013}
1014
1015bool GeneratorImpl::EmitTypeConversion(std::ostream& out,
1016 const sem::Call* call,
1017 const sem::TypeConversion* conv) {
dan sinclairff7cf212022-10-03 14:05:23 +00001018 if (!EmitType(out, conv->Target(), ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001019 return false;
1020 }
1021 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001022
dan sinclair41e4d9a2022-05-01 14:40:55 +00001023 if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
1024 return false;
1025 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001026
dan sinclair41e4d9a2022-05-01 14:40:55 +00001027 out << ")";
1028 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001029}
1030
dan sinclair6e77b472022-10-20 13:38:28 +00001031bool GeneratorImpl::EmitTypeInitializer(std::ostream& out,
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001032 const sem::Call* call,
dan sinclair6e77b472022-10-20 13:38:28 +00001033 const sem::TypeInitializer* ctor) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001034 auto* type = call->Type();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001035
dan sinclair6e77b472022-10-20 13:38:28 +00001036 // If the type initializer is empty then we need to construct with the zero
dan sinclair41e4d9a2022-05-01 14:40:55 +00001037 // value for all components.
Ben Clayton958a4642022-07-26 07:55:24 +00001038 if (call->Arguments().IsEmpty()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001039 return EmitZeroValue(out, type);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001040 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001041
dan sinclair6e77b472022-10-20 13:38:28 +00001042 // Single parameter matrix initializers must be identity initializer.
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001043 // It could also be conversions between f16 and f32 matrix when f16 is properly supported.
Ben Clayton958a4642022-07-26 07:55:24 +00001044 if (type->Is<sem::Matrix>() && call->Arguments().Length() == 1) {
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001045 if (!ctor->Parameters()[0]->Type()->UnwrapRef()->is_float_matrix()) {
1046 TINT_UNREACHABLE(Writer, diagnostics_)
dan sinclair6e77b472022-10-20 13:38:28 +00001047 << "found a single-parameter matrix initializer that is not identity initializer";
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001048 return false;
Ben Clayton3b5edf12022-05-16 21:14:11 +00001049 }
1050 }
1051
dan sinclair41e4d9a2022-05-01 14:40:55 +00001052 bool brackets = type->IsAnyOf<sem::Array, sem::Struct>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001053
dan sinclair41e4d9a2022-05-01 14:40:55 +00001054 // For single-value vector initializers, swizzle the scalar to the right
1055 // vector dimension using .x
1056 const bool is_single_value_vector_init = type->is_scalar_vector() &&
Ben Clayton958a4642022-07-26 07:55:24 +00001057 call->Arguments().Length() == 1 &&
dan sinclair41e4d9a2022-05-01 14:40:55 +00001058 ctor->Parameters()[0]->Type()->is_scalar();
1059
Ben Clayton6c098ba2022-07-14 20:46:39 +00001060 if (brackets) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001061 out << "{";
1062 } else {
dan sinclairff7cf212022-10-03 14:05:23 +00001063 if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001064 return false;
1065 }
1066 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001067 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001068
dan sinclair41e4d9a2022-05-01 14:40:55 +00001069 if (is_single_value_vector_init) {
1070 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001071 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001072
dan sinclair41e4d9a2022-05-01 14:40:55 +00001073 bool first = true;
1074 for (auto* e : call->Arguments()) {
1075 if (!first) {
1076 out << ", ";
1077 }
1078 first = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001079
dan sinclair41e4d9a2022-05-01 14:40:55 +00001080 if (!EmitExpression(out, e->Declaration())) {
1081 return false;
1082 }
1083 }
1084
1085 if (is_single_value_vector_init) {
1086 out << ")." << std::string(type->As<sem::Vector>()->Width(), 'x');
1087 }
1088
1089 out << (brackets ? "}" : ")");
1090 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001091}
1092
1093bool GeneratorImpl::EmitUniformBufferAccess(
1094 std::ostream& out,
1095 const ast::CallExpression* expr,
1096 const transform::DecomposeMemoryAccess::Intrinsic* intrinsic) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001097 const auto& args = expr->args;
1098 auto* offset_arg = builder_.Sem().Get(args[1]);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001099
dan sinclair41e4d9a2022-05-01 14:40:55 +00001100 uint32_t scalar_offset_value = 0;
1101 std::string scalar_offset_expr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001102
dan sinclair41e4d9a2022-05-01 14:40:55 +00001103 // If true, use scalar_offset_value, otherwise use scalar_offset_expr
1104 bool scalar_offset_constant = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001105
Ben Claytonaa037ac2022-06-29 19:07:30 +00001106 if (auto* val = offset_arg->ConstantValue()) {
1107 TINT_ASSERT(Writer, val->Type()->Is<sem::U32>());
1108 scalar_offset_value = static_cast<uint32_t>(std::get<AInt>(val->Value()));
dan sinclair41e4d9a2022-05-01 14:40:55 +00001109 scalar_offset_value /= 4; // bytes -> scalar index
1110 scalar_offset_constant = true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001111 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001112
dan sinclair41e4d9a2022-05-01 14:40:55 +00001113 if (!scalar_offset_constant) {
1114 // UBO offset not compile-time known.
1115 // Calculate the scalar offset into a temporary.
1116 scalar_offset_expr = UniqueIdentifier("scalar_offset");
1117 auto pre = line();
1118 pre << "const uint " << scalar_offset_expr << " = (";
1119 if (!EmitExpression(pre, args[1])) { // offset
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001120 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001121 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001122 pre << ") / 4;";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001123 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001124
1125 using Op = transform::DecomposeMemoryAccess::Intrinsic::Op;
1126 using DataType = transform::DecomposeMemoryAccess::Intrinsic::DataType;
1127 switch (intrinsic->op) {
1128 case Op::kLoad: {
1129 auto cast = [&](const char* to, auto&& load) {
1130 out << to << "(";
1131 auto result = load();
1132 out << ")";
1133 return result;
1134 };
1135 auto load_scalar = [&]() {
1136 if (!EmitExpression(out, args[0])) { // buffer
1137 return false;
1138 }
1139 if (scalar_offset_constant) {
1140 char swizzle[] = {'x', 'y', 'z', 'w'};
1141 out << "[" << (scalar_offset_value / 4) << "]."
1142 << swizzle[scalar_offset_value & 3];
1143 } else {
1144 out << "[" << scalar_offset_expr << " / 4][" << scalar_offset_expr << " % 4]";
1145 }
1146 return true;
1147 };
1148 // Has a minimum alignment of 8 bytes, so is either .xy or .zw
1149 auto load_vec2 = [&] {
1150 if (scalar_offset_constant) {
1151 if (!EmitExpression(out, args[0])) { // buffer
1152 return false;
1153 }
1154 out << "[" << (scalar_offset_value / 4) << "]";
1155 out << ((scalar_offset_value & 2) == 0 ? ".xy" : ".zw");
1156 } else {
1157 std::string ubo_load = UniqueIdentifier("ubo_load");
1158 {
1159 auto pre = line();
1160 pre << "uint4 " << ubo_load << " = ";
1161 if (!EmitExpression(pre, args[0])) { // buffer
1162 return false;
1163 }
1164 pre << "[" << scalar_offset_expr << " / 4];";
1165 }
1166 out << "((" << scalar_offset_expr << " & 2) ? " << ubo_load
1167 << ".zw : " << ubo_load << ".xy)";
1168 }
1169 return true;
1170 };
1171 // vec4 has a minimum alignment of 16 bytes, easiest case
1172 auto load_vec4 = [&] {
1173 if (!EmitExpression(out, args[0])) { // buffer
1174 return false;
1175 }
1176 if (scalar_offset_constant) {
1177 out << "[" << (scalar_offset_value / 4) << "]";
1178 } else {
1179 out << "[" << scalar_offset_expr << " / 4]";
1180 }
1181 return true;
1182 };
1183 // vec3 has a minimum alignment of 16 bytes, so is just a .xyz swizzle
1184 auto load_vec3 = [&] {
1185 if (!load_vec4()) {
1186 return false;
1187 }
1188 out << ".xyz";
1189 return true;
1190 };
1191 switch (intrinsic->type) {
1192 case DataType::kU32:
1193 return load_scalar();
1194 case DataType::kF32:
1195 return cast("asfloat", load_scalar);
1196 case DataType::kI32:
1197 return cast("asint", load_scalar);
1198 case DataType::kVec2U32:
1199 return load_vec2();
1200 case DataType::kVec2F32:
1201 return cast("asfloat", load_vec2);
1202 case DataType::kVec2I32:
1203 return cast("asint", load_vec2);
1204 case DataType::kVec3U32:
1205 return load_vec3();
1206 case DataType::kVec3F32:
1207 return cast("asfloat", load_vec3);
1208 case DataType::kVec3I32:
1209 return cast("asint", load_vec3);
1210 case DataType::kVec4U32:
1211 return load_vec4();
1212 case DataType::kVec4F32:
1213 return cast("asfloat", load_vec4);
1214 case DataType::kVec4I32:
1215 return cast("asint", load_vec4);
1216 }
1217 TINT_UNREACHABLE(Writer, diagnostics_)
1218 << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1219 << static_cast<int>(intrinsic->type);
1220 return false;
1221 }
1222 default:
1223 break;
1224 }
1225 TINT_UNREACHABLE(Writer, diagnostics_)
1226 << "unsupported DecomposeMemoryAccess::Intrinsic::Op: " << static_cast<int>(intrinsic->op);
1227 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001228}
1229
1230bool GeneratorImpl::EmitStorageBufferAccess(
1231 std::ostream& out,
1232 const ast::CallExpression* expr,
1233 const transform::DecomposeMemoryAccess::Intrinsic* intrinsic) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001234 const auto& args = expr->args;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001235
dan sinclair41e4d9a2022-05-01 14:40:55 +00001236 using Op = transform::DecomposeMemoryAccess::Intrinsic::Op;
1237 using DataType = transform::DecomposeMemoryAccess::Intrinsic::DataType;
1238 switch (intrinsic->op) {
1239 case Op::kLoad: {
1240 auto load = [&](const char* cast, int n) {
1241 if (cast) {
1242 out << cast << "(";
1243 }
1244 if (!EmitExpression(out, args[0])) { // buffer
1245 return false;
1246 }
1247 out << ".Load";
1248 if (n > 1) {
1249 out << n;
1250 }
1251 ScopedParen sp(out);
1252 if (!EmitExpression(out, args[1])) { // offset
1253 return false;
1254 }
1255 if (cast) {
1256 out << ")";
1257 }
1258 return true;
1259 };
1260 switch (intrinsic->type) {
1261 case DataType::kU32:
1262 return load(nullptr, 1);
1263 case DataType::kF32:
1264 return load("asfloat", 1);
1265 case DataType::kI32:
1266 return load("asint", 1);
1267 case DataType::kVec2U32:
1268 return load(nullptr, 2);
1269 case DataType::kVec2F32:
1270 return load("asfloat", 2);
1271 case DataType::kVec2I32:
1272 return load("asint", 2);
1273 case DataType::kVec3U32:
1274 return load(nullptr, 3);
1275 case DataType::kVec3F32:
1276 return load("asfloat", 3);
1277 case DataType::kVec3I32:
1278 return load("asint", 3);
1279 case DataType::kVec4U32:
1280 return load(nullptr, 4);
1281 case DataType::kVec4F32:
1282 return load("asfloat", 4);
1283 case DataType::kVec4I32:
1284 return load("asint", 4);
1285 }
1286 TINT_UNREACHABLE(Writer, diagnostics_)
1287 << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1288 << static_cast<int>(intrinsic->type);
1289 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001290 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001291
1292 case Op::kStore: {
1293 auto store = [&](int n) {
1294 if (!EmitExpression(out, args[0])) { // buffer
1295 return false;
1296 }
1297 out << ".Store";
1298 if (n > 1) {
1299 out << n;
1300 }
1301 ScopedParen sp1(out);
1302 if (!EmitExpression(out, args[1])) { // offset
1303 return false;
1304 }
1305 out << ", asuint";
1306 ScopedParen sp2(out);
1307 if (!EmitExpression(out, args[2])) { // value
1308 return false;
1309 }
1310 return true;
1311 };
1312 switch (intrinsic->type) {
1313 case DataType::kU32:
1314 return store(1);
1315 case DataType::kF32:
1316 return store(1);
1317 case DataType::kI32:
1318 return store(1);
1319 case DataType::kVec2U32:
1320 return store(2);
1321 case DataType::kVec2F32:
1322 return store(2);
1323 case DataType::kVec2I32:
1324 return store(2);
1325 case DataType::kVec3U32:
1326 return store(3);
1327 case DataType::kVec3F32:
1328 return store(3);
1329 case DataType::kVec3I32:
1330 return store(3);
1331 case DataType::kVec4U32:
1332 return store(4);
1333 case DataType::kVec4F32:
1334 return store(4);
1335 case DataType::kVec4I32:
1336 return store(4);
1337 }
1338 TINT_UNREACHABLE(Writer, diagnostics_)
1339 << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1340 << static_cast<int>(intrinsic->type);
1341 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001342 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001343 default:
1344 // Break out to error case below/
1345 // Note that atomic intrinsics are generated as functions.
1346 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001347 }
1348
dan sinclair41e4d9a2022-05-01 14:40:55 +00001349 TINT_UNREACHABLE(Writer, diagnostics_)
1350 << "unsupported DecomposeMemoryAccess::Intrinsic::Op: " << static_cast<int>(intrinsic->op);
1351 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001352}
1353
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001354bool GeneratorImpl::EmitStorageAtomicIntrinsic(
1355 const ast::Function* func,
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001356 const transform::DecomposeMemoryAccess::Intrinsic* intrinsic) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001357 using Op = transform::DecomposeMemoryAccess::Intrinsic::Op;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001358
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001359 const sem::Function* sem_func = builder_.Sem().Get(func);
1360 auto* result_ty = sem_func->ReturnType();
1361 const auto& params = sem_func->Parameters();
1362 const auto name = builder_.Symbols().NameFor(func->symbol);
1363 auto& buf = *current_buffer_;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001364
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001365 auto rmw = [&](const char* hlsl) -> bool {
1366 {
1367 auto fn = line(&buf);
Ben Claytond2e0db32022-10-12 18:49:15 +00001368 if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001369 name)) {
1370 return false;
1371 }
1372 fn << "(RWByteAddressBuffer buffer, uint offset, ";
Ben Claytond2e0db32022-10-12 18:49:15 +00001373 if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001374 "value")) {
1375 return false;
1376 }
1377 fn << ") {";
1378 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001379
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001380 buf.IncrementIndent();
1381 TINT_DEFER({
1382 buf.DecrementIndent();
1383 line(&buf) << "}";
1384 line(&buf);
1385 });
1386
1387 {
1388 auto l = line(&buf);
Ben Claytond2e0db32022-10-12 18:49:15 +00001389 if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001390 "original_value")) {
1391 return false;
1392 }
1393 l << " = 0;";
1394 }
1395 {
1396 auto l = line(&buf);
1397 l << "buffer." << hlsl << "(offset, ";
1398 if (intrinsic->op == Op::kAtomicSub) {
1399 l << "-";
1400 }
1401 l << "value, original_value);";
1402 }
1403 line(&buf) << "return original_value;";
1404 return true;
1405 };
1406
1407 switch (intrinsic->op) {
1408 case Op::kAtomicAdd:
1409 return rmw("InterlockedAdd");
1410
1411 case Op::kAtomicSub:
1412 // Use add with the operand negated.
1413 return rmw("InterlockedAdd");
1414
1415 case Op::kAtomicMax:
1416 return rmw("InterlockedMax");
1417
1418 case Op::kAtomicMin:
1419 return rmw("InterlockedMin");
1420
1421 case Op::kAtomicAnd:
1422 return rmw("InterlockedAnd");
1423
1424 case Op::kAtomicOr:
1425 return rmw("InterlockedOr");
1426
1427 case Op::kAtomicXor:
1428 return rmw("InterlockedXor");
1429
1430 case Op::kAtomicExchange:
1431 return rmw("InterlockedExchange");
1432
1433 case Op::kAtomicLoad: {
1434 // HLSL does not have an InterlockedLoad, so we emulate it with
1435 // InterlockedOr using 0 as the OR value
dan sinclair41e4d9a2022-05-01 14:40:55 +00001436 {
1437 auto fn = line(&buf);
Ben Claytond2e0db32022-10-12 18:49:15 +00001438 if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone,
1439 ast::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001440 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001441 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001442 fn << "(RWByteAddressBuffer buffer, uint offset) {";
1443 }
1444
1445 buf.IncrementIndent();
1446 TINT_DEFER({
1447 buf.DecrementIndent();
1448 line(&buf) << "}";
1449 line(&buf);
1450 });
1451
1452 {
1453 auto l = line(&buf);
Ben Claytond2e0db32022-10-12 18:49:15 +00001454 if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone,
1455 ast::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001456 return false;
1457 }
1458 l << " = 0;";
1459 }
1460
1461 line(&buf) << "buffer.InterlockedOr(offset, 0, value);";
1462 line(&buf) << "return value;";
1463 return true;
1464 }
1465 case Op::kAtomicStore: {
1466 // HLSL does not have an InterlockedStore, so we emulate it with
1467 // InterlockedExchange and discard the returned value
1468 auto* value_ty = params[2]->Type()->UnwrapRef();
1469 {
1470 auto fn = line(&buf);
1471 fn << "void " << name << "(RWByteAddressBuffer buffer, uint offset, ";
Ben Claytond2e0db32022-10-12 18:49:15 +00001472 if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone,
1473 ast::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001474 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001475 }
1476 fn << ") {";
1477 }
1478
1479 buf.IncrementIndent();
1480 TINT_DEFER({
1481 buf.DecrementIndent();
1482 line(&buf) << "}";
1483 line(&buf);
1484 });
1485
1486 {
1487 auto l = line(&buf);
Ben Claytond2e0db32022-10-12 18:49:15 +00001488 if (!EmitTypeAndName(l, value_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001489 "ignored")) {
1490 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001491 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001492 l << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001493 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001494 line(&buf) << "buffer.InterlockedExchange(offset, value, ignored);";
1495 return true;
1496 }
1497 case Op::kAtomicCompareExchangeWeak: {
1498 // NOTE: We don't need to emit the return type struct here as DecomposeMemoryAccess
1499 // already added it to the AST, and it should have already been emitted by now.
1500 auto* value_ty = params[2]->Type()->UnwrapRef();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001501 {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001502 auto fn = line(&buf);
Ben Claytond2e0db32022-10-12 18:49:15 +00001503 if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone,
1504 ast::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001505 return false;
1506 }
1507 fn << "(RWByteAddressBuffer buffer, uint offset, ";
Ben Claytond2e0db32022-10-12 18:49:15 +00001508 if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone,
1509 ast::Access::kUndefined, "compare")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001510 return false;
1511 }
1512 fn << ", ";
Ben Claytond2e0db32022-10-12 18:49:15 +00001513 if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone,
1514 ast::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001515 return false;
1516 }
1517 fn << ") {";
1518 }
1519
1520 buf.IncrementIndent();
1521 TINT_DEFER({
1522 buf.DecrementIndent();
1523 line(&buf) << "}";
1524 line(&buf);
1525 });
1526
1527 { // T result = {0};
dan sinclair41e4d9a2022-05-01 14:40:55 +00001528 auto l = line(&buf);
Ben Claytond2e0db32022-10-12 18:49:15 +00001529 if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone,
1530 ast::Access::kUndefined, "result")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001531 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001532 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001533 l << "=";
1534 if (!EmitZeroValue(l, result_ty)) {
1535 return false;
1536 }
1537 l << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001538 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001539
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001540 line(&buf) << "buffer.InterlockedCompareExchange(offset, compare, value, "
1541 "result.old_value);";
1542 line(&buf) << "result.exchanged = result.old_value == compare;";
1543 line(&buf) << "return result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001544
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001545 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001546 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001547 default:
1548 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001549 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001550
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001551 TINT_UNREACHABLE(Writer, diagnostics_)
1552 << "unsupported atomic DecomposeMemoryAccess::Intrinsic::Op: "
1553 << static_cast<int>(intrinsic->op);
1554 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001555}
1556
1557bool GeneratorImpl::EmitWorkgroupAtomicCall(std::ostream& out,
1558 const ast::CallExpression* expr,
1559 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001560 std::string result = UniqueIdentifier("atomic_result");
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001561
dan sinclair41e4d9a2022-05-01 14:40:55 +00001562 if (!builtin->ReturnType()->Is<sem::Void>()) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001563 auto pre = line();
dan sinclairff7cf212022-10-03 14:05:23 +00001564 if (!EmitTypeAndName(pre, builtin->ReturnType(), ast::AddressSpace::kNone,
Ben Claytond2e0db32022-10-12 18:49:15 +00001565 ast::Access::kUndefined, result)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001566 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001567 }
1568 pre << " = ";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001569 if (!EmitZeroValue(pre, builtin->ReturnType())) {
1570 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001571 }
1572 pre << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001573 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001574
dan sinclair41e4d9a2022-05-01 14:40:55 +00001575 auto call = [&](const char* name) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001576 auto pre = line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001577 pre << name;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001578
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001579 {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001580 ScopedParen sp(pre);
Ben Clayton783b1692022-08-02 17:03:35 +00001581 for (size_t i = 0; i < expr->args.Length(); i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001582 auto* arg = expr->args[i];
1583 if (i > 0) {
1584 pre << ", ";
1585 }
1586 if (i == 1 && builtin->Type() == sem::BuiltinType::kAtomicSub) {
1587 // Sub uses InterlockedAdd with the operand negated.
1588 pre << "-";
1589 }
1590 if (!EmitExpression(pre, arg)) {
1591 return false;
1592 }
1593 }
1594
1595 pre << ", " << result;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001596 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001597
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001598 pre << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001599
dan sinclair41e4d9a2022-05-01 14:40:55 +00001600 out << result;
1601 return true;
1602 };
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001603
dan sinclair41e4d9a2022-05-01 14:40:55 +00001604 switch (builtin->Type()) {
1605 case sem::BuiltinType::kAtomicLoad: {
1606 // HLSL does not have an InterlockedLoad, so we emulate it with
1607 // InterlockedOr using 0 as the OR value
1608 auto pre = line();
1609 pre << "InterlockedOr";
1610 {
1611 ScopedParen sp(pre);
1612 if (!EmitExpression(pre, expr->args[0])) {
1613 return false;
1614 }
1615 pre << ", 0, " << result;
1616 }
1617 pre << ";";
1618
1619 out << result;
1620 return true;
1621 }
1622 case sem::BuiltinType::kAtomicStore: {
1623 // HLSL does not have an InterlockedStore, so we emulate it with
1624 // InterlockedExchange and discard the returned value
1625 { // T result = 0;
1626 auto pre = line();
1627 auto* value_ty = builtin->Parameters()[1]->Type()->UnwrapRef();
Ben Claytond2e0db32022-10-12 18:49:15 +00001628 if (!EmitTypeAndName(pre, value_ty, ast::AddressSpace::kNone,
1629 ast::Access::kUndefined, result)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001630 return false;
1631 }
1632 pre << " = ";
1633 if (!EmitZeroValue(pre, value_ty)) {
1634 return false;
1635 }
1636 pre << ";";
1637 }
1638
1639 out << "InterlockedExchange";
1640 {
1641 ScopedParen sp(out);
1642 if (!EmitExpression(out, expr->args[0])) {
1643 return false;
1644 }
1645 out << ", ";
1646 if (!EmitExpression(out, expr->args[1])) {
1647 return false;
1648 }
1649 out << ", " << result;
1650 }
1651 return true;
1652 }
1653 case sem::BuiltinType::kAtomicCompareExchangeWeak: {
Ben Clayton329dfd72022-11-23 00:05:05 +00001654 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<sem::Struct>())) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001655 return false;
1656 }
1657
dan sinclair41e4d9a2022-05-01 14:40:55 +00001658 auto* dest = expr->args[0];
1659 auto* compare_value = expr->args[1];
1660 auto* value = expr->args[2];
1661
1662 std::string compare = UniqueIdentifier("atomic_compare_value");
1663
1664 { // T compare_value = <compare_value>;
1665 auto pre = line();
Antonio Maioranof99671b2022-06-23 13:14:54 +00001666 if (!EmitTypeAndName(pre, TypeOf(compare_value)->UnwrapRef(),
Ben Claytond2e0db32022-10-12 18:49:15 +00001667 ast::AddressSpace::kNone, ast::Access::kUndefined, compare)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001668 return false;
1669 }
1670 pre << " = ";
1671 if (!EmitExpression(pre, compare_value)) {
1672 return false;
1673 }
1674 pre << ";";
1675 }
1676
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001677 { // InterlockedCompareExchange(dst, compare, value, result.old_value);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001678 auto pre = line();
1679 pre << "InterlockedCompareExchange";
1680 {
1681 ScopedParen sp(pre);
1682 if (!EmitExpression(pre, dest)) {
1683 return false;
1684 }
1685 pre << ", " << compare << ", ";
1686 if (!EmitExpression(pre, value)) {
1687 return false;
1688 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001689 pre << ", " << result << ".old_value";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001690 }
1691 pre << ";";
1692 }
1693
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001694 // result.exchanged = result.old_value == compare;
1695 line() << result << ".exchanged = " << result << ".old_value == " << compare << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001696
1697 out << result;
1698 return true;
1699 }
1700
1701 case sem::BuiltinType::kAtomicAdd:
1702 case sem::BuiltinType::kAtomicSub:
1703 return call("InterlockedAdd");
1704
1705 case sem::BuiltinType::kAtomicMax:
1706 return call("InterlockedMax");
1707
1708 case sem::BuiltinType::kAtomicMin:
1709 return call("InterlockedMin");
1710
1711 case sem::BuiltinType::kAtomicAnd:
1712 return call("InterlockedAnd");
1713
1714 case sem::BuiltinType::kAtomicOr:
1715 return call("InterlockedOr");
1716
1717 case sem::BuiltinType::kAtomicXor:
1718 return call("InterlockedXor");
1719
1720 case sem::BuiltinType::kAtomicExchange:
1721 return call("InterlockedExchange");
1722
1723 default:
1724 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001725 }
1726
dan sinclair41e4d9a2022-05-01 14:40:55 +00001727 TINT_UNREACHABLE(Writer, diagnostics_) << "unsupported atomic builtin: " << builtin->Type();
1728 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001729}
1730
dan sinclair41e4d9a2022-05-01 14:40:55 +00001731bool GeneratorImpl::EmitSelectCall(std::ostream& out, const ast::CallExpression* expr) {
1732 auto* expr_false = expr->args[0];
1733 auto* expr_true = expr->args[1];
1734 auto* expr_cond = expr->args[2];
1735 ScopedParen paren(out);
1736 if (!EmitExpression(out, expr_cond)) {
1737 return false;
1738 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001739
dan sinclair41e4d9a2022-05-01 14:40:55 +00001740 out << " ? ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001741
dan sinclair41e4d9a2022-05-01 14:40:55 +00001742 if (!EmitExpression(out, expr_true)) {
1743 return false;
1744 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001745
dan sinclair41e4d9a2022-05-01 14:40:55 +00001746 out << " : ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001747
dan sinclair41e4d9a2022-05-01 14:40:55 +00001748 if (!EmitExpression(out, expr_false)) {
1749 return false;
1750 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001751
dan sinclair41e4d9a2022-05-01 14:40:55 +00001752 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001753}
1754
1755bool GeneratorImpl::EmitModfCall(std::ostream& out,
1756 const ast::CallExpression* expr,
1757 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001758 return CallBuiltinHelper(
1759 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
1760 auto* ty = builtin->Parameters()[0]->Type();
1761 auto in = params[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001762
dan sinclair41e4d9a2022-05-01 14:40:55 +00001763 std::string width;
1764 if (auto* vec = ty->As<sem::Vector>()) {
1765 width = std::to_string(vec->Width());
1766 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001767
dan sinclair41e4d9a2022-05-01 14:40:55 +00001768 // Emit the builtin return type unique to this overload. This does not
1769 // exist in the AST, so it will not be generated in Generate().
1770 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<sem::Struct>())) {
1771 return false;
1772 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001773
dan sinclair41e4d9a2022-05-01 14:40:55 +00001774 {
1775 auto l = line(b);
dan sinclairff7cf212022-10-03 14:05:23 +00001776 if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
Ben Claytond2e0db32022-10-12 18:49:15 +00001777 ast::Access::kUndefined, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001778 return false;
1779 }
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00001780 l << " result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001781 }
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00001782 line(b) << "result.fract = modf(" << params[0] << ", result.whole);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001783 line(b) << "return result;";
1784 return true;
1785 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001786}
1787
1788bool GeneratorImpl::EmitFrexpCall(std::ostream& out,
1789 const ast::CallExpression* expr,
1790 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001791 return CallBuiltinHelper(
1792 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
1793 auto* ty = builtin->Parameters()[0]->Type();
1794 auto in = params[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001795
dan sinclair41e4d9a2022-05-01 14:40:55 +00001796 std::string width;
1797 if (auto* vec = ty->As<sem::Vector>()) {
1798 width = std::to_string(vec->Width());
1799 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001800
dan sinclair41e4d9a2022-05-01 14:40:55 +00001801 // Emit the builtin return type unique to this overload. This does not
1802 // exist in the AST, so it will not be generated in Generate().
1803 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<sem::Struct>())) {
1804 return false;
1805 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001806
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00001807 std::string member_type;
1808 if (Is<sem::F16>(sem::Type::DeepestElementOf(ty))) {
1809 member_type = width.empty() ? "float16_t" : ("vector<float16_t, " + width + ">");
1810 } else {
1811 member_type = "float" + width;
1812 }
1813
1814 line(b) << member_type << " exp;";
Ben Clayton10fae7a2022-11-14 15:29:29 +00001815 line(b) << member_type << " fract = frexp(" << in << ", exp);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001816 {
1817 auto l = line(b);
dan sinclairff7cf212022-10-03 14:05:23 +00001818 if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
Ben Claytond2e0db32022-10-12 18:49:15 +00001819 ast::Access::kUndefined, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001820 return false;
1821 }
Ben Clayton10fae7a2022-11-14 15:29:29 +00001822 l << " result = {fract, int" << width << "(exp)};";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001823 }
1824 line(b) << "return result;";
1825 return true;
1826 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001827}
1828
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001829bool GeneratorImpl::EmitDegreesCall(std::ostream& out,
1830 const ast::CallExpression* expr,
1831 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001832 return CallBuiltinHelper(out, expr, builtin,
1833 [&](TextBuffer* b, const std::vector<std::string>& params) {
1834 line(b) << "return " << params[0] << " * " << std::setprecision(20)
1835 << sem::kRadToDeg << ";";
1836 return true;
1837 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001838}
1839
1840bool GeneratorImpl::EmitRadiansCall(std::ostream& out,
1841 const ast::CallExpression* expr,
1842 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001843 return CallBuiltinHelper(out, expr, builtin,
1844 [&](TextBuffer* b, const std::vector<std::string>& params) {
1845 line(b) << "return " << params[0] << " * " << std::setprecision(20)
1846 << sem::kDegToRad << ";";
1847 return true;
1848 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001849}
1850
Ben Clayton2bea9052022-11-02 00:09:50 +00001851bool GeneratorImpl::EmitQuantizeToF16Call(std::ostream& out,
1852 const ast::CallExpression* expr,
1853 const sem::Builtin* builtin) {
1854 // Emulate by casting to min16float and back again.
1855 std::string width;
1856 if (auto* vec = builtin->ReturnType()->As<sem::Vector>()) {
1857 width = std::to_string(vec->Width());
1858 }
1859 out << "float" << width << "(min16float" << width << "(";
1860 if (!EmitExpression(out, expr->args[0])) {
1861 return false;
1862 }
1863 out << "))";
1864 return true;
1865}
1866
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001867bool GeneratorImpl::EmitDataPackingCall(std::ostream& out,
1868 const ast::CallExpression* expr,
1869 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001870 return CallBuiltinHelper(
1871 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
1872 uint32_t dims = 2;
1873 bool is_signed = false;
1874 uint32_t scale = 65535;
Ben Clayton73683022022-10-06 19:23:29 +00001875 if (builtin->Type() == sem::BuiltinType::kPack4X8Snorm ||
1876 builtin->Type() == sem::BuiltinType::kPack4X8Unorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001877 dims = 4;
1878 scale = 255;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001879 }
Ben Clayton73683022022-10-06 19:23:29 +00001880 if (builtin->Type() == sem::BuiltinType::kPack4X8Snorm ||
1881 builtin->Type() == sem::BuiltinType::kPack2X16Snorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001882 is_signed = true;
1883 scale = (scale - 1) / 2;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001884 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001885 switch (builtin->Type()) {
Ben Clayton73683022022-10-06 19:23:29 +00001886 case sem::BuiltinType::kPack4X8Snorm:
1887 case sem::BuiltinType::kPack4X8Unorm:
1888 case sem::BuiltinType::kPack2X16Snorm:
1889 case sem::BuiltinType::kPack2X16Unorm: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001890 {
1891 auto l = line(b);
1892 l << (is_signed ? "" : "u") << "int" << dims
1893 << " i = " << (is_signed ? "" : "u") << "int" << dims << "(round(clamp("
1894 << params[0] << ", " << (is_signed ? "-1.0" : "0.0") << ", 1.0) * "
1895 << scale << ".0))";
1896 if (is_signed) {
1897 l << " & " << (dims == 4 ? "0xff" : "0xffff");
1898 }
1899 l << ";";
1900 }
1901 {
1902 auto l = line(b);
1903 l << "return ";
1904 if (is_signed) {
1905 l << "asuint";
1906 }
1907 l << "(i.x | i.y << " << (32 / dims);
1908 if (dims == 4) {
1909 l << " | i.z << 16 | i.w << 24";
1910 }
1911 l << ");";
1912 }
1913 break;
1914 }
Ben Clayton73683022022-10-06 19:23:29 +00001915 case sem::BuiltinType::kPack2X16Float: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001916 line(b) << "uint2 i = f32tof16(" << params[0] << ");";
1917 line(b) << "return i.x | (i.y << 16);";
1918 break;
1919 }
1920 default:
1921 diagnostics_.add_error(diag::System::Writer,
1922 "Internal error: unhandled data packing builtin");
1923 return false;
1924 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001925
dan sinclair41e4d9a2022-05-01 14:40:55 +00001926 return true;
1927 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001928}
1929
1930bool GeneratorImpl::EmitDataUnpackingCall(std::ostream& out,
1931 const ast::CallExpression* expr,
1932 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001933 return CallBuiltinHelper(
1934 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
1935 uint32_t dims = 2;
1936 bool is_signed = false;
1937 uint32_t scale = 65535;
Ben Clayton73683022022-10-06 19:23:29 +00001938 if (builtin->Type() == sem::BuiltinType::kUnpack4X8Snorm ||
1939 builtin->Type() == sem::BuiltinType::kUnpack4X8Unorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001940 dims = 4;
1941 scale = 255;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001942 }
Ben Clayton73683022022-10-06 19:23:29 +00001943 if (builtin->Type() == sem::BuiltinType::kUnpack4X8Snorm ||
1944 builtin->Type() == sem::BuiltinType::kUnpack2X16Snorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001945 is_signed = true;
1946 scale = (scale - 1) / 2;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001947 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001948 switch (builtin->Type()) {
Ben Clayton73683022022-10-06 19:23:29 +00001949 case sem::BuiltinType::kUnpack4X8Snorm:
1950 case sem::BuiltinType::kUnpack2X16Snorm: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001951 line(b) << "int j = int(" << params[0] << ");";
1952 { // Perform sign extension on the converted values.
1953 auto l = line(b);
1954 l << "int" << dims << " i = int" << dims << "(";
1955 if (dims == 2) {
1956 l << "j << 16, j) >> 16";
1957 } else {
1958 l << "j << 24, j << 16, j << 8, j) >> 24";
1959 }
1960 l << ";";
1961 }
1962 line(b) << "return clamp(float" << dims << "(i) / " << scale << ".0, "
1963 << (is_signed ? "-1.0" : "0.0") << ", 1.0);";
1964 break;
1965 }
Ben Clayton73683022022-10-06 19:23:29 +00001966 case sem::BuiltinType::kUnpack4X8Unorm:
1967 case sem::BuiltinType::kUnpack2X16Unorm: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001968 line(b) << "uint j = " << params[0] << ";";
1969 {
1970 auto l = line(b);
1971 l << "uint" << dims << " i = uint" << dims << "(";
1972 l << "j & " << (dims == 2 ? "0xffff" : "0xff") << ", ";
1973 if (dims == 4) {
1974 l << "(j >> " << (32 / dims) << ") & 0xff, (j >> 16) & 0xff, j >> 24";
1975 } else {
1976 l << "j >> " << (32 / dims);
1977 }
1978 l << ");";
1979 }
1980 line(b) << "return float" << dims << "(i) / " << scale << ".0;";
1981 break;
1982 }
Ben Clayton73683022022-10-06 19:23:29 +00001983 case sem::BuiltinType::kUnpack2X16Float:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001984 line(b) << "uint i = " << params[0] << ";";
1985 line(b) << "return f16tof32(uint2(i & 0xffff, i >> 16));";
1986 break;
1987 default:
1988 diagnostics_.add_error(diag::System::Writer,
1989 "Internal error: unhandled data packing builtin");
1990 return false;
1991 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001992
dan sinclair41e4d9a2022-05-01 14:40:55 +00001993 return true;
1994 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001995}
1996
Jiawei Shaoab975702022-05-13 00:09:56 +00001997bool GeneratorImpl::EmitDP4aCall(std::ostream& out,
1998 const ast::CallExpression* expr,
1999 const sem::Builtin* builtin) {
2000 // TODO(crbug.com/tint/1497): support the polyfill version of DP4a functions.
2001 return CallBuiltinHelper(
2002 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2003 std::string functionName;
2004 switch (builtin->Type()) {
2005 case sem::BuiltinType::kDot4I8Packed:
Jiawei Shao1c759212022-05-15 13:53:21 +00002006 line(b) << "int accumulator = 0;";
Jiawei Shaoab975702022-05-13 00:09:56 +00002007 functionName = "dot4add_i8packed";
2008 break;
2009 case sem::BuiltinType::kDot4U8Packed:
Jiawei Shao1c759212022-05-15 13:53:21 +00002010 line(b) << "uint accumulator = 0u;";
Jiawei Shaoab975702022-05-13 00:09:56 +00002011 functionName = "dot4add_u8packed";
2012 break;
2013 default:
2014 diagnostics_.add_error(diag::System::Writer,
2015 "Internal error: unhandled DP4a builtin");
2016 return false;
2017 }
2018 line(b) << "return " << functionName << "(" << params[0] << ", " << params[1]
Jiawei Shao1c759212022-05-15 13:53:21 +00002019 << ", accumulator);";
Jiawei Shaoab975702022-05-13 00:09:56 +00002020
2021 return true;
2022 });
2023}
2024
dan sinclair41e4d9a2022-05-01 14:40:55 +00002025bool GeneratorImpl::EmitBarrierCall(std::ostream& out, const sem::Builtin* builtin) {
2026 // TODO(crbug.com/tint/661): Combine sequential barriers to a single
2027 // instruction.
2028 if (builtin->Type() == sem::BuiltinType::kWorkgroupBarrier) {
2029 out << "GroupMemoryBarrierWithGroupSync()";
2030 } else if (builtin->Type() == sem::BuiltinType::kStorageBarrier) {
2031 out << "DeviceMemoryBarrierWithGroupSync()";
2032 } else {
2033 TINT_UNREACHABLE(Writer, diagnostics_)
2034 << "unexpected barrier builtin type " << sem::str(builtin->Type());
2035 return false;
2036 }
2037 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002038}
2039
2040bool GeneratorImpl::EmitTextureCall(std::ostream& out,
2041 const sem::Call* call,
2042 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002043 using Usage = sem::ParameterUsage;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002044
dan sinclair41e4d9a2022-05-01 14:40:55 +00002045 auto& signature = builtin->Signature();
2046 auto* expr = call->Declaration();
2047 auto arguments = expr->args;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002048
dan sinclair41e4d9a2022-05-01 14:40:55 +00002049 // Returns the argument with the given usage
2050 auto arg = [&](Usage usage) {
2051 int idx = signature.IndexOf(usage);
dan sinclair3a2a2792022-06-29 14:38:15 +00002052 return (idx >= 0) ? arguments[static_cast<size_t>(idx)] : nullptr;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002053 };
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002054
dan sinclair41e4d9a2022-05-01 14:40:55 +00002055 auto* texture = arg(Usage::kTexture);
2056 if (!texture) {
2057 TINT_ICE(Writer, diagnostics_) << "missing texture argument";
2058 return false;
2059 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002060
dan sinclair41e4d9a2022-05-01 14:40:55 +00002061 auto* texture_type = TypeOf(texture)->UnwrapRef()->As<sem::Texture>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002062
dan sinclair41e4d9a2022-05-01 14:40:55 +00002063 switch (builtin->Type()) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002064 case sem::BuiltinType::kTextureDimensions:
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002065 case sem::BuiltinType::kTextureNumLayers:
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002066 case sem::BuiltinType::kTextureNumLevels:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002067 case sem::BuiltinType::kTextureNumSamples: {
2068 // All of these builtins use the GetDimensions() method on the texture
2069 bool is_ms =
2070 texture_type->IsAnyOf<sem::MultisampledTexture, sem::DepthMultisampledTexture>();
2071 int num_dimensions = 0;
2072 std::string swizzle;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002073
dan sinclair41e4d9a2022-05-01 14:40:55 +00002074 switch (builtin->Type()) {
2075 case sem::BuiltinType::kTextureDimensions:
2076 switch (texture_type->dim()) {
2077 case ast::TextureDimension::kNone:
2078 TINT_ICE(Writer, diagnostics_) << "texture dimension is kNone";
2079 return false;
2080 case ast::TextureDimension::k1d:
2081 num_dimensions = 1;
2082 break;
2083 case ast::TextureDimension::k2d:
2084 num_dimensions = is_ms ? 3 : 2;
2085 swizzle = is_ms ? ".xy" : "";
2086 break;
2087 case ast::TextureDimension::k2dArray:
2088 num_dimensions = is_ms ? 4 : 3;
2089 swizzle = ".xy";
2090 break;
2091 case ast::TextureDimension::k3d:
2092 num_dimensions = 3;
2093 break;
2094 case ast::TextureDimension::kCube:
2095 num_dimensions = 2;
2096 break;
2097 case ast::TextureDimension::kCubeArray:
2098 num_dimensions = 3;
2099 swizzle = ".xy";
2100 break;
2101 }
2102 break;
2103 case sem::BuiltinType::kTextureNumLayers:
2104 switch (texture_type->dim()) {
2105 default:
2106 TINT_ICE(Writer, diagnostics_) << "texture dimension is not arrayed";
2107 return false;
2108 case ast::TextureDimension::k2dArray:
2109 num_dimensions = is_ms ? 4 : 3;
2110 swizzle = ".z";
2111 break;
2112 case ast::TextureDimension::kCubeArray:
2113 num_dimensions = 3;
2114 swizzle = ".z";
2115 break;
2116 }
2117 break;
2118 case sem::BuiltinType::kTextureNumLevels:
2119 switch (texture_type->dim()) {
2120 default:
2121 TINT_ICE(Writer, diagnostics_)
2122 << "texture dimension does not support mips";
2123 return false;
2124 case ast::TextureDimension::k1d:
2125 num_dimensions = 2;
2126 swizzle = ".y";
2127 break;
2128 case ast::TextureDimension::k2d:
2129 case ast::TextureDimension::kCube:
2130 num_dimensions = 3;
2131 swizzle = ".z";
2132 break;
2133 case ast::TextureDimension::k2dArray:
2134 case ast::TextureDimension::k3d:
2135 case ast::TextureDimension::kCubeArray:
2136 num_dimensions = 4;
2137 swizzle = ".w";
2138 break;
2139 }
2140 break;
2141 case sem::BuiltinType::kTextureNumSamples:
2142 switch (texture_type->dim()) {
2143 default:
2144 TINT_ICE(Writer, diagnostics_)
2145 << "texture dimension does not support multisampling";
2146 return false;
2147 case ast::TextureDimension::k2d:
2148 num_dimensions = 3;
2149 swizzle = ".z";
2150 break;
2151 case ast::TextureDimension::k2dArray:
2152 num_dimensions = 4;
2153 swizzle = ".w";
2154 break;
2155 }
2156 break;
2157 default:
2158 TINT_ICE(Writer, diagnostics_) << "unexpected builtin";
2159 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002160 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002161
2162 auto* level_arg = arg(Usage::kLevel);
2163
2164 if (level_arg) {
2165 // `NumberOfLevels` is a non-optional argument if `MipLevel` was passed.
2166 // Increment the number of dimensions for the temporary vector to
2167 // accommodate this.
2168 num_dimensions++;
2169
2170 // If the swizzle was empty, the expression will evaluate to the whole
2171 // vector. As we've grown the vector by one element, we now need to
2172 // swizzle to keep the result expression equivalent.
2173 if (swizzle.empty()) {
2174 static constexpr const char* swizzles[] = {"", ".x", ".xy", ".xyz"};
2175 swizzle = swizzles[num_dimensions - 1];
2176 }
2177 }
2178
2179 if (num_dimensions > 4) {
2180 TINT_ICE(Writer, diagnostics_) << "Texture query builtin temporary vector has "
2181 << num_dimensions << " dimensions";
2182 return false;
2183 }
2184
2185 // Declare a variable to hold the queried texture info
2186 auto dims = UniqueIdentifier(kTempNamePrefix);
2187 if (num_dimensions == 1) {
2188 line() << "int " << dims << ";";
2189 } else {
2190 line() << "int" << num_dimensions << " " << dims << ";";
2191 }
2192
2193 { // texture.GetDimensions(...)
2194 auto pre = line();
2195 if (!EmitExpression(pre, texture)) {
2196 return false;
2197 }
2198 pre << ".GetDimensions(";
2199
2200 if (level_arg) {
2201 if (!EmitExpression(pre, level_arg)) {
2202 return false;
2203 }
2204 pre << ", ";
2205 } else if (builtin->Type() == sem::BuiltinType::kTextureNumLevels) {
2206 pre << "0, ";
2207 }
2208
2209 if (num_dimensions == 1) {
2210 pre << dims;
2211 } else {
2212 static constexpr char xyzw[] = {'x', 'y', 'z', 'w'};
2213 if (num_dimensions < 0 || num_dimensions > 4) {
2214 TINT_ICE(Writer, diagnostics_)
2215 << "vector dimensions are " << num_dimensions;
2216 return false;
2217 }
2218 for (int i = 0; i < num_dimensions; i++) {
2219 if (i > 0) {
2220 pre << ", ";
2221 }
2222 pre << dims << "." << xyzw[i];
2223 }
2224 }
2225
2226 pre << ");";
2227 }
2228
2229 // The out parameters of the GetDimensions() call is now in temporary
2230 // `dims` variable. This may be packed with other data, so the final
2231 // expression may require a swizzle.
2232 out << dims << swizzle;
2233 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002234 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002235 default:
2236 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002237 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002238
Austin Eng86a617f2022-05-19 20:08:19 +00002239 if (!EmitExpression(out, texture)) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002240 return false;
Austin Eng86a617f2022-05-19 20:08:19 +00002241 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002242
2243 // If pack_level_in_coords is true, then the mip level will be appended as the
2244 // last value of the coordinates argument. If the WGSL builtin overload does
2245 // not have a level parameter and pack_level_in_coords is true, then a zero
2246 // mip level will be inserted.
2247 bool pack_level_in_coords = false;
2248
2249 uint32_t hlsl_ret_width = 4u;
2250
2251 switch (builtin->Type()) {
2252 case sem::BuiltinType::kTextureSample:
2253 out << ".Sample(";
2254 break;
2255 case sem::BuiltinType::kTextureSampleBias:
2256 out << ".SampleBias(";
2257 break;
2258 case sem::BuiltinType::kTextureSampleLevel:
2259 out << ".SampleLevel(";
2260 break;
2261 case sem::BuiltinType::kTextureSampleGrad:
2262 out << ".SampleGrad(";
2263 break;
2264 case sem::BuiltinType::kTextureSampleCompare:
2265 out << ".SampleCmp(";
2266 hlsl_ret_width = 1;
2267 break;
2268 case sem::BuiltinType::kTextureSampleCompareLevel:
2269 out << ".SampleCmpLevelZero(";
2270 hlsl_ret_width = 1;
2271 break;
2272 case sem::BuiltinType::kTextureLoad:
2273 out << ".Load(";
2274 // Multisampled textures do not support mip-levels.
2275 if (!texture_type->Is<sem::MultisampledTexture>()) {
2276 pack_level_in_coords = true;
2277 }
2278 break;
2279 case sem::BuiltinType::kTextureGather:
2280 out << ".Gather";
2281 if (builtin->Parameters()[0]->Usage() == sem::ParameterUsage::kComponent) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00002282 switch (call->Arguments()[0]->ConstantValue()->As<AInt>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002283 case 0:
2284 out << "Red";
2285 break;
2286 case 1:
2287 out << "Green";
2288 break;
2289 case 2:
2290 out << "Blue";
2291 break;
2292 case 3:
2293 out << "Alpha";
2294 break;
2295 }
2296 }
2297 out << "(";
2298 break;
2299 case sem::BuiltinType::kTextureGatherCompare:
2300 out << ".GatherCmp(";
2301 break;
2302 case sem::BuiltinType::kTextureStore:
2303 out << "[";
2304 break;
2305 default:
2306 diagnostics_.add_error(diag::System::Writer,
2307 "Internal compiler error: Unhandled texture builtin '" +
2308 std::string(builtin->str()) + "'");
2309 return false;
2310 }
2311
2312 if (auto* sampler = arg(Usage::kSampler)) {
Austin Eng86a617f2022-05-19 20:08:19 +00002313 if (!EmitExpression(out, sampler)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002314 return false;
Austin Eng86a617f2022-05-19 20:08:19 +00002315 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002316 out << ", ";
2317 }
2318
2319 auto* param_coords = arg(Usage::kCoords);
2320 if (!param_coords) {
2321 TINT_ICE(Writer, diagnostics_) << "missing coords argument";
2322 return false;
2323 }
2324
2325 auto emit_vector_appended_with_i32_zero = [&](const ast::Expression* vector) {
2326 auto* i32 = builder_.create<sem::I32>();
Ben Clayton0ce9ab02022-05-05 20:23:40 +00002327 auto* zero = builder_.Expr(0_i);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002328 auto* stmt = builder_.Sem().Get(vector)->Stmt();
Ben Claytonaa037ac2022-06-29 19:07:30 +00002329 builder_.Sem().Add(
Ben Clayton83bd7382022-07-15 23:46:31 +00002330 zero, builder_.create<sem::Expression>(zero, i32, sem::EvaluationStage::kRuntime, stmt,
2331 /* constant_value */ nullptr,
Ben Claytonaa037ac2022-06-29 19:07:30 +00002332 /* has_side_effects */ false));
dan sinclair41e4d9a2022-05-01 14:40:55 +00002333 auto* packed = AppendVector(&builder_, vector, zero);
2334 return EmitExpression(out, packed->Declaration());
2335 };
2336
2337 auto emit_vector_appended_with_level = [&](const ast::Expression* vector) {
2338 if (auto* level = arg(Usage::kLevel)) {
2339 auto* packed = AppendVector(&builder_, vector, level);
2340 return EmitExpression(out, packed->Declaration());
2341 }
2342 return emit_vector_appended_with_i32_zero(vector);
2343 };
2344
2345 if (auto* array_index = arg(Usage::kArrayIndex)) {
2346 // Array index needs to be appended to the coordinates.
2347 auto* packed = AppendVector(&builder_, param_coords, array_index);
2348 if (pack_level_in_coords) {
2349 // Then mip level needs to be appended to the coordinates.
2350 if (!emit_vector_appended_with_level(packed->Declaration())) {
2351 return false;
2352 }
2353 } else {
2354 if (!EmitExpression(out, packed->Declaration())) {
2355 return false;
2356 }
2357 }
2358 } else if (pack_level_in_coords) {
2359 // Mip level needs to be appended to the coordinates.
2360 if (!emit_vector_appended_with_level(param_coords)) {
2361 return false;
2362 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002363 } else {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002364 if (!EmitExpression(out, param_coords)) {
2365 return false;
2366 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002367 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002368
dan sinclair41e4d9a2022-05-01 14:40:55 +00002369 for (auto usage : {Usage::kDepthRef, Usage::kBias, Usage::kLevel, Usage::kDdx, Usage::kDdy,
2370 Usage::kSampleIndex, Usage::kOffset}) {
2371 if (usage == Usage::kLevel && pack_level_in_coords) {
2372 continue; // mip level already packed in coordinates.
2373 }
2374 if (auto* e = arg(usage)) {
2375 out << ", ";
2376 if (!EmitExpression(out, e)) {
2377 return false;
2378 }
2379 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002380 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002381
dan sinclair41e4d9a2022-05-01 14:40:55 +00002382 if (builtin->Type() == sem::BuiltinType::kTextureStore) {
2383 out << "] = ";
2384 if (!EmitExpression(out, arg(Usage::kValue))) {
2385 return false;
2386 }
2387 } else {
2388 out << ")";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002389
dan sinclair41e4d9a2022-05-01 14:40:55 +00002390 // If the builtin return type does not match the number of elements of the
2391 // HLSL builtin, we need to swizzle the expression to generate the correct
2392 // number of components.
2393 uint32_t wgsl_ret_width = 1;
2394 if (auto* vec = builtin->ReturnType()->As<sem::Vector>()) {
2395 wgsl_ret_width = vec->Width();
2396 }
2397 if (wgsl_ret_width < hlsl_ret_width) {
2398 out << ".";
2399 for (uint32_t i = 0; i < wgsl_ret_width; i++) {
2400 out << "xyz"[i];
2401 }
2402 }
2403 if (wgsl_ret_width > hlsl_ret_width) {
2404 TINT_ICE(Writer, diagnostics_)
2405 << "WGSL return width (" << wgsl_ret_width << ") is wider than HLSL return width ("
2406 << hlsl_ret_width << ") for " << builtin->Type();
2407 return false;
2408 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002409 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002410
dan sinclair41e4d9a2022-05-01 14:40:55 +00002411 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002412}
2413
2414std::string GeneratorImpl::generate_builtin_name(const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002415 switch (builtin->Type()) {
2416 case sem::BuiltinType::kAbs:
2417 case sem::BuiltinType::kAcos:
2418 case sem::BuiltinType::kAll:
2419 case sem::BuiltinType::kAny:
2420 case sem::BuiltinType::kAsin:
2421 case sem::BuiltinType::kAtan:
2422 case sem::BuiltinType::kAtan2:
2423 case sem::BuiltinType::kCeil:
2424 case sem::BuiltinType::kClamp:
2425 case sem::BuiltinType::kCos:
2426 case sem::BuiltinType::kCosh:
2427 case sem::BuiltinType::kCross:
2428 case sem::BuiltinType::kDeterminant:
2429 case sem::BuiltinType::kDistance:
2430 case sem::BuiltinType::kDot:
2431 case sem::BuiltinType::kExp:
2432 case sem::BuiltinType::kExp2:
2433 case sem::BuiltinType::kFloor:
2434 case sem::BuiltinType::kFrexp:
2435 case sem::BuiltinType::kLdexp:
2436 case sem::BuiltinType::kLength:
2437 case sem::BuiltinType::kLog:
2438 case sem::BuiltinType::kLog2:
2439 case sem::BuiltinType::kMax:
2440 case sem::BuiltinType::kMin:
2441 case sem::BuiltinType::kModf:
2442 case sem::BuiltinType::kNormalize:
2443 case sem::BuiltinType::kPow:
2444 case sem::BuiltinType::kReflect:
2445 case sem::BuiltinType::kRefract:
2446 case sem::BuiltinType::kRound:
Ben Clayton751e6682022-09-13 22:57:52 +00002447 case sem::BuiltinType::kSaturate:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002448 case sem::BuiltinType::kSign:
2449 case sem::BuiltinType::kSin:
2450 case sem::BuiltinType::kSinh:
2451 case sem::BuiltinType::kSqrt:
2452 case sem::BuiltinType::kStep:
2453 case sem::BuiltinType::kTan:
2454 case sem::BuiltinType::kTanh:
2455 case sem::BuiltinType::kTranspose:
2456 case sem::BuiltinType::kTrunc:
2457 return builtin->str();
Antonio Maioranoab4c0352022-05-20 01:58:40 +00002458 case sem::BuiltinType::kCountOneBits: // uint
dan sinclair41e4d9a2022-05-01 14:40:55 +00002459 return "countbits";
2460 case sem::BuiltinType::kDpdx:
2461 return "ddx";
2462 case sem::BuiltinType::kDpdxCoarse:
2463 return "ddx_coarse";
2464 case sem::BuiltinType::kDpdxFine:
2465 return "ddx_fine";
2466 case sem::BuiltinType::kDpdy:
2467 return "ddy";
2468 case sem::BuiltinType::kDpdyCoarse:
2469 return "ddy_coarse";
2470 case sem::BuiltinType::kDpdyFine:
2471 return "ddy_fine";
2472 case sem::BuiltinType::kFaceForward:
2473 return "faceforward";
2474 case sem::BuiltinType::kFract:
2475 return "frac";
2476 case sem::BuiltinType::kFma:
2477 return "mad";
2478 case sem::BuiltinType::kFwidth:
2479 case sem::BuiltinType::kFwidthCoarse:
2480 case sem::BuiltinType::kFwidthFine:
2481 return "fwidth";
2482 case sem::BuiltinType::kInverseSqrt:
2483 return "rsqrt";
2484 case sem::BuiltinType::kMix:
2485 return "lerp";
Antonio Maioranoab4c0352022-05-20 01:58:40 +00002486 case sem::BuiltinType::kReverseBits: // uint
dan sinclair41e4d9a2022-05-01 14:40:55 +00002487 return "reversebits";
2488 case sem::BuiltinType::kSmoothstep:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002489 return "smoothstep";
2490 default:
2491 diagnostics_.add_error(diag::System::Writer,
2492 "Unknown builtin method: " + std::string(builtin->str()));
2493 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002494
dan sinclair41e4d9a2022-05-01 14:40:55 +00002495 return "";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002496}
2497
2498bool GeneratorImpl::EmitCase(const ast::SwitchStatement* s, size_t case_idx) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002499 auto* stmt = s->body[case_idx];
dan sinclairf148f082022-10-19 15:55:02 +00002500 auto* sem = builder_.Sem().Get<sem::CaseStatement>(stmt);
2501 for (auto* selector : sem->Selectors()) {
2502 auto out = line();
2503 if (selector->IsDefault()) {
2504 out << "default";
2505 } else {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002506 out << "case ";
Ben Clayton329dfd72022-11-23 00:05:05 +00002507 if (!EmitConstant(out, selector->Value(), /* is_variable_initializer */ false)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002508 return false;
2509 }
dan sinclairf148f082022-10-19 15:55:02 +00002510 }
2511 out << ":";
2512 if (selector == sem->Selectors().back()) {
2513 out << " {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002514 }
2515 }
2516
2517 increment_indent();
2518 TINT_DEFER({
2519 decrement_indent();
2520 line() << "}";
2521 });
2522
2523 // Emit the case statement
2524 if (!EmitStatements(stmt->body->statements)) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002525 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002526 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002527
dan sinclair267f1742022-11-15 00:30:33 +00002528 if (!tint::IsAnyOf<ast::BreakStatement>(stmt->body->Last())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002529 line() << "break;";
2530 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002531
dan sinclair41e4d9a2022-05-01 14:40:55 +00002532 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002533}
2534
2535bool GeneratorImpl::EmitContinue(const ast::ContinueStatement*) {
dan sinclair4b88dbc2022-06-16 15:27:38 +00002536 if (!emit_continuing_ || !emit_continuing_()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002537 return false;
2538 }
2539 line() << "continue;";
2540 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002541}
2542
2543bool GeneratorImpl::EmitDiscard(const ast::DiscardStatement*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002544 // TODO(dsinclair): Verify this is correct when the discard semantics are
2545 // defined for WGSL (https://github.com/gpuweb/gpuweb/issues/361)
2546 line() << "discard;";
2547 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002548}
2549
dan sinclair41e4d9a2022-05-01 14:40:55 +00002550bool GeneratorImpl::EmitExpression(std::ostream& out, const ast::Expression* expr) {
Ben Claytone9f8b092022-06-01 13:14:39 +00002551 if (auto* sem = builder_.Sem().Get(expr)) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00002552 if (auto* constant = sem->ConstantValue()) {
Ben Clayton329dfd72022-11-23 00:05:05 +00002553 bool is_variable_initializer = false;
2554 if (auto* stmt = sem->Stmt()) {
2555 if (auto* decl = As<ast::VariableDeclStatement>(stmt->Declaration())) {
2556 is_variable_initializer = decl->variable->initializer == expr;
2557 }
2558 }
2559 return EmitConstant(out, constant, is_variable_initializer);
Ben Claytone9f8b092022-06-01 13:14:39 +00002560 }
2561 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002562 return Switch(
Ben Claytonb90b6bf2022-08-23 16:23:05 +00002563 expr, //
2564 [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(out, a); },
2565 [&](const ast::BinaryExpression* b) { return EmitBinary(out, b); },
2566 [&](const ast::BitcastExpression* b) { return EmitBitcast(out, b); },
2567 [&](const ast::CallExpression* c) { return EmitCall(out, c); },
2568 [&](const ast::IdentifierExpression* i) { return EmitIdentifier(out, i); },
2569 [&](const ast::LiteralExpression* l) { return EmitLiteral(out, l); },
2570 [&](const ast::MemberAccessorExpression* m) { return EmitMemberAccessor(out, m); },
2571 [&](const ast::UnaryOpExpression* u) { return EmitUnaryOp(out, u); },
2572 [&](Default) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002573 diagnostics_.add_error(diag::System::Writer, "unknown expression type: " +
2574 std::string(expr->TypeInfo().name));
2575 return false;
2576 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002577}
2578
dan sinclair41e4d9a2022-05-01 14:40:55 +00002579bool GeneratorImpl::EmitIdentifier(std::ostream& out, const ast::IdentifierExpression* expr) {
2580 out << builder_.Symbols().NameFor(expr->symbol);
2581 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002582}
2583
2584bool GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002585 {
2586 auto out = line();
2587 out << "if (";
2588 if (!EmitExpression(out, stmt->condition)) {
2589 return false;
2590 }
2591 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002592 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002593
dan sinclair41e4d9a2022-05-01 14:40:55 +00002594 if (!EmitStatementsWithIndent(stmt->body->statements)) {
James Price26ebe5e2022-04-29 00:14:53 +00002595 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002596 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002597
dan sinclair41e4d9a2022-05-01 14:40:55 +00002598 if (stmt->else_statement) {
2599 line() << "} else {";
2600 if (auto* block = stmt->else_statement->As<ast::BlockStatement>()) {
2601 if (!EmitStatementsWithIndent(block->statements)) {
2602 return false;
2603 }
2604 } else {
Ben Clayton783b1692022-08-02 17:03:35 +00002605 if (!EmitStatementsWithIndent(utils::Vector{stmt->else_statement})) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002606 return false;
2607 }
2608 }
2609 }
2610 line() << "}";
2611
2612 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002613}
2614
2615bool GeneratorImpl::EmitFunction(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002616 auto* sem = builder_.Sem().Get(func);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002617
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002618 // Emit storage atomic helpers
2619 if (auto* intrinsic =
2620 ast::GetAttribute<transform::DecomposeMemoryAccess::Intrinsic>(func->attributes)) {
dan sinclairff7cf212022-10-03 14:05:23 +00002621 if (intrinsic->address_space == ast::AddressSpace::kStorage && intrinsic->IsAtomic()) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002622 if (!EmitStorageAtomicIntrinsic(func, intrinsic)) {
2623 return false;
2624 }
2625 }
2626 return true;
2627 }
2628
dan sinclair41e4d9a2022-05-01 14:40:55 +00002629 if (ast::HasAttribute<ast::InternalAttribute>(func->attributes)) {
2630 // An internal function. Do not emit.
2631 return true;
2632 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002633
dan sinclair41e4d9a2022-05-01 14:40:55 +00002634 {
2635 auto out = line();
2636 auto name = builder_.Symbols().NameFor(func->symbol);
2637 // If the function returns an array, then we need to declare a typedef for
2638 // this.
2639 if (sem->ReturnType()->Is<sem::Array>()) {
2640 auto typedef_name = UniqueIdentifier(name + "_ret");
2641 auto pre = line();
2642 pre << "typedef ";
dan sinclairff7cf212022-10-03 14:05:23 +00002643 if (!EmitTypeAndName(pre, sem->ReturnType(), ast::AddressSpace::kNone,
dan sinclair41e4d9a2022-05-01 14:40:55 +00002644 ast::Access::kReadWrite, typedef_name)) {
2645 return false;
2646 }
2647 pre << ";";
2648 out << typedef_name;
2649 } else {
dan sinclairff7cf212022-10-03 14:05:23 +00002650 if (!EmitType(out, sem->ReturnType(), ast::AddressSpace::kNone, ast::Access::kReadWrite,
dan sinclair41e4d9a2022-05-01 14:40:55 +00002651 "")) {
2652 return false;
2653 }
2654 }
2655
2656 out << " " << name << "(";
2657
2658 bool first = true;
2659
2660 for (auto* v : sem->Parameters()) {
2661 if (!first) {
2662 out << ", ";
2663 }
2664 first = false;
2665
2666 auto const* type = v->Type();
dan sinclairff7cf212022-10-03 14:05:23 +00002667 auto address_space = ast::AddressSpace::kNone;
Ben Claytond2e0db32022-10-12 18:49:15 +00002668 auto access = ast::Access::kUndefined;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002669
2670 if (auto* ptr = type->As<sem::Pointer>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002671 type = ptr->StoreType();
dan sinclairff7cf212022-10-03 14:05:23 +00002672 switch (ptr->AddressSpace()) {
2673 case ast::AddressSpace::kStorage:
2674 case ast::AddressSpace::kUniform:
Ben Clayton2032d032022-06-15 19:32:37 +00002675 // Not allowed by WGSL, but is used by certain transforms (e.g. DMA) to pass
2676 // storage buffers and uniform buffers down into transform-generated
2677 // functions. In this situation we want to generate the parameter without an
dan sinclairff7cf212022-10-03 14:05:23 +00002678 // 'inout', using the address space and access from the pointer.
2679 address_space = ptr->AddressSpace();
Ben Clayton2032d032022-06-15 19:32:37 +00002680 access = ptr->Access();
2681 break;
2682 default:
2683 // Transform regular WGSL pointer parameters in to `inout` parameters.
2684 out << "inout ";
2685 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002686 }
2687
dan sinclairff7cf212022-10-03 14:05:23 +00002688 // Note: WGSL only allows for AddressSpace::kNone on parameters, however
dan sinclair41e4d9a2022-05-01 14:40:55 +00002689 // the sanitizer transforms generates load / store functions for storage
2690 // or uniform buffers. These functions have a buffer parameter with
dan sinclairff7cf212022-10-03 14:05:23 +00002691 // AddressSpace::kStorage or AddressSpace::kUniform. This is required to
dan sinclair41e4d9a2022-05-01 14:40:55 +00002692 // correctly translate the parameter to a [RW]ByteAddressBuffer for
2693 // storage buffers and a uint4[N] for uniform buffers.
dan sinclairff7cf212022-10-03 14:05:23 +00002694 if (!EmitTypeAndName(out, type, address_space, access,
dan sinclair41e4d9a2022-05-01 14:40:55 +00002695 builder_.Symbols().NameFor(v->Declaration()->symbol))) {
2696 return false;
2697 }
2698 }
2699 out << ") {";
2700 }
2701
Ben Claytond9222f42022-10-14 13:44:54 +00002702 if (sem->DiscardStatement() && !sem->ReturnType()->Is<sem::Void>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002703 // BUG(crbug.com/tint/1081): work around non-void functions with discard
2704 // failing compilation sometimes
2705 if (!EmitFunctionBodyWithDiscard(func)) {
2706 return false;
2707 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002708 } else {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002709 if (!EmitStatementsWithIndent(func->body->statements)) {
2710 return false;
2711 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002712 }
2713
dan sinclair41e4d9a2022-05-01 14:40:55 +00002714 line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002715
dan sinclair41e4d9a2022-05-01 14:40:55 +00002716 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002717}
2718
2719bool GeneratorImpl::EmitFunctionBodyWithDiscard(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002720 // FXC sometimes fails to compile functions that discard with 'Not all control
2721 // paths return a value'. We work around this by wrapping the function body
2722 // within an "if (true) { <body> } return <default return type obj>;" so that
2723 // there is always an (unused) return statement.
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002724
dan sinclair41e4d9a2022-05-01 14:40:55 +00002725 auto* sem = builder_.Sem().Get(func);
Ben Claytond9222f42022-10-14 13:44:54 +00002726 TINT_ASSERT(Writer, sem->DiscardStatement() && !sem->ReturnType()->Is<sem::Void>());
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002727
dan sinclair41e4d9a2022-05-01 14:40:55 +00002728 ScopedIndent si(this);
2729 line() << "if (true) {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002730
dan sinclair41e4d9a2022-05-01 14:40:55 +00002731 if (!EmitStatementsWithIndent(func->body->statements)) {
2732 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002733 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002734
dan sinclair41e4d9a2022-05-01 14:40:55 +00002735 line() << "}";
2736
2737 // Return an unused result that matches the type of the return value
2738 auto name = builder_.Symbols().NameFor(builder_.Symbols().New("unused"));
2739 {
2740 auto out = line();
dan sinclairff7cf212022-10-03 14:05:23 +00002741 if (!EmitTypeAndName(out, sem->ReturnType(), ast::AddressSpace::kNone,
dan sinclair41e4d9a2022-05-01 14:40:55 +00002742 ast::Access::kReadWrite, name)) {
2743 return false;
2744 }
2745 out << ";";
2746 }
2747 line() << "return " << name << ";";
2748
2749 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002750}
2751
2752bool GeneratorImpl::EmitGlobalVariable(const ast::Variable* global) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00002753 return Switch(
2754 global, //
2755 [&](const ast::Var* var) {
2756 auto* sem = builder_.Sem().Get(global);
dan sinclairff7cf212022-10-03 14:05:23 +00002757 switch (sem->AddressSpace()) {
2758 case ast::AddressSpace::kUniform:
Ben Claytondcdf66e2022-06-17 12:48:51 +00002759 return EmitUniformVariable(var, sem);
dan sinclairff7cf212022-10-03 14:05:23 +00002760 case ast::AddressSpace::kStorage:
Ben Claytondcdf66e2022-06-17 12:48:51 +00002761 return EmitStorageVariable(var, sem);
dan sinclairff7cf212022-10-03 14:05:23 +00002762 case ast::AddressSpace::kHandle:
Ben Claytondcdf66e2022-06-17 12:48:51 +00002763 return EmitHandleVariable(var, sem);
dan sinclairff7cf212022-10-03 14:05:23 +00002764 case ast::AddressSpace::kPrivate:
Ben Claytondcdf66e2022-06-17 12:48:51 +00002765 return EmitPrivateVariable(sem);
dan sinclairff7cf212022-10-03 14:05:23 +00002766 case ast::AddressSpace::kWorkgroup:
Ben Claytondcdf66e2022-06-17 12:48:51 +00002767 return EmitWorkgroupVariable(sem);
dan sinclairff7cf212022-10-03 14:05:23 +00002768 case ast::AddressSpace::kPushConstant:
dan sinclair4abf28e2022-08-02 15:55:35 +00002769 diagnostics_.add_error(
2770 diag::System::Writer,
dan sinclairff7cf212022-10-03 14:05:23 +00002771 "unhandled address space " + utils::ToString(sem->AddressSpace()));
dan sinclair4abf28e2022-08-02 15:55:35 +00002772 return false;
dan sinclair8dbd4d02022-07-27 18:54:05 +00002773 default: {
Ben Claytondcdf66e2022-06-17 12:48:51 +00002774 TINT_ICE(Writer, diagnostics_)
dan sinclairff7cf212022-10-03 14:05:23 +00002775 << "unhandled address space " << sem->AddressSpace();
Ben Claytondcdf66e2022-06-17 12:48:51 +00002776 return false;
dan sinclair8dbd4d02022-07-27 18:54:05 +00002777 }
Ben Claytondcdf66e2022-06-17 12:48:51 +00002778 }
2779 },
dan sinclairf6a94042022-09-09 16:16:19 +00002780 [&](const ast::Override*) {
2781 // Override is removed with SubstituteOverride
Ben Clayton490d9882022-09-21 21:05:45 +00002782 diagnostics_.add_error(diag::System::Writer,
Ben Claytonf10a5792022-10-13 13:47:39 +00002783 "override-expressions should have been removed with the "
Ben Clayton490d9882022-09-21 21:05:45 +00002784 "SubstituteOverride transform");
dan sinclairf6a94042022-09-09 16:16:19 +00002785 return false;
2786 },
Ben Clayton19576e92022-06-28 12:44:16 +00002787 [&](const ast::Const*) {
2788 return true; // Constants are embedded at their use
2789 },
Ben Claytondcdf66e2022-06-17 12:48:51 +00002790 [&](Default) {
2791 TINT_ICE(Writer, diagnostics_)
2792 << "unhandled global variable type " << global->TypeInfo().name;
dan sinclair4abf28e2022-08-02 15:55:35 +00002793
Ben Claytondcdf66e2022-06-17 12:48:51 +00002794 return false;
2795 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002796}
2797
Ben Claytondcdf66e2022-06-17 12:48:51 +00002798bool GeneratorImpl::EmitUniformVariable(const ast::Var* var, const sem::Variable* sem) {
dan sinclairacdf6e12022-08-24 15:47:25 +00002799 auto binding_point = sem->As<sem::GlobalVariable>()->BindingPoint();
Ben Claytondcdf66e2022-06-17 12:48:51 +00002800 auto* type = sem->Type()->UnwrapRef();
2801 auto name = builder_.Symbols().NameFor(var->symbol);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002802 line() << "cbuffer cbuffer_" << name << RegisterAndSpace('b', binding_point) << " {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002803
dan sinclair41e4d9a2022-05-01 14:40:55 +00002804 {
2805 ScopedIndent si(this);
2806 auto out = line();
dan sinclairff7cf212022-10-03 14:05:23 +00002807 if (!EmitTypeAndName(out, type, ast::AddressSpace::kUniform, sem->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002808 return false;
2809 }
2810 out << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002811 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002812
dan sinclair41e4d9a2022-05-01 14:40:55 +00002813 line() << "};";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002814
dan sinclair41e4d9a2022-05-01 14:40:55 +00002815 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002816}
2817
Ben Claytondcdf66e2022-06-17 12:48:51 +00002818bool GeneratorImpl::EmitStorageVariable(const ast::Var* var, const sem::Variable* sem) {
2819 auto* type = sem->Type()->UnwrapRef();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002820 auto out = line();
dan sinclairff7cf212022-10-03 14:05:23 +00002821 if (!EmitTypeAndName(out, type, ast::AddressSpace::kStorage, sem->Access(),
Ben Claytondcdf66e2022-06-17 12:48:51 +00002822 builder_.Symbols().NameFor(var->symbol))) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002823 return false;
2824 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002825
dan sinclairacdf6e12022-08-24 15:47:25 +00002826 auto* global_sem = sem->As<sem::GlobalVariable>();
2827 out << RegisterAndSpace(sem->Access() == ast::Access::kRead ? 't' : 'u',
2828 global_sem->BindingPoint())
dan sinclair41e4d9a2022-05-01 14:40:55 +00002829 << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002830
dan sinclair41e4d9a2022-05-01 14:40:55 +00002831 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002832}
2833
Ben Claytondcdf66e2022-06-17 12:48:51 +00002834bool GeneratorImpl::EmitHandleVariable(const ast::Var* var, const sem::Variable* sem) {
2835 auto* unwrapped_type = sem->Type()->UnwrapRef();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002836 auto out = line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002837
Ben Claytondcdf66e2022-06-17 12:48:51 +00002838 auto name = builder_.Symbols().NameFor(var->symbol);
2839 auto* type = sem->Type()->UnwrapRef();
dan sinclairff7cf212022-10-03 14:05:23 +00002840 if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002841 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002842 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002843
dan sinclair41e4d9a2022-05-01 14:40:55 +00002844 const char* register_space = nullptr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002845
dan sinclair41e4d9a2022-05-01 14:40:55 +00002846 if (unwrapped_type->Is<sem::Texture>()) {
2847 register_space = "t";
2848 if (unwrapped_type->Is<sem::StorageTexture>()) {
2849 register_space = "u";
2850 }
2851 } else if (unwrapped_type->Is<sem::Sampler>()) {
2852 register_space = "s";
2853 }
2854
2855 if (register_space) {
dan sinclairacdf6e12022-08-24 15:47:25 +00002856 auto bp = sem->As<sem::GlobalVariable>()->BindingPoint();
2857 out << " : register(" << register_space << bp.binding << ", space" << bp.group << ")";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002858 }
2859
2860 out << ";";
2861 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002862}
2863
2864bool GeneratorImpl::EmitPrivateVariable(const sem::Variable* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002865 auto* decl = var->Declaration();
2866 auto out = line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002867
dan sinclair41e4d9a2022-05-01 14:40:55 +00002868 out << "static ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002869
dan sinclair41e4d9a2022-05-01 14:40:55 +00002870 auto name = builder_.Symbols().NameFor(decl->symbol);
2871 auto* type = var->Type()->UnwrapRef();
dan sinclairff7cf212022-10-03 14:05:23 +00002872 if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002873 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002874 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002875
dan sinclair41e4d9a2022-05-01 14:40:55 +00002876 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00002877 if (auto* initializer = decl->initializer) {
2878 if (!EmitExpression(out, initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002879 return false;
2880 }
2881 } else {
2882 if (!EmitZeroValue(out, var->Type()->UnwrapRef())) {
2883 return false;
2884 }
2885 }
2886
2887 out << ";";
2888 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002889}
2890
2891bool GeneratorImpl::EmitWorkgroupVariable(const sem::Variable* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002892 auto* decl = var->Declaration();
2893 auto out = line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002894
dan sinclair41e4d9a2022-05-01 14:40:55 +00002895 out << "groupshared ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002896
dan sinclair41e4d9a2022-05-01 14:40:55 +00002897 auto name = builder_.Symbols().NameFor(decl->symbol);
2898 auto* type = var->Type()->UnwrapRef();
dan sinclairff7cf212022-10-03 14:05:23 +00002899 if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002900 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002901 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002902
dan sinclair6e77b472022-10-20 13:38:28 +00002903 if (auto* initializer = decl->initializer) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002904 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00002905 if (!EmitExpression(out, initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002906 return false;
2907 }
2908 }
2909
2910 out << ";";
2911 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002912}
2913
Ben Claytonf3302292022-07-27 18:48:06 +00002914std::string GeneratorImpl::builtin_to_attribute(ast::BuiltinValue builtin) const {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002915 switch (builtin) {
Ben Claytonf3302292022-07-27 18:48:06 +00002916 case ast::BuiltinValue::kPosition:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002917 return "SV_Position";
Ben Claytonf3302292022-07-27 18:48:06 +00002918 case ast::BuiltinValue::kVertexIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002919 return "SV_VertexID";
Ben Claytonf3302292022-07-27 18:48:06 +00002920 case ast::BuiltinValue::kInstanceIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002921 return "SV_InstanceID";
Ben Claytonf3302292022-07-27 18:48:06 +00002922 case ast::BuiltinValue::kFrontFacing:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002923 return "SV_IsFrontFace";
Ben Claytonf3302292022-07-27 18:48:06 +00002924 case ast::BuiltinValue::kFragDepth:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002925 return "SV_Depth";
Ben Claytonf3302292022-07-27 18:48:06 +00002926 case ast::BuiltinValue::kLocalInvocationId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002927 return "SV_GroupThreadID";
Ben Claytonf3302292022-07-27 18:48:06 +00002928 case ast::BuiltinValue::kLocalInvocationIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002929 return "SV_GroupIndex";
Ben Claytonf3302292022-07-27 18:48:06 +00002930 case ast::BuiltinValue::kGlobalInvocationId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002931 return "SV_DispatchThreadID";
Ben Claytonf3302292022-07-27 18:48:06 +00002932 case ast::BuiltinValue::kWorkgroupId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002933 return "SV_GroupID";
Ben Claytonf3302292022-07-27 18:48:06 +00002934 case ast::BuiltinValue::kSampleIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002935 return "SV_SampleIndex";
Ben Claytonf3302292022-07-27 18:48:06 +00002936 case ast::BuiltinValue::kSampleMask:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002937 return "SV_Coverage";
2938 default:
2939 break;
2940 }
2941 return "";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002942}
2943
dan sinclair41e4d9a2022-05-01 14:40:55 +00002944std::string GeneratorImpl::interpolation_to_modifiers(ast::InterpolationType type,
2945 ast::InterpolationSampling sampling) const {
2946 std::string modifiers;
2947 switch (type) {
2948 case ast::InterpolationType::kPerspective:
2949 modifiers += "linear ";
2950 break;
2951 case ast::InterpolationType::kLinear:
2952 modifiers += "noperspective ";
2953 break;
2954 case ast::InterpolationType::kFlat:
2955 modifiers += "nointerpolation ";
2956 break;
Ben Claytond2e0db32022-10-12 18:49:15 +00002957 case ast::InterpolationType::kUndefined:
Ben Claytonf9ed9d32022-10-11 19:49:17 +00002958 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002959 }
2960 switch (sampling) {
2961 case ast::InterpolationSampling::kCentroid:
2962 modifiers += "centroid ";
2963 break;
2964 case ast::InterpolationSampling::kSample:
2965 modifiers += "sample ";
2966 break;
2967 case ast::InterpolationSampling::kCenter:
Ben Claytond2e0db32022-10-12 18:49:15 +00002968 case ast::InterpolationSampling::kUndefined:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002969 break;
2970 }
2971 return modifiers;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002972}
2973
2974bool GeneratorImpl::EmitEntryPointFunction(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002975 auto* func_sem = builder_.Sem().Get(func);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002976
dan sinclair41e4d9a2022-05-01 14:40:55 +00002977 {
2978 auto out = line();
2979 if (func->PipelineStage() == ast::PipelineStage::kCompute) {
2980 // Emit the workgroup_size attribute.
2981 auto wgsize = func_sem->WorkgroupSize();
2982 out << "[numthreads(";
dan sinclair3a2a2792022-06-29 14:38:15 +00002983 for (size_t i = 0; i < 3; i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002984 if (i > 0) {
2985 out << ", ";
2986 }
Ben Clayton490d9882022-09-21 21:05:45 +00002987 if (!wgsize[i].has_value()) {
2988 diagnostics_.add_error(
2989 diag::System::Writer,
Ben Claytonf10a5792022-10-13 13:47:39 +00002990 "override-expressions should have been removed with the SubstituteOverride "
Ben Clayton490d9882022-09-21 21:05:45 +00002991 "transform");
2992 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002993 }
Ben Clayton490d9882022-09-21 21:05:45 +00002994 out << std::to_string(wgsize[i].value());
dan sinclair41e4d9a2022-05-01 14:40:55 +00002995 }
2996 out << ")]" << std::endl;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002997 }
2998
dan sinclair41e4d9a2022-05-01 14:40:55 +00002999 out << func->return_type->FriendlyName(builder_.Symbols());
3000
3001 out << " " << builder_.Symbols().NameFor(func->symbol) << "(";
3002
3003 bool first = true;
3004
3005 // Emit entry point parameters.
3006 for (auto* var : func->params) {
3007 auto* sem = builder_.Sem().Get(var);
3008 auto* type = sem->Type();
3009 if (!type->Is<sem::Struct>()) {
3010 // ICE likely indicates that the CanonicalizeEntryPointIO transform was
3011 // not run, or a builtin parameter was added after it was run.
3012 TINT_ICE(Writer, diagnostics_) << "Unsupported non-struct entry point parameter";
3013 }
3014
3015 if (!first) {
3016 out << ", ";
3017 }
3018 first = false;
3019
dan sinclairff7cf212022-10-03 14:05:23 +00003020 if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(),
dan sinclair41e4d9a2022-05-01 14:40:55 +00003021 builder_.Symbols().NameFor(var->symbol))) {
3022 return false;
3023 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003024 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003025
3026 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003027 }
3028
dan sinclair41e4d9a2022-05-01 14:40:55 +00003029 {
3030 ScopedIndent si(this);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003031
dan sinclair41e4d9a2022-05-01 14:40:55 +00003032 if (!EmitStatements(func->body->statements)) {
3033 return false;
3034 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003035
dan sinclair41e4d9a2022-05-01 14:40:55 +00003036 if (!Is<ast::ReturnStatement>(func->body->Last())) {
Ben Clayton4a92a3c2022-07-18 20:50:02 +00003037 ast::ReturnStatement ret(ProgramID(), ast::NodeID{}, Source{});
dan sinclair41e4d9a2022-05-01 14:40:55 +00003038 if (!EmitStatement(&ret)) {
3039 return false;
3040 }
3041 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003042 }
3043
dan sinclair41e4d9a2022-05-01 14:40:55 +00003044 line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003045
dan sinclair41e4d9a2022-05-01 14:40:55 +00003046 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003047}
3048
Ben Clayton329dfd72022-11-23 00:05:05 +00003049bool GeneratorImpl::EmitConstant(std::ostream& out,
3050 const sem::Constant* constant,
3051 bool is_variable_initializer) {
Ben Clayton50414802022-06-24 08:06:19 +00003052 return Switch(
Ben Claytonaa037ac2022-06-29 19:07:30 +00003053 constant->Type(), //
Ben Clayton50414802022-06-24 08:06:19 +00003054 [&](const sem::Bool*) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003055 out << (constant->As<AInt>() ? "true" : "false");
Ben Claytone9f8b092022-06-01 13:14:39 +00003056 return true;
Ben Clayton50414802022-06-24 08:06:19 +00003057 },
3058 [&](const sem::F32*) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003059 PrintF32(out, constant->As<float>());
Ben Clayton50414802022-06-24 08:06:19 +00003060 return true;
3061 },
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003062 [&](const sem::F16*) {
3063 // emit a f16 scalar with explicit float16_t type declaration.
3064 out << "float16_t(";
Antonio Maiorano679cf4f2022-09-03 21:43:01 +00003065 PrintF16(out, constant->As<float>());
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003066 out << ")";
Antonio Maiorano679cf4f2022-09-03 21:43:01 +00003067 return true;
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003068 },
Ben Clayton50414802022-06-24 08:06:19 +00003069 [&](const sem::I32*) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003070 out << constant->As<AInt>();
Ben Clayton50414802022-06-24 08:06:19 +00003071 return true;
3072 },
3073 [&](const sem::U32*) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003074 out << constant->As<AInt>() << "u";
Ben Clayton50414802022-06-24 08:06:19 +00003075 return true;
3076 },
3077 [&](const sem::Vector* v) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003078 if (constant->AllEqual()) {
Ben Clayton50414802022-06-24 08:06:19 +00003079 {
3080 ScopedParen sp(out);
Ben Clayton329dfd72022-11-23 00:05:05 +00003081 if (!EmitConstant(out, constant->Index(0), is_variable_initializer)) {
Ben Clayton50414802022-06-24 08:06:19 +00003082 return false;
3083 }
3084 }
3085 out << ".";
Ben Claytonaa037ac2022-06-29 19:07:30 +00003086 for (size_t i = 0; i < v->Width(); i++) {
Ben Clayton50414802022-06-24 08:06:19 +00003087 out << "x";
3088 }
3089 return true;
3090 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003091
Ben Claytond2e0db32022-10-12 18:49:15 +00003092 if (!EmitType(out, v, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
Ben Clayton50414802022-06-24 08:06:19 +00003093 return false;
3094 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003095
Ben Clayton50414802022-06-24 08:06:19 +00003096 ScopedParen sp(out);
Ben Claytone9f8b092022-06-01 13:14:39 +00003097
Ben Claytonaa037ac2022-06-29 19:07:30 +00003098 for (size_t i = 0; i < v->Width(); i++) {
3099 if (i > 0) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003100 out << ", ";
3101 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003102 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003103 return false;
3104 }
3105 }
3106 return true;
Ben Clayton50414802022-06-24 08:06:19 +00003107 },
3108 [&](const sem::Matrix* m) {
Ben Claytond2e0db32022-10-12 18:49:15 +00003109 if (!EmitType(out, m, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003110 return false;
3111 }
Ben Clayton50414802022-06-24 08:06:19 +00003112
3113 ScopedParen sp(out);
3114
Ben Claytonaa037ac2022-06-29 19:07:30 +00003115 for (size_t i = 0; i < m->columns(); i++) {
3116 if (i > 0) {
Ben Clayton50414802022-06-24 08:06:19 +00003117 out << ", ";
3118 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003119 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Clayton50414802022-06-24 08:06:19 +00003120 return false;
3121 }
3122 }
3123 return true;
3124 },
Ben Clayton19576e92022-06-28 12:44:16 +00003125 [&](const sem::Array* a) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003126 if (constant->AllZero()) {
Ben Clayton19576e92022-06-28 12:44:16 +00003127 out << "(";
Ben Claytond2e0db32022-10-12 18:49:15 +00003128 if (!EmitType(out, a, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
Ben Clayton19576e92022-06-28 12:44:16 +00003129 return false;
3130 }
3131 out << ")0";
3132 return true;
3133 }
3134
3135 out << "{";
3136 TINT_DEFER(out << "}");
3137
dan sinclair78f80672022-09-22 22:28:21 +00003138 auto count = a->ConstantCount();
3139 if (!count) {
3140 diagnostics_.add_error(diag::System::Writer, sem::Array::kErrExpectedConstantCount);
3141 return false;
3142 }
3143
3144 for (size_t i = 0; i < count; i++) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003145 if (i > 0) {
Ben Clayton19576e92022-06-28 12:44:16 +00003146 out << ", ";
3147 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003148 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Clayton19576e92022-06-28 12:44:16 +00003149 return false;
3150 }
3151 }
3152
3153 return true;
3154 },
Ben Clayton6c098ba2022-07-14 20:46:39 +00003155 [&](const sem::Struct* s) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003156 if (!EmitStructType(&helpers_, s)) {
3157 return false;
3158 }
3159
Ben Clayton6c098ba2022-07-14 20:46:39 +00003160 if (constant->AllZero()) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003161 out << "(" << StructName(s) << ")0";
Ben Clayton6c098ba2022-07-14 20:46:39 +00003162 return true;
3163 }
3164
Ben Clayton329dfd72022-11-23 00:05:05 +00003165 auto emit_member_values = [&](std::ostream& o) {
3166 o << "{";
3167 for (size_t i = 0; i < s->Members().size(); i++) {
3168 if (i > 0) {
3169 o << ", ";
3170 }
3171 if (!EmitConstant(o, constant->Index(i), is_variable_initializer)) {
3172 return false;
3173 }
Ben Clayton6c098ba2022-07-14 20:46:39 +00003174 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003175 o << "}";
3176 return true;
3177 };
3178
3179 if (is_variable_initializer) {
3180 if (!emit_member_values(out)) {
Ben Clayton6c098ba2022-07-14 20:46:39 +00003181 return false;
3182 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003183 } else {
3184 // HLSL requires structure initializers to be assigned directly to a variable.
3185 auto name = UniqueIdentifier("c");
3186 {
3187 auto decl = line();
3188 decl << "const " << StructName(s) << " " << name << " = ";
3189 if (!emit_member_values(decl)) {
3190 return false;
3191 }
3192 decl << ";";
3193 }
3194 out << name;
Ben Clayton6c098ba2022-07-14 20:46:39 +00003195 }
3196
3197 return true;
3198 },
Ben Claytone9f8b092022-06-01 13:14:39 +00003199 [&](Default) {
3200 diagnostics_.add_error(
3201 diag::System::Writer,
Ben Claytonaa037ac2022-06-29 19:07:30 +00003202 "unhandled constant type: " + builder_.FriendlyName(constant->Type()));
Ben Claytone9f8b092022-06-01 13:14:39 +00003203 return false;
3204 });
3205}
3206
dan sinclair41e4d9a2022-05-01 14:40:55 +00003207bool GeneratorImpl::EmitLiteral(std::ostream& out, const ast::LiteralExpression* lit) {
3208 return Switch(
3209 lit,
3210 [&](const ast::BoolLiteralExpression* l) {
3211 out << (l->value ? "true" : "false");
3212 return true;
3213 },
Ben Clayton3ad927c2022-05-25 23:12:14 +00003214 [&](const ast::FloatLiteralExpression* l) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003215 if (l->suffix == ast::FloatLiteralExpression::Suffix::kH) {
3216 // Emit f16 literal with explicit float16_t type declaration.
3217 out << "float16_t(";
Antonio Maiorano679cf4f2022-09-03 21:43:01 +00003218 PrintF16(out, static_cast<float>(l->value));
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003219 out << ")";
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003220 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003221 PrintF32(out, static_cast<float>(l->value));
dan sinclair41e4d9a2022-05-01 14:40:55 +00003222 return true;
3223 },
Ben Clayton8822e292022-05-04 22:18:49 +00003224 [&](const ast::IntLiteralExpression* i) {
3225 out << i->value;
3226 switch (i->suffix) {
3227 case ast::IntLiteralExpression::Suffix::kNone:
3228 case ast::IntLiteralExpression::Suffix::kI:
3229 return true;
3230 case ast::IntLiteralExpression::Suffix::kU:
3231 out << "u";
3232 return true;
3233 }
3234 diagnostics_.add_error(diag::System::Writer, "unknown integer literal suffix type");
3235 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003236 },
3237 [&](Default) {
3238 diagnostics_.add_error(diag::System::Writer, "unknown literal type");
3239 return false;
3240 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003241}
3242
dan sinclair41e4d9a2022-05-01 14:40:55 +00003243bool GeneratorImpl::EmitValue(std::ostream& out, const sem::Type* type, int value) {
3244 return Switch(
3245 type,
3246 [&](const sem::Bool*) {
3247 out << (value == 0 ? "false" : "true");
3248 return true;
3249 },
3250 [&](const sem::F32*) {
3251 out << value << ".0f";
3252 return true;
3253 },
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003254 [&](const sem::F16*) {
3255 out << "float16_t(" << value << ".0h)";
3256 return true;
3257 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00003258 [&](const sem::I32*) {
3259 out << value;
3260 return true;
3261 },
3262 [&](const sem::U32*) {
3263 out << value << "u";
3264 return true;
3265 },
3266 [&](const sem::Vector* vec) {
dan sinclairff7cf212022-10-03 14:05:23 +00003267 if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003268 return false;
3269 }
3270 ScopedParen sp(out);
3271 for (uint32_t i = 0; i < vec->Width(); i++) {
3272 if (i != 0) {
3273 out << ", ";
3274 }
3275 if (!EmitValue(out, vec->type(), value)) {
3276 return false;
3277 }
3278 }
3279 return true;
3280 },
3281 [&](const sem::Matrix* mat) {
dan sinclairff7cf212022-10-03 14:05:23 +00003282 if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003283 return false;
3284 }
3285 ScopedParen sp(out);
3286 for (uint32_t i = 0; i < (mat->rows() * mat->columns()); i++) {
3287 if (i != 0) {
3288 out << ", ";
3289 }
3290 if (!EmitValue(out, mat->type(), value)) {
3291 return false;
3292 }
3293 }
3294 return true;
3295 },
3296 [&](const sem::Struct*) {
3297 out << "(";
3298 TINT_DEFER(out << ")" << value);
Ben Claytond2e0db32022-10-12 18:49:15 +00003299 return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "");
dan sinclair41e4d9a2022-05-01 14:40:55 +00003300 },
3301 [&](const sem::Array*) {
3302 out << "(";
3303 TINT_DEFER(out << ")" << value);
Ben Claytond2e0db32022-10-12 18:49:15 +00003304 return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "");
dan sinclair41e4d9a2022-05-01 14:40:55 +00003305 },
3306 [&](Default) {
3307 diagnostics_.add_error(
3308 diag::System::Writer,
3309 "Invalid type for value emission: " + type->FriendlyName(builder_.Symbols()));
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003310 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003311 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003312}
3313
3314bool GeneratorImpl::EmitZeroValue(std::ostream& out, const sem::Type* type) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003315 return EmitValue(out, type, 0);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003316}
3317
3318bool GeneratorImpl::EmitLoop(const ast::LoopStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003319 auto emit_continuing = [this, stmt]() {
3320 if (stmt->continuing && !stmt->continuing->Empty()) {
3321 if (!EmitBlock(stmt->continuing)) {
3322 return false;
3323 }
3324 }
3325 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003326 };
3327
3328 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
Antonio Maiorano06844a52022-09-29 16:53:58 +00003329 line() << "while (true) {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003330 {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003331 ScopedIndent si(this);
3332 if (!EmitStatements(stmt->body->statements)) {
3333 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003334 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003335 if (!emit_continuing_()) {
3336 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003337 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003338 }
3339 line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003340
dan sinclair41e4d9a2022-05-01 14:40:55 +00003341 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003342}
3343
dan sinclair41e4d9a2022-05-01 14:40:55 +00003344bool GeneratorImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
3345 // Nest a for loop with a new block. In HLSL the initializer scope is not
3346 // nested by the for-loop, so we may get variable redefinitions.
3347 line() << "{";
3348 increment_indent();
3349 TINT_DEFER({
3350 decrement_indent();
3351 line() << "}";
3352 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003353
dan sinclair41e4d9a2022-05-01 14:40:55 +00003354 TextBuffer init_buf;
3355 if (auto* init = stmt->initializer) {
3356 TINT_SCOPED_ASSIGNMENT(current_buffer_, &init_buf);
3357 if (!EmitStatement(init)) {
3358 return false;
3359 }
3360 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003361
dan sinclair41e4d9a2022-05-01 14:40:55 +00003362 TextBuffer cond_pre;
3363 std::stringstream cond_buf;
3364 if (auto* cond = stmt->condition) {
3365 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
3366 if (!EmitExpression(cond_buf, cond)) {
3367 return false;
3368 }
3369 }
3370
3371 TextBuffer cont_buf;
3372 if (auto* cont = stmt->continuing) {
3373 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cont_buf);
3374 if (!EmitStatement(cont)) {
3375 return false;
3376 }
3377 }
3378
3379 // If the for-loop has a multi-statement conditional and / or continuing, then
3380 // we cannot emit this as a regular for-loop in HLSL. Instead we need to
3381 // generate a `while(true)` loop.
3382 bool emit_as_loop = cond_pre.lines.size() > 0 || cont_buf.lines.size() > 1;
3383
3384 // If the for-loop has multi-statement initializer, or is going to be emitted
3385 // as a `while(true)` loop, then declare the initializer statement(s) before
3386 // the loop.
3387 if (init_buf.lines.size() > 1 || (stmt->initializer && emit_as_loop)) {
3388 current_buffer_->Append(init_buf);
3389 init_buf.lines.clear(); // Don't emit the initializer again in the 'for'
3390 }
3391
3392 if (emit_as_loop) {
3393 auto emit_continuing = [&]() {
3394 current_buffer_->Append(cont_buf);
3395 return true;
3396 };
3397
3398 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
Antonio Maiorano06844a52022-09-29 16:53:58 +00003399 line() << "while (true) {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003400 increment_indent();
3401 TINT_DEFER({
3402 decrement_indent();
3403 line() << "}";
3404 });
3405
3406 if (stmt->condition) {
3407 current_buffer_->Append(cond_pre);
3408 line() << "if (!(" << cond_buf.str() << ")) { break; }";
3409 }
3410
3411 if (!EmitStatements(stmt->body->statements)) {
3412 return false;
3413 }
3414
3415 if (!emit_continuing_()) {
3416 return false;
3417 }
3418 } else {
3419 // For-loop can be generated.
3420 {
3421 auto out = line();
Antonio Maiorano06844a52022-09-29 16:53:58 +00003422 out << "for";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003423 {
3424 ScopedParen sp(out);
3425
3426 if (!init_buf.lines.empty()) {
3427 out << init_buf.lines[0].content << " ";
3428 } else {
3429 out << "; ";
3430 }
3431
3432 out << cond_buf.str() << "; ";
3433
3434 if (!cont_buf.lines.empty()) {
3435 out << TrimSuffix(cont_buf.lines[0].content, ";");
3436 }
3437 }
3438 out << " {";
3439 }
3440 {
3441 auto emit_continuing = [] { return true; };
3442 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
3443 if (!EmitStatementsWithIndent(stmt->body->statements)) {
3444 return false;
3445 }
3446 }
3447 line() << "}";
3448 }
3449
3450 return true;
3451}
3452
dan sinclair49d1a2d2022-06-16 12:01:27 +00003453bool GeneratorImpl::EmitWhile(const ast::WhileStatement* stmt) {
3454 TextBuffer cond_pre;
3455 std::stringstream cond_buf;
3456 {
3457 auto* cond = stmt->condition;
3458 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
3459 if (!EmitExpression(cond_buf, cond)) {
3460 return false;
3461 }
3462 }
3463
dan sinclair4b88dbc2022-06-16 15:27:38 +00003464 auto emit_continuing = [&]() { return true; };
3465 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
3466
dan sinclair49d1a2d2022-06-16 12:01:27 +00003467 // If the while has a multi-statement conditional, then we cannot emit this
3468 // as a regular while in HLSL. Instead we need to generate a `while(true)` loop.
3469 bool emit_as_loop = cond_pre.lines.size() > 0;
3470 if (emit_as_loop) {
Antonio Maiorano06844a52022-09-29 16:53:58 +00003471 line() << "while (true) {";
dan sinclair49d1a2d2022-06-16 12:01:27 +00003472 increment_indent();
3473 TINT_DEFER({
3474 decrement_indent();
3475 line() << "}";
3476 });
3477
3478 current_buffer_->Append(cond_pre);
3479 line() << "if (!(" << cond_buf.str() << ")) { break; }";
3480 if (!EmitStatements(stmt->body->statements)) {
3481 return false;
3482 }
3483 } else {
3484 // While can be generated.
3485 {
3486 auto out = line();
Antonio Maiorano06844a52022-09-29 16:53:58 +00003487 out << "while";
dan sinclair49d1a2d2022-06-16 12:01:27 +00003488 {
3489 ScopedParen sp(out);
3490 out << cond_buf.str();
3491 }
3492 out << " {";
3493 }
3494 if (!EmitStatementsWithIndent(stmt->body->statements)) {
3495 return false;
3496 }
3497 line() << "}";
3498 }
3499
3500 return true;
3501}
3502
dan sinclair41e4d9a2022-05-01 14:40:55 +00003503bool GeneratorImpl::EmitMemberAccessor(std::ostream& out,
3504 const ast::MemberAccessorExpression* expr) {
3505 if (!EmitExpression(out, expr->structure)) {
3506 return false;
3507 }
3508 out << ".";
3509
Ben Clayton10fae7a2022-11-14 15:29:29 +00003510 auto* sem = builder_.Sem().Get(expr);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003511
Ben Clayton10fae7a2022-11-14 15:29:29 +00003512 return Switch(
3513 sem,
3514 [&](const sem::Swizzle*) {
3515 // Swizzles output the name directly
3516 out << builder_.Symbols().NameFor(expr->member->symbol);
3517 return true;
3518 },
3519 [&](const sem::StructMemberAccess* member_access) {
3520 out << program_->Symbols().NameFor(member_access->Member()->Name());
3521 return true;
3522 },
3523 [&](Default) {
3524 TINT_ICE(Writer, diagnostics_)
3525 << "unknown member access type: " << sem->TypeInfo().name;
3526 return false;
3527 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003528}
3529
3530bool GeneratorImpl::EmitReturn(const ast::ReturnStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003531 if (stmt->value) {
3532 auto out = line();
3533 out << "return ";
3534 if (!EmitExpression(out, stmt->value)) {
3535 return false;
3536 }
3537 out << ";";
3538 } else {
3539 line() << "return;";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003540 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003541 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003542}
3543
3544bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003545 return Switch(
3546 stmt,
3547 [&](const ast::AssignmentStatement* a) { //
3548 return EmitAssign(a);
3549 },
3550 [&](const ast::BlockStatement* b) { //
3551 return EmitBlock(b);
3552 },
3553 [&](const ast::BreakStatement* b) { //
3554 return EmitBreak(b);
3555 },
dan sinclairb8b0c212022-10-20 22:45:50 +00003556 [&](const ast::BreakIfStatement* b) { //
3557 return EmitBreakIf(b);
3558 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00003559 [&](const ast::CallStatement* c) { //
3560 auto out = line();
3561 if (!EmitCall(out, c->expr)) {
3562 return false;
3563 }
3564 out << ";";
3565 return true;
3566 },
3567 [&](const ast::ContinueStatement* c) { //
3568 return EmitContinue(c);
3569 },
3570 [&](const ast::DiscardStatement* d) { //
3571 return EmitDiscard(d);
3572 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00003573 [&](const ast::IfStatement* i) { //
3574 return EmitIf(i);
3575 },
3576 [&](const ast::LoopStatement* l) { //
3577 return EmitLoop(l);
3578 },
3579 [&](const ast::ForLoopStatement* l) { //
3580 return EmitForLoop(l);
3581 },
dan sinclair49d1a2d2022-06-16 12:01:27 +00003582 [&](const ast::WhileStatement* l) { //
3583 return EmitWhile(l);
3584 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00003585 [&](const ast::ReturnStatement* r) { //
3586 return EmitReturn(r);
3587 },
3588 [&](const ast::SwitchStatement* s) { //
3589 return EmitSwitch(s);
3590 },
3591 [&](const ast::VariableDeclStatement* v) { //
Ben Claytondcdf66e2022-06-17 12:48:51 +00003592 return Switch(
3593 v->variable, //
3594 [&](const ast::Var* var) { return EmitVar(var); },
3595 [&](const ast::Let* let) { return EmitLet(let); },
Ben Clayton19576e92022-06-28 12:44:16 +00003596 [&](const ast::Const*) {
3597 return true; // Constants are embedded at their use
3598 },
Ben Claytondcdf66e2022-06-17 12:48:51 +00003599 [&](Default) { //
3600 TINT_ICE(Writer, diagnostics_)
3601 << "unknown variable type: " << v->variable->TypeInfo().name;
3602 return false;
3603 });
dan sinclair41e4d9a2022-05-01 14:40:55 +00003604 },
Ben Claytonb4744ac2022-08-03 07:01:08 +00003605 [&](const ast::StaticAssert*) {
3606 return true; // Not emitted
3607 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00003608 [&](Default) { //
3609 diagnostics_.add_error(diag::System::Writer,
3610 "unknown statement type: " + std::string(stmt->TypeInfo().name));
3611 return false;
3612 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003613}
3614
3615bool GeneratorImpl::EmitDefaultOnlySwitch(const ast::SwitchStatement* stmt) {
dan sinclairf148f082022-10-19 15:55:02 +00003616 TINT_ASSERT(Writer, stmt->body.Length() == 1 && stmt->body[0]->ContainsDefault());
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003617
dan sinclair41e4d9a2022-05-01 14:40:55 +00003618 // FXC fails to compile a switch with just a default case, ignoring the
3619 // default case body. We work around this here by emitting the default case
3620 // without the switch.
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003621
dan sinclair41e4d9a2022-05-01 14:40:55 +00003622 // Emit the switch condition as-is in case it has side-effects (e.g.
3623 // function call). Note that's it's fine not to assign the result of the
3624 // expression.
3625 {
3626 auto out = line();
3627 if (!EmitExpression(out, stmt->condition)) {
3628 return false;
3629 }
3630 out << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003631 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003632
dan sinclair41e4d9a2022-05-01 14:40:55 +00003633 // Emit "do { <default case body> } while(false);". We use a 'do' loop so
3634 // that break statements work as expected, and make it 'while (false)' in
3635 // case there isn't a break statement.
3636 line() << "do {";
3637 {
3638 ScopedIndent si(this);
3639 if (!EmitStatements(stmt->body[0]->body->statements)) {
3640 return false;
3641 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003642 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003643 line() << "} while (false);";
3644 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003645}
3646
3647bool GeneratorImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003648 // BUG(crbug.com/tint/1188): work around default-only switches
dan sinclairf148f082022-10-19 15:55:02 +00003649 if (stmt->body.Length() == 1 && stmt->body[0]->selectors.Length() == 1 &&
3650 stmt->body[0]->ContainsDefault()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003651 return EmitDefaultOnlySwitch(stmt);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003652 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003653
dan sinclair41e4d9a2022-05-01 14:40:55 +00003654 { // switch(expr) {
3655 auto out = line();
3656 out << "switch(";
3657 if (!EmitExpression(out, stmt->condition)) {
3658 return false;
3659 }
3660 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003661 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003662
dan sinclair41e4d9a2022-05-01 14:40:55 +00003663 {
3664 ScopedIndent si(this);
Ben Clayton783b1692022-08-02 17:03:35 +00003665 for (size_t i = 0; i < stmt->body.Length(); i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003666 if (!EmitCase(stmt, i)) {
3667 return false;
3668 }
3669 }
3670 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003671
dan sinclair41e4d9a2022-05-01 14:40:55 +00003672 line() << "}";
3673
3674 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003675}
3676
3677bool GeneratorImpl::EmitType(std::ostream& out,
3678 const sem::Type* type,
dan sinclairff7cf212022-10-03 14:05:23 +00003679 ast::AddressSpace address_space,
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003680 ast::Access access,
3681 const std::string& name,
3682 bool* name_printed /* = nullptr */) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003683 if (name_printed) {
3684 *name_printed = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003685 }
dan sinclairff7cf212022-10-03 14:05:23 +00003686 switch (address_space) {
3687 case ast::AddressSpace::kStorage:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003688 if (access != ast::Access::kRead) {
3689 out << "RW";
3690 }
3691 out << "ByteAddressBuffer";
3692 return true;
dan sinclairff7cf212022-10-03 14:05:23 +00003693 case ast::AddressSpace::kUniform: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003694 auto array_length = (type->Size() + 15) / 16;
3695 out << "uint4 " << name << "[" << array_length << "]";
3696 if (name_printed) {
3697 *name_printed = true;
3698 }
3699 return true;
3700 }
3701 default:
3702 break;
3703 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003704
dan sinclair41e4d9a2022-05-01 14:40:55 +00003705 return Switch(
3706 type,
3707 [&](const sem::Array* ary) {
3708 const sem::Type* base_type = ary;
3709 std::vector<uint32_t> sizes;
3710 while (auto* arr = base_type->As<sem::Array>()) {
3711 if (arr->IsRuntimeSized()) {
3712 TINT_ICE(Writer, diagnostics_)
dan sinclair78f80672022-09-22 22:28:21 +00003713 << "runtime arrays may only exist in storage buffers, which should have "
Ben Clayton3a68ab42022-06-24 08:30:28 +00003714 "been transformed into a ByteAddressBuffer";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003715 return false;
3716 }
dan sinclair78f80672022-09-22 22:28:21 +00003717 const auto count = arr->ConstantCount();
3718 if (!count) {
3719 diagnostics_.add_error(diag::System::Writer,
3720 sem::Array::kErrExpectedConstantCount);
3721 return false;
3722 }
3723
3724 sizes.push_back(count.value());
dan sinclair41e4d9a2022-05-01 14:40:55 +00003725 base_type = arr->ElemType();
3726 }
dan sinclairff7cf212022-10-03 14:05:23 +00003727 if (!EmitType(out, base_type, address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003728 return false;
3729 }
3730 if (!name.empty()) {
3731 out << " " << name;
3732 if (name_printed) {
3733 *name_printed = true;
3734 }
3735 }
3736 for (uint32_t size : sizes) {
3737 out << "[" << size << "]";
3738 }
3739 return true;
3740 },
3741 [&](const sem::Bool*) {
3742 out << "bool";
3743 return true;
3744 },
3745 [&](const sem::F32*) {
3746 out << "float";
3747 return true;
3748 },
Zhaoming Jiang62bfd312022-05-13 12:01:11 +00003749 [&](const sem::F16*) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003750 out << "float16_t";
3751 return true;
Zhaoming Jiang62bfd312022-05-13 12:01:11 +00003752 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00003753 [&](const sem::I32*) {
3754 out << "int";
3755 return true;
3756 },
3757 [&](const sem::Matrix* mat) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003758 if (mat->type()->Is<sem::F16>()) {
3759 // Use matrix<type, N, M> for f16 matrix
3760 out << "matrix<";
dan sinclairff7cf212022-10-03 14:05:23 +00003761 if (!EmitType(out, mat->type(), address_space, access, "")) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003762 return false;
3763 }
3764 out << ", " << mat->columns() << ", " << mat->rows() << ">";
3765 return true;
3766 }
dan sinclairff7cf212022-10-03 14:05:23 +00003767 if (!EmitType(out, mat->type(), address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003768 return false;
3769 }
3770 // Note: HLSL's matrices are declared as <type>NxM, where N is the
3771 // number of rows and M is the number of columns. Despite HLSL's
3772 // matrices being column-major by default, the index operator and
dan sinclair6e77b472022-10-20 13:38:28 +00003773 // initializers actually operate on row-vectors, where as WGSL operates
dan sinclair41e4d9a2022-05-01 14:40:55 +00003774 // on column vectors. To simplify everything we use the transpose of the
3775 // matrices. See:
3776 // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-per-component-math#matrix-ordering
3777 out << mat->columns() << "x" << mat->rows();
3778 return true;
3779 },
3780 [&](const sem::Pointer*) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003781 TINT_ICE(Writer, diagnostics_)
dan sinclair41e4d9a2022-05-01 14:40:55 +00003782 << "Attempting to emit pointer type. These should have been "
3783 "removed with the InlinePointerLets transform";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003784 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003785 },
3786 [&](const sem::Sampler* sampler) {
3787 out << "Sampler";
3788 if (sampler->IsComparison()) {
3789 out << "Comparison";
3790 }
3791 out << "State";
3792 return true;
3793 },
3794 [&](const sem::Struct* str) {
3795 out << StructName(str);
3796 return true;
3797 },
3798 [&](const sem::Texture* tex) {
3799 if (tex->Is<sem::ExternalTexture>()) {
3800 TINT_ICE(Writer, diagnostics_)
3801 << "Multiplanar external texture transform was not run.";
3802 return false;
3803 }
Brandon Jones6661b282022-02-25 20:14:52 +00003804
dan sinclair41e4d9a2022-05-01 14:40:55 +00003805 auto* storage = tex->As<sem::StorageTexture>();
3806 auto* ms = tex->As<sem::MultisampledTexture>();
3807 auto* depth_ms = tex->As<sem::DepthMultisampledTexture>();
3808 auto* sampled = tex->As<sem::SampledTexture>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003809
dan sinclair41e4d9a2022-05-01 14:40:55 +00003810 if (storage && storage->access() != ast::Access::kRead) {
3811 out << "RW";
3812 }
3813 out << "Texture";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003814
dan sinclair41e4d9a2022-05-01 14:40:55 +00003815 switch (tex->dim()) {
3816 case ast::TextureDimension::k1d:
3817 out << "1D";
3818 break;
3819 case ast::TextureDimension::k2d:
3820 out << ((ms || depth_ms) ? "2DMS" : "2D");
3821 break;
3822 case ast::TextureDimension::k2dArray:
3823 out << ((ms || depth_ms) ? "2DMSArray" : "2DArray");
3824 break;
3825 case ast::TextureDimension::k3d:
3826 out << "3D";
3827 break;
3828 case ast::TextureDimension::kCube:
3829 out << "Cube";
3830 break;
3831 case ast::TextureDimension::kCubeArray:
3832 out << "CubeArray";
3833 break;
3834 default:
3835 TINT_UNREACHABLE(Writer, diagnostics_)
3836 << "unexpected TextureDimension " << tex->dim();
3837 return false;
3838 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003839
dan sinclair41e4d9a2022-05-01 14:40:55 +00003840 if (storage) {
3841 auto* component = image_format_to_rwtexture_type(storage->texel_format());
3842 if (component == nullptr) {
3843 TINT_ICE(Writer, diagnostics_) << "Unsupported StorageTexture TexelFormat: "
3844 << static_cast<int>(storage->texel_format());
3845 return false;
3846 }
3847 out << "<" << component << ">";
3848 } else if (depth_ms) {
3849 out << "<float4>";
3850 } else if (sampled || ms) {
3851 auto* subtype = sampled ? sampled->type() : ms->type();
3852 out << "<";
3853 if (subtype->Is<sem::F32>()) {
3854 out << "float4";
3855 } else if (subtype->Is<sem::I32>()) {
3856 out << "int4";
3857 } else if (subtype->Is<sem::U32>()) {
3858 out << "uint4";
3859 } else {
3860 TINT_ICE(Writer, diagnostics_) << "Unsupported multisampled texture type";
3861 return false;
3862 }
3863 out << ">";
3864 }
3865 return true;
3866 },
3867 [&](const sem::U32*) {
3868 out << "uint";
3869 return true;
3870 },
3871 [&](const sem::Vector* vec) {
3872 auto width = vec->Width();
3873 if (vec->type()->Is<sem::F32>() && width >= 1 && width <= 4) {
3874 out << "float" << width;
3875 } else if (vec->type()->Is<sem::I32>() && width >= 1 && width <= 4) {
3876 out << "int" << width;
3877 } else if (vec->type()->Is<sem::U32>() && width >= 1 && width <= 4) {
3878 out << "uint" << width;
3879 } else if (vec->type()->Is<sem::Bool>() && width >= 1 && width <= 4) {
3880 out << "bool" << width;
3881 } else {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003882 // For example, use "vector<float16_t, N>" for f16 vector.
dan sinclair41e4d9a2022-05-01 14:40:55 +00003883 out << "vector<";
dan sinclairff7cf212022-10-03 14:05:23 +00003884 if (!EmitType(out, vec->type(), address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003885 return false;
3886 }
3887 out << ", " << width << ">";
3888 }
3889 return true;
3890 },
3891 [&](const sem::Atomic* atomic) {
dan sinclairff7cf212022-10-03 14:05:23 +00003892 return EmitType(out, atomic->Type(), address_space, access, name);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003893 },
3894 [&](const sem::Void*) {
3895 out << "void";
3896 return true;
3897 },
3898 [&](Default) {
3899 diagnostics_.add_error(diag::System::Writer, "unknown type in EmitType");
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003900 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003901 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003902}
3903
3904bool GeneratorImpl::EmitTypeAndName(std::ostream& out,
3905 const sem::Type* type,
dan sinclairff7cf212022-10-03 14:05:23 +00003906 ast::AddressSpace address_space,
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003907 ast::Access access,
3908 const std::string& name) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003909 bool name_printed = false;
dan sinclairff7cf212022-10-03 14:05:23 +00003910 if (!EmitType(out, type, address_space, access, name, &name_printed)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003911 return false;
3912 }
3913 if (!name.empty() && !name_printed) {
3914 out << " " << name;
3915 }
3916 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003917}
3918
3919bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003920 auto it = emitted_structs_.emplace(str);
3921 if (!it.second) {
3922 return true;
3923 }
3924
dan sinclair41e4d9a2022-05-01 14:40:55 +00003925 line(b) << "struct " << StructName(str) << " {";
3926 {
3927 ScopedIndent si(b);
3928 for (auto* mem : str->Members()) {
3929 auto mem_name = builder_.Symbols().NameFor(mem->Name());
dan sinclair41e4d9a2022-05-01 14:40:55 +00003930 auto* ty = mem->Type();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003931 auto out = line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003932 std::string pre, post;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003933 if (auto* decl = mem->Declaration()) {
3934 for (auto* attr : decl->attributes) {
dan sinclairf9eeed62022-09-07 22:25:24 +00003935 if (attr->Is<ast::LocationAttribute>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003936 auto& pipeline_stage_uses = str->PipelineStageUses();
3937 if (pipeline_stage_uses.size() != 1) {
3938 TINT_ICE(Writer, diagnostics_) << "invalid entry point IO struct uses";
3939 }
3940
dan sinclairf9eeed62022-09-07 22:25:24 +00003941 auto loc = mem->Location().value();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003942 if (pipeline_stage_uses.count(sem::PipelineStageUsage::kVertexInput)) {
dan sinclairf9eeed62022-09-07 22:25:24 +00003943 post += " : TEXCOORD" + std::to_string(loc);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003944 } else if (pipeline_stage_uses.count(
3945 sem::PipelineStageUsage::kVertexOutput)) {
dan sinclairf9eeed62022-09-07 22:25:24 +00003946 post += " : TEXCOORD" + std::to_string(loc);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003947 } else if (pipeline_stage_uses.count(
3948 sem::PipelineStageUsage::kFragmentInput)) {
dan sinclairf9eeed62022-09-07 22:25:24 +00003949 post += " : TEXCOORD" + std::to_string(loc);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003950 } else if (pipeline_stage_uses.count(
3951 sem::PipelineStageUsage::kFragmentOutput)) {
dan sinclairf9eeed62022-09-07 22:25:24 +00003952 post += " : SV_Target" + std::to_string(loc);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003953 } else {
3954 TINT_ICE(Writer, diagnostics_) << "invalid use of location attribute";
3955 }
3956 } else if (auto* builtin = attr->As<ast::BuiltinAttribute>()) {
3957 auto name = builtin_to_attribute(builtin->builtin);
3958 if (name.empty()) {
3959 diagnostics_.add_error(diag::System::Writer, "unsupported builtin");
3960 return false;
3961 }
3962 post += " : " + name;
3963 } else if (auto* interpolate = attr->As<ast::InterpolateAttribute>()) {
3964 auto mod =
3965 interpolation_to_modifiers(interpolate->type, interpolate->sampling);
3966 if (mod.empty()) {
3967 diagnostics_.add_error(diag::System::Writer,
3968 "unsupported interpolation");
3969 return false;
3970 }
3971 pre += mod;
3972
3973 } else if (attr->Is<ast::InvariantAttribute>()) {
3974 // Note: `precise` is not exactly the same as `invariant`, but is
3975 // stricter and therefore provides the necessary guarantees.
3976 // See discussion here: https://github.com/gpuweb/gpuweb/issues/893
3977 pre += "precise ";
3978 } else if (!attr->IsAnyOf<ast::StructMemberAlignAttribute,
3979 ast::StructMemberOffsetAttribute,
3980 ast::StructMemberSizeAttribute>()) {
3981 TINT_ICE(Writer, diagnostics_)
3982 << "unhandled struct member attribute: " << attr->Name();
3983 return false;
3984 }
3985 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003986 }
3987
dan sinclair41e4d9a2022-05-01 14:40:55 +00003988 out << pre;
dan sinclairff7cf212022-10-03 14:05:23 +00003989 if (!EmitTypeAndName(out, ty, ast::AddressSpace::kNone, ast::Access::kReadWrite,
dan sinclair41e4d9a2022-05-01 14:40:55 +00003990 mem_name)) {
3991 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003992 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003993 out << post << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003994 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003995 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003996
dan sinclair41e4d9a2022-05-01 14:40:55 +00003997 line(b) << "};";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003998 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003999}
4000
dan sinclair41e4d9a2022-05-01 14:40:55 +00004001bool GeneratorImpl::EmitUnaryOp(std::ostream& out, const ast::UnaryOpExpression* expr) {
4002 switch (expr->op) {
4003 case ast::UnaryOp::kIndirection:
4004 case ast::UnaryOp::kAddressOf:
4005 return EmitExpression(out, expr->expr);
4006 case ast::UnaryOp::kComplement:
4007 out << "~";
4008 break;
4009 case ast::UnaryOp::kNot:
4010 out << "!";
4011 break;
4012 case ast::UnaryOp::kNegation:
4013 out << "-";
4014 break;
4015 }
4016 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004017
dan sinclair41e4d9a2022-05-01 14:40:55 +00004018 if (!EmitExpression(out, expr->expr)) {
4019 return false;
4020 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004021
dan sinclair41e4d9a2022-05-01 14:40:55 +00004022 out << ")";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004023
dan sinclair41e4d9a2022-05-01 14:40:55 +00004024 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004025}
4026
Ben Claytondcdf66e2022-06-17 12:48:51 +00004027bool GeneratorImpl::EmitVar(const ast::Var* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004028 auto* sem = builder_.Sem().Get(var);
4029 auto* type = sem->Type()->UnwrapRef();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004030
dan sinclair41e4d9a2022-05-01 14:40:55 +00004031 auto out = line();
dan sinclairff7cf212022-10-03 14:05:23 +00004032 if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(),
dan sinclair41e4d9a2022-05-01 14:40:55 +00004033 builder_.Symbols().NameFor(var->symbol))) {
4034 return false;
4035 }
4036
4037 out << " = ";
4038
dan sinclair6e77b472022-10-20 13:38:28 +00004039 if (var->initializer) {
4040 if (!EmitExpression(out, var->initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004041 return false;
4042 }
4043 } else {
4044 if (!EmitZeroValue(out, type)) {
4045 return false;
4046 }
4047 }
4048 out << ";";
4049
4050 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004051}
4052
Ben Claytondcdf66e2022-06-17 12:48:51 +00004053bool GeneratorImpl::EmitLet(const ast::Let* let) {
4054 auto* sem = builder_.Sem().Get(let);
4055 auto* type = sem->Type()->UnwrapRef();
4056
4057 auto out = line();
4058 out << "const ";
Ben Claytond2e0db32022-10-12 18:49:15 +00004059 if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined,
Ben Claytondcdf66e2022-06-17 12:48:51 +00004060 builder_.Symbols().NameFor(let->symbol))) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004061 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004062 }
Ben Claytondcdf66e2022-06-17 12:48:51 +00004063 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00004064 if (!EmitExpression(out, let->initializer)) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00004065 return false;
4066 }
4067 out << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004068
Ben Claytondcdf66e2022-06-17 12:48:51 +00004069 return true;
4070}
4071
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004072template <typename F>
4073bool GeneratorImpl::CallBuiltinHelper(std::ostream& out,
4074 const ast::CallExpression* call,
4075 const sem::Builtin* builtin,
4076 F&& build) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004077 // Generate the helper function if it hasn't been created already
4078 auto fn = utils::GetOrCreate(builtins_, builtin, [&]() -> std::string {
4079 TextBuffer b;
4080 TINT_DEFER(helpers_.Append(b));
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004081
dan sinclair41e4d9a2022-05-01 14:40:55 +00004082 auto fn_name = UniqueIdentifier(std::string("tint_") + sem::str(builtin->Type()));
4083 std::vector<std::string> parameter_names;
4084 {
4085 auto decl = line(&b);
dan sinclairff7cf212022-10-03 14:05:23 +00004086 if (!EmitTypeAndName(decl, builtin->ReturnType(), ast::AddressSpace::kNone,
Ben Claytond2e0db32022-10-12 18:49:15 +00004087 ast::Access::kUndefined, fn_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004088 return "";
4089 }
4090 {
4091 ScopedParen sp(decl);
4092 for (auto* param : builtin->Parameters()) {
4093 if (!parameter_names.empty()) {
4094 decl << ", ";
4095 }
4096 auto param_name = "param_" + std::to_string(parameter_names.size());
4097 const auto* ty = param->Type();
4098 if (auto* ptr = ty->As<sem::Pointer>()) {
4099 decl << "inout ";
4100 ty = ptr->StoreType();
4101 }
Ben Claytond2e0db32022-10-12 18:49:15 +00004102 if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone,
4103 ast::Access::kUndefined, param_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004104 return "";
4105 }
4106 parameter_names.emplace_back(std::move(param_name));
4107 }
4108 }
4109 decl << " {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004110 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004111 {
4112 ScopedIndent si(&b);
4113 if (!build(&b, parameter_names)) {
4114 return "";
4115 }
4116 }
4117 line(&b) << "}";
4118 line(&b);
4119 return fn_name;
4120 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004121
dan sinclair41e4d9a2022-05-01 14:40:55 +00004122 if (fn.empty()) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004123 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004124 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004125
4126 // Call the helper
4127 out << fn;
4128 {
4129 ScopedParen sp(out);
4130 bool first = true;
4131 for (auto* arg : call->args) {
4132 if (!first) {
4133 out << ", ";
4134 }
4135 first = false;
4136 if (!EmitExpression(out, arg)) {
4137 return false;
4138 }
4139 }
4140 }
4141 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004142}
4143
dan sinclair6a5bef12022-04-07 14:30:24 +00004144} // namespace tint::writer::hlsl