blob: 658beeb8495bb597c311ed7e444147a88487c9a9 [file] [log] [blame]
Austin Engcc2516a2023-10-17 20:57:54 +00001/// Copyright 2020 The Dawn & Tint Authors
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002//
Austin Engcc2516a2023-10-17 20:57:54 +00003// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
Ryan Harrisondbc13af2022-02-21 15:19:07 +00005//
Austin Engcc2516a2023-10-17 20:57:54 +00006// 1. Redistributions of source code must retain the above copyright notice, this
7// list of conditions and the following disclaimer.
Ryan Harrisondbc13af2022-02-21 15:19:07 +00008//
Austin Engcc2516a2023-10-17 20:57:54 +00009// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12//
13// 3. Neither the name of the copyright holder nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Ryan Harrisondbc13af2022-02-21 15:19:07 +000027
dan sinclair0bfeaf92023-07-26 17:39:51 +000028#include "src/tint/lang/hlsl/writer/ast_printer/ast_printer.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000029
Ryan Harrisondbc13af2022-02-21 15:19:07 +000030#include <cmath>
31#include <functional>
32#include <iomanip>
Ryan Harrisondbc13af2022-02-21 15:19:07 +000033#include <utility>
34#include <vector>
35
dan sinclair352f8c82023-07-21 00:40:07 +000036#include "src/tint/lang/core/constant/splat.h"
37#include "src/tint/lang/core/constant/value.h"
dan sinclairce6dffe2023-08-14 21:01:40 +000038#include "src/tint/lang/core/fluent_types.h"
dan sinclair352f8c82023-07-21 00:40:07 +000039#include "src/tint/lang/core/type/array.h"
40#include "src/tint/lang/core/type/atomic.h"
41#include "src/tint/lang/core/type/depth_multisampled_texture.h"
dan sinclair352f8c82023-07-21 00:40:07 +000042#include "src/tint/lang/core/type/multisampled_texture.h"
43#include "src/tint/lang/core/type/sampled_texture.h"
44#include "src/tint/lang/core/type/storage_texture.h"
45#include "src/tint/lang/core/type/texture_dimension.h"
Ben Clayton7a5f54ea2023-09-06 16:58:22 +000046#include "src/tint/lang/hlsl/writer/ast_raise/calculate_array_length.h"
47#include "src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access.h"
48#include "src/tint/lang/hlsl/writer/ast_raise/localize_struct_array_assignment.h"
49#include "src/tint/lang/hlsl/writer/ast_raise/num_workgroups_from_uniform.h"
Jiawei Shaoa87d0632023-11-16 00:21:08 +000050#include "src/tint/lang/hlsl/writer/ast_raise/pixel_local.h"
Ben Clayton7a5f54ea2023-09-06 16:58:22 +000051#include "src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.h"
Antonio Maiorano26a41b92024-02-05 21:36:47 +000052#include "src/tint/lang/hlsl/writer/common/option_helpers.h"
dan sinclair99181d82023-07-20 01:14:15 +000053#include "src/tint/lang/wgsl/ast/call_statement.h"
dan sinclair99181d82023-07-20 01:14:15 +000054#include "src/tint/lang/wgsl/ast/internal_attribute.h"
55#include "src/tint/lang/wgsl/ast/interpolate_attribute.h"
56#include "src/tint/lang/wgsl/ast/transform/add_empty_entry_point.h"
57#include "src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h"
58#include "src/tint/lang/wgsl/ast/transform/binding_remapper.h"
59#include "src/tint/lang/wgsl/ast/transform/builtin_polyfill.h"
dan sinclair99181d82023-07-20 01:14:15 +000060#include "src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h"
dan sinclair99181d82023-07-20 01:14:15 +000061#include "src/tint/lang/wgsl/ast/transform/demote_to_helper.h"
62#include "src/tint/lang/wgsl/ast/transform/direct_variable_access.h"
63#include "src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h"
64#include "src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h"
Ben Clayton7494e832023-07-25 15:30:31 +000065#include "src/tint/lang/wgsl/ast/transform/manager.h"
dan sinclair99181d82023-07-20 01:14:15 +000066#include "src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h"
dan sinclair99181d82023-07-20 01:14:15 +000067#include "src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h"
68#include "src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h"
dan sinclair997bc012024-02-22 02:08:41 +000069#include "src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.h"
dan sinclair99181d82023-07-20 01:14:15 +000070#include "src/tint/lang/wgsl/ast/transform/remove_phonies.h"
71#include "src/tint/lang/wgsl/ast/transform/robustness.h"
72#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.h"
dan sinclair99181d82023-07-20 01:14:15 +000073#include "src/tint/lang/wgsl/ast/transform/unshadow.h"
74#include "src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h"
75#include "src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h"
76#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
dan sinclaira4c17352023-07-20 09:21:10 +000077#include "src/tint/lang/wgsl/helpers/append_vector.h"
78#include "src/tint/lang/wgsl/helpers/check_supported_extensions.h"
dan sinclaird3b13692023-07-20 01:14:15 +000079#include "src/tint/lang/wgsl/sem/block_statement.h"
80#include "src/tint/lang/wgsl/sem/call.h"
81#include "src/tint/lang/wgsl/sem/function.h"
82#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
83#include "src/tint/lang/wgsl/sem/module.h"
84#include "src/tint/lang/wgsl/sem/statement.h"
85#include "src/tint/lang/wgsl/sem/struct.h"
86#include "src/tint/lang/wgsl/sem/switch_statement.h"
87#include "src/tint/lang/wgsl/sem/value_constructor.h"
88#include "src/tint/lang/wgsl/sem/value_conversion.h"
89#include "src/tint/lang/wgsl/sem/variable.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000090#include "src/tint/utils/containers/map.h"
Ben Claytonf848af22023-07-28 16:37:32 +000091#include "src/tint/utils/ice/ice.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000092#include "src/tint/utils/macros/compiler.h"
93#include "src/tint/utils/macros/defer.h"
94#include "src/tint/utils/macros/scoped_assignment.h"
95#include "src/tint/utils/rtti/switch.h"
Ben Clayton16be11d2023-08-01 00:37:35 +000096#include "src/tint/utils/strconv/float_to_string.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000097#include "src/tint/utils/text/string.h"
98#include "src/tint/utils/text/string_stream.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000099
dan sinclairce6dffe2023-08-14 21:01:40 +0000100using namespace tint::core::number_suffixes; // NOLINT
101using namespace tint::core::fluent_types; // NOLINT
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000102
dan sinclair0bfeaf92023-07-26 17:39:51 +0000103namespace tint::hlsl::writer {
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000104namespace {
105
106const char kTempNamePrefix[] = "tint_tmp";
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000107
Ben Claytoncd52f382023-08-07 13:11:08 +0000108const char* image_format_to_rwtexture_type(core::TexelFormat image_format) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000109 switch (image_format) {
Ben Claytoncd52f382023-08-07 13:11:08 +0000110 case core::TexelFormat::kBgra8Unorm:
111 case core::TexelFormat::kRgba8Unorm:
112 case core::TexelFormat::kRgba8Snorm:
113 case core::TexelFormat::kRgba16Float:
114 case core::TexelFormat::kR32Float:
115 case core::TexelFormat::kRg32Float:
116 case core::TexelFormat::kRgba32Float:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000117 return "float4";
Ben Claytoncd52f382023-08-07 13:11:08 +0000118 case core::TexelFormat::kRgba8Uint:
119 case core::TexelFormat::kRgba16Uint:
120 case core::TexelFormat::kR32Uint:
121 case core::TexelFormat::kRg32Uint:
122 case core::TexelFormat::kRgba32Uint:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000123 return "uint4";
Ben Claytoncd52f382023-08-07 13:11:08 +0000124 case core::TexelFormat::kRgba8Sint:
125 case core::TexelFormat::kRgba16Sint:
126 case core::TexelFormat::kR32Sint:
127 case core::TexelFormat::kRg32Sint:
128 case core::TexelFormat::kRgba32Sint:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000129 return "int4";
130 default:
131 return nullptr;
132 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000133}
134
dan sinclairbae54e72023-07-28 15:01:54 +0000135void PrintF32(StringStream& out, float value) {
Ben Claytone9f8b092022-06-01 13:14:39 +0000136 if (std::isinf(value)) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000137 out << "0.0f " << (value >= 0 ? "/* inf */" : "/* -inf */");
Ben Claytone9f8b092022-06-01 13:14:39 +0000138 } else if (std::isnan(value)) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000139 out << "0.0f /* nan */";
Ben Claytone9f8b092022-06-01 13:14:39 +0000140 } else {
dan sinclair37e9d112023-11-20 13:18:28 +0000141 out << tint::strconv::FloatToString(value) << "f";
Ben Claytone9f8b092022-06-01 13:14:39 +0000142 }
143}
144
dan sinclairbae54e72023-07-28 15:01:54 +0000145void PrintF16(StringStream& out, float value) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000146 if (std::isinf(value)) {
147 out << "0.0h " << (value >= 0 ? "/* inf */" : "/* -inf */");
148 } else if (std::isnan(value)) {
149 out << "0.0h /* nan */";
Zhaoming Jianga5988a32022-07-11 15:43:38 +0000150 } else {
dan sinclair37e9d112023-11-20 13:18:28 +0000151 out << tint::strconv::FloatToString(value) << "h";
Zhaoming Jianga5988a32022-07-11 15:43:38 +0000152 }
153}
154
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000155// Helper for writing " : register(RX, spaceY)", where R is the register, X is
156// the binding point binding value, and Y is the binding point group value.
157struct RegisterAndSpace {
Ben Clayton10252582023-07-25 20:53:25 +0000158 RegisterAndSpace(char r, BindingPoint bp) : reg(r), binding_point(bp) {}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000159
dan sinclair41e4d9a2022-05-01 14:40:55 +0000160 const char reg;
Ben Clayton10252582023-07-25 20:53:25 +0000161 BindingPoint const binding_point;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000162};
163
dan sinclairbae54e72023-07-28 15:01:54 +0000164StringStream& operator<<(StringStream& s, const RegisterAndSpace& rs) {
Peng Huangc00ff7f2023-03-31 17:55:19 +0000165 s << " : register(" << rs.reg << rs.binding_point.binding;
166 // Omit the space if it's 0, as it's the default.
167 // SM 5.0 doesn't support spaces, so we don't emit them if group is 0 for better compatibility.
168 if (rs.binding_point.group == 0) {
169 s << ")";
170 } else {
171 s << ", space" << rs.binding_point.group << ")";
172 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000173 return s;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000174}
175
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000176} // namespace
177
178SanitizedResult::SanitizedResult() = default;
179SanitizedResult::~SanitizedResult() = default;
180SanitizedResult::SanitizedResult(SanitizedResult&&) = default;
181
Ben Clayton5ed5cc42023-09-22 10:31:04 +0000182SanitizedResult Sanitize(const Program& in, const Options& options) {
Ben Clayton7494e832023-07-25 15:30:31 +0000183 ast::transform::Manager manager;
184 ast::transform::DataMap data;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000185
James Priceb4acbb82023-05-11 21:27:16 +0000186 manager.Add<ast::transform::DisableUniformityAnalysis>();
James Price791b4352022-05-11 13:50:33 +0000187
Ben Clayton46ee6392022-11-09 22:04:11 +0000188 // ExpandCompoundAssignment must come before BuiltinPolyfill
James Priceb4acbb82023-05-11 21:27:16 +0000189 manager.Add<ast::transform::ExpandCompoundAssignment>();
Ben Clayton46ee6392022-11-09 22:04:11 +0000190
James Priceb4acbb82023-05-11 21:27:16 +0000191 manager.Add<ast::transform::Unshadow>(); // Must come before DirectVariableAccess
Ben Clayton03de0e82023-03-02 20:48:48 +0000192
Ben Clayton03de0e82023-03-02 20:48:48 +0000193 // LocalizeStructArrayAssignment must come after:
194 // * SimplifyPointers, because it assumes assignment to arrays in structs are
195 // done directly, not indirectly.
196 // TODO(crbug.com/tint/1340): See if we can get rid of the duplicate
197 // SimplifyPointers transform. Can't do it right now because
198 // LocalizeStructArrayAssignment introduces pointers.
James Priceb4acbb82023-05-11 21:27:16 +0000199 manager.Add<ast::transform::SimplifyPointers>();
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000200 manager.Add<LocalizeStructArrayAssignment>();
Ben Clayton03de0e82023-03-02 20:48:48 +0000201
James Priceb4acbb82023-05-11 21:27:16 +0000202 manager.Add<ast::transform::PromoteSideEffectsToDecl>();
Ben Clayton03de0e82023-03-02 20:48:48 +0000203
204 if (!options.disable_robustness) {
Ben Clayton8525ff22023-03-06 21:05:01 +0000205 // Robustness must come after PromoteSideEffectsToDecl
206 // Robustness must come before BuiltinPolyfill and CanonicalizeEntryPointIO
James Priceb4acbb82023-05-11 21:27:16 +0000207 manager.Add<ast::transform::Robustness>();
Jiawei Shao455e4b82023-06-10 00:32:22 +0000208
209 ast::transform::Robustness::Config config = {};
Ben Clayton10252582023-07-25 20:53:25 +0000210 config.bindings_ignored = std::unordered_set<BindingPoint>(
Antonio Maiorano26a41b92024-02-05 21:36:47 +0000211 options.bindings.ignored_by_robustness_transform.cbegin(),
212 options.bindings.ignored_by_robustness_transform.cend());
Jiawei Shao2e119632023-06-21 01:43:25 +0000213
214 // Direct3D guarantees to return zero for any resource that is accessed out of bounds, and
215 // according to the description of the assembly store_uav_typed, out of bounds addressing
216 // means nothing gets written to memory.
217 config.texture_action = ast::transform::Robustness::Action::kIgnore;
218
Jiawei Shao455e4b82023-06-10 00:32:22 +0000219 data.Add<ast::transform::Robustness::Config>(config);
Ben Clayton03de0e82023-03-02 20:48:48 +0000220 }
221
Antonio Maiorano26a41b92024-02-05 21:36:47 +0000222 ExternalTextureOptions external_texture_options{};
223 RemapperData remapper_data{};
224 ArrayLengthFromUniformOptions array_length_from_uniform_options{};
225 PopulateBindingRelatedOptions(options, remapper_data, external_texture_options,
226 array_length_from_uniform_options);
Ben Clayton03de0e82023-03-02 20:48:48 +0000227
James Priceb4acbb82023-05-11 21:27:16 +0000228 manager.Add<ast::transform::BindingRemapper>();
dan sinclair15215502023-09-26 11:31:47 +0000229 // D3D11 and 12 registers like `t3` and `c3` have the same bindingOffset number in
230 // the remapping but should not be considered a collision because they have
231 // different types.
James Priceb4acbb82023-05-11 21:27:16 +0000232 data.Add<ast::transform::BindingRemapper::Remappings>(
Antonio Maiorano26a41b92024-02-05 21:36:47 +0000233 remapper_data, options.bindings.access_controls, /* allow_collisions */ true);
234
235 // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
236 // MultiplanarExternalTexture must come after BindingRemapper
237 data.Add<ast::transform::MultiplanarExternalTexture::NewBindingPoints>(
238 external_texture_options.bindings_map, /* may_collide */ true);
239 manager.Add<ast::transform::MultiplanarExternalTexture>();
dan sinclair39d40652023-03-15 13:41:46 +0000240
dan sinclair41e4d9a2022-05-01 14:40:55 +0000241 { // Builtin polyfills
James Priceb4acbb82023-05-11 21:27:16 +0000242 ast::transform::BuiltinPolyfill::Builtins polyfills;
243 polyfills.acosh = ast::transform::BuiltinPolyfill::Level::kFull;
dan sinclaird23f2962022-06-28 15:27:44 +0000244 polyfills.asinh = true;
James Priceb4acbb82023-05-11 21:27:16 +0000245 polyfills.atanh = ast::transform::BuiltinPolyfill::Level::kFull;
Ben Clayton02f04d92022-11-03 19:15:17 +0000246 polyfills.bitshift_modulo = true;
Ben Clayton6dbb4632022-10-31 17:54:49 +0000247 polyfills.clamp_int = true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000248 // TODO(crbug.com/tint/1449): Some of these can map to HLSL's `firstbitlow`
249 // and `firstbithigh`.
Ben Claytoncc3f8512023-03-09 21:26:12 +0000250 polyfills.conv_f32_to_iu32 = true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000251 polyfills.count_leading_zeros = true;
252 polyfills.count_trailing_zeros = true;
James Priceb4acbb82023-05-11 21:27:16 +0000253 polyfills.extract_bits = ast::transform::BuiltinPolyfill::Level::kFull;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000254 polyfills.first_leading_bit = true;
255 polyfills.first_trailing_bit = true;
James Priceb4acbb82023-05-11 21:27:16 +0000256 polyfills.insert_bits = ast::transform::BuiltinPolyfill::Level::kFull;
Jiawei Shao825f6262024-01-08 10:05:27 +0000257 polyfills.int_div_mod = !options.disable_polyfill_integer_div_mod;
Antonio Maioranoee665a42023-02-14 16:12:59 +0000258 polyfills.precise_float_mod = true;
Zhaoming Jiang04529be2023-02-27 02:59:50 +0000259 polyfills.reflect_vec2_f32 = options.polyfill_reflect_vec2_f32;
Ben Claytonc4ebf2c2022-09-22 22:59:16 +0000260 polyfills.texture_sample_base_clamp_to_edge_2d_f32 = true;
James Price128980f2023-01-06 02:25:06 +0000261 polyfills.workgroup_uniform_load = true;
Jiawei Shao7c0a8012023-11-29 00:25:56 +0000262 polyfills.dot_4x8_packed = options.polyfill_dot_4x8_packed;
Jiawei Shaodd817232024-01-12 08:10:46 +0000263 polyfills.pack_unpack_4x8 = options.polyfill_pack_unpack_4x8;
264 // Currently Pack4xU8Clamp() must be polyfilled because on latest DXC pack_clamp_u8()
265 // receives an int32_t4 as its input.
266 // See https://github.com/microsoft/DirectXShaderCompiler/issues/5091 for more details.
267 polyfills.pack_4xu8_clamp = true;
James Priceb4acbb82023-05-11 21:27:16 +0000268 data.Add<ast::transform::BuiltinPolyfill::Config>(polyfills);
269 manager.Add<ast::transform::BuiltinPolyfill>(); // Must come before DirectVariableAccess
dan sinclair41e4d9a2022-05-01 14:40:55 +0000270 }
Ben Clayton27aa57c2022-02-22 23:13:39 +0000271
James Priceb4acbb82023-05-11 21:27:16 +0000272 manager.Add<ast::transform::DirectVariableAccess>();
Antonio Maioranofa00fe92023-05-03 15:30:54 +0000273
dan sinclair41e4d9a2022-05-01 14:40:55 +0000274 if (!options.disable_workgroup_init) {
275 // ZeroInitWorkgroupMemory must come before CanonicalizeEntryPointIO as
276 // ZeroInitWorkgroupMemory may inject new builtin parameters.
James Priceb4acbb82023-05-11 21:27:16 +0000277 manager.Add<ast::transform::ZeroInitWorkgroupMemory>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000278 }
Ben Clayton03de0e82023-03-02 20:48:48 +0000279
Jiawei Shaoa87d0632023-11-16 00:21:08 +0000280 {
281 PixelLocal::Config cfg;
282 for (auto it : options.pixel_local_options.attachments) {
283 cfg.pls_member_to_rov_reg.Add(it.first, it.second);
284 }
285 for (auto it : options.pixel_local_options.attachment_formats) {
286 core::TexelFormat format = core::TexelFormat::kUndefined;
287 switch (it.second) {
288 case PixelLocalOptions::TexelFormat::kR32Sint:
289 format = core::TexelFormat::kR32Sint;
290 break;
291 case PixelLocalOptions::TexelFormat::kR32Uint:
292 format = core::TexelFormat::kR32Uint;
293 break;
294 case PixelLocalOptions::TexelFormat::kR32Float:
295 format = core::TexelFormat::kR32Float;
296 break;
297 default:
298 TINT_ICE() << "missing texel format for pixel local storage attachment";
299 return SanitizedResult();
300 }
301 cfg.pls_member_to_rov_format.Add(it.first, format);
302 }
303 cfg.rov_group_index = options.pixel_local_options.pixel_local_group_index;
304 data.Add<PixelLocal::Config>(cfg);
305 manager.Add<PixelLocal>();
306 }
307
Ben Clayton03de0e82023-03-02 20:48:48 +0000308 // CanonicalizeEntryPointIO must come after Robustness
James Priceb4acbb82023-05-11 21:27:16 +0000309 manager.Add<ast::transform::CanonicalizeEntryPointIO>();
shrekshaof9c66332022-11-22 21:36:27 +0000310
shrekshao8f0607a2023-04-18 01:34:41 +0000311 if (options.truncate_interstage_variables) {
shrekshaof9c66332022-11-22 21:36:27 +0000312 // When interstage_locations is empty, it means there's no user-defined interstage variables
shrekshao8f0607a2023-04-18 01:34:41 +0000313 // being used in the next stage. Still, HLSL compiler register mismatch could happen, if
314 // there's builtin inputs used in the next stage. So we still run
315 // TruncateInterstageVariables transform.
shrekshaof9c66332022-11-22 21:36:27 +0000316
317 // TruncateInterstageVariables itself will skip when interstage_locations matches exactly
318 // with the current stage output.
319
320 // Build the config for internal TruncateInterstageVariables transform.
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000321 TruncateInterstageVariables::Config truncate_interstage_variables_cfg;
shrekshaof9c66332022-11-22 21:36:27 +0000322 truncate_interstage_variables_cfg.interstage_locations =
323 std::move(options.interstage_locations);
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000324 manager.Add<TruncateInterstageVariables>();
325 data.Add<TruncateInterstageVariables::Config>(std::move(truncate_interstage_variables_cfg));
shrekshaof9c66332022-11-22 21:36:27 +0000326 }
327
dan sinclair41e4d9a2022-05-01 14:40:55 +0000328 // NumWorkgroupsFromUniform must come after CanonicalizeEntryPointIO, as it
329 // assumes that num_workgroups builtins only appear as struct members and are
330 // only accessed directly via member accessors.
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000331 manager.Add<NumWorkgroupsFromUniform>();
James Priceb4acbb82023-05-11 21:27:16 +0000332 manager.Add<ast::transform::VectorizeScalarMatrixInitializers>();
333 manager.Add<ast::transform::SimplifyPointers>();
334 manager.Add<ast::transform::RemovePhonies>();
James Price744d0eb2022-11-09 19:58:59 +0000335
Ben Clayton03de0e82023-03-02 20:48:48 +0000336 // Build the config for the internal ArrayLengthFromUniform transform.
James Priceb4acbb82023-05-11 21:27:16 +0000337 ast::transform::ArrayLengthFromUniform::Config array_length_from_uniform_cfg(
Antonio Maiorano26a41b92024-02-05 21:36:47 +0000338 array_length_from_uniform_options.ubo_binding);
Ben Clayton03de0e82023-03-02 20:48:48 +0000339 array_length_from_uniform_cfg.bindpoint_to_size_index =
Antonio Maiorano26a41b92024-02-05 21:36:47 +0000340 std::move(array_length_from_uniform_options.bindpoint_to_size_index);
Ben Clayton03de0e82023-03-02 20:48:48 +0000341
James Price744d0eb2022-11-09 19:58:59 +0000342 // DemoteToHelper must come after CanonicalizeEntryPointIO, PromoteSideEffectsToDecl, and
343 // ExpandCompoundAssignment.
344 // TODO(crbug.com/tint/1752): This is only necessary when FXC is being used.
James Priceb4acbb82023-05-11 21:27:16 +0000345 manager.Add<ast::transform::DemoteToHelper>();
James Price744d0eb2022-11-09 19:58:59 +0000346
Ben Clayton559e5a22023-04-17 14:24:58 +0000347 // ArrayLengthFromUniform must come after SimplifyPointers as it assumes that the form of the
348 // array length argument is &var.array.
James Priceb4acbb82023-05-11 21:27:16 +0000349 manager.Add<ast::transform::ArrayLengthFromUniform>();
350 data.Add<ast::transform::ArrayLengthFromUniform::Config>(
351 std::move(array_length_from_uniform_cfg));
dan sinclair41e4d9a2022-05-01 14:40:55 +0000352 // DecomposeMemoryAccess must come after:
Ben Clayton559e5a22023-04-17 14:24:58 +0000353 // * SimplifyPointers, as we cannot take the address of calls to
354 // DecomposeMemoryAccess::Intrinsic and we need to fold away the address-of and dereferences
355 // of `*(&(intrinsic_load()))` expressions.
dan sinclair41e4d9a2022-05-01 14:40:55 +0000356 // * RemovePhonies, as phonies can be assigned a pointer to a
357 // non-constructible buffer, or dynamic array, which DMA cannot cope with.
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000358 manager.Add<DecomposeMemoryAccess>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000359 // CalculateArrayLength must come after DecomposeMemoryAccess, as
360 // DecomposeMemoryAccess special-cases the arrayLength() intrinsic, which
361 // will be transformed by CalculateArrayLength
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000362 manager.Add<CalculateArrayLength>();
James Priceb4acbb82023-05-11 21:27:16 +0000363 manager.Add<ast::transform::PromoteInitializersToLet>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000364
dan sinclair997bc012024-02-22 02:08:41 +0000365 manager.Add<ast::transform::RemoveContinueInSwitch>();
Antonio Maioranob3497102022-03-31 15:02:25 +0000366
James Priceb4acbb82023-05-11 21:27:16 +0000367 manager.Add<ast::transform::AddEmptyEntryPoint>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000368
James Priceb4acbb82023-05-11 21:27:16 +0000369 data.Add<ast::transform::CanonicalizeEntryPointIO::Config>(
370 ast::transform::CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000371 data.Add<NumWorkgroupsFromUniform::Config>(options.root_constant_binding_point);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000372
dan sinclair41e4d9a2022-05-01 14:40:55 +0000373 SanitizedResult result;
Ben Clayton7494e832023-07-25 15:30:31 +0000374 ast::transform::DataMap outputs;
James Price0e6534e2023-05-17 01:21:45 +0000375 result.program = manager.Run(in, data, outputs);
376 if (auto* res = outputs.Get<ast::transform::ArrayLengthFromUniform::Result>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000377 result.used_array_length_from_uniform_indices = std::move(res->used_size_indices);
378 }
379 return result;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000380}
381
Ben Clayton5ed5cc42023-09-22 10:31:04 +0000382ASTPrinter::ASTPrinter(const Program& program) : builder_(ProgramBuilder::Wrap(program)) {}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000383
dan sinclair0bfeaf92023-07-26 17:39:51 +0000384ASTPrinter::~ASTPrinter() = default;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000385
dan sinclair0bfeaf92023-07-26 17:39:51 +0000386bool ASTPrinter::Generate() {
Ben Clayton2550b492023-10-11 10:41:12 +0000387 if (!tint::wgsl::CheckSupportedExtensions(
dan sinclair0bfeaf92023-07-26 17:39:51 +0000388 "HLSL", builder_.AST(), diagnostics_,
dan sinclairbae54e72023-07-28 15:01:54 +0000389 Vector{
Ben Clayton11653892023-09-19 19:15:59 +0000390 wgsl::Extension::kChromiumDisableUniformityAnalysis,
Ben Clayton11653892023-09-19 19:15:59 +0000391 wgsl::Extension::kChromiumExperimentalPushConstant,
Ben Clayton11653892023-09-19 19:15:59 +0000392 wgsl::Extension::kChromiumExperimentalSubgroups,
393 wgsl::Extension::kF16,
394 wgsl::Extension::kChromiumInternalDualSourceBlending,
Jiawei Shaoa87d0632023-11-16 00:21:08 +0000395 wgsl::Extension::kChromiumExperimentalPixelLocal,
dan sinclair0bfeaf92023-07-26 17:39:51 +0000396 })) {
Ben Clayton1a567782022-10-14 13:38:27 +0000397 return false;
398 }
399
dan sinclairbae54e72023-07-28 15:01:54 +0000400 const tint::TypeInfo* last_kind = nullptr;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000401 size_t last_padding_line = 0;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000402
dan sinclair41e4d9a2022-05-01 14:40:55 +0000403 auto* mod = builder_.Sem().Module();
404 for (auto* decl : mod->DependencyOrderedDeclarations()) {
James Price631aaa32023-11-02 15:03:02 +0000405 if (decl->IsAnyOf<ast::Alias, ast::DiagnosticDirective, ast::Enable, ast::Requires,
406 ast::ConstAssert>()) {
Ben Claytonb4744ac2022-08-03 07:01:08 +0000407 continue; // These are not emitted.
James Price791b4352022-05-11 13:50:33 +0000408 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000409
dan sinclair41e4d9a2022-05-01 14:40:55 +0000410 // Emit a new line between declarations if the type of declaration has
411 // changed, or we're about to emit a function
412 auto* kind = &decl->TypeInfo();
413 if (current_buffer_->lines.size() != last_padding_line) {
414 if (last_kind && (last_kind != kind || decl->Is<ast::Function>())) {
dan sinclair67a18932023-06-26 19:54:49 +0000415 Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000416 last_padding_line = current_buffer_->lines.size();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000417 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000418 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000419 last_kind = kind;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000420
Ben Clayton2d501c52024-01-17 01:27:37 +0000421 global_insertion_point_ = current_buffer_->lines.size();
422
dan sinclair41e4d9a2022-05-01 14:40:55 +0000423 bool ok = Switch(
424 decl,
425 [&](const ast::Variable* global) { //
426 return EmitGlobalVariable(global);
427 },
428 [&](const ast::Struct* str) {
429 auto* ty = builder_.Sem().Get(str);
dan sinclairff7cf212022-10-03 14:05:23 +0000430 auto address_space_uses = ty->AddressSpaceUsage();
Ben Clayton8be957f2024-01-29 16:00:36 +0000431 if (address_space_uses.Count() !=
432 ((address_space_uses.Contains(core::AddressSpace::kStorage) ? 1u : 0u) +
433 (address_space_uses.Contains(core::AddressSpace::kUniform) ? 1u : 0u))) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000434 // The structure is used as something other than a storage buffer or
435 // uniform buffer, so it needs to be emitted.
436 // Storage buffer are read and written to via a ByteAddressBuffer
437 // instead of true structure.
438 // Structures used as uniform buffer are read from an array of
439 // vectors instead of true structure.
440 return EmitStructType(current_buffer_, ty);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000441 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000442 return true;
443 },
444 [&](const ast::Function* func) {
445 if (func->IsEntryPoint()) {
446 return EmitEntryPointFunction(func);
447 }
448 return EmitFunction(func);
Ben Claytond6082c52023-10-26 16:02:01 +0000449 }, //
450 TINT_ICE_ON_NO_MATCH);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000451
452 if (!ok) {
453 return false;
454 }
455 }
456
457 if (!helpers_.lines.empty()) {
458 current_buffer_->Insert(helpers_, 0, 0);
459 }
460
461 return true;
462}
463
dan sinclair0bfeaf92023-07-26 17:39:51 +0000464bool ASTPrinter::EmitDynamicVectorAssignment(const ast::AssignmentStatement* stmt,
dan sinclaircedcdf32023-08-10 02:39:48 +0000465 const core::type::Vector* vec) {
Ben Clayton7a27d6c2024-01-31 19:44:28 +0000466 auto name = tint::GetOrAdd(dynamic_vector_write_, vec, [&]() -> std::string {
Ben Clayton80144d22024-01-26 23:45:49 +0000467 std::string fn = UniqueIdentifier("set_vector_element");
dan sinclair41e4d9a2022-05-01 14:40:55 +0000468 {
dan sinclair67a18932023-06-26 19:54:49 +0000469 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000470 out << "void " << fn << "(inout ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000471 if (!EmitTypeAndName(out, vec, core::AddressSpace::kUndefined, core::Access::kUndefined,
472 "vec")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000473 return "";
474 }
475 out << ", int idx, ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000476 if (!EmitTypeAndName(out, vec->type(), core::AddressSpace::kUndefined,
477 core::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000478 return "";
479 }
480 out << ") {";
481 }
482 {
483 ScopedIndent si(&helpers_);
dan sinclair67a18932023-06-26 19:54:49 +0000484 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000485 switch (vec->Width()) {
486 case 2:
487 out << "vec = (idx.xx == int2(0, 1)) ? val.xx : vec;";
488 break;
489 case 3:
490 out << "vec = (idx.xxx == int3(0, 1, 2)) ? val.xxx : vec;";
491 break;
492 case 4:
493 out << "vec = (idx.xxxx == int4(0, 1, 2, 3)) ? val.xxxx : vec;";
494 break;
495 default:
Ben Claytonf848af22023-07-28 16:37:32 +0000496 TINT_UNREACHABLE() << "invalid vector size " << vec->Width();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000497 break;
498 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000499 }
dan sinclair67a18932023-06-26 19:54:49 +0000500 Line(&helpers_) << "}";
501 Line(&helpers_);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000502 return fn;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000503 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000504
dan sinclair41e4d9a2022-05-01 14:40:55 +0000505 if (name.empty()) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000506 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000507 }
508
dan sinclair41e4d9a2022-05-01 14:40:55 +0000509 auto* ast_access_expr = stmt->lhs->As<ast::IndexAccessorExpression>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000510
dan sinclair67a18932023-06-26 19:54:49 +0000511 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000512 out << name << "(";
513 if (!EmitExpression(out, ast_access_expr->object)) {
514 return false;
515 }
516 out << ", ";
517 if (!EmitExpression(out, ast_access_expr->index)) {
518 return false;
519 }
520 out << ", ";
521 if (!EmitExpression(out, stmt->rhs)) {
522 return false;
523 }
524 out << ");";
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000525
dan sinclair41e4d9a2022-05-01 14:40:55 +0000526 return true;
527}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000528
dan sinclair0bfeaf92023-07-26 17:39:51 +0000529bool ASTPrinter::EmitDynamicMatrixVectorAssignment(const ast::AssignmentStatement* stmt,
dan sinclaircedcdf32023-08-10 02:39:48 +0000530 const core::type::Matrix* mat) {
Ben Clayton7a27d6c2024-01-31 19:44:28 +0000531 auto name = tint::GetOrAdd(dynamic_matrix_vector_write_, mat, [&]() -> std::string {
Ben Clayton80144d22024-01-26 23:45:49 +0000532 std::string fn = UniqueIdentifier("set_matrix_column");
dan sinclair41e4d9a2022-05-01 14:40:55 +0000533 {
dan sinclair67a18932023-06-26 19:54:49 +0000534 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000535 out << "void " << fn << "(inout ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000536 if (!EmitTypeAndName(out, mat, core::AddressSpace::kUndefined, core::Access::kUndefined,
537 "mat")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000538 return "";
539 }
540 out << ", int col, ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000541 if (!EmitTypeAndName(out, mat->ColumnType(), core::AddressSpace::kUndefined,
542 core::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000543 return "";
544 }
545 out << ") {";
546 }
547 {
548 ScopedIndent si(&helpers_);
dan sinclair67a18932023-06-26 19:54:49 +0000549 Line(&helpers_) << "switch (col) {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000550 {
551 ScopedIndent si2(&helpers_);
552 for (uint32_t i = 0; i < mat->columns(); ++i) {
dan sinclair67a18932023-06-26 19:54:49 +0000553 Line(&helpers_) << "case " << i << ": mat[" << i << "] = val; break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000554 }
555 }
dan sinclair67a18932023-06-26 19:54:49 +0000556 Line(&helpers_) << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000557 }
dan sinclair67a18932023-06-26 19:54:49 +0000558 Line(&helpers_) << "}";
559 Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000560 return fn;
561 });
562
563 if (name.empty()) {
564 return false;
565 }
566
567 auto* ast_access_expr = stmt->lhs->As<ast::IndexAccessorExpression>();
568
dan sinclair67a18932023-06-26 19:54:49 +0000569 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000570 out << name << "(";
571 if (!EmitExpression(out, ast_access_expr->object)) {
572 return false;
573 }
574 out << ", ";
575 if (!EmitExpression(out, ast_access_expr->index)) {
576 return false;
577 }
578 out << ", ";
579 if (!EmitExpression(out, stmt->rhs)) {
580 return false;
581 }
582 out << ");";
583
584 return true;
585}
586
dan sinclair0bfeaf92023-07-26 17:39:51 +0000587bool ASTPrinter::EmitDynamicMatrixScalarAssignment(const ast::AssignmentStatement* stmt,
dan sinclaircedcdf32023-08-10 02:39:48 +0000588 const core::type::Matrix* mat) {
Antonio Maioranof031ca22023-02-02 22:16:42 +0000589 auto* lhs_row_access = stmt->lhs->As<ast::IndexAccessorExpression>();
590 auto* lhs_col_access = lhs_row_access->object->As<ast::IndexAccessorExpression>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000591
Ben Clayton7a27d6c2024-01-31 19:44:28 +0000592 auto name = tint::GetOrAdd(dynamic_matrix_scalar_write_, mat, [&]() -> std::string {
Ben Clayton80144d22024-01-26 23:45:49 +0000593 std::string fn = UniqueIdentifier("set_matrix_scalar");
dan sinclair41e4d9a2022-05-01 14:40:55 +0000594 {
dan sinclair67a18932023-06-26 19:54:49 +0000595 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000596 out << "void " << fn << "(inout ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000597 if (!EmitTypeAndName(out, mat, core::AddressSpace::kUndefined, core::Access::kUndefined,
598 "mat")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000599 return "";
600 }
601 out << ", int col, int row, ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000602 if (!EmitTypeAndName(out, mat->type(), core::AddressSpace::kUndefined,
603 core::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000604 return "";
605 }
606 out << ") {";
607 }
608 {
609 ScopedIndent si(&helpers_);
dan sinclair67a18932023-06-26 19:54:49 +0000610 Line(&helpers_) << "switch (col) {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000611 {
612 ScopedIndent si2(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000613 for (uint32_t i = 0; i < mat->columns(); ++i) {
dan sinclair67a18932023-06-26 19:54:49 +0000614 Line(&helpers_) << "case " << i << ":";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000615 {
616 auto vec_name = "mat[" + std::to_string(i) + "]";
617 ScopedIndent si3(&helpers_);
618 {
dan sinclair67a18932023-06-26 19:54:49 +0000619 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000620 switch (mat->rows()) {
621 case 2:
622 out << vec_name
623 << " = (row.xx == int2(0, 1)) ? val.xx : " << vec_name
624 << ";";
625 break;
626 case 3:
627 out << vec_name
628 << " = (row.xxx == int3(0, 1, 2)) ? val.xxx : " << vec_name
629 << ";";
630 break;
631 case 4:
632 out << vec_name
633 << " = (row.xxxx == int4(0, 1, 2, 3)) ? val.xxxx : "
634 << vec_name << ";";
635 break;
Antonio Maioranof031ca22023-02-02 22:16:42 +0000636 default: {
637 auto* vec = TypeOf(lhs_row_access->object)
Antonio Maiorano08d92792024-01-11 20:51:50 +0000638 ->UnwrapPtrOrRef()
dan sinclaircedcdf32023-08-10 02:39:48 +0000639 ->As<core::type::Vector>();
Ben Claytonf848af22023-07-28 16:37:32 +0000640 TINT_UNREACHABLE() << "invalid vector size " << vec->Width();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000641 break;
Antonio Maioranof031ca22023-02-02 22:16:42 +0000642 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000643 }
644 }
dan sinclair67a18932023-06-26 19:54:49 +0000645 Line(&helpers_) << "break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000646 }
647 }
648 }
dan sinclair67a18932023-06-26 19:54:49 +0000649 Line(&helpers_) << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000650 }
dan sinclair67a18932023-06-26 19:54:49 +0000651 Line(&helpers_) << "}";
652 Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000653 return fn;
654 });
655
656 if (name.empty()) {
657 return false;
658 }
659
dan sinclair67a18932023-06-26 19:54:49 +0000660 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000661 out << name << "(";
Antonio Maioranof031ca22023-02-02 22:16:42 +0000662 if (!EmitExpression(out, lhs_col_access->object)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000663 return false;
664 }
665 out << ", ";
666 if (!EmitExpression(out, lhs_col_access->index)) {
667 return false;
668 }
669 out << ", ";
670 if (!EmitExpression(out, lhs_row_access->index)) {
671 return false;
672 }
673 out << ", ";
674 if (!EmitExpression(out, stmt->rhs)) {
675 return false;
676 }
677 out << ");";
678
679 return true;
680}
681
dan sinclairbae54e72023-07-28 15:01:54 +0000682bool ASTPrinter::EmitIndexAccessor(StringStream& out, const ast::IndexAccessorExpression* expr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000683 if (!EmitExpression(out, expr->object)) {
684 return false;
685 }
686 out << "[";
687
688 if (!EmitExpression(out, expr->index)) {
689 return false;
690 }
691 out << "]";
692
693 return true;
694}
695
Ben Clayton52e6a0f2024-02-21 08:11:33 +0000696bool ASTPrinter::EmitBitcastCall(StringStream& out, const ast::CallExpression* call) {
697 auto* arg = call->args[0];
698 auto* src_type = TypeOf(arg)->UnwrapRef();
699 auto* dst_type = TypeOf(call);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000700
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000701 auto* src_el_type = src_type->DeepestElement();
702 auto* dst_el_type = dst_type->DeepestElement();
703
704 if (!dst_el_type->is_integer_scalar() && !dst_el_type->is_float_scalar()) {
Ben Claytonc27315a2024-02-26 20:24:06 +0000705 diagnostics_.AddError(diag::System::Writer, Source{})
706 << "Unable to do bitcast to type " << dst_el_type->FriendlyName();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000707 return false;
708 }
709
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000710 // Handle identity bitcast.
711 if (src_type == dst_type) {
Ben Clayton52e6a0f2024-02-21 08:11:33 +0000712 return EmitExpression(out, arg);
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000713 }
714
715 // Handle the f16 types using polyfill functions
dan sinclaircedcdf32023-08-10 02:39:48 +0000716 if (src_el_type->Is<core::type::F16>() || dst_el_type->Is<core::type::F16>()) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000717 auto f16_bitcast_polyfill = [&]() {
dan sinclaircedcdf32023-08-10 02:39:48 +0000718 if (src_el_type->Is<core::type::F16>()) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000719 // Source type must be vec2<f16> or vec4<f16>, since type f16 and vec3<f16> can only
720 // have identity bitcast.
dan sinclaircedcdf32023-08-10 02:39:48 +0000721 auto* src_vec = src_type->As<core::type::Vector>();
Ben Claytonf848af22023-07-28 16:37:32 +0000722 TINT_ASSERT(src_vec);
723 TINT_ASSERT(((src_vec->Width() == 2u) || (src_vec->Width() == 4u)));
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000724
725 // Bitcast f16 types to others by converting the given f16 value to f32 and call
726 // f32tof16 to get the bits. This should be safe, because the convertion is precise
727 // for finite and infinite f16 value as they are exactly representable by f32, and
728 // WGSL spec allow any result if f16 value is NaN.
Ben Clayton7a27d6c2024-01-31 19:44:28 +0000729 return tint::GetOrAdd(
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000730 bitcast_funcs_, BinaryType{{src_type, dst_type}}, [&]() -> std::string {
731 TextBuffer b;
732 TINT_DEFER(helpers_.Append(b));
733
734 auto fn_name = UniqueIdentifier(std::string("tint_bitcast_from_f16"));
735 {
736 auto decl = Line(&b);
Ben Claytoncd52f382023-08-07 13:11:08 +0000737 if (!EmitTypeAndName(decl, dst_type, core::AddressSpace::kUndefined,
738 core::Access::kUndefined, fn_name)) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000739 return "";
740 }
741 {
742 ScopedParen sp(decl);
Ben Claytoncd52f382023-08-07 13:11:08 +0000743 if (!EmitTypeAndName(decl, src_type, core::AddressSpace::kUndefined,
744 core::Access::kUndefined, "src")) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000745 return "";
746 }
747 }
748 decl << " {";
749 }
750 {
751 ScopedIndent si(&b);
752 {
753 Line(&b) << "uint" << src_vec->Width() << " r = f32tof16(float"
754 << src_vec->Width() << "(src));";
755
756 {
757 auto s = Line(&b);
758 s << "return as";
Ben Claytoncd52f382023-08-07 13:11:08 +0000759 if (!EmitType(s, dst_el_type, core::AddressSpace::kUndefined,
760 core::Access::kReadWrite, "")) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000761 return "";
762 }
763 s << "(";
764 switch (src_vec->Width()) {
765 case 2: {
766 s << "uint((r.x & 0xffff) | ((r.y & 0xffff) << 16))";
767 break;
768 }
769 case 4: {
770 s << "uint2((r.x & 0xffff) | ((r.y & 0xffff) << 16), "
771 "(r.z & 0xffff) | ((r.w & 0xffff) << 16))";
772 break;
773 }
774 }
775 s << ");";
776 }
777 }
778 }
779 Line(&b) << "}";
780 Line(&b);
781 return fn_name;
782 });
783 } else {
784 // Destination type must be vec2<f16> or vec4<f16>.
dan sinclaircedcdf32023-08-10 02:39:48 +0000785 auto* dst_vec = dst_type->As<core::type::Vector>();
Ben Claytonf848af22023-07-28 16:37:32 +0000786 TINT_ASSERT((dst_vec && ((dst_vec->Width() == 2u) || (dst_vec->Width() == 4u)) &&
dan sinclaircedcdf32023-08-10 02:39:48 +0000787 dst_el_type->Is<core::type::F16>()));
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000788 // Source type must be f32/i32/u32 or vec2<f32/i32/u32>.
dan sinclaircedcdf32023-08-10 02:39:48 +0000789 auto* src_vec = src_type->As<core::type::Vector>();
790 TINT_ASSERT(
791 (src_type->IsAnyOf<core::type::I32, core::type::U32, core::type::F32>() ||
792 (src_vec && src_vec->Width() == 2u &&
793 src_el_type->IsAnyOf<core::type::I32, core::type::U32, core::type::F32>())));
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000794 std::string src_type_suffix = (src_vec ? "2" : "");
795
796 // Bitcast other types to f16 types by reinterpreting their bits as f16 using
797 // f16tof32, and convert the result f32 to f16. This should be safe, because the
798 // convertion is precise for finite and infinite f16 result value as they are
799 // exactly representable by f32, and WGSL spec allow any result if f16 result value
800 // would be NaN.
Ben Clayton7a27d6c2024-01-31 19:44:28 +0000801 return tint::GetOrAdd(
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000802 bitcast_funcs_, BinaryType{{src_type, dst_type}}, [&]() -> std::string {
803 TextBuffer b;
804 TINT_DEFER(helpers_.Append(b));
805
806 auto fn_name = UniqueIdentifier(std::string("tint_bitcast_to_f16"));
807 {
808 auto decl = Line(&b);
Ben Claytoncd52f382023-08-07 13:11:08 +0000809 if (!EmitTypeAndName(decl, dst_type, core::AddressSpace::kUndefined,
810 core::Access::kUndefined, fn_name)) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000811 return "";
812 }
813 {
814 ScopedParen sp(decl);
Ben Claytoncd52f382023-08-07 13:11:08 +0000815 if (!EmitTypeAndName(decl, src_type, core::AddressSpace::kUndefined,
816 core::Access::kUndefined, "src")) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000817 return "";
818 }
819 }
820 decl << " {";
821 }
822 {
823 ScopedIndent si(&b);
824 {
825 // Convert the source to uint for f16tof32.
826 Line(&b) << "uint" << src_type_suffix << " v = asuint(src);";
827 // Reinterprete the low 16 bits and high 16 bits
828 Line(&b) << "float" << src_type_suffix
829 << " t_low = f16tof32(v & 0xffff);";
830 Line(&b) << "float" << src_type_suffix
831 << " t_high = f16tof32((v >> 16) & 0xffff);";
832 // Construct the result f16 vector
833 {
834 auto s = Line(&b);
835 s << "return ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000836 if (!EmitType(s, dst_type, core::AddressSpace::kUndefined,
837 core::Access::kReadWrite, "")) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000838 return "";
839 }
840 s << "(";
841 switch (dst_vec->Width()) {
842 case 2: {
843 s << "t_low.x, t_high.x";
844 break;
845 }
846 case 4: {
847 s << "t_low.x, t_high.x, t_low.y, t_high.y";
848 break;
849 }
850 }
851 s << ");";
852 }
853 }
854 }
855 Line(&b) << "}";
856 Line(&b);
857 return fn_name;
858 });
859 }
860 };
861
862 // Get or create the polyfill
863 auto fn = f16_bitcast_polyfill();
864 if (fn.empty()) {
865 return false;
866 }
867 // Call the polyfill
868 out << fn;
869 {
870 ScopedParen sp(out);
Ben Clayton52e6a0f2024-02-21 08:11:33 +0000871 if (!EmitExpression(out, arg)) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000872 return false;
873 }
874 }
875
876 return true;
877 }
878
879 // Otherwise, bitcasting between non-f16 types.
dan sinclaircedcdf32023-08-10 02:39:48 +0000880 TINT_ASSERT((!src_el_type->Is<core::type::F16>() && !dst_el_type->Is<core::type::F16>()));
dan sinclair41e4d9a2022-05-01 14:40:55 +0000881 out << "as";
Ben Claytoncd52f382023-08-07 13:11:08 +0000882 if (!EmitType(out, dst_el_type, core::AddressSpace::kUndefined, core::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000883 return false;
884 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000885 out << "(";
Ben Clayton52e6a0f2024-02-21 08:11:33 +0000886 if (!EmitExpression(out, arg)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000887 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000888 }
889 out << ")";
890 return true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000891}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000892
dan sinclair0bfeaf92023-07-26 17:39:51 +0000893bool ASTPrinter::EmitAssign(const ast::AssignmentStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000894 if (auto* lhs_access = stmt->lhs->As<ast::IndexAccessorExpression>()) {
Antonio Maiorano08d92792024-01-11 20:51:50 +0000895 auto validate_obj_not_pointer = [&](const core::type::Type* object_ty) {
896 if (TINT_UNLIKELY(object_ty->Is<core::type::Pointer>())) {
897 TINT_ICE() << "lhs of index accessor should not be a pointer. These should have "
898 "been removed by transforms such as SimplifyPointers, "
899 "DecomposeMemoryAccess, and DirectVariableAccess";
900 return false;
901 }
902 return true;
903 };
904
dan sinclair41e4d9a2022-05-01 14:40:55 +0000905 // BUG(crbug.com/tint/1333): work around assignment of scalar to matrices
906 // with at least one dynamic index
907 if (auto* lhs_sub_access = lhs_access->object->As<ast::IndexAccessorExpression>()) {
Antonio Maiorano08d92792024-01-11 20:51:50 +0000908 const auto* lhs_sub_access_type = TypeOf(lhs_sub_access->object);
909 if (!validate_obj_not_pointer(lhs_sub_access_type)) {
910 return false;
911 }
912 if (auto* mat = lhs_sub_access_type->UnwrapRef()->As<core::type::Matrix>()) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +0000913 auto* rhs_row_idx_sem = builder_.Sem().GetVal(lhs_access->index);
914 auto* rhs_col_idx_sem = builder_.Sem().GetVal(lhs_sub_access->index);
Antonio Maioranof031ca22023-02-02 22:16:42 +0000915 if (!rhs_row_idx_sem->ConstantValue() || !rhs_col_idx_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000916 return EmitDynamicMatrixScalarAssignment(stmt, mat);
917 }
918 }
919 }
920 // BUG(crbug.com/tint/1333): work around assignment of vector to matrices
921 // with dynamic indices
Antonio Maiorano08d92792024-01-11 20:51:50 +0000922 const auto* lhs_access_type = TypeOf(lhs_access->object);
923 if (!validate_obj_not_pointer(lhs_access_type)) {
924 return false;
925 }
926 if (auto* mat = lhs_access_type->UnwrapRef()->As<core::type::Matrix>()) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +0000927 auto* lhs_index_sem = builder_.Sem().GetVal(lhs_access->index);
Ben Claytonaa037ac2022-06-29 19:07:30 +0000928 if (!lhs_index_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000929 return EmitDynamicMatrixVectorAssignment(stmt, mat);
930 }
931 }
932 // BUG(crbug.com/tint/534): work around assignment to vectors with dynamic
933 // indices
Antonio Maiorano08d92792024-01-11 20:51:50 +0000934 if (auto* vec = lhs_access_type->UnwrapRef()->As<core::type::Vector>()) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +0000935 auto* rhs_sem = builder_.Sem().GetVal(lhs_access->index);
Ben Claytonaa037ac2022-06-29 19:07:30 +0000936 if (!rhs_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000937 return EmitDynamicVectorAssignment(stmt, vec);
938 }
939 }
940 }
941
dan sinclair67a18932023-06-26 19:54:49 +0000942 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000943 if (!EmitExpression(out, stmt->lhs)) {
944 return false;
945 }
946 out << " = ";
947 if (!EmitExpression(out, stmt->rhs)) {
948 return false;
949 }
950 out << ";";
951 return true;
952}
953
dan sinclairbae54e72023-07-28 15:01:54 +0000954bool ASTPrinter::EmitBinary(StringStream& out, const ast::BinaryExpression* expr) {
Ben Claytonedc51ab2023-08-08 07:58:19 +0000955 if (expr->op == core::BinaryOp::kLogicalAnd || expr->op == core::BinaryOp::kLogicalOr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000956 auto name = UniqueIdentifier(kTempNamePrefix);
957
958 {
dan sinclair67a18932023-06-26 19:54:49 +0000959 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000960 pre << "bool " << name << " = ";
961 if (!EmitExpression(pre, expr->lhs)) {
962 return false;
963 }
964 pre << ";";
965 }
966
Ben Claytonedc51ab2023-08-08 07:58:19 +0000967 if (expr->op == core::BinaryOp::kLogicalOr) {
dan sinclair67a18932023-06-26 19:54:49 +0000968 Line() << "if (!" << name << ") {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000969 } else {
dan sinclair67a18932023-06-26 19:54:49 +0000970 Line() << "if (" << name << ") {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000971 }
972
973 {
974 ScopedIndent si(this);
dan sinclair67a18932023-06-26 19:54:49 +0000975 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000976 pre << name << " = ";
977 if (!EmitExpression(pre, expr->rhs)) {
978 return false;
979 }
980 pre << ";";
981 }
982
dan sinclair67a18932023-06-26 19:54:49 +0000983 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000984
985 out << "(" << name << ")";
986 return true;
987 }
988
989 auto* lhs_type = TypeOf(expr->lhs)->UnwrapRef();
990 auto* rhs_type = TypeOf(expr->rhs)->UnwrapRef();
991 // Multiplying by a matrix requires the use of `mul` in order to get the
992 // type of multiply we desire.
Ben Claytonedc51ab2023-08-08 07:58:19 +0000993 if (expr->op == core::BinaryOp::kMultiply &&
dan sinclaircedcdf32023-08-10 02:39:48 +0000994 ((lhs_type->Is<core::type::Vector>() && rhs_type->Is<core::type::Matrix>()) ||
995 (lhs_type->Is<core::type::Matrix>() && rhs_type->Is<core::type::Vector>()) ||
996 (lhs_type->Is<core::type::Matrix>() && rhs_type->Is<core::type::Matrix>()))) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000997 // Matrices are transposed, so swap LHS and RHS.
998 out << "mul(";
999 if (!EmitExpression(out, expr->rhs)) {
1000 return false;
1001 }
1002 out << ", ";
1003 if (!EmitExpression(out, expr->lhs)) {
1004 return false;
1005 }
1006 out << ")";
1007
1008 return true;
1009 }
1010
dan sinclairb2ba57b2023-02-28 15:14:09 +00001011 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001012
1013 if (!EmitExpression(out, expr->lhs)) {
1014 return false;
1015 }
1016 out << " ";
1017
1018 switch (expr->op) {
Ben Claytonedc51ab2023-08-08 07:58:19 +00001019 case core::BinaryOp::kAnd:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001020 out << "&";
1021 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001022 case core::BinaryOp::kOr:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001023 out << "|";
1024 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001025 case core::BinaryOp::kXor:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001026 out << "^";
1027 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001028 case core::BinaryOp::kLogicalAnd:
1029 case core::BinaryOp::kLogicalOr: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001030 // These are both handled above.
Ben Claytonf848af22023-07-28 16:37:32 +00001031 TINT_UNREACHABLE();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001032 return false;
1033 }
Ben Claytonedc51ab2023-08-08 07:58:19 +00001034 case core::BinaryOp::kEqual:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001035 out << "==";
1036 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001037 case core::BinaryOp::kNotEqual:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001038 out << "!=";
1039 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001040 case core::BinaryOp::kLessThan:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001041 out << "<";
1042 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001043 case core::BinaryOp::kGreaterThan:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001044 out << ">";
1045 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001046 case core::BinaryOp::kLessThanEqual:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001047 out << "<=";
1048 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001049 case core::BinaryOp::kGreaterThanEqual:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001050 out << ">=";
1051 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001052 case core::BinaryOp::kShiftLeft:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001053 out << "<<";
1054 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001055 case core::BinaryOp::kShiftRight:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001056 // TODO(dsinclair): MSL is based on C++14, and >> in C++14 has
1057 // implementation-defined behaviour for negative LHS. We may have to
1058 // generate extra code to implement WGSL-specified behaviour for negative
1059 // LHS.
1060 out << R"(>>)";
1061 break;
1062
Ben Claytonedc51ab2023-08-08 07:58:19 +00001063 case core::BinaryOp::kAdd:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001064 out << "+";
1065 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001066 case core::BinaryOp::kSubtract:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001067 out << "-";
1068 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001069 case core::BinaryOp::kMultiply:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001070 out << "*";
1071 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001072 case core::BinaryOp::kDivide:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001073 out << "/";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001074 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001075 case core::BinaryOp::kModulo:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001076 out << "%";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001077 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001078 }
1079 out << " ";
1080
1081 if (!EmitExpression(out, expr->rhs)) {
1082 return false;
1083 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001084
1085 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001086}
1087
dan sinclairbae54e72023-07-28 15:01:54 +00001088bool ASTPrinter::EmitStatements(VectorRef<const ast::Statement*> stmts) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001089 for (auto* s : stmts) {
1090 if (!EmitStatement(s)) {
1091 return false;
1092 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001093 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001094 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001095}
1096
dan sinclairbae54e72023-07-28 15:01:54 +00001097bool ASTPrinter::EmitStatementsWithIndent(VectorRef<const ast::Statement*> stmts) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001098 ScopedIndent si(this);
1099 return EmitStatements(stmts);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001100}
1101
dan sinclair0bfeaf92023-07-26 17:39:51 +00001102bool ASTPrinter::EmitBlock(const ast::BlockStatement* stmt) {
dan sinclair67a18932023-06-26 19:54:49 +00001103 Line() << "{";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001104 if (!EmitStatementsWithIndent(stmt->statements)) {
1105 return false;
1106 }
dan sinclair67a18932023-06-26 19:54:49 +00001107 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001108 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001109}
1110
dan sinclair0bfeaf92023-07-26 17:39:51 +00001111bool ASTPrinter::EmitBreak(const ast::BreakStatement*) {
dan sinclair67a18932023-06-26 19:54:49 +00001112 Line() << "break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001113 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001114}
1115
dan sinclair0bfeaf92023-07-26 17:39:51 +00001116bool ASTPrinter::EmitBreakIf(const ast::BreakIfStatement* b) {
dan sinclair67a18932023-06-26 19:54:49 +00001117 auto out = Line();
dan sinclairb8b0c212022-10-20 22:45:50 +00001118 out << "if (";
1119 if (!EmitExpression(out, b->condition)) {
1120 return false;
1121 }
1122 out << ") { break; }";
1123 return true;
1124}
1125
dan sinclairbae54e72023-07-28 15:01:54 +00001126bool ASTPrinter::EmitCall(StringStream& out, const ast::CallExpression* expr) {
Ben Claytone9f8b092022-06-01 13:14:39 +00001127 auto* call = builder_.Sem().Get<sem::Call>(expr);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001128 auto* target = call->Target();
1129 return Switch(
Ben Clayton54a104e2023-02-22 20:04:40 +00001130 target, //
1131 [&](const sem::Function* func) { return EmitFunctionCall(out, call, func); },
Ben Claytond9766dc2023-09-21 12:41:20 +00001132 [&](const sem::BuiltinFn* builtin) { return EmitBuiltinCall(out, call, builtin); },
Ben Clayton54a104e2023-02-22 20:04:40 +00001133 [&](const sem::ValueConversion* conv) { return EmitValueConversion(out, call, conv); },
1134 [&](const sem::ValueConstructor* ctor) { return EmitValueConstructor(out, call, ctor); },
Ben Claytond6082c52023-10-26 16:02:01 +00001135 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001136}
1137
dan sinclairbae54e72023-07-28 15:01:54 +00001138bool ASTPrinter::EmitFunctionCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001139 const sem::Call* call,
1140 const sem::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001141 auto* expr = call->Declaration();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001142
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001143 if (ast::HasAttribute<CalculateArrayLength::BufferSizeIntrinsic>(
dan sinclair41e4d9a2022-05-01 14:40:55 +00001144 func->Declaration()->attributes)) {
1145 // Special function generated by the CalculateArrayLength transform for
1146 // calling X.GetDimensions(Y)
1147 if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
1148 return false;
1149 }
1150 out << ".GetDimensions(";
1151 if (!EmitExpression(out, call->Arguments()[1]->Declaration())) {
1152 return false;
1153 }
1154 out << ")";
1155 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001156 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001157
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001158 if (auto* intrinsic =
1159 ast::GetAttribute<DecomposeMemoryAccess::Intrinsic>(func->Declaration()->attributes)) {
dan sinclairff7cf212022-10-03 14:05:23 +00001160 switch (intrinsic->address_space) {
Ben Claytoncd52f382023-08-07 13:11:08 +00001161 case core::AddressSpace::kUniform:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001162 return EmitUniformBufferAccess(out, expr, intrinsic);
Ben Claytoncd52f382023-08-07 13:11:08 +00001163 case core::AddressSpace::kStorage:
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001164 if (!intrinsic->IsAtomic()) {
1165 return EmitStorageBufferAccess(out, expr, intrinsic);
1166 }
1167 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001168 default:
Ben Claytonf848af22023-07-28 16:37:32 +00001169 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic address space:"
1170 << intrinsic->address_space;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001171 return false;
1172 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001173 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001174
James Pricefe24b4a2023-08-08 01:40:43 +00001175 if (auto* wave_intrinsic =
1176 ast::GetAttribute<ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic>(
1177 func->Declaration()->attributes)) {
1178 switch (wave_intrinsic->op) {
1179 case ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic::Op::kWaveGetLaneCount:
1180 out << "WaveGetLaneCount()";
1181 return true;
1182 case ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic::Op::kWaveGetLaneIndex:
1183 out << "WaveGetLaneIndex()";
1184 return true;
1185 }
1186 }
1187
dan sinclaird026e132023-04-18 19:38:25 +00001188 out << func->Declaration()->name->symbol.Name() << "(";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001189
1190 bool first = true;
1191 for (auto* arg : call->Arguments()) {
1192 if (!first) {
1193 out << ", ";
1194 }
1195 first = false;
1196
1197 if (!EmitExpression(out, arg->Declaration())) {
1198 return false;
1199 }
1200 }
1201
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001202 out << ")";
1203 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001204}
1205
dan sinclairbae54e72023-07-28 15:01:54 +00001206bool ASTPrinter::EmitBuiltinCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001207 const sem::Call* call,
Ben Claytond9766dc2023-09-21 12:41:20 +00001208 const sem::BuiltinFn* builtin) {
1209 const auto type = builtin->Fn();
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001210
dan sinclair41e4d9a2022-05-01 14:40:55 +00001211 auto* expr = call->Declaration();
1212 if (builtin->IsTexture()) {
1213 return EmitTextureCall(out, call, builtin);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001214 }
Ben Clayton52e6a0f2024-02-21 08:11:33 +00001215 if (type == wgsl::BuiltinFn::kBitcast) {
1216 return EmitBitcastCall(out, expr);
1217 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001218 if (type == wgsl::BuiltinFn::kSelect) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001219 return EmitSelectCall(out, expr);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001220 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001221 if (type == wgsl::BuiltinFn::kModf) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001222 return EmitModfCall(out, expr, builtin);
1223 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001224 if (type == wgsl::BuiltinFn::kFrexp) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001225 return EmitFrexpCall(out, expr, builtin);
1226 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001227 if (type == wgsl::BuiltinFn::kDegrees) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001228 return EmitDegreesCall(out, expr, builtin);
1229 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001230 if (type == wgsl::BuiltinFn::kRadians) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001231 return EmitRadiansCall(out, expr, builtin);
1232 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001233 if (type == wgsl::BuiltinFn::kSign) {
dan sinclair70927862023-01-11 13:18:29 +00001234 return EmitSignCall(out, call, builtin);
1235 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001236 if (type == wgsl::BuiltinFn::kQuantizeToF16) {
Ben Clayton2bea9052022-11-02 00:09:50 +00001237 return EmitQuantizeToF16Call(out, expr, builtin);
1238 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001239 if (type == wgsl::BuiltinFn::kTrunc) {
Antonio Maiorano5cf943e2023-03-27 18:55:25 +00001240 return EmitTruncCall(out, expr, builtin);
1241 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001242 if (builtin->IsDataPacking()) {
1243 return EmitDataPackingCall(out, expr, builtin);
1244 }
1245 if (builtin->IsDataUnpacking()) {
1246 return EmitDataUnpackingCall(out, expr, builtin);
1247 }
1248 if (builtin->IsBarrier()) {
1249 return EmitBarrierCall(out, builtin);
1250 }
1251 if (builtin->IsAtomic()) {
1252 return EmitWorkgroupAtomicCall(out, expr, builtin);
1253 }
Jiawei Shao53c0fa92023-12-08 01:24:00 +00001254 if (builtin->IsPacked4x8IntegerDotProductBuiltin()) {
1255 return EmitPacked4x8IntegerDotProductBuiltinCall(out, expr, builtin);
Jiawei Shaoab975702022-05-13 00:09:56 +00001256 }
James Price501983f2023-08-08 01:37:22 +00001257 if (builtin->IsSubgroup()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00001258 if (builtin->Fn() == wgsl::BuiltinFn::kSubgroupBroadcast) {
David Netoeea786f2023-09-21 05:32:53 +00001259 // Fall through the regular path.
1260 } else {
1261 return EmitSubgroupCall(out, expr, builtin);
1262 }
James Price501983f2023-08-08 01:37:22 +00001263 }
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001264
dan sinclair41e4d9a2022-05-01 14:40:55 +00001265 auto name = generate_builtin_name(builtin);
1266 if (name.empty()) {
1267 return false;
1268 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001269
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001270 // Handle single argument builtins that only accept and return uint (not int overload). We need
1271 // to explicitly cast the return value (we also cast the arg for good measure). See
1272 // crbug.com/tint/1550
Ben Claytondfc815c2023-09-25 15:38:43 +00001273 if (type == wgsl::BuiltinFn::kCountOneBits || type == wgsl::BuiltinFn::kReverseBits) {
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001274 auto* arg = call->Arguments()[0];
Ben Clayton6c337aa2022-12-07 19:25:17 +00001275 if (arg->Type()->UnwrapRef()->is_signed_integer_scalar_or_vector()) {
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001276 out << "asint(" << name << "(asuint(";
1277 if (!EmitExpression(out, arg->Declaration())) {
1278 return false;
1279 }
1280 out << ")))";
1281 return true;
1282 }
1283 }
1284
dan sinclair41e4d9a2022-05-01 14:40:55 +00001285 out << name << "(";
1286
1287 bool first = true;
1288 for (auto* arg : call->Arguments()) {
1289 if (!first) {
1290 out << ", ";
1291 }
1292 first = false;
1293
1294 if (!EmitExpression(out, arg->Declaration())) {
1295 return false;
1296 }
1297 }
1298
1299 out << ")";
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001300
dan sinclair41e4d9a2022-05-01 14:40:55 +00001301 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001302}
1303
dan sinclairbae54e72023-07-28 15:01:54 +00001304bool ASTPrinter::EmitValueConversion(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001305 const sem::Call* call,
1306 const sem::ValueConversion* conv) {
Ben Claytoncd52f382023-08-07 13:11:08 +00001307 if (!EmitType(out, conv->Target(), core::AddressSpace::kUndefined, core::Access::kReadWrite,
1308 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001309 return false;
1310 }
1311 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001312
dan sinclair41e4d9a2022-05-01 14:40:55 +00001313 if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
1314 return false;
1315 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001316
dan sinclair41e4d9a2022-05-01 14:40:55 +00001317 out << ")";
1318 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001319}
1320
dan sinclairbae54e72023-07-28 15:01:54 +00001321bool ASTPrinter::EmitValueConstructor(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001322 const sem::Call* call,
1323 const sem::ValueConstructor* ctor) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001324 auto* type = call->Type();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001325
Ben Clayton54a104e2023-02-22 20:04:40 +00001326 // If the value constructor arguments are empty then we need to construct with the zero value
1327 // for all components.
Ben Clayton958a4642022-07-26 07:55:24 +00001328 if (call->Arguments().IsEmpty()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001329 return EmitZeroValue(out, type);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001330 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001331
dan sinclair6e77b472022-10-20 13:38:28 +00001332 // Single parameter matrix initializers must be identity initializer.
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001333 // It could also be conversions between f16 and f32 matrix when f16 is properly supported.
dan sinclaircedcdf32023-08-10 02:39:48 +00001334 if (type->Is<core::type::Matrix>() && call->Arguments().Length() == 1) {
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001335 if (!ctor->Parameters()[0]->Type()->UnwrapRef()->is_float_matrix()) {
Ben Claytonf848af22023-07-28 16:37:32 +00001336 TINT_UNREACHABLE()
dan sinclair6e77b472022-10-20 13:38:28 +00001337 << "found a single-parameter matrix initializer that is not identity initializer";
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001338 return false;
Ben Clayton3b5edf12022-05-16 21:14:11 +00001339 }
1340 }
1341
dan sinclaircedcdf32023-08-10 02:39:48 +00001342 bool brackets = type->IsAnyOf<core::type::Array, core::type::Struct>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001343
dan sinclair41e4d9a2022-05-01 14:40:55 +00001344 // For single-value vector initializers, swizzle the scalar to the right
1345 // vector dimension using .x
dan sinclaircedcdf32023-08-10 02:39:48 +00001346 const bool is_single_value_vector_init =
1347 type->is_scalar_vector() && call->Arguments().Length() == 1 &&
1348 ctor->Parameters()[0]->Type()->Is<core::type::Scalar>();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001349
Ben Clayton6c098ba2022-07-14 20:46:39 +00001350 if (brackets) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001351 out << "{";
1352 } else {
Ben Claytoncd52f382023-08-07 13:11:08 +00001353 if (!EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001354 return false;
1355 }
1356 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001357 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001358
dan sinclair41e4d9a2022-05-01 14:40:55 +00001359 if (is_single_value_vector_init) {
1360 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001361 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001362
dan sinclair41e4d9a2022-05-01 14:40:55 +00001363 bool first = true;
1364 for (auto* e : call->Arguments()) {
1365 if (!first) {
1366 out << ", ";
1367 }
1368 first = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001369
dan sinclair41e4d9a2022-05-01 14:40:55 +00001370 if (!EmitExpression(out, e->Declaration())) {
1371 return false;
1372 }
1373 }
1374
1375 if (is_single_value_vector_init) {
dan sinclaircedcdf32023-08-10 02:39:48 +00001376 out << ")." << std::string(type->As<core::type::Vector>()->Width(), 'x');
dan sinclair41e4d9a2022-05-01 14:40:55 +00001377 }
1378
1379 out << (brackets ? "}" : ")");
1380 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001381}
1382
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001383bool ASTPrinter::EmitUniformBufferAccess(StringStream& out,
1384 const ast::CallExpression* expr,
1385 const DecomposeMemoryAccess::Intrinsic* intrinsic) {
dan sinclaird026e132023-04-18 19:38:25 +00001386 auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001387 auto* const offset = expr->args[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001388
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001389 // offset in bytes
1390 uint32_t scalar_offset_bytes = 0;
1391 // offset in uint (4 bytes)
1392 uint32_t scalar_offset_index = 0;
1393 // expression to calculate offset in bytes
1394 std::string scalar_offset_bytes_expr;
1395 // expression to calculate offset in uint, by dividing scalar_offset_bytes_expr by 4
1396 std::string scalar_offset_index_expr;
1397 // expression to calculate offset in uint, independently
1398 std::string scalar_offset_index_unified_expr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001399
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001400 // If true, use scalar_offset_index, otherwise use scalar_offset_index_expr
dan sinclair41e4d9a2022-05-01 14:40:55 +00001401 bool scalar_offset_constant = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001402
Ben Clayton1a1b5272023-02-24 17:16:55 +00001403 if (auto* val = builder_.Sem().GetVal(offset)->ConstantValue()) {
dan sinclaircedcdf32023-08-10 02:39:48 +00001404 TINT_ASSERT(val->Type()->Is<core::type::U32>());
dan sinclairb53b8cf2022-12-15 16:25:31 +00001405 scalar_offset_bytes = static_cast<uint32_t>(val->ValueAs<AInt>());
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001406 scalar_offset_index = scalar_offset_bytes / 4; // bytes -> scalar index
dan sinclair41e4d9a2022-05-01 14:40:55 +00001407 scalar_offset_constant = true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001408 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001409
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001410 // If true, scalar_offset_bytes or scalar_offset_bytes_expr should be used, otherwise only use
1411 // scalar_offset_index or scalar_offset_index_unified_expr. Currently only loading f16 scalar
1412 // require using offset in bytes.
1413 const bool need_offset_in_bytes =
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001414 intrinsic->type == DecomposeMemoryAccess::Intrinsic::DataType::kF16;
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001415
dan sinclair41e4d9a2022-05-01 14:40:55 +00001416 if (!scalar_offset_constant) {
1417 // UBO offset not compile-time known.
1418 // Calculate the scalar offset into a temporary.
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001419 if (need_offset_in_bytes) {
1420 scalar_offset_bytes_expr = UniqueIdentifier("scalar_offset_bytes");
1421 scalar_offset_index_expr = UniqueIdentifier("scalar_offset_index");
1422 {
dan sinclair67a18932023-06-26 19:54:49 +00001423 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001424 pre << "const uint " << scalar_offset_bytes_expr << " = (";
Ben Clayton1a1b5272023-02-24 17:16:55 +00001425 if (!EmitExpression(pre, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001426 return false;
1427 }
1428 pre << ");";
1429 }
dan sinclair67a18932023-06-26 19:54:49 +00001430 Line() << "const uint " << scalar_offset_index_expr << " = " << scalar_offset_bytes_expr
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001431 << " / 4;";
1432 } else {
1433 scalar_offset_index_unified_expr = UniqueIdentifier("scalar_offset");
dan sinclair67a18932023-06-26 19:54:49 +00001434 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001435 pre << "const uint " << scalar_offset_index_unified_expr << " = (";
Ben Clayton1a1b5272023-02-24 17:16:55 +00001436 if (!EmitExpression(pre, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001437 return false;
1438 }
1439 pre << ") / 4;";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001440 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001441 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001442
Austin Enga0e96b52023-02-18 00:39:01 +00001443 const char swizzle[] = {'x', 'y', 'z', 'w'};
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001444
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001445 using Op = DecomposeMemoryAccess::Intrinsic::Op;
1446 using DataType = DecomposeMemoryAccess::Intrinsic::DataType;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001447 switch (intrinsic->op) {
1448 case Op::kLoad: {
1449 auto cast = [&](const char* to, auto&& load) {
1450 out << to << "(";
1451 auto result = load();
1452 out << ")";
1453 return result;
1454 };
dan sinclairbae54e72023-07-28 15:01:54 +00001455 auto load_u32_to = [&](StringStream& target) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001456 target << buffer;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001457 if (scalar_offset_constant) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001458 target << "[" << (scalar_offset_index / 4) << "]."
1459 << swizzle[scalar_offset_index & 3];
dan sinclair41e4d9a2022-05-01 14:40:55 +00001460 } else {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001461 target << "[" << scalar_offset_index_unified_expr << " / 4]["
1462 << scalar_offset_index_unified_expr << " % 4]";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001463 }
1464 return true;
1465 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001466 auto load_u32 = [&] { return load_u32_to(out); };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001467 // Has a minimum alignment of 8 bytes, so is either .xy or .zw
dan sinclairbae54e72023-07-28 15:01:54 +00001468 auto load_vec2_u32_to = [&](StringStream& target) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001469 if (scalar_offset_constant) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001470 target << buffer << "[" << (scalar_offset_index / 4) << "]"
1471 << ((scalar_offset_index & 2) == 0 ? ".xy" : ".zw");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001472 } else {
1473 std::string ubo_load = UniqueIdentifier("ubo_load");
1474 {
dan sinclair67a18932023-06-26 19:54:49 +00001475 auto pre = Line();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001476 pre << "uint4 " << ubo_load << " = " << buffer << "["
1477 << scalar_offset_index_unified_expr << " / 4];";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001478 }
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001479 target << "((" << scalar_offset_index_unified_expr << " & 2) ? " << ubo_load
1480 << ".zw : " << ubo_load << ".xy)";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001481 }
1482 return true;
1483 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001484 auto load_vec2_u32 = [&] { return load_vec2_u32_to(out); };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001485 // vec4 has a minimum alignment of 16 bytes, easiest case
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001486 auto load_vec4_u32 = [&] {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001487 out << buffer;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001488 if (scalar_offset_constant) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001489 out << "[" << (scalar_offset_index / 4) << "]";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001490 } else {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001491 out << "[" << scalar_offset_index_unified_expr << " / 4]";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001492 }
1493 return true;
1494 };
1495 // vec3 has a minimum alignment of 16 bytes, so is just a .xyz swizzle
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001496 auto load_vec3_u32 = [&] {
1497 if (!load_vec4_u32()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001498 return false;
1499 }
1500 out << ".xyz";
1501 return true;
1502 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001503 auto load_scalar_f16 = [&] {
1504 // offset bytes = 4k, ((buffer[index].x) & 0xFFFF)
1505 // offset bytes = 4k+2, ((buffer[index].x >> 16) & 0xFFFF)
Ben Clayton1a1b5272023-02-24 17:16:55 +00001506 out << "float16_t(f16tof32(((" << buffer;
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001507 if (scalar_offset_constant) {
1508 out << "[" << (scalar_offset_index / 4) << "]."
1509 << swizzle[scalar_offset_index & 3];
1510 // WGSL spec ensure little endian memory layout.
1511 if (scalar_offset_bytes % 4 == 0) {
1512 out << ") & 0xFFFF)";
1513 } else {
1514 out << " >> 16) & 0xFFFF)";
1515 }
1516 } else {
1517 out << "[" << scalar_offset_index_expr << " / 4][" << scalar_offset_index_expr
1518 << " % 4] >> (" << scalar_offset_bytes_expr
1519 << " % 4 == 0 ? 0 : 16)) & 0xFFFF)";
1520 }
1521 out << "))";
1522 return true;
1523 };
1524 auto load_vec2_f16 = [&] {
1525 // vec2<f16> is aligned to 4 bytes
1526 // Preclude code load the vec2<f16> data as a uint:
1527 // uint ubo_load = buffer[id0][id1];
1528 // Loading code convert it to vec2<f16>:
1529 // vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)),
1530 // float16_t(f16tof32(ubo_load >> 16)))
1531 std::string ubo_load = UniqueIdentifier("ubo_load");
1532 {
dan sinclair67a18932023-06-26 19:54:49 +00001533 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001534 // Load the 4 bytes f16 vector as an uint
1535 pre << "uint " << ubo_load << " = ";
1536 if (!load_u32_to(pre)) {
1537 return false;
1538 }
1539 pre << ";";
1540 }
1541 out << "vector<float16_t, 2>(float16_t(f16tof32(" << ubo_load
1542 << " & 0xFFFF)), float16_t(f16tof32(" << ubo_load << " >> 16)))";
1543 return true;
1544 };
1545 auto load_vec3_f16 = [&] {
1546 // vec3<f16> is aligned to 8 bytes
1547 // Preclude code load the vec3<f16> data as uint2 and convert its elements to
1548 // float16_t:
1549 // uint2 ubo_load = buffer[id0].xy;
1550 // /* The low 8 bits of two uint are the x and z elements of vec3<f16> */
1551 // vector<float16_t> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load &
1552 // 0xFFFF));
1553 // /* The high 8 bits of first uint is the y element of vec3<f16> */
1554 // float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
1555 // Loading code convert it to vec3<f16>:
1556 // vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1])
1557 std::string ubo_load = UniqueIdentifier("ubo_load");
1558 std::string ubo_load_xz = UniqueIdentifier(ubo_load + "_xz");
1559 std::string ubo_load_y = UniqueIdentifier(ubo_load + "_y");
1560 {
dan sinclair67a18932023-06-26 19:54:49 +00001561 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001562 // Load the 8 bytes uint2 with the f16 vector at lower 6 bytes
1563 pre << "uint2 " << ubo_load << " = ";
1564 if (!load_vec2_u32_to(pre)) {
1565 return false;
1566 }
1567 pre << ";";
1568 }
1569 {
dan sinclair67a18932023-06-26 19:54:49 +00001570 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001571 pre << "vector<float16_t, 2> " << ubo_load_xz
1572 << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " & 0xFFFF));";
1573 }
1574 {
dan sinclair67a18932023-06-26 19:54:49 +00001575 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001576 pre << "float16_t " << ubo_load_y << " = f16tof32(" << ubo_load
1577 << "[0] >> 16);";
1578 }
1579 out << "vector<float16_t, 3>(" << ubo_load_xz << "[0], " << ubo_load_y << ", "
1580 << ubo_load_xz << "[1])";
1581 return true;
1582 };
1583 auto load_vec4_f16 = [&] {
1584 // vec4<f16> is aligned to 8 bytes
1585 // Preclude code load the vec4<f16> data as uint2 and convert its elements to
1586 // float16_t:
1587 // uint2 ubo_load = buffer[id0].xy;
1588 // /* The low 8 bits of two uint are the x and z elements of vec4<f16> */
1589 // vector<float16_t> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load &
1590 // 0xFFFF));
1591 // /* The high 8 bits of two uint are the y and w elements of vec4<f16> */
1592 // vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >>
1593 // 16));
1594 // Loading code convert it to vec4<f16>:
1595 // vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1],
1596 // ubo_load_yw[1])
1597 std::string ubo_load = UniqueIdentifier("ubo_load");
1598 std::string ubo_load_xz = UniqueIdentifier(ubo_load + "_xz");
1599 std::string ubo_load_yw = UniqueIdentifier(ubo_load + "_yw");
1600 {
dan sinclair67a18932023-06-26 19:54:49 +00001601 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001602 // Load the 8 bytes f16 vector as an uint2
1603 pre << "uint2 " << ubo_load << " = ";
1604 if (!load_vec2_u32_to(pre)) {
1605 return false;
1606 }
1607 pre << ";";
1608 }
1609 {
dan sinclair67a18932023-06-26 19:54:49 +00001610 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001611 pre << "vector<float16_t, 2> " << ubo_load_xz
1612 << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " & 0xFFFF));";
1613 }
1614 {
dan sinclair67a18932023-06-26 19:54:49 +00001615 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001616 pre << "vector<float16_t, 2> " << ubo_load_yw
1617 << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " >> 16));";
1618 }
1619 out << "vector<float16_t, 4>(" << ubo_load_xz << "[0], " << ubo_load_yw << "[0], "
1620 << ubo_load_xz << "[1], " << ubo_load_yw << "[1])";
1621 return true;
1622 };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001623 switch (intrinsic->type) {
1624 case DataType::kU32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001625 return load_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001626 case DataType::kF32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001627 return cast("asfloat", load_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001628 case DataType::kI32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001629 return cast("asint", load_u32);
1630 case DataType::kF16:
1631 return load_scalar_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001632 case DataType::kVec2U32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001633 return load_vec2_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001634 case DataType::kVec2F32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001635 return cast("asfloat", load_vec2_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001636 case DataType::kVec2I32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001637 return cast("asint", load_vec2_u32);
1638 case DataType::kVec2F16:
1639 return load_vec2_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001640 case DataType::kVec3U32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001641 return load_vec3_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001642 case DataType::kVec3F32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001643 return cast("asfloat", load_vec3_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001644 case DataType::kVec3I32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001645 return cast("asint", load_vec3_u32);
1646 case DataType::kVec3F16:
1647 return load_vec3_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001648 case DataType::kVec4U32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001649 return load_vec4_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001650 case DataType::kVec4F32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001651 return cast("asfloat", load_vec4_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001652 case DataType::kVec4I32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001653 return cast("asint", load_vec4_u32);
1654 case DataType::kVec4F16:
1655 return load_vec4_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001656 }
Ben Claytonf848af22023-07-28 16:37:32 +00001657 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1658 << static_cast<int>(intrinsic->type);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001659 return false;
1660 }
1661 default:
1662 break;
1663 }
Ben Claytonf848af22023-07-28 16:37:32 +00001664 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::Op: "
1665 << static_cast<int>(intrinsic->op);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001666 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001667}
1668
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001669bool ASTPrinter::EmitStorageBufferAccess(StringStream& out,
1670 const ast::CallExpression* expr,
1671 const DecomposeMemoryAccess::Intrinsic* intrinsic) {
dan sinclaird026e132023-04-18 19:38:25 +00001672 auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001673 auto* const offset = expr->args[0];
Ben Clayton407137a2023-06-28 21:02:32 +00001674 auto* const value = expr->args.Length() > 1 ? expr->args[1] : nullptr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001675
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001676 using Op = DecomposeMemoryAccess::Intrinsic::Op;
1677 using DataType = DecomposeMemoryAccess::Intrinsic::DataType;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001678 switch (intrinsic->op) {
1679 case Op::kLoad: {
1680 auto load = [&](const char* cast, int n) {
1681 if (cast) {
1682 out << cast << "(";
1683 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001684 out << buffer << ".Load";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001685 if (n > 1) {
1686 out << n;
1687 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00001688 ScopedParen sp(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001689 if (!EmitExpression(out, offset)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001690 return false;
1691 }
1692 if (cast) {
1693 out << ")";
1694 }
1695 return true;
1696 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001697 // Templated load used for f16 types, requires SM6.2 or higher and DXC
1698 // Used by loading f16 types, e.g. for f16 type, set type parameter to "float16_t"
1699 // to emit `buffer.Load<float16_t>(offset)`.
1700 auto templated_load = [&](const char* type) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001701 out << buffer << ".Load<" << type << ">"; // templated load
dan sinclairb2ba57b2023-02-28 15:14:09 +00001702 ScopedParen sp(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001703 if (!EmitExpression(out, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001704 return false;
1705 }
1706 return true;
1707 };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001708 switch (intrinsic->type) {
1709 case DataType::kU32:
1710 return load(nullptr, 1);
1711 case DataType::kF32:
1712 return load("asfloat", 1);
1713 case DataType::kI32:
1714 return load("asint", 1);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001715 case DataType::kF16:
1716 return templated_load("float16_t");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001717 case DataType::kVec2U32:
1718 return load(nullptr, 2);
1719 case DataType::kVec2F32:
1720 return load("asfloat", 2);
1721 case DataType::kVec2I32:
1722 return load("asint", 2);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001723 case DataType::kVec2F16:
1724 return templated_load("vector<float16_t, 2> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001725 case DataType::kVec3U32:
1726 return load(nullptr, 3);
1727 case DataType::kVec3F32:
1728 return load("asfloat", 3);
1729 case DataType::kVec3I32:
1730 return load("asint", 3);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001731 case DataType::kVec3F16:
1732 return templated_load("vector<float16_t, 3> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001733 case DataType::kVec4U32:
1734 return load(nullptr, 4);
1735 case DataType::kVec4F32:
1736 return load("asfloat", 4);
1737 case DataType::kVec4I32:
1738 return load("asint", 4);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001739 case DataType::kVec4F16:
1740 return templated_load("vector<float16_t, 4> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001741 }
Ben Claytonf848af22023-07-28 16:37:32 +00001742 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1743 << static_cast<int>(intrinsic->type);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001744 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001745 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001746
1747 case Op::kStore: {
1748 auto store = [&](int n) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001749 out << buffer << ".Store";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001750 if (n > 1) {
1751 out << n;
1752 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00001753 ScopedParen sp1(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001754 if (!EmitExpression(out, offset)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001755 return false;
1756 }
1757 out << ", asuint";
dan sinclairb2ba57b2023-02-28 15:14:09 +00001758 ScopedParen sp2(out);
Antonio Maioranod1368d72023-09-05 20:29:56 +00001759 if (!EmitTextureOrStorageBufferCallArgExpression(out, value)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001760 return false;
1761 }
1762 return true;
1763 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001764 // Templated stored used for f16 types, requires SM6.2 or higher and DXC
1765 // Used by storing f16 types, e.g. for f16 type, set type parameter to "float16_t"
1766 // to emit `buffer.Store<float16_t>(offset)`.
1767 auto templated_store = [&](const char* type) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001768 out << buffer << ".Store<" << type << ">"; // templated store
dan sinclairb2ba57b2023-02-28 15:14:09 +00001769 ScopedParen sp1(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001770 if (!EmitExpression(out, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001771 return false;
1772 }
1773 out << ", ";
Ben Clayton1a1b5272023-02-24 17:16:55 +00001774 if (!EmitExpression(out, value)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001775 return false;
1776 }
1777 return true;
1778 };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001779 switch (intrinsic->type) {
1780 case DataType::kU32:
1781 return store(1);
1782 case DataType::kF32:
1783 return store(1);
1784 case DataType::kI32:
1785 return store(1);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001786 case DataType::kF16:
1787 return templated_store("float16_t");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001788 case DataType::kVec2U32:
1789 return store(2);
1790 case DataType::kVec2F32:
1791 return store(2);
1792 case DataType::kVec2I32:
1793 return store(2);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001794 case DataType::kVec2F16:
1795 return templated_store("vector<float16_t, 2> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001796 case DataType::kVec3U32:
1797 return store(3);
1798 case DataType::kVec3F32:
1799 return store(3);
1800 case DataType::kVec3I32:
1801 return store(3);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001802 case DataType::kVec3F16:
1803 return templated_store("vector<float16_t, 3> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001804 case DataType::kVec4U32:
1805 return store(4);
1806 case DataType::kVec4F32:
1807 return store(4);
1808 case DataType::kVec4I32:
1809 return store(4);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001810 case DataType::kVec4F16:
1811 return templated_store("vector<float16_t, 4> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001812 }
Ben Claytonf848af22023-07-28 16:37:32 +00001813 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1814 << static_cast<int>(intrinsic->type);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001815 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001816 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001817 default:
Ben Clayton1a1b5272023-02-24 17:16:55 +00001818 // Break out to error case below
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001819 // Note that atomic intrinsics are generated as functions.
1820 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001821 }
1822
Ben Claytonf848af22023-07-28 16:37:32 +00001823 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::Op: "
1824 << static_cast<int>(intrinsic->op);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001825 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001826}
1827
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001828bool ASTPrinter::EmitStorageAtomicIntrinsic(const ast::Function* func,
1829 const DecomposeMemoryAccess::Intrinsic* intrinsic) {
1830 using Op = DecomposeMemoryAccess::Intrinsic::Op;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001831
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001832 const sem::Function* sem_func = builder_.Sem().Get(func);
1833 auto* result_ty = sem_func->ReturnType();
dan sinclaird026e132023-04-18 19:38:25 +00001834 const auto name = func->name->symbol.Name();
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001835 auto& buf = *current_buffer_;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001836
dan sinclaird026e132023-04-18 19:38:25 +00001837 auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001838
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001839 auto rmw = [&](const char* hlsl) -> bool {
1840 {
dan sinclair67a18932023-06-26 19:54:49 +00001841 auto fn = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001842 if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
1843 core::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001844 return false;
1845 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001846 fn << "(uint offset, ";
Ben Claytoncd52f382023-08-07 13:11:08 +00001847 if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
1848 core::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001849 return false;
1850 }
1851 fn << ") {";
1852 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001853
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001854 buf.IncrementIndent();
1855 TINT_DEFER({
1856 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001857 Line(&buf) << "}";
1858 Line(&buf);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001859 });
1860
1861 {
dan sinclair67a18932023-06-26 19:54:49 +00001862 auto l = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001863 if (!EmitTypeAndName(l, result_ty, core::AddressSpace::kUndefined,
1864 core::Access::kUndefined, "original_value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001865 return false;
1866 }
1867 l << " = 0;";
1868 }
1869 {
dan sinclair67a18932023-06-26 19:54:49 +00001870 auto l = Line(&buf);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001871 l << buffer << "." << hlsl << "(offset, ";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001872 if (intrinsic->op == Op::kAtomicSub) {
1873 l << "-";
1874 }
1875 l << "value, original_value);";
1876 }
dan sinclair67a18932023-06-26 19:54:49 +00001877 Line(&buf) << "return original_value;";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001878 return true;
1879 };
1880
1881 switch (intrinsic->op) {
1882 case Op::kAtomicAdd:
1883 return rmw("InterlockedAdd");
1884
1885 case Op::kAtomicSub:
1886 // Use add with the operand negated.
1887 return rmw("InterlockedAdd");
1888
1889 case Op::kAtomicMax:
1890 return rmw("InterlockedMax");
1891
1892 case Op::kAtomicMin:
1893 return rmw("InterlockedMin");
1894
1895 case Op::kAtomicAnd:
1896 return rmw("InterlockedAnd");
1897
1898 case Op::kAtomicOr:
1899 return rmw("InterlockedOr");
1900
1901 case Op::kAtomicXor:
1902 return rmw("InterlockedXor");
1903
1904 case Op::kAtomicExchange:
1905 return rmw("InterlockedExchange");
1906
1907 case Op::kAtomicLoad: {
1908 // HLSL does not have an InterlockedLoad, so we emulate it with
1909 // InterlockedOr using 0 as the OR value
dan sinclair41e4d9a2022-05-01 14:40:55 +00001910 {
dan sinclair67a18932023-06-26 19:54:49 +00001911 auto fn = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001912 if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
1913 core::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001914 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001915 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001916 fn << "(uint offset) {";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001917 }
1918
1919 buf.IncrementIndent();
1920 TINT_DEFER({
1921 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001922 Line(&buf) << "}";
1923 Line(&buf);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001924 });
1925
1926 {
dan sinclair67a18932023-06-26 19:54:49 +00001927 auto l = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001928 if (!EmitTypeAndName(l, result_ty, core::AddressSpace::kUndefined,
1929 core::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001930 return false;
1931 }
1932 l << " = 0;";
1933 }
1934
dan sinclair67a18932023-06-26 19:54:49 +00001935 Line(&buf) << buffer << ".InterlockedOr(offset, 0, value);";
1936 Line(&buf) << "return value;";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001937 return true;
1938 }
1939 case Op::kAtomicStore: {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001940 auto* const value_ty = sem_func->Parameters()[1]->Type()->UnwrapRef();
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001941 // HLSL does not have an InterlockedStore, so we emulate it with
1942 // InterlockedExchange and discard the returned value
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001943 {
dan sinclair67a18932023-06-26 19:54:49 +00001944 auto fn = Line(&buf);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001945 fn << "void " << name << "(uint offset, ";
Ben Claytoncd52f382023-08-07 13:11:08 +00001946 if (!EmitTypeAndName(fn, value_ty, core::AddressSpace::kUndefined,
1947 core::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001948 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001949 }
1950 fn << ") {";
1951 }
1952
1953 buf.IncrementIndent();
1954 TINT_DEFER({
1955 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001956 Line(&buf) << "}";
1957 Line(&buf);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001958 });
1959
1960 {
dan sinclair67a18932023-06-26 19:54:49 +00001961 auto l = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001962 if (!EmitTypeAndName(l, value_ty, core::AddressSpace::kUndefined,
1963 core::Access::kUndefined, "ignored")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001964 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001965 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001966 l << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001967 }
dan sinclair67a18932023-06-26 19:54:49 +00001968 Line(&buf) << buffer << ".InterlockedExchange(offset, value, ignored);";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001969 return true;
1970 }
1971 case Op::kAtomicCompareExchangeWeak: {
dan sinclaircedcdf32023-08-10 02:39:48 +00001972 if (!EmitStructType(&helpers_, result_ty->As<core::type::Struct>())) {
Ben Clayton4b8a3d32023-06-13 16:55:57 +00001973 return false;
1974 }
1975
Ben Clayton1a1b5272023-02-24 17:16:55 +00001976 auto* const value_ty = sem_func->Parameters()[1]->Type()->UnwrapRef();
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001977 // NOTE: We don't need to emit the return type struct here as DecomposeMemoryAccess
1978 // already added it to the AST, and it should have already been emitted by now.
dan sinclair41e4d9a2022-05-01 14:40:55 +00001979 {
dan sinclair67a18932023-06-26 19:54:49 +00001980 auto fn = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001981 if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
1982 core::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001983 return false;
1984 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001985 fn << "(uint offset, ";
Ben Claytoncd52f382023-08-07 13:11:08 +00001986 if (!EmitTypeAndName(fn, value_ty, core::AddressSpace::kUndefined,
1987 core::Access::kUndefined, "compare")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001988 return false;
1989 }
1990 fn << ", ";
Ben Claytoncd52f382023-08-07 13:11:08 +00001991 if (!EmitTypeAndName(fn, value_ty, core::AddressSpace::kUndefined,
1992 core::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001993 return false;
1994 }
1995 fn << ") {";
1996 }
1997
1998 buf.IncrementIndent();
1999 TINT_DEFER({
2000 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00002001 Line(&buf) << "}";
2002 Line(&buf);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002003 });
2004
2005 { // T result = {0};
dan sinclair67a18932023-06-26 19:54:49 +00002006 auto l = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00002007 if (!EmitTypeAndName(l, result_ty, core::AddressSpace::kUndefined,
2008 core::Access::kUndefined, "result")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002009 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002010 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002011 l << "=";
2012 if (!EmitZeroValue(l, result_ty)) {
2013 return false;
2014 }
2015 l << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002016 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002017
dan sinclair67a18932023-06-26 19:54:49 +00002018 Line(&buf) << buffer
Ben Clayton1a1b5272023-02-24 17:16:55 +00002019 << ".InterlockedCompareExchange(offset, compare, value, result.old_value);";
dan sinclair67a18932023-06-26 19:54:49 +00002020 Line(&buf) << "result.exchanged = result.old_value == compare;";
2021 Line(&buf) << "return result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002022
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002023 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002024 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002025 default:
2026 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002027 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002028
Ben Claytonf848af22023-07-28 16:37:32 +00002029 TINT_UNREACHABLE() << "unsupported atomic DecomposeMemoryAccess::Intrinsic::Op: "
2030 << static_cast<int>(intrinsic->op);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002031 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002032}
2033
dan sinclairbae54e72023-07-28 15:01:54 +00002034bool ASTPrinter::EmitWorkgroupAtomicCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002035 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002036 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002037 std::string result = UniqueIdentifier("atomic_result");
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002038
dan sinclaircedcdf32023-08-10 02:39:48 +00002039 if (!builtin->ReturnType()->Is<core::type::Void>()) {
dan sinclair67a18932023-06-26 19:54:49 +00002040 auto pre = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00002041 if (!EmitTypeAndName(pre, builtin->ReturnType(), core::AddressSpace::kUndefined,
2042 core::Access::kUndefined, result)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002043 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002044 }
2045 pre << " = ";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002046 if (!EmitZeroValue(pre, builtin->ReturnType())) {
2047 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002048 }
2049 pre << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002050 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002051
dan sinclair41e4d9a2022-05-01 14:40:55 +00002052 auto call = [&](const char* name) {
dan sinclair67a18932023-06-26 19:54:49 +00002053 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002054 pre << name;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002055
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002056 {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002057 ScopedParen sp(pre);
Ben Clayton783b1692022-08-02 17:03:35 +00002058 for (size_t i = 0; i < expr->args.Length(); i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002059 auto* arg = expr->args[i];
2060 if (i > 0) {
2061 pre << ", ";
2062 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002063 if (!EmitExpression(pre, arg)) {
2064 return false;
2065 }
2066 }
2067
2068 pre << ", " << result;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002069 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002070
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002071 pre << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002072
dan sinclair41e4d9a2022-05-01 14:40:55 +00002073 out << result;
2074 return true;
2075 };
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002076
Ben Claytond9766dc2023-09-21 12:41:20 +00002077 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002078 case wgsl::BuiltinFn::kAtomicLoad: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002079 // HLSL does not have an InterlockedLoad, so we emulate it with
2080 // InterlockedOr using 0 as the OR value
dan sinclair67a18932023-06-26 19:54:49 +00002081 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002082 pre << "InterlockedOr";
2083 {
2084 ScopedParen sp(pre);
2085 if (!EmitExpression(pre, expr->args[0])) {
2086 return false;
2087 }
2088 pre << ", 0, " << result;
2089 }
2090 pre << ";";
2091
2092 out << result;
2093 return true;
2094 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002095 case wgsl::BuiltinFn::kAtomicStore: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002096 // HLSL does not have an InterlockedStore, so we emulate it with
2097 // InterlockedExchange and discard the returned value
2098 { // T result = 0;
dan sinclair67a18932023-06-26 19:54:49 +00002099 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002100 auto* value_ty = builtin->Parameters()[1]->Type()->UnwrapRef();
Ben Claytoncd52f382023-08-07 13:11:08 +00002101 if (!EmitTypeAndName(pre, value_ty, core::AddressSpace::kUndefined,
2102 core::Access::kUndefined, result)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002103 return false;
2104 }
2105 pre << " = ";
2106 if (!EmitZeroValue(pre, value_ty)) {
2107 return false;
2108 }
2109 pre << ";";
2110 }
2111
2112 out << "InterlockedExchange";
2113 {
dan sinclairb2ba57b2023-02-28 15:14:09 +00002114 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002115 if (!EmitExpression(out, expr->args[0])) {
2116 return false;
2117 }
2118 out << ", ";
2119 if (!EmitExpression(out, expr->args[1])) {
2120 return false;
2121 }
2122 out << ", " << result;
2123 }
2124 return true;
2125 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002126 case wgsl::BuiltinFn::kAtomicCompareExchangeWeak: {
dan sinclaircedcdf32023-08-10 02:39:48 +00002127 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002128 return false;
2129 }
2130
dan sinclair41e4d9a2022-05-01 14:40:55 +00002131 auto* dest = expr->args[0];
2132 auto* compare_value = expr->args[1];
2133 auto* value = expr->args[2];
2134
2135 std::string compare = UniqueIdentifier("atomic_compare_value");
2136
2137 { // T compare_value = <compare_value>;
dan sinclair67a18932023-06-26 19:54:49 +00002138 auto pre = Line();
Antonio Maioranof99671b2022-06-23 13:14:54 +00002139 if (!EmitTypeAndName(pre, TypeOf(compare_value)->UnwrapRef(),
Ben Claytoncd52f382023-08-07 13:11:08 +00002140 core::AddressSpace::kUndefined, core::Access::kUndefined,
dan sinclair61c16eb2023-01-21 23:44:38 +00002141 compare)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002142 return false;
2143 }
2144 pre << " = ";
2145 if (!EmitExpression(pre, compare_value)) {
2146 return false;
2147 }
2148 pre << ";";
2149 }
2150
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002151 { // InterlockedCompareExchange(dst, compare, value, result.old_value);
dan sinclair67a18932023-06-26 19:54:49 +00002152 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002153 pre << "InterlockedCompareExchange";
2154 {
2155 ScopedParen sp(pre);
2156 if (!EmitExpression(pre, dest)) {
2157 return false;
2158 }
2159 pre << ", " << compare << ", ";
2160 if (!EmitExpression(pre, value)) {
2161 return false;
2162 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002163 pre << ", " << result << ".old_value";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002164 }
2165 pre << ";";
2166 }
2167
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002168 // result.exchanged = result.old_value == compare;
dan sinclair67a18932023-06-26 19:54:49 +00002169 Line() << result << ".exchanged = " << result << ".old_value == " << compare << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002170
2171 out << result;
2172 return true;
2173 }
2174
Ben Claytondfc815c2023-09-25 15:38:43 +00002175 case wgsl::BuiltinFn::kAtomicAdd:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002176 return call("InterlockedAdd");
2177
Stephen White42491242023-10-18 18:02:20 +00002178 case wgsl::BuiltinFn::kAtomicSub: {
2179 auto pre = Line();
2180 // Sub uses InterlockedAdd with the operand negated.
2181 pre << "InterlockedAdd";
2182 {
2183 ScopedParen sp(pre);
2184 TINT_ASSERT(expr->args.Length() == 2);
2185
2186 if (!EmitExpression(pre, expr->args[0])) {
2187 return false;
2188 }
2189 pre << ", -";
2190 {
2191 ScopedParen argSP(pre);
2192 if (!EmitExpression(pre, expr->args[1])) {
2193 return false;
2194 }
2195 }
2196
2197 pre << ", " << result;
2198 }
2199
2200 pre << ";";
2201
2202 out << result;
2203 }
2204 return true;
2205
Ben Claytondfc815c2023-09-25 15:38:43 +00002206 case wgsl::BuiltinFn::kAtomicMax:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002207 return call("InterlockedMax");
2208
Ben Claytondfc815c2023-09-25 15:38:43 +00002209 case wgsl::BuiltinFn::kAtomicMin:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002210 return call("InterlockedMin");
2211
Ben Claytondfc815c2023-09-25 15:38:43 +00002212 case wgsl::BuiltinFn::kAtomicAnd:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002213 return call("InterlockedAnd");
2214
Ben Claytondfc815c2023-09-25 15:38:43 +00002215 case wgsl::BuiltinFn::kAtomicOr:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002216 return call("InterlockedOr");
2217
Ben Claytondfc815c2023-09-25 15:38:43 +00002218 case wgsl::BuiltinFn::kAtomicXor:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002219 return call("InterlockedXor");
2220
Ben Claytondfc815c2023-09-25 15:38:43 +00002221 case wgsl::BuiltinFn::kAtomicExchange:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002222 return call("InterlockedExchange");
2223
2224 default:
2225 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002226 }
2227
Ben Claytond9766dc2023-09-21 12:41:20 +00002228 TINT_UNREACHABLE() << "unsupported atomic builtin: " << builtin->Fn();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002229 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002230}
2231
dan sinclairbae54e72023-07-28 15:01:54 +00002232bool ASTPrinter::EmitSelectCall(StringStream& out, const ast::CallExpression* expr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002233 auto* expr_false = expr->args[0];
2234 auto* expr_true = expr->args[1];
2235 auto* expr_cond = expr->args[2];
dan sinclairb2ba57b2023-02-28 15:14:09 +00002236 ScopedParen paren(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002237 if (!EmitExpression(out, expr_cond)) {
2238 return false;
2239 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002240
dan sinclair41e4d9a2022-05-01 14:40:55 +00002241 out << " ? ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002242
dan sinclair41e4d9a2022-05-01 14:40:55 +00002243 if (!EmitExpression(out, expr_true)) {
2244 return false;
2245 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002246
dan sinclair41e4d9a2022-05-01 14:40:55 +00002247 out << " : ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002248
dan sinclair41e4d9a2022-05-01 14:40:55 +00002249 if (!EmitExpression(out, expr_false)) {
2250 return false;
2251 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002252
dan sinclair41e4d9a2022-05-01 14:40:55 +00002253 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002254}
2255
dan sinclairbae54e72023-07-28 15:01:54 +00002256bool ASTPrinter::EmitModfCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002257 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002258 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002259 return CallBuiltinHelper(
2260 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2261 auto* ty = builtin->Parameters()[0]->Type();
2262 auto in = params[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002263
dan sinclair41e4d9a2022-05-01 14:40:55 +00002264 std::string width;
dan sinclaircedcdf32023-08-10 02:39:48 +00002265 if (auto* vec = ty->As<core::type::Vector>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002266 width = std::to_string(vec->Width());
2267 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002268
dan sinclair41e4d9a2022-05-01 14:40:55 +00002269 // Emit the builtin return type unique to this overload. This does not
2270 // exist in the AST, so it will not be generated in Generate().
dan sinclaircedcdf32023-08-10 02:39:48 +00002271 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002272 return false;
2273 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002274
dan sinclair41e4d9a2022-05-01 14:40:55 +00002275 {
dan sinclair67a18932023-06-26 19:54:49 +00002276 auto l = Line(b);
Ben Claytoncd52f382023-08-07 13:11:08 +00002277 if (!EmitType(l, builtin->ReturnType(), core::AddressSpace::kUndefined,
2278 core::Access::kUndefined, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002279 return false;
2280 }
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00002281 l << " result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002282 }
dan sinclair67a18932023-06-26 19:54:49 +00002283 Line(b) << "result.fract = modf(" << params[0] << ", result.whole);";
2284 Line(b) << "return result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002285 return true;
2286 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002287}
2288
dan sinclairbae54e72023-07-28 15:01:54 +00002289bool ASTPrinter::EmitFrexpCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002290 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002291 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002292 return CallBuiltinHelper(
2293 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2294 auto* ty = builtin->Parameters()[0]->Type();
2295 auto in = params[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002296
dan sinclair41e4d9a2022-05-01 14:40:55 +00002297 std::string width;
dan sinclaircedcdf32023-08-10 02:39:48 +00002298 if (auto* vec = ty->As<core::type::Vector>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002299 width = std::to_string(vec->Width());
2300 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002301
dan sinclair41e4d9a2022-05-01 14:40:55 +00002302 // Emit the builtin return type unique to this overload. This does not
2303 // exist in the AST, so it will not be generated in Generate().
dan sinclaircedcdf32023-08-10 02:39:48 +00002304 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002305 return false;
2306 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002307
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00002308 std::string member_type;
dan sinclaircedcdf32023-08-10 02:39:48 +00002309 if (Is<core::type::F16>(ty->DeepestElement())) {
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00002310 member_type = width.empty() ? "float16_t" : ("vector<float16_t, " + width + ">");
2311 } else {
2312 member_type = "float" + width;
2313 }
2314
dan sinclair67a18932023-06-26 19:54:49 +00002315 Line(b) << member_type << " exp;";
2316 Line(b) << member_type << " fract = sign(" << in << ") * frexp(" << in << ", exp);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002317 {
dan sinclair67a18932023-06-26 19:54:49 +00002318 auto l = Line(b);
Ben Claytoncd52f382023-08-07 13:11:08 +00002319 if (!EmitType(l, builtin->ReturnType(), core::AddressSpace::kUndefined,
2320 core::Access::kUndefined, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002321 return false;
2322 }
Ben Clayton10fae7a2022-11-14 15:29:29 +00002323 l << " result = {fract, int" << width << "(exp)};";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002324 }
dan sinclair67a18932023-06-26 19:54:49 +00002325 Line(b) << "return result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002326 return true;
2327 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002328}
2329
dan sinclairbae54e72023-07-28 15:01:54 +00002330bool ASTPrinter::EmitDegreesCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002331 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002332 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002333 return CallBuiltinHelper(out, expr, builtin,
2334 [&](TextBuffer* b, const std::vector<std::string>& params) {
dan sinclair67a18932023-06-26 19:54:49 +00002335 Line(b) << "return " << params[0] << " * " << std::setprecision(20)
dan sinclair41e4d9a2022-05-01 14:40:55 +00002336 << sem::kRadToDeg << ";";
2337 return true;
2338 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002339}
2340
dan sinclairbae54e72023-07-28 15:01:54 +00002341bool ASTPrinter::EmitRadiansCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002342 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002343 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002344 return CallBuiltinHelper(out, expr, builtin,
2345 [&](TextBuffer* b, const std::vector<std::string>& params) {
dan sinclair67a18932023-06-26 19:54:49 +00002346 Line(b) << "return " << params[0] << " * " << std::setprecision(20)
dan sinclair41e4d9a2022-05-01 14:40:55 +00002347 << sem::kDegToRad << ";";
2348 return true;
2349 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002350}
2351
dan sinclair70927862023-01-11 13:18:29 +00002352// The HLSL `sign` method always returns an `int` result (scalar or vector). In WGSL the result is
2353// expected to be the same type as the argument. This injects a cast to the expected WGSL result
2354// type after the call to `sign`.
Ben Claytond9766dc2023-09-21 12:41:20 +00002355bool ASTPrinter::EmitSignCall(StringStream& out, const sem::Call* call, const sem::BuiltinFn*) {
dan sinclair70927862023-01-11 13:18:29 +00002356 auto* arg = call->Arguments()[0];
Ben Claytoncd52f382023-08-07 13:11:08 +00002357 if (!EmitType(out, arg->Type(), core::AddressSpace::kUndefined, core::Access::kReadWrite, "")) {
dan sinclair70927862023-01-11 13:18:29 +00002358 return false;
2359 }
2360 out << "(sign(";
2361 if (!EmitExpression(out, arg->Declaration())) {
2362 return false;
2363 }
2364 out << "))";
2365 return true;
2366}
2367
dan sinclairbae54e72023-07-28 15:01:54 +00002368bool ASTPrinter::EmitQuantizeToF16Call(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002369 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002370 const sem::BuiltinFn* builtin) {
Antonio Maiorano51249b82023-03-31 08:00:33 +00002371 // Cast to f16 and back
Ben Clayton2bea9052022-11-02 00:09:50 +00002372 std::string width;
dan sinclaircedcdf32023-08-10 02:39:48 +00002373 if (auto* vec = builtin->ReturnType()->As<core::type::Vector>()) {
Ben Clayton2bea9052022-11-02 00:09:50 +00002374 width = std::to_string(vec->Width());
2375 }
Antonio Maiorano51249b82023-03-31 08:00:33 +00002376 out << "f16tof32(f32tof16"
2377 << "(";
Ben Clayton2bea9052022-11-02 00:09:50 +00002378 if (!EmitExpression(out, expr->args[0])) {
2379 return false;
2380 }
2381 out << "))";
2382 return true;
2383}
2384
dan sinclairbae54e72023-07-28 15:01:54 +00002385bool ASTPrinter::EmitTruncCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002386 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002387 const sem::BuiltinFn* builtin) {
Antonio Maiorano5cf943e2023-03-27 18:55:25 +00002388 // HLSL's trunc is broken for very large/small float values.
2389 // See crbug.com/tint/1883
2390 return CallBuiltinHelper( //
2391 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2392 // value < 0 ? ceil(value) : floor(value)
dan sinclair67a18932023-06-26 19:54:49 +00002393 Line(b) << "return " << params[0] << " < 0 ? ceil(" << params[0] << ") : floor("
Antonio Maiorano5cf943e2023-03-27 18:55:25 +00002394 << params[0] << ");";
2395 return true;
2396 });
2397}
2398
dan sinclairbae54e72023-07-28 15:01:54 +00002399bool ASTPrinter::EmitDataPackingCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002400 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002401 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002402 return CallBuiltinHelper(
2403 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2404 uint32_t dims = 2;
2405 bool is_signed = false;
2406 uint32_t scale = 65535;
Ben Claytondfc815c2023-09-25 15:38:43 +00002407 if (builtin->Fn() == wgsl::BuiltinFn::kPack4X8Snorm ||
2408 builtin->Fn() == wgsl::BuiltinFn::kPack4X8Unorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002409 dims = 4;
2410 scale = 255;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002411 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002412 if (builtin->Fn() == wgsl::BuiltinFn::kPack4X8Snorm ||
2413 builtin->Fn() == wgsl::BuiltinFn::kPack2X16Snorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002414 is_signed = true;
2415 scale = (scale - 1) / 2;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002416 }
Ben Claytond9766dc2023-09-21 12:41:20 +00002417 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002418 case wgsl::BuiltinFn::kPack4X8Snorm:
2419 case wgsl::BuiltinFn::kPack4X8Unorm:
2420 case wgsl::BuiltinFn::kPack2X16Snorm:
2421 case wgsl::BuiltinFn::kPack2X16Unorm: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002422 {
dan sinclair67a18932023-06-26 19:54:49 +00002423 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002424 l << (is_signed ? "" : "u") << "int" << dims
2425 << " i = " << (is_signed ? "" : "u") << "int" << dims << "(round(clamp("
2426 << params[0] << ", " << (is_signed ? "-1.0" : "0.0") << ", 1.0) * "
2427 << scale << ".0))";
2428 if (is_signed) {
2429 l << " & " << (dims == 4 ? "0xff" : "0xffff");
2430 }
2431 l << ";";
2432 }
2433 {
dan sinclair67a18932023-06-26 19:54:49 +00002434 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002435 l << "return ";
2436 if (is_signed) {
2437 l << "asuint";
2438 }
2439 l << "(i.x | i.y << " << (32 / dims);
2440 if (dims == 4) {
2441 l << " | i.z << 16 | i.w << 24";
2442 }
2443 l << ");";
2444 }
2445 break;
2446 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002447 case wgsl::BuiltinFn::kPack2X16Float: {
dan sinclair67a18932023-06-26 19:54:49 +00002448 Line(b) << "uint2 i = f32tof16(" << params[0] << ");";
2449 Line(b) << "return i.x | (i.y << 16);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002450 break;
2451 }
2452 default:
Ben Claytonc27315a2024-02-26 20:24:06 +00002453 TINT_ICE() << " unhandled data packing builtin";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002454 return false;
2455 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002456
dan sinclair41e4d9a2022-05-01 14:40:55 +00002457 return true;
2458 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002459}
2460
dan sinclairbae54e72023-07-28 15:01:54 +00002461bool ASTPrinter::EmitDataUnpackingCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002462 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002463 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002464 return CallBuiltinHelper(
2465 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2466 uint32_t dims = 2;
2467 bool is_signed = false;
2468 uint32_t scale = 65535;
Ben Claytondfc815c2023-09-25 15:38:43 +00002469 if (builtin->Fn() == wgsl::BuiltinFn::kUnpack4X8Snorm ||
2470 builtin->Fn() == wgsl::BuiltinFn::kUnpack4X8Unorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002471 dims = 4;
2472 scale = 255;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002473 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002474 if (builtin->Fn() == wgsl::BuiltinFn::kUnpack4X8Snorm ||
2475 builtin->Fn() == wgsl::BuiltinFn::kUnpack2X16Snorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002476 is_signed = true;
2477 scale = (scale - 1) / 2;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002478 }
Ben Claytond9766dc2023-09-21 12:41:20 +00002479 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002480 case wgsl::BuiltinFn::kUnpack4X8Snorm:
2481 case wgsl::BuiltinFn::kUnpack2X16Snorm: {
dan sinclair67a18932023-06-26 19:54:49 +00002482 Line(b) << "int j = int(" << params[0] << ");";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002483 { // Perform sign extension on the converted values.
dan sinclair67a18932023-06-26 19:54:49 +00002484 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002485 l << "int" << dims << " i = int" << dims << "(";
2486 if (dims == 2) {
2487 l << "j << 16, j) >> 16";
2488 } else {
2489 l << "j << 24, j << 16, j << 8, j) >> 24";
2490 }
2491 l << ";";
2492 }
dan sinclair67a18932023-06-26 19:54:49 +00002493 Line(b) << "return clamp(float" << dims << "(i) / " << scale << ".0, "
dan sinclair41e4d9a2022-05-01 14:40:55 +00002494 << (is_signed ? "-1.0" : "0.0") << ", 1.0);";
2495 break;
2496 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002497 case wgsl::BuiltinFn::kUnpack4X8Unorm:
2498 case wgsl::BuiltinFn::kUnpack2X16Unorm: {
dan sinclair67a18932023-06-26 19:54:49 +00002499 Line(b) << "uint j = " << params[0] << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002500 {
dan sinclair67a18932023-06-26 19:54:49 +00002501 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002502 l << "uint" << dims << " i = uint" << dims << "(";
2503 l << "j & " << (dims == 2 ? "0xffff" : "0xff") << ", ";
2504 if (dims == 4) {
2505 l << "(j >> " << (32 / dims) << ") & 0xff, (j >> 16) & 0xff, j >> 24";
2506 } else {
2507 l << "j >> " << (32 / dims);
2508 }
2509 l << ");";
2510 }
dan sinclair67a18932023-06-26 19:54:49 +00002511 Line(b) << "return float" << dims << "(i) / " << scale << ".0;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002512 break;
2513 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002514 case wgsl::BuiltinFn::kUnpack2X16Float:
dan sinclair67a18932023-06-26 19:54:49 +00002515 Line(b) << "uint i = " << params[0] << ";";
2516 Line(b) << "return f16tof32(uint2(i & 0xffff, i >> 16));";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002517 break;
2518 default:
Ben Claytonc27315a2024-02-26 20:24:06 +00002519 TINT_ICE() << "unhandled data packing builtin";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002520 return false;
2521 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002522
dan sinclair41e4d9a2022-05-01 14:40:55 +00002523 return true;
2524 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002525}
2526
Jiawei Shao53c0fa92023-12-08 01:24:00 +00002527bool ASTPrinter::EmitPacked4x8IntegerDotProductBuiltinCall(StringStream& out,
2528 const ast::CallExpression* expr,
2529 const sem::BuiltinFn* builtin) {
Jiawei Shaodd817232024-01-12 08:10:46 +00002530 switch (builtin->Fn()) {
2531 case wgsl::BuiltinFn::kDot4I8Packed:
2532 case wgsl::BuiltinFn::kDot4U8Packed:
2533 break;
2534 case wgsl::BuiltinFn::kPack4XI8: {
2535 out << "uint(pack_s8(";
2536 if (!EmitExpression(out, expr->args[0])) {
2537 return false;
2538 }
2539 out << "))";
2540 return true;
2541 }
2542 case wgsl::BuiltinFn::kPack4XU8: {
2543 out << "uint(pack_u8(";
2544 if (!EmitExpression(out, expr->args[0])) {
2545 return false;
2546 }
2547 out << "))";
2548 return true;
2549 }
2550 case wgsl::BuiltinFn::kPack4XI8Clamp: {
2551 out << "uint(pack_clamp_s8(";
2552 if (!EmitExpression(out, expr->args[0])) {
2553 return false;
2554 }
2555 out << "))";
2556 return true;
2557 }
2558 case wgsl::BuiltinFn::kUnpack4XI8: {
2559 out << "unpack_s8s32(int8_t4_packed(";
2560 if (!EmitExpression(out, expr->args[0])) {
2561 return false;
2562 }
2563 out << "))";
2564 return true;
2565 }
2566 case wgsl::BuiltinFn::kUnpack4XU8: {
2567 out << "unpack_u8u32(uint8_t4_packed(";
2568 if (!EmitExpression(out, expr->args[0])) {
2569 return false;
2570 }
2571 out << "))";
2572 return true;
2573 }
2574 case wgsl::BuiltinFn::kPack4XU8Clamp:
2575 default:
2576 TINT_UNIMPLEMENTED() << builtin->Fn();
2577 return false;
2578 }
2579
Ben Claytonc27315a2024-02-26 20:24:06 +00002580 return CallBuiltinHelper(out, expr, builtin,
2581 [&](TextBuffer* b, const std::vector<std::string>& params) {
2582 std::string functionName;
2583 switch (builtin->Fn()) {
2584 case wgsl::BuiltinFn::kDot4I8Packed:
2585 Line(b) << "int accumulator = 0;";
2586 functionName = "dot4add_i8packed";
2587 break;
2588 case wgsl::BuiltinFn::kDot4U8Packed:
2589 Line(b) << "uint accumulator = 0u;";
2590 functionName = "dot4add_u8packed";
2591 break;
2592 default:
2593 TINT_ICE() << "Internal error: unhandled DP4a builtin";
2594 return false;
2595 }
2596 Line(b) << "return " << functionName << "(" << params[0] << ", "
2597 << params[1] << ", accumulator);";
Jiawei Shaoab975702022-05-13 00:09:56 +00002598
Ben Claytonc27315a2024-02-26 20:24:06 +00002599 return true;
2600 });
Jiawei Shaoab975702022-05-13 00:09:56 +00002601}
2602
Ben Claytond9766dc2023-09-21 12:41:20 +00002603bool ASTPrinter::EmitBarrierCall(StringStream& out, const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002604 // TODO(crbug.com/tint/661): Combine sequential barriers to a single
2605 // instruction.
Ben Claytondfc815c2023-09-25 15:38:43 +00002606 if (builtin->Fn() == wgsl::BuiltinFn::kWorkgroupBarrier) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002607 out << "GroupMemoryBarrierWithGroupSync()";
Ben Claytondfc815c2023-09-25 15:38:43 +00002608 } else if (builtin->Fn() == wgsl::BuiltinFn::kStorageBarrier) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002609 out << "DeviceMemoryBarrierWithGroupSync()";
Ben Claytondfc815c2023-09-25 15:38:43 +00002610 } else if (builtin->Fn() == wgsl::BuiltinFn::kTextureBarrier) {
James Pricea6287df2023-08-11 00:45:54 +00002611 out << "DeviceMemoryBarrierWithGroupSync()";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002612 } else {
Ben Claytondfc815c2023-09-25 15:38:43 +00002613 TINT_UNREACHABLE() << "unexpected barrier builtin type " << builtin->Fn();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002614 return false;
2615 }
2616 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002617}
2618
James Price501983f2023-08-08 01:37:22 +00002619bool ASTPrinter::EmitSubgroupCall(StringStream& out,
2620 [[maybe_unused]] const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002621 const sem::BuiltinFn* builtin) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002622 if (builtin->Fn() == wgsl::BuiltinFn::kSubgroupBallot) {
James Price501983f2023-08-08 01:37:22 +00002623 out << "WaveActiveBallot(true)";
2624 } else {
David Netoeea786f2023-09-21 05:32:53 +00002625 // subgroupBroadcast is already handled in the regular builtin flow.
Ben Claytondfc815c2023-09-25 15:38:43 +00002626 TINT_UNREACHABLE() << "unexpected subgroup builtin type " << builtin->Fn();
James Price501983f2023-08-08 01:37:22 +00002627 return false;
2628 }
2629 return true;
2630}
2631
Antonio Maioranod1368d72023-09-05 20:29:56 +00002632bool ASTPrinter::EmitTextureOrStorageBufferCallArgExpression(StringStream& out,
2633 const ast::Expression* expr) {
2634 // TODO(crbug.com/tint/1976): Workaround DXC bug that fails to compile texture/storage function
2635 // calls with signed integer splatted constants. DXC fails to convert the coord arg, for e.g.
2636 // `0.xxx`, from a vector of 64-bit ints to a vector of 32-bit ints to match the texture load
2637 // parameter type. We work around this for now by explicitly casting the splatted constant to
2638 // the right type, for e.g. `int3(0.xxx)`.
2639 bool emitted_cast = false;
2640 if (auto* sem = builder_.Sem().GetVal(expr)) {
2641 if (auto* constant = sem->ConstantValue()) {
2642 if (auto* splat = constant->As<core::constant::Splat>()) {
2643 if (splat->Type()->is_signed_integer_vector()) {
2644 if (!EmitType(out, constant->Type(), core::AddressSpace::kUndefined,
2645 core::Access::kUndefined, "")) {
2646 return false;
2647 }
2648 out << "(";
2649 emitted_cast = true;
2650 }
2651 }
2652 }
2653 }
2654 if (!EmitExpression(out, expr)) {
2655 return false;
2656 }
2657 if (emitted_cast) {
2658 out << ")";
2659 }
2660 return true;
2661}
2662
dan sinclairbae54e72023-07-28 15:01:54 +00002663bool ASTPrinter::EmitTextureCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002664 const sem::Call* call,
Ben Claytond9766dc2023-09-21 12:41:20 +00002665 const sem::BuiltinFn* builtin) {
Ben Clayton015fbe72023-08-09 07:46:44 +00002666 using Usage = core::ParameterUsage;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002667
dan sinclair41e4d9a2022-05-01 14:40:55 +00002668 auto& signature = builtin->Signature();
2669 auto* expr = call->Declaration();
2670 auto arguments = expr->args;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002671
dan sinclair41e4d9a2022-05-01 14:40:55 +00002672 // Returns the argument with the given usage
2673 auto arg = [&](Usage usage) {
2674 int idx = signature.IndexOf(usage);
dan sinclair3a2a2792022-06-29 14:38:15 +00002675 return (idx >= 0) ? arguments[static_cast<size_t>(idx)] : nullptr;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002676 };
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002677
dan sinclair41e4d9a2022-05-01 14:40:55 +00002678 auto* texture = arg(Usage::kTexture);
Ben Clayton884f9522023-01-12 22:52:57 +00002679 if (TINT_UNLIKELY(!texture)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002680 TINT_ICE() << "missing texture argument";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002681 return false;
2682 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002683
dan sinclaircedcdf32023-08-10 02:39:48 +00002684 auto* texture_type = TypeOf(texture)->UnwrapRef()->As<core::type::Texture>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002685
Ben Claytond9766dc2023-09-21 12:41:20 +00002686 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002687 case wgsl::BuiltinFn::kTextureDimensions:
2688 case wgsl::BuiltinFn::kTextureNumLayers:
2689 case wgsl::BuiltinFn::kTextureNumLevels:
2690 case wgsl::BuiltinFn::kTextureNumSamples: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002691 // All of these builtins use the GetDimensions() method on the texture
dan sinclaircedcdf32023-08-10 02:39:48 +00002692 bool is_ms = texture_type->IsAnyOf<core::type::MultisampledTexture,
2693 core::type::DepthMultisampledTexture>();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002694 int num_dimensions = 0;
2695 std::string swizzle;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002696
Ben Claytond9766dc2023-09-21 12:41:20 +00002697 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002698 case wgsl::BuiltinFn::kTextureDimensions:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002699 switch (texture_type->dim()) {
dan sinclaircedcdf32023-08-10 02:39:48 +00002700 case core::type::TextureDimension::kNone:
Ben Claytonf848af22023-07-28 16:37:32 +00002701 TINT_ICE() << "texture dimension is kNone";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002702 return false;
dan sinclaircedcdf32023-08-10 02:39:48 +00002703 case core::type::TextureDimension::k1d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002704 num_dimensions = 1;
2705 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002706 case core::type::TextureDimension::k2d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002707 num_dimensions = is_ms ? 3 : 2;
2708 swizzle = is_ms ? ".xy" : "";
2709 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002710 case core::type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002711 num_dimensions = is_ms ? 4 : 3;
2712 swizzle = ".xy";
2713 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002714 case core::type::TextureDimension::k3d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002715 num_dimensions = 3;
2716 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002717 case core::type::TextureDimension::kCube:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002718 num_dimensions = 2;
2719 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002720 case core::type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002721 num_dimensions = 3;
2722 swizzle = ".xy";
2723 break;
2724 }
2725 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002726 case wgsl::BuiltinFn::kTextureNumLayers:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002727 switch (texture_type->dim()) {
2728 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002729 TINT_ICE() << "texture dimension is not arrayed";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002730 return false;
dan sinclaircedcdf32023-08-10 02:39:48 +00002731 case core::type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002732 num_dimensions = is_ms ? 4 : 3;
2733 swizzle = ".z";
2734 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002735 case core::type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002736 num_dimensions = 3;
2737 swizzle = ".z";
2738 break;
2739 }
2740 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002741 case wgsl::BuiltinFn::kTextureNumLevels:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002742 switch (texture_type->dim()) {
2743 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002744 TINT_ICE() << "texture dimension does not support mips";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002745 return false;
dan sinclaircedcdf32023-08-10 02:39:48 +00002746 case core::type::TextureDimension::k1d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002747 num_dimensions = 2;
2748 swizzle = ".y";
2749 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002750 case core::type::TextureDimension::k2d:
2751 case core::type::TextureDimension::kCube:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002752 num_dimensions = 3;
2753 swizzle = ".z";
2754 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002755 case core::type::TextureDimension::k2dArray:
2756 case core::type::TextureDimension::k3d:
2757 case core::type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002758 num_dimensions = 4;
2759 swizzle = ".w";
2760 break;
2761 }
2762 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002763 case wgsl::BuiltinFn::kTextureNumSamples:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002764 switch (texture_type->dim()) {
2765 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002766 TINT_ICE() << "texture dimension does not support multisampling";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002767 return false;
dan sinclaircedcdf32023-08-10 02:39:48 +00002768 case core::type::TextureDimension::k2d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002769 num_dimensions = 3;
2770 swizzle = ".z";
2771 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002772 case core::type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002773 num_dimensions = 4;
2774 swizzle = ".w";
2775 break;
2776 }
2777 break;
2778 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002779 TINT_ICE() << "unexpected builtin";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002780 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002781 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002782
2783 auto* level_arg = arg(Usage::kLevel);
2784
2785 if (level_arg) {
2786 // `NumberOfLevels` is a non-optional argument if `MipLevel` was passed.
2787 // Increment the number of dimensions for the temporary vector to
2788 // accommodate this.
2789 num_dimensions++;
2790
2791 // If the swizzle was empty, the expression will evaluate to the whole
2792 // vector. As we've grown the vector by one element, we now need to
2793 // swizzle to keep the result expression equivalent.
2794 if (swizzle.empty()) {
2795 static constexpr const char* swizzles[] = {"", ".x", ".xy", ".xyz"};
2796 swizzle = swizzles[num_dimensions - 1];
2797 }
2798 }
2799
Ben Clayton884f9522023-01-12 22:52:57 +00002800 if (TINT_UNLIKELY(num_dimensions > 4)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002801 TINT_ICE() << "Texture query builtin temporary vector has " << num_dimensions
2802 << " dimensions";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002803 return false;
2804 }
2805
2806 // Declare a variable to hold the queried texture info
2807 auto dims = UniqueIdentifier(kTempNamePrefix);
2808 if (num_dimensions == 1) {
dan sinclair67a18932023-06-26 19:54:49 +00002809 Line() << "uint " << dims << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002810 } else {
dan sinclair67a18932023-06-26 19:54:49 +00002811 Line() << "uint" << num_dimensions << " " << dims << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002812 }
2813
2814 { // texture.GetDimensions(...)
dan sinclair67a18932023-06-26 19:54:49 +00002815 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002816 if (!EmitExpression(pre, texture)) {
2817 return false;
2818 }
2819 pre << ".GetDimensions(";
2820
2821 if (level_arg) {
2822 if (!EmitExpression(pre, level_arg)) {
2823 return false;
2824 }
2825 pre << ", ";
Ben Claytondfc815c2023-09-25 15:38:43 +00002826 } else if (builtin->Fn() == wgsl::BuiltinFn::kTextureNumLevels) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002827 pre << "0, ";
2828 }
2829
2830 if (num_dimensions == 1) {
2831 pre << dims;
2832 } else {
2833 static constexpr char xyzw[] = {'x', 'y', 'z', 'w'};
Ben Clayton884f9522023-01-12 22:52:57 +00002834 if (TINT_UNLIKELY(num_dimensions < 0 || num_dimensions > 4)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002835 TINT_ICE() << "vector dimensions are " << num_dimensions;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002836 return false;
2837 }
2838 for (int i = 0; i < num_dimensions; i++) {
2839 if (i > 0) {
2840 pre << ", ";
2841 }
2842 pre << dims << "." << xyzw[i];
2843 }
2844 }
2845
2846 pre << ");";
2847 }
2848
2849 // The out parameters of the GetDimensions() call is now in temporary
2850 // `dims` variable. This may be packed with other data, so the final
2851 // expression may require a swizzle.
2852 out << dims << swizzle;
2853 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002854 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002855 default:
2856 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002857 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002858
Austin Eng86a617f2022-05-19 20:08:19 +00002859 if (!EmitExpression(out, texture)) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002860 return false;
Austin Eng86a617f2022-05-19 20:08:19 +00002861 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002862
2863 // If pack_level_in_coords is true, then the mip level will be appended as the
2864 // last value of the coordinates argument. If the WGSL builtin overload does
2865 // not have a level parameter and pack_level_in_coords is true, then a zero
2866 // mip level will be inserted.
2867 bool pack_level_in_coords = false;
2868
2869 uint32_t hlsl_ret_width = 4u;
2870
Ben Claytond9766dc2023-09-21 12:41:20 +00002871 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002872 case wgsl::BuiltinFn::kTextureSample:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002873 out << ".Sample(";
2874 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002875 case wgsl::BuiltinFn::kTextureSampleBias:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002876 out << ".SampleBias(";
2877 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002878 case wgsl::BuiltinFn::kTextureSampleLevel:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002879 out << ".SampleLevel(";
2880 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002881 case wgsl::BuiltinFn::kTextureSampleGrad:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002882 out << ".SampleGrad(";
2883 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002884 case wgsl::BuiltinFn::kTextureSampleCompare:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002885 out << ".SampleCmp(";
2886 hlsl_ret_width = 1;
2887 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002888 case wgsl::BuiltinFn::kTextureSampleCompareLevel:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002889 out << ".SampleCmpLevelZero(";
2890 hlsl_ret_width = 1;
2891 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002892 case wgsl::BuiltinFn::kTextureLoad:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002893 out << ".Load(";
Jiawei Shaoa54554a2023-11-13 08:24:12 +00002894 // Multisampled textures and read-write storage textures do not support mip-levels.
2895 if (texture_type->Is<core::type::MultisampledTexture>()) {
2896 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002897 }
Jiawei Shaoa54554a2023-11-13 08:24:12 +00002898 if (auto* storage_texture_type = texture_type->As<core::type::StorageTexture>()) {
2899 if (storage_texture_type->access() == core::Access::kReadWrite) {
2900 break;
2901 }
2902 }
2903 pack_level_in_coords = true;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002904 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002905 case wgsl::BuiltinFn::kTextureGather:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002906 out << ".Gather";
Ben Clayton015fbe72023-08-09 07:46:44 +00002907 if (builtin->Parameters()[0]->Usage() == core::ParameterUsage::kComponent) {
dan sinclair5addefb2022-12-14 20:46:32 +00002908 switch (call->Arguments()[0]->ConstantValue()->ValueAs<AInt>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002909 case 0:
2910 out << "Red";
2911 break;
2912 case 1:
2913 out << "Green";
2914 break;
2915 case 2:
2916 out << "Blue";
2917 break;
2918 case 3:
2919 out << "Alpha";
2920 break;
2921 }
2922 }
2923 out << "(";
2924 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002925 case wgsl::BuiltinFn::kTextureGatherCompare:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002926 out << ".GatherCmp(";
2927 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002928 case wgsl::BuiltinFn::kTextureStore:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002929 out << "[";
2930 break;
2931 default:
Ben Claytonc27315a2024-02-26 20:24:06 +00002932 TINT_ICE() << "Unhandled texture builtin '" << builtin << "'";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002933 return false;
2934 }
2935
2936 if (auto* sampler = arg(Usage::kSampler)) {
Austin Eng86a617f2022-05-19 20:08:19 +00002937 if (!EmitExpression(out, sampler)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002938 return false;
Austin Eng86a617f2022-05-19 20:08:19 +00002939 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002940 out << ", ";
2941 }
2942
2943 auto* param_coords = arg(Usage::kCoords);
Ben Clayton884f9522023-01-12 22:52:57 +00002944 if (TINT_UNLIKELY(!param_coords)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002945 TINT_ICE() << "missing coords argument";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002946 return false;
2947 }
2948
2949 auto emit_vector_appended_with_i32_zero = [&](const ast::Expression* vector) {
dan sinclaircedcdf32023-08-10 02:39:48 +00002950 auto* i32 = builder_.create<core::type::I32>();
Ben Clayton0ce9ab02022-05-05 20:23:40 +00002951 auto* zero = builder_.Expr(0_i);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002952 auto* stmt = builder_.Sem().Get(vector)->Stmt();
Ben Clayton3fb9a3f2023-02-04 21:20:26 +00002953 builder_.Sem().Add(zero, builder_.create<sem::ValueExpression>(
Ben Clayton36c61552023-08-08 07:58:19 +00002954 zero, i32, core::EvaluationStage::kRuntime, stmt,
Ben Clayton3fb9a3f2023-02-04 21:20:26 +00002955 /* constant_value */ nullptr,
2956 /* has_side_effects */ false));
Ben Clayton2550b492023-10-11 10:41:12 +00002957 auto* packed = tint::wgsl::AppendVector(&builder_, vector, zero);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002958 return EmitExpression(out, packed->Declaration());
2959 };
2960
2961 auto emit_vector_appended_with_level = [&](const ast::Expression* vector) {
2962 if (auto* level = arg(Usage::kLevel)) {
Ben Clayton2550b492023-10-11 10:41:12 +00002963 auto* packed = tint::wgsl::AppendVector(&builder_, vector, level);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002964 return EmitExpression(out, packed->Declaration());
2965 }
2966 return emit_vector_appended_with_i32_zero(vector);
2967 };
2968
2969 if (auto* array_index = arg(Usage::kArrayIndex)) {
2970 // Array index needs to be appended to the coordinates.
Ben Clayton2550b492023-10-11 10:41:12 +00002971 auto* packed = tint::wgsl::AppendVector(&builder_, param_coords, array_index);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002972 if (pack_level_in_coords) {
2973 // Then mip level needs to be appended to the coordinates.
2974 if (!emit_vector_appended_with_level(packed->Declaration())) {
2975 return false;
2976 }
2977 } else {
2978 if (!EmitExpression(out, packed->Declaration())) {
2979 return false;
2980 }
2981 }
2982 } else if (pack_level_in_coords) {
2983 // Mip level needs to be appended to the coordinates.
2984 if (!emit_vector_appended_with_level(param_coords)) {
2985 return false;
2986 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002987 } else if (builtin->Fn() == wgsl::BuiltinFn::kTextureStore) {
Antonio Maioranob0dfccd2023-08-30 14:49:42 +00002988 // param_coords is an index expression, not a function arg
dan sinclair41e4d9a2022-05-01 14:40:55 +00002989 if (!EmitExpression(out, param_coords)) {
2990 return false;
2991 }
Antonio Maioranod1368d72023-09-05 20:29:56 +00002992 } else if (!EmitTextureOrStorageBufferCallArgExpression(out, param_coords)) {
Antonio Maioranob0dfccd2023-08-30 14:49:42 +00002993 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002994 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002995
dan sinclair41e4d9a2022-05-01 14:40:55 +00002996 for (auto usage : {Usage::kDepthRef, Usage::kBias, Usage::kLevel, Usage::kDdx, Usage::kDdy,
2997 Usage::kSampleIndex, Usage::kOffset}) {
2998 if (usage == Usage::kLevel && pack_level_in_coords) {
2999 continue; // mip level already packed in coordinates.
3000 }
3001 if (auto* e = arg(usage)) {
3002 out << ", ";
Antonio Maioranod1368d72023-09-05 20:29:56 +00003003 if (!EmitTextureOrStorageBufferCallArgExpression(out, e)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003004 return false;
3005 }
3006 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003007 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003008
Ben Claytondfc815c2023-09-25 15:38:43 +00003009 if (builtin->Fn() == wgsl::BuiltinFn::kTextureStore) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003010 out << "] = ";
3011 if (!EmitExpression(out, arg(Usage::kValue))) {
3012 return false;
3013 }
3014 } else {
3015 out << ")";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003016
dan sinclair41e4d9a2022-05-01 14:40:55 +00003017 // If the builtin return type does not match the number of elements of the
3018 // HLSL builtin, we need to swizzle the expression to generate the correct
3019 // number of components.
3020 uint32_t wgsl_ret_width = 1;
dan sinclaircedcdf32023-08-10 02:39:48 +00003021 if (auto* vec = builtin->ReturnType()->As<core::type::Vector>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003022 wgsl_ret_width = vec->Width();
3023 }
3024 if (wgsl_ret_width < hlsl_ret_width) {
3025 out << ".";
3026 for (uint32_t i = 0; i < wgsl_ret_width; i++) {
3027 out << "xyz"[i];
3028 }
3029 }
Ben Clayton884f9522023-01-12 22:52:57 +00003030 if (TINT_UNLIKELY(wgsl_ret_width > hlsl_ret_width)) {
Ben Claytonf848af22023-07-28 16:37:32 +00003031 TINT_ICE() << "WGSL return width (" << wgsl_ret_width
3032 << ") is wider than HLSL return width (" << hlsl_ret_width << ") for "
Ben Claytond9766dc2023-09-21 12:41:20 +00003033 << builtin->Fn();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003034 return false;
3035 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003036 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003037
dan sinclair41e4d9a2022-05-01 14:40:55 +00003038 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003039}
3040
Ben Claytond9766dc2023-09-21 12:41:20 +00003041std::string ASTPrinter::generate_builtin_name(const sem::BuiltinFn* builtin) {
3042 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00003043 case wgsl::BuiltinFn::kAbs:
3044 case wgsl::BuiltinFn::kAcos:
3045 case wgsl::BuiltinFn::kAll:
3046 case wgsl::BuiltinFn::kAny:
3047 case wgsl::BuiltinFn::kAsin:
3048 case wgsl::BuiltinFn::kAtan:
3049 case wgsl::BuiltinFn::kAtan2:
3050 case wgsl::BuiltinFn::kCeil:
3051 case wgsl::BuiltinFn::kClamp:
3052 case wgsl::BuiltinFn::kCos:
3053 case wgsl::BuiltinFn::kCosh:
3054 case wgsl::BuiltinFn::kCross:
3055 case wgsl::BuiltinFn::kDeterminant:
3056 case wgsl::BuiltinFn::kDistance:
3057 case wgsl::BuiltinFn::kDot:
3058 case wgsl::BuiltinFn::kExp:
3059 case wgsl::BuiltinFn::kExp2:
3060 case wgsl::BuiltinFn::kFloor:
3061 case wgsl::BuiltinFn::kFrexp:
3062 case wgsl::BuiltinFn::kLdexp:
3063 case wgsl::BuiltinFn::kLength:
3064 case wgsl::BuiltinFn::kLog:
3065 case wgsl::BuiltinFn::kLog2:
3066 case wgsl::BuiltinFn::kMax:
3067 case wgsl::BuiltinFn::kMin:
3068 case wgsl::BuiltinFn::kModf:
3069 case wgsl::BuiltinFn::kNormalize:
3070 case wgsl::BuiltinFn::kPow:
3071 case wgsl::BuiltinFn::kReflect:
3072 case wgsl::BuiltinFn::kRefract:
3073 case wgsl::BuiltinFn::kRound:
3074 case wgsl::BuiltinFn::kSaturate:
3075 case wgsl::BuiltinFn::kSin:
3076 case wgsl::BuiltinFn::kSinh:
3077 case wgsl::BuiltinFn::kSqrt:
3078 case wgsl::BuiltinFn::kStep:
3079 case wgsl::BuiltinFn::kTan:
3080 case wgsl::BuiltinFn::kTanh:
3081 case wgsl::BuiltinFn::kTranspose:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003082 return builtin->str();
Ben Claytondfc815c2023-09-25 15:38:43 +00003083 case wgsl::BuiltinFn::kCountOneBits: // uint
dan sinclair41e4d9a2022-05-01 14:40:55 +00003084 return "countbits";
Ben Claytondfc815c2023-09-25 15:38:43 +00003085 case wgsl::BuiltinFn::kDpdx:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003086 return "ddx";
Ben Claytondfc815c2023-09-25 15:38:43 +00003087 case wgsl::BuiltinFn::kDpdxCoarse:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003088 return "ddx_coarse";
Ben Claytondfc815c2023-09-25 15:38:43 +00003089 case wgsl::BuiltinFn::kDpdxFine:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003090 return "ddx_fine";
Ben Claytondfc815c2023-09-25 15:38:43 +00003091 case wgsl::BuiltinFn::kDpdy:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003092 return "ddy";
Ben Claytondfc815c2023-09-25 15:38:43 +00003093 case wgsl::BuiltinFn::kDpdyCoarse:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003094 return "ddy_coarse";
Ben Claytondfc815c2023-09-25 15:38:43 +00003095 case wgsl::BuiltinFn::kDpdyFine:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003096 return "ddy_fine";
Ben Claytondfc815c2023-09-25 15:38:43 +00003097 case wgsl::BuiltinFn::kFaceForward:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003098 return "faceforward";
Ben Claytondfc815c2023-09-25 15:38:43 +00003099 case wgsl::BuiltinFn::kFract:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003100 return "frac";
Ben Claytondfc815c2023-09-25 15:38:43 +00003101 case wgsl::BuiltinFn::kFma:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003102 return "mad";
Ben Claytondfc815c2023-09-25 15:38:43 +00003103 case wgsl::BuiltinFn::kFwidth:
3104 case wgsl::BuiltinFn::kFwidthCoarse:
3105 case wgsl::BuiltinFn::kFwidthFine:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003106 return "fwidth";
Ben Claytondfc815c2023-09-25 15:38:43 +00003107 case wgsl::BuiltinFn::kInverseSqrt:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003108 return "rsqrt";
Ben Claytondfc815c2023-09-25 15:38:43 +00003109 case wgsl::BuiltinFn::kMix:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003110 return "lerp";
Ben Claytondfc815c2023-09-25 15:38:43 +00003111 case wgsl::BuiltinFn::kReverseBits: // uint
dan sinclair41e4d9a2022-05-01 14:40:55 +00003112 return "reversebits";
Ben Claytondfc815c2023-09-25 15:38:43 +00003113 case wgsl::BuiltinFn::kSmoothstep:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003114 return "smoothstep";
Ben Claytondfc815c2023-09-25 15:38:43 +00003115 case wgsl::BuiltinFn::kSubgroupBroadcast:
David Netoeea786f2023-09-21 05:32:53 +00003116 return "WaveReadLaneAt";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003117 default:
Ben Claytonc27315a2024-02-26 20:24:06 +00003118 diagnostics_.AddError(diag::System::Writer, Source{})
3119 << "Unknown builtin method: " << builtin->str();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003120 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003121
dan sinclair41e4d9a2022-05-01 14:40:55 +00003122 return "";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003123}
3124
dan sinclair0bfeaf92023-07-26 17:39:51 +00003125bool ASTPrinter::EmitCase(const ast::SwitchStatement* s, size_t case_idx) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003126 auto* stmt = s->body[case_idx];
dan sinclairf148f082022-10-19 15:55:02 +00003127 auto* sem = builder_.Sem().Get<sem::CaseStatement>(stmt);
3128 for (auto* selector : sem->Selectors()) {
dan sinclair67a18932023-06-26 19:54:49 +00003129 auto out = Line();
dan sinclairf148f082022-10-19 15:55:02 +00003130 if (selector->IsDefault()) {
3131 out << "default";
3132 } else {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003133 out << "case ";
Ben Clayton329dfd72022-11-23 00:05:05 +00003134 if (!EmitConstant(out, selector->Value(), /* is_variable_initializer */ false)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003135 return false;
3136 }
dan sinclairf148f082022-10-19 15:55:02 +00003137 }
3138 out << ":";
3139 if (selector == sem->Selectors().back()) {
3140 out << " {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003141 }
3142 }
3143
dan sinclair67a18932023-06-26 19:54:49 +00003144 IncrementIndent();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003145 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00003146 DecrementIndent();
3147 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003148 });
3149
3150 // Emit the case statement
3151 if (!EmitStatements(stmt->body->statements)) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003152 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003153 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003154
dan sinclairbae54e72023-07-28 15:01:54 +00003155 if (!tint::IsAnyOf<ast::BreakStatement>(stmt->body->Last())) {
dan sinclair67a18932023-06-26 19:54:49 +00003156 Line() << "break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003157 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003158
dan sinclair41e4d9a2022-05-01 14:40:55 +00003159 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003160}
3161
dan sinclair0bfeaf92023-07-26 17:39:51 +00003162bool ASTPrinter::EmitContinue(const ast::ContinueStatement*) {
dan sinclair4b88dbc2022-06-16 15:27:38 +00003163 if (!emit_continuing_ || !emit_continuing_()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003164 return false;
3165 }
dan sinclair67a18932023-06-26 19:54:49 +00003166 Line() << "continue;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003167 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003168}
3169
dan sinclair0bfeaf92023-07-26 17:39:51 +00003170bool ASTPrinter::EmitDiscard(const ast::DiscardStatement*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003171 // TODO(dsinclair): Verify this is correct when the discard semantics are
3172 // defined for WGSL (https://github.com/gpuweb/gpuweb/issues/361)
dan sinclair67a18932023-06-26 19:54:49 +00003173 Line() << "discard;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003174 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003175}
3176
dan sinclairbae54e72023-07-28 15:01:54 +00003177bool ASTPrinter::EmitExpression(StringStream& out, const ast::Expression* expr) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +00003178 if (auto* sem = builder_.Sem().GetVal(expr)) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003179 if (auto* constant = sem->ConstantValue()) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003180 bool is_variable_initializer = false;
3181 if (auto* stmt = sem->Stmt()) {
3182 if (auto* decl = As<ast::VariableDeclStatement>(stmt->Declaration())) {
3183 is_variable_initializer = decl->variable->initializer == expr;
3184 }
3185 }
3186 return EmitConstant(out, constant, is_variable_initializer);
Ben Claytone9f8b092022-06-01 13:14:39 +00003187 }
3188 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003189 return Switch(
Ben Claytonb90b6bf2022-08-23 16:23:05 +00003190 expr, //
3191 [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(out, a); },
3192 [&](const ast::BinaryExpression* b) { return EmitBinary(out, b); },
Ben Claytonb90b6bf2022-08-23 16:23:05 +00003193 [&](const ast::CallExpression* c) { return EmitCall(out, c); },
3194 [&](const ast::IdentifierExpression* i) { return EmitIdentifier(out, i); },
3195 [&](const ast::LiteralExpression* l) { return EmitLiteral(out, l); },
3196 [&](const ast::MemberAccessorExpression* m) { return EmitMemberAccessor(out, m); },
Ben Claytond6082c52023-10-26 16:02:01 +00003197 [&](const ast::UnaryOpExpression* u) { return EmitUnaryOp(out, u); }, //
3198 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003199}
3200
dan sinclairbae54e72023-07-28 15:01:54 +00003201bool ASTPrinter::EmitIdentifier(StringStream& out, const ast::IdentifierExpression* expr) {
dan sinclaird026e132023-04-18 19:38:25 +00003202 out << expr->identifier->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003203 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003204}
3205
dan sinclair0bfeaf92023-07-26 17:39:51 +00003206bool ASTPrinter::EmitIf(const ast::IfStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003207 {
dan sinclair67a18932023-06-26 19:54:49 +00003208 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003209 out << "if (";
3210 if (!EmitExpression(out, stmt->condition)) {
3211 return false;
3212 }
3213 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003214 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003215
dan sinclair41e4d9a2022-05-01 14:40:55 +00003216 if (!EmitStatementsWithIndent(stmt->body->statements)) {
James Price26ebe5e2022-04-29 00:14:53 +00003217 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003218 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003219
dan sinclair41e4d9a2022-05-01 14:40:55 +00003220 if (stmt->else_statement) {
dan sinclair67a18932023-06-26 19:54:49 +00003221 Line() << "} else {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003222 if (auto* block = stmt->else_statement->As<ast::BlockStatement>()) {
3223 if (!EmitStatementsWithIndent(block->statements)) {
3224 return false;
3225 }
3226 } else {
dan sinclairbae54e72023-07-28 15:01:54 +00003227 if (!EmitStatementsWithIndent(Vector{stmt->else_statement})) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003228 return false;
3229 }
3230 }
3231 }
dan sinclair67a18932023-06-26 19:54:49 +00003232 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003233
3234 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003235}
3236
dan sinclair0bfeaf92023-07-26 17:39:51 +00003237bool ASTPrinter::EmitFunction(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003238 auto* sem = builder_.Sem().Get(func);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003239
Antonio Maiorano08f4b552022-05-31 13:20:28 +00003240 // Emit storage atomic helpers
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00003241 if (auto* intrinsic = ast::GetAttribute<DecomposeMemoryAccess::Intrinsic>(func->attributes)) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003242 if (intrinsic->address_space == core::AddressSpace::kStorage && intrinsic->IsAtomic()) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00003243 if (!EmitStorageAtomicIntrinsic(func, intrinsic)) {
3244 return false;
3245 }
3246 }
3247 return true;
3248 }
3249
dan sinclair41e4d9a2022-05-01 14:40:55 +00003250 if (ast::HasAttribute<ast::InternalAttribute>(func->attributes)) {
3251 // An internal function. Do not emit.
3252 return true;
3253 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003254
dan sinclair41e4d9a2022-05-01 14:40:55 +00003255 {
dan sinclair67a18932023-06-26 19:54:49 +00003256 auto out = Line();
dan sinclaird026e132023-04-18 19:38:25 +00003257 auto name = func->name->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003258 // If the function returns an array, then we need to declare a typedef for
3259 // this.
dan sinclaircedcdf32023-08-10 02:39:48 +00003260 if (sem->ReturnType()->Is<core::type::Array>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003261 auto typedef_name = UniqueIdentifier(name + "_ret");
dan sinclair67a18932023-06-26 19:54:49 +00003262 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003263 pre << "typedef ";
Ben Claytoncd52f382023-08-07 13:11:08 +00003264 if (!EmitTypeAndName(pre, sem->ReturnType(), core::AddressSpace::kUndefined,
3265 core::Access::kReadWrite, typedef_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003266 return false;
3267 }
3268 pre << ";";
3269 out << typedef_name;
3270 } else {
Ben Claytoncd52f382023-08-07 13:11:08 +00003271 if (!EmitType(out, sem->ReturnType(), core::AddressSpace::kUndefined,
3272 core::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003273 return false;
3274 }
3275 }
3276
3277 out << " " << name << "(";
3278
3279 bool first = true;
3280
3281 for (auto* v : sem->Parameters()) {
3282 if (!first) {
3283 out << ", ";
3284 }
3285 first = false;
3286
3287 auto const* type = v->Type();
Ben Claytoncd52f382023-08-07 13:11:08 +00003288 auto address_space = core::AddressSpace::kUndefined;
3289 auto access = core::Access::kUndefined;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003290
dan sinclaircedcdf32023-08-10 02:39:48 +00003291 if (auto* ptr = type->As<core::type::Pointer>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003292 type = ptr->StoreType();
dan sinclairff7cf212022-10-03 14:05:23 +00003293 switch (ptr->AddressSpace()) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003294 case core::AddressSpace::kStorage:
3295 case core::AddressSpace::kUniform:
Ben Clayton2032d032022-06-15 19:32:37 +00003296 // Not allowed by WGSL, but is used by certain transforms (e.g. DMA) to pass
3297 // storage buffers and uniform buffers down into transform-generated
3298 // functions. In this situation we want to generate the parameter without an
dan sinclairff7cf212022-10-03 14:05:23 +00003299 // 'inout', using the address space and access from the pointer.
3300 address_space = ptr->AddressSpace();
Ben Clayton2032d032022-06-15 19:32:37 +00003301 access = ptr->Access();
3302 break;
3303 default:
3304 // Transform regular WGSL pointer parameters in to `inout` parameters.
3305 out << "inout ";
3306 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003307 }
3308
Ben Clayton1b90f932023-02-18 12:37:34 +00003309 // Note: WGSL only allows for AddressSpace::kUndefined on parameters, however
dan sinclair41e4d9a2022-05-01 14:40:55 +00003310 // the sanitizer transforms generates load / store functions for storage
3311 // or uniform buffers. These functions have a buffer parameter with
dan sinclairff7cf212022-10-03 14:05:23 +00003312 // AddressSpace::kStorage or AddressSpace::kUniform. This is required to
dan sinclair41e4d9a2022-05-01 14:40:55 +00003313 // correctly translate the parameter to a [RW]ByteAddressBuffer for
3314 // storage buffers and a uint4[N] for uniform buffers.
dan sinclairff7cf212022-10-03 14:05:23 +00003315 if (!EmitTypeAndName(out, type, address_space, access,
dan sinclaird026e132023-04-18 19:38:25 +00003316 v->Declaration()->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003317 return false;
3318 }
3319 }
3320 out << ") {";
3321 }
3322
dan sinclaircedcdf32023-08-10 02:39:48 +00003323 if (sem->DiscardStatement() && !sem->ReturnType()->Is<core::type::Void>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003324 // BUG(crbug.com/tint/1081): work around non-void functions with discard
3325 // failing compilation sometimes
3326 if (!EmitFunctionBodyWithDiscard(func)) {
3327 return false;
3328 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003329 } else {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003330 if (!EmitStatementsWithIndent(func->body->statements)) {
3331 return false;
3332 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003333 }
3334
dan sinclair67a18932023-06-26 19:54:49 +00003335 Line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003336
dan sinclair41e4d9a2022-05-01 14:40:55 +00003337 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003338}
3339
dan sinclair0bfeaf92023-07-26 17:39:51 +00003340bool ASTPrinter::EmitFunctionBodyWithDiscard(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003341 // FXC sometimes fails to compile functions that discard with 'Not all control
3342 // paths return a value'. We work around this by wrapping the function body
3343 // within an "if (true) { <body> } return <default return type obj>;" so that
3344 // there is always an (unused) return statement.
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003345
dan sinclair41e4d9a2022-05-01 14:40:55 +00003346 auto* sem = builder_.Sem().Get(func);
dan sinclaircedcdf32023-08-10 02:39:48 +00003347 TINT_ASSERT(sem->DiscardStatement() && !sem->ReturnType()->Is<core::type::Void>());
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003348
dan sinclair41e4d9a2022-05-01 14:40:55 +00003349 ScopedIndent si(this);
dan sinclair67a18932023-06-26 19:54:49 +00003350 Line() << "if (true) {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003351
dan sinclair41e4d9a2022-05-01 14:40:55 +00003352 if (!EmitStatementsWithIndent(func->body->statements)) {
3353 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003354 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003355
dan sinclair67a18932023-06-26 19:54:49 +00003356 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003357
3358 // Return an unused result that matches the type of the return value
dan sinclaird026e132023-04-18 19:38:25 +00003359 auto name = builder_.Symbols().New("unused").Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003360 {
dan sinclair67a18932023-06-26 19:54:49 +00003361 auto out = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00003362 if (!EmitTypeAndName(out, sem->ReturnType(), core::AddressSpace::kUndefined,
3363 core::Access::kReadWrite, name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003364 return false;
3365 }
3366 out << ";";
3367 }
dan sinclair67a18932023-06-26 19:54:49 +00003368 Line() << "return " << name << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003369
3370 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003371}
3372
dan sinclair0bfeaf92023-07-26 17:39:51 +00003373bool ASTPrinter::EmitGlobalVariable(const ast::Variable* global) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00003374 return Switch(
3375 global, //
3376 [&](const ast::Var* var) {
3377 auto* sem = builder_.Sem().Get(global);
dan sinclairff7cf212022-10-03 14:05:23 +00003378 switch (sem->AddressSpace()) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003379 case core::AddressSpace::kUniform:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003380 return EmitUniformVariable(var, sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003381 case core::AddressSpace::kStorage:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003382 return EmitStorageVariable(var, sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003383 case core::AddressSpace::kHandle:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003384 return EmitHandleVariable(var, sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003385 case core::AddressSpace::kPrivate:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003386 return EmitPrivateVariable(sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003387 case core::AddressSpace::kWorkgroup:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003388 return EmitWorkgroupVariable(sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003389 case core::AddressSpace::kPushConstant:
Ben Claytonc27315a2024-02-26 20:24:06 +00003390 diagnostics_.AddError(diag::System::Writer, Source{})
3391 << "unhandled address space " << sem->AddressSpace();
dan sinclair4abf28e2022-08-02 15:55:35 +00003392 return false;
dan sinclair8dbd4d02022-07-27 18:54:05 +00003393 default: {
Ben Claytonf848af22023-07-28 16:37:32 +00003394 TINT_ICE() << "unhandled address space " << sem->AddressSpace();
Ben Claytondcdf66e2022-06-17 12:48:51 +00003395 return false;
dan sinclair8dbd4d02022-07-27 18:54:05 +00003396 }
Ben Claytondcdf66e2022-06-17 12:48:51 +00003397 }
3398 },
dan sinclairf6a94042022-09-09 16:16:19 +00003399 [&](const ast::Override*) {
3400 // Override is removed with SubstituteOverride
Ben Claytonc27315a2024-02-26 20:24:06 +00003401 diagnostics_.AddError(diag::System::Writer, Source{})
3402 << "override-expressions should have been removed with the SubstituteOverride "
3403 "transform";
dan sinclairf6a94042022-09-09 16:16:19 +00003404 return false;
3405 },
Ben Clayton19576e92022-06-28 12:44:16 +00003406 [&](const ast::Const*) {
3407 return true; // Constants are embedded at their use
Ben Claytond6082c52023-10-26 16:02:01 +00003408 }, //
3409 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003410}
3411
dan sinclair0bfeaf92023-07-26 17:39:51 +00003412bool ASTPrinter::EmitUniformVariable(const ast::Var* var, const sem::Variable* sem) {
Ben Clayton65e38242023-11-09 19:37:44 +00003413 auto binding_point = *sem->As<sem::GlobalVariable>()->Attributes().binding_point;
Ben Claytondcdf66e2022-06-17 12:48:51 +00003414 auto* type = sem->Type()->UnwrapRef();
dan sinclaird026e132023-04-18 19:38:25 +00003415 auto name = var->name->symbol.Name();
dan sinclair67a18932023-06-26 19:54:49 +00003416 Line() << "cbuffer cbuffer_" << name << RegisterAndSpace('b', binding_point) << " {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003417
dan sinclair41e4d9a2022-05-01 14:40:55 +00003418 {
3419 ScopedIndent si(this);
dan sinclair67a18932023-06-26 19:54:49 +00003420 auto out = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00003421 if (!EmitTypeAndName(out, type, core::AddressSpace::kUniform, sem->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003422 return false;
3423 }
3424 out << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003425 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003426
dan sinclair67a18932023-06-26 19:54:49 +00003427 Line() << "};";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003428
dan sinclair41e4d9a2022-05-01 14:40:55 +00003429 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003430}
3431
dan sinclair0bfeaf92023-07-26 17:39:51 +00003432bool ASTPrinter::EmitStorageVariable(const ast::Var* var, const sem::Variable* sem) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00003433 auto* type = sem->Type()->UnwrapRef();
dan sinclair67a18932023-06-26 19:54:49 +00003434 auto out = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00003435 if (!EmitTypeAndName(out, type, core::AddressSpace::kStorage, sem->Access(),
dan sinclaird026e132023-04-18 19:38:25 +00003436 var->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003437 return false;
3438 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003439
dan sinclairacdf6e12022-08-24 15:47:25 +00003440 auto* global_sem = sem->As<sem::GlobalVariable>();
Ben Claytoncd52f382023-08-07 13:11:08 +00003441 out << RegisterAndSpace(sem->Access() == core::Access::kRead ? 't' : 'u',
Ben Clayton65e38242023-11-09 19:37:44 +00003442 *global_sem->Attributes().binding_point)
dan sinclair41e4d9a2022-05-01 14:40:55 +00003443 << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003444
dan sinclair41e4d9a2022-05-01 14:40:55 +00003445 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003446}
3447
dan sinclair0bfeaf92023-07-26 17:39:51 +00003448bool ASTPrinter::EmitHandleVariable(const ast::Var* var, const sem::Variable* sem) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00003449 auto* unwrapped_type = sem->Type()->UnwrapRef();
dan sinclair67a18932023-06-26 19:54:49 +00003450 auto out = Line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003451
dan sinclaird026e132023-04-18 19:38:25 +00003452 auto name = var->name->symbol.Name();
Ben Claytondcdf66e2022-06-17 12:48:51 +00003453 auto* type = sem->Type()->UnwrapRef();
Jiawei Shaoa87d0632023-11-16 00:21:08 +00003454 if (ast::HasAttribute<PixelLocal::RasterizerOrderedView>(var->attributes)) {
3455 TINT_ASSERT(!type->Is<core::type::MultisampledTexture>());
3456 auto* storage = type->As<core::type::StorageTexture>();
3457 if (!storage) {
3458 TINT_ICE() << "Rasterizer Ordered View type isn't storage texture";
3459 return false;
3460 }
3461 out << "RasterizerOrderedTexture2D";
3462 auto* component = image_format_to_rwtexture_type(storage->texel_format());
3463 if (TINT_UNLIKELY(!component)) {
3464 TINT_ICE() << "Unsupported StorageTexture TexelFormat: "
3465 << static_cast<int>(storage->texel_format());
3466 return false;
3467 }
3468 out << "<" << component << "> " << name;
3469 } else if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003470 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003471 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003472
dan sinclair41e4d9a2022-05-01 14:40:55 +00003473 const char* register_space = nullptr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003474
dan sinclaircedcdf32023-08-10 02:39:48 +00003475 if (unwrapped_type->Is<core::type::Texture>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003476 register_space = "t";
James Price88c231b2023-08-15 12:47:28 +00003477 if (auto* st = unwrapped_type->As<core::type::StorageTexture>();
3478 st && st->access() != core::Access::kRead) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003479 register_space = "u";
3480 }
dan sinclaircedcdf32023-08-10 02:39:48 +00003481 } else if (unwrapped_type->Is<core::type::Sampler>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003482 register_space = "s";
3483 }
3484
3485 if (register_space) {
Ben Clayton65e38242023-11-09 19:37:44 +00003486 auto bp = sem->As<sem::GlobalVariable>()->Attributes().binding_point;
Ben Clayton5f4847c2023-04-19 13:24:27 +00003487 out << " : register(" << register_space << bp->binding;
Peng Huangc00ff7f2023-03-31 17:55:19 +00003488 // Omit the space if it's 0, as it's the default.
3489 // SM 5.0 doesn't support spaces, so we don't emit them if group is 0 for better
3490 // compatibility.
Ben Clayton5f4847c2023-04-19 13:24:27 +00003491 if (bp->group == 0) {
Peng Huangc00ff7f2023-03-31 17:55:19 +00003492 out << ")";
3493 } else {
Ben Clayton5f4847c2023-04-19 13:24:27 +00003494 out << ", space" << bp->group << ")";
Peng Huangc00ff7f2023-03-31 17:55:19 +00003495 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003496 }
3497
3498 out << ";";
3499 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003500}
3501
dan sinclair0bfeaf92023-07-26 17:39:51 +00003502bool ASTPrinter::EmitPrivateVariable(const sem::Variable* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003503 auto* decl = var->Declaration();
dan sinclair67a18932023-06-26 19:54:49 +00003504 auto out = Line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003505
dan sinclair41e4d9a2022-05-01 14:40:55 +00003506 out << "static ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003507
dan sinclaird026e132023-04-18 19:38:25 +00003508 auto name = decl->name->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003509 auto* type = var->Type()->UnwrapRef();
dan sinclairff7cf212022-10-03 14:05:23 +00003510 if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003511 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003512 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003513
dan sinclair41e4d9a2022-05-01 14:40:55 +00003514 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00003515 if (auto* initializer = decl->initializer) {
3516 if (!EmitExpression(out, initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003517 return false;
3518 }
3519 } else {
3520 if (!EmitZeroValue(out, var->Type()->UnwrapRef())) {
3521 return false;
3522 }
3523 }
3524
3525 out << ";";
3526 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003527}
3528
dan sinclair0bfeaf92023-07-26 17:39:51 +00003529bool ASTPrinter::EmitWorkgroupVariable(const sem::Variable* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003530 auto* decl = var->Declaration();
dan sinclair67a18932023-06-26 19:54:49 +00003531 auto out = Line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003532
dan sinclair41e4d9a2022-05-01 14:40:55 +00003533 out << "groupshared ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003534
dan sinclaird026e132023-04-18 19:38:25 +00003535 auto name = decl->name->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003536 auto* type = var->Type()->UnwrapRef();
dan sinclairff7cf212022-10-03 14:05:23 +00003537 if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003538 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003539 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003540
dan sinclair6e77b472022-10-20 13:38:28 +00003541 if (auto* initializer = decl->initializer) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003542 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00003543 if (!EmitExpression(out, initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003544 return false;
3545 }
3546 }
3547
3548 out << ";";
3549 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003550}
3551
Ben Claytoncd52f382023-08-07 13:11:08 +00003552std::string ASTPrinter::builtin_to_attribute(core::BuiltinValue builtin) const {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003553 switch (builtin) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003554 case core::BuiltinValue::kPosition:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003555 return "SV_Position";
Ben Claytoncd52f382023-08-07 13:11:08 +00003556 case core::BuiltinValue::kVertexIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003557 return "SV_VertexID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003558 case core::BuiltinValue::kInstanceIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003559 return "SV_InstanceID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003560 case core::BuiltinValue::kFrontFacing:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003561 return "SV_IsFrontFace";
Ben Claytoncd52f382023-08-07 13:11:08 +00003562 case core::BuiltinValue::kFragDepth:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003563 return "SV_Depth";
Ben Claytoncd52f382023-08-07 13:11:08 +00003564 case core::BuiltinValue::kLocalInvocationId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003565 return "SV_GroupThreadID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003566 case core::BuiltinValue::kLocalInvocationIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003567 return "SV_GroupIndex";
Ben Claytoncd52f382023-08-07 13:11:08 +00003568 case core::BuiltinValue::kGlobalInvocationId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003569 return "SV_DispatchThreadID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003570 case core::BuiltinValue::kWorkgroupId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003571 return "SV_GroupID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003572 case core::BuiltinValue::kSampleIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003573 return "SV_SampleIndex";
Ben Claytoncd52f382023-08-07 13:11:08 +00003574 case core::BuiltinValue::kSampleMask:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003575 return "SV_Coverage";
3576 default:
3577 break;
3578 }
3579 return "";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003580}
3581
Ben Claytoncd52f382023-08-07 13:11:08 +00003582std::string ASTPrinter::interpolation_to_modifiers(core::InterpolationType type,
3583 core::InterpolationSampling sampling) const {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003584 std::string modifiers;
3585 switch (type) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003586 case core::InterpolationType::kPerspective:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003587 modifiers += "linear ";
3588 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003589 case core::InterpolationType::kLinear:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003590 modifiers += "noperspective ";
3591 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003592 case core::InterpolationType::kFlat:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003593 modifiers += "nointerpolation ";
3594 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003595 case core::InterpolationType::kUndefined:
Ben Claytonf9ed9d32022-10-11 19:49:17 +00003596 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003597 }
3598 switch (sampling) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003599 case core::InterpolationSampling::kCentroid:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003600 modifiers += "centroid ";
3601 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003602 case core::InterpolationSampling::kSample:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003603 modifiers += "sample ";
3604 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003605 case core::InterpolationSampling::kCenter:
3606 case core::InterpolationSampling::kUndefined:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003607 break;
3608 }
3609 return modifiers;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003610}
3611
dan sinclair0bfeaf92023-07-26 17:39:51 +00003612bool ASTPrinter::EmitEntryPointFunction(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003613 auto* func_sem = builder_.Sem().Get(func);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003614
dan sinclair41e4d9a2022-05-01 14:40:55 +00003615 {
dan sinclair67a18932023-06-26 19:54:49 +00003616 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003617 if (func->PipelineStage() == ast::PipelineStage::kCompute) {
3618 // Emit the workgroup_size attribute.
3619 auto wgsize = func_sem->WorkgroupSize();
3620 out << "[numthreads(";
dan sinclair3a2a2792022-06-29 14:38:15 +00003621 for (size_t i = 0; i < 3; i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003622 if (i > 0) {
3623 out << ", ";
3624 }
Ben Clayton490d9882022-09-21 21:05:45 +00003625 if (!wgsize[i].has_value()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00003626 diagnostics_.AddError(diag::System::Writer, Source{})
3627 << "override-expressions should have been removed with the "
3628 "SubstituteOverride transform";
Ben Clayton490d9882022-09-21 21:05:45 +00003629 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003630 }
Ben Clayton490d9882022-09-21 21:05:45 +00003631 out << std::to_string(wgsize[i].value());
dan sinclair41e4d9a2022-05-01 14:40:55 +00003632 }
3633 out << ")]" << std::endl;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003634 }
3635
Ben Claytoncd52f382023-08-07 13:11:08 +00003636 if (!EmitTypeAndName(out, func_sem->ReturnType(), core::AddressSpace::kUndefined,
3637 core::Access::kUndefined, func->name->symbol.Name())) {
Ben Clayton19068572023-02-07 21:28:09 +00003638 return false;
3639 }
3640 out << "(";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003641
3642 bool first = true;
3643
3644 // Emit entry point parameters.
3645 for (auto* var : func->params) {
3646 auto* sem = builder_.Sem().Get(var);
3647 auto* type = sem->Type();
dan sinclaircedcdf32023-08-10 02:39:48 +00003648 if (TINT_UNLIKELY(!type->Is<core::type::Struct>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003649 // ICE likely indicates that the CanonicalizeEntryPointIO transform was
3650 // not run, or a builtin parameter was added after it was run.
Ben Claytonf848af22023-07-28 16:37:32 +00003651 TINT_ICE() << "Unsupported non-struct entry point parameter";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003652 }
3653
3654 if (!first) {
3655 out << ", ";
3656 }
3657 first = false;
3658
dan sinclairff7cf212022-10-03 14:05:23 +00003659 if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(),
dan sinclaird026e132023-04-18 19:38:25 +00003660 var->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003661 return false;
3662 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003663 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003664
3665 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003666 }
3667
dan sinclair41e4d9a2022-05-01 14:40:55 +00003668 {
3669 ScopedIndent si(this);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003670
dan sinclair41e4d9a2022-05-01 14:40:55 +00003671 if (!EmitStatements(func->body->statements)) {
3672 return false;
3673 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003674
dan sinclair41e4d9a2022-05-01 14:40:55 +00003675 if (!Is<ast::ReturnStatement>(func->body->Last())) {
dan sinclair637a2fe2023-07-24 21:11:41 +00003676 ast::ReturnStatement ret(GenerationID(), ast::NodeID{}, Source{});
dan sinclair41e4d9a2022-05-01 14:40:55 +00003677 if (!EmitStatement(&ret)) {
3678 return false;
3679 }
3680 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003681 }
3682
dan sinclair67a18932023-06-26 19:54:49 +00003683 Line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003684
dan sinclair41e4d9a2022-05-01 14:40:55 +00003685 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003686}
3687
dan sinclairbae54e72023-07-28 15:01:54 +00003688bool ASTPrinter::EmitConstant(StringStream& out,
dan sinclair464b3b82023-08-09 14:14:28 +00003689 const core::constant::Value* constant,
dan sinclair0bfeaf92023-07-26 17:39:51 +00003690 bool is_variable_initializer) {
Ben Clayton50414802022-06-24 08:06:19 +00003691 return Switch(
Ben Claytonaa037ac2022-06-29 19:07:30 +00003692 constant->Type(), //
dan sinclaircedcdf32023-08-10 02:39:48 +00003693 [&](const core::type::Bool*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003694 out << (constant->ValueAs<AInt>() ? "true" : "false");
Ben Claytone9f8b092022-06-01 13:14:39 +00003695 return true;
Ben Clayton50414802022-06-24 08:06:19 +00003696 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003697 [&](const core::type::F32*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003698 PrintF32(out, constant->ValueAs<f32>());
Ben Clayton50414802022-06-24 08:06:19 +00003699 return true;
3700 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003701 [&](const core::type::F16*) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003702 // emit a f16 scalar with explicit float16_t type declaration.
3703 out << "float16_t(";
dan sinclair5addefb2022-12-14 20:46:32 +00003704 PrintF16(out, constant->ValueAs<f16>());
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003705 out << ")";
Antonio Maiorano679cf4f2022-09-03 21:43:01 +00003706 return true;
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003707 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003708 [&](const core::type::I32*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003709 out << constant->ValueAs<AInt>();
Ben Clayton50414802022-06-24 08:06:19 +00003710 return true;
3711 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003712 [&](const core::type::U32*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003713 out << constant->ValueAs<AInt>() << "u";
Ben Clayton50414802022-06-24 08:06:19 +00003714 return true;
3715 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003716 [&](const core::type::Vector* v) {
dan sinclair464b3b82023-08-09 14:14:28 +00003717 if (auto* splat = constant->As<core::constant::Splat>()) {
Ben Clayton50414802022-06-24 08:06:19 +00003718 {
dan sinclairb2ba57b2023-02-28 15:14:09 +00003719 ScopedParen sp(out);
James Price7bca4d72023-03-13 19:05:16 +00003720 if (!EmitConstant(out, splat->el, is_variable_initializer)) {
Ben Clayton50414802022-06-24 08:06:19 +00003721 return false;
3722 }
3723 }
3724 out << ".";
Ben Claytonaa037ac2022-06-29 19:07:30 +00003725 for (size_t i = 0; i < v->Width(); i++) {
Ben Clayton50414802022-06-24 08:06:19 +00003726 out << "x";
3727 }
3728 return true;
3729 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003730
Ben Claytoncd52f382023-08-07 13:11:08 +00003731 if (!EmitType(out, v, core::AddressSpace::kUndefined, core::Access::kUndefined, "")) {
Ben Clayton50414802022-06-24 08:06:19 +00003732 return false;
3733 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003734
dan sinclairb2ba57b2023-02-28 15:14:09 +00003735 ScopedParen sp(out);
Ben Claytone9f8b092022-06-01 13:14:39 +00003736
Ben Claytonaa037ac2022-06-29 19:07:30 +00003737 for (size_t i = 0; i < v->Width(); i++) {
3738 if (i > 0) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003739 out << ", ";
3740 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003741 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003742 return false;
3743 }
3744 }
3745 return true;
Ben Clayton50414802022-06-24 08:06:19 +00003746 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003747 [&](const core::type::Matrix* m) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003748 if (!EmitType(out, m, core::AddressSpace::kUndefined, core::Access::kUndefined, "")) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003749 return false;
3750 }
Ben Clayton50414802022-06-24 08:06:19 +00003751
dan sinclairb2ba57b2023-02-28 15:14:09 +00003752 ScopedParen sp(out);
Ben Clayton50414802022-06-24 08:06:19 +00003753
Ben Claytonaa037ac2022-06-29 19:07:30 +00003754 for (size_t i = 0; i < m->columns(); i++) {
3755 if (i > 0) {
Ben Clayton50414802022-06-24 08:06:19 +00003756 out << ", ";
3757 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003758 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Clayton50414802022-06-24 08:06:19 +00003759 return false;
3760 }
3761 }
3762 return true;
3763 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003764 [&](const core::type::Array* a) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003765 if (constant->AllZero()) {
Ben Clayton19576e92022-06-28 12:44:16 +00003766 out << "(";
Ben Claytoncd52f382023-08-07 13:11:08 +00003767 if (!EmitType(out, a, core::AddressSpace::kUndefined, core::Access::kUndefined,
3768 "")) {
Ben Clayton19576e92022-06-28 12:44:16 +00003769 return false;
3770 }
3771 out << ")0";
3772 return true;
3773 }
3774
3775 out << "{";
3776 TINT_DEFER(out << "}");
3777
dan sinclair78f80672022-09-22 22:28:21 +00003778 auto count = a->ConstantCount();
3779 if (!count) {
Ben Claytonc27315a2024-02-26 20:24:06 +00003780 diagnostics_.AddError(diag::System::Writer, Source{})
3781 << core::type::Array::kErrExpectedConstantCount;
dan sinclair78f80672022-09-22 22:28:21 +00003782 return false;
3783 }
3784
3785 for (size_t i = 0; i < count; i++) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003786 if (i > 0) {
Ben Clayton19576e92022-06-28 12:44:16 +00003787 out << ", ";
3788 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003789 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Clayton19576e92022-06-28 12:44:16 +00003790 return false;
3791 }
3792 }
3793
3794 return true;
3795 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003796 [&](const core::type::Struct* s) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003797 if (!EmitStructType(&helpers_, s)) {
3798 return false;
3799 }
3800
Ben Clayton6c098ba2022-07-14 20:46:39 +00003801 if (constant->AllZero()) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003802 out << "(" << StructName(s) << ")0";
Ben Clayton6c098ba2022-07-14 20:46:39 +00003803 return true;
3804 }
3805
dan sinclairbae54e72023-07-28 15:01:54 +00003806 auto emit_member_values = [&](StringStream& o) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003807 o << "{";
dan sinclairad9cd0a2022-12-06 20:01:54 +00003808 for (size_t i = 0; i < s->Members().Length(); i++) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003809 if (i > 0) {
3810 o << ", ";
3811 }
3812 if (!EmitConstant(o, constant->Index(i), is_variable_initializer)) {
3813 return false;
3814 }
Ben Clayton6c098ba2022-07-14 20:46:39 +00003815 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003816 o << "}";
3817 return true;
3818 };
3819
3820 if (is_variable_initializer) {
3821 if (!emit_member_values(out)) {
Ben Clayton6c098ba2022-07-14 20:46:39 +00003822 return false;
3823 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003824 } else {
3825 // HLSL requires structure initializers to be assigned directly to a variable.
Ben Clayton2d501c52024-01-17 01:27:37 +00003826 // For these constants use 'static const' at global-scope. 'const' at global scope
3827 // creates a variable who's initializer is ignored, and the value is expected to be
3828 // provided in a cbuffer. 'static const' is a true value-embedded-in-the-shader-code
3829 // constant. We also emit these for function-local constant expressions for
3830 // consistency and to ensure that these are not computed at execution time.
Ben Clayton329dfd72022-11-23 00:05:05 +00003831 auto name = UniqueIdentifier("c");
3832 {
Ben Clayton2d501c52024-01-17 01:27:37 +00003833 StringStream decl;
3834 decl << "static const " << StructName(s) << " " << name << " = ";
Ben Clayton329dfd72022-11-23 00:05:05 +00003835 if (!emit_member_values(decl)) {
3836 return false;
3837 }
3838 decl << ";";
Ben Clayton2d501c52024-01-17 01:27:37 +00003839 current_buffer_->Insert(decl.str(), global_insertion_point_++, 0);
Ben Clayton329dfd72022-11-23 00:05:05 +00003840 }
3841 out << name;
Ben Clayton6c098ba2022-07-14 20:46:39 +00003842 }
3843
3844 return true;
Ben Claytond6082c52023-10-26 16:02:01 +00003845 }, //
3846 TINT_ICE_ON_NO_MATCH);
Ben Claytone9f8b092022-06-01 13:14:39 +00003847}
3848
dan sinclairbae54e72023-07-28 15:01:54 +00003849bool ASTPrinter::EmitLiteral(StringStream& out, const ast::LiteralExpression* lit) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003850 return Switch(
3851 lit,
3852 [&](const ast::BoolLiteralExpression* l) {
3853 out << (l->value ? "true" : "false");
3854 return true;
3855 },
Ben Clayton3ad927c2022-05-25 23:12:14 +00003856 [&](const ast::FloatLiteralExpression* l) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003857 if (l->suffix == ast::FloatLiteralExpression::Suffix::kH) {
3858 // Emit f16 literal with explicit float16_t type declaration.
3859 out << "float16_t(";
Antonio Maiorano679cf4f2022-09-03 21:43:01 +00003860 PrintF16(out, static_cast<float>(l->value));
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003861 out << ")";
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003862 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003863 PrintF32(out, static_cast<float>(l->value));
dan sinclair41e4d9a2022-05-01 14:40:55 +00003864 return true;
3865 },
Ben Clayton8822e292022-05-04 22:18:49 +00003866 [&](const ast::IntLiteralExpression* i) {
3867 out << i->value;
3868 switch (i->suffix) {
3869 case ast::IntLiteralExpression::Suffix::kNone:
3870 case ast::IntLiteralExpression::Suffix::kI:
3871 return true;
3872 case ast::IntLiteralExpression::Suffix::kU:
3873 out << "u";
3874 return true;
3875 }
Ben Claytonc27315a2024-02-26 20:24:06 +00003876 diagnostics_.AddError(diag::System::Writer, Source{})
3877 << "unknown integer literal suffix type";
Ben Clayton8822e292022-05-04 22:18:49 +00003878 return false;
Ben Claytond6082c52023-10-26 16:02:01 +00003879 }, //
3880 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003881}
3882
dan sinclaircedcdf32023-08-10 02:39:48 +00003883bool ASTPrinter::EmitValue(StringStream& out, const core::type::Type* type, int value) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003884 return Switch(
3885 type,
dan sinclaircedcdf32023-08-10 02:39:48 +00003886 [&](const core::type::Bool*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003887 out << (value == 0 ? "false" : "true");
3888 return true;
3889 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003890 [&](const core::type::F32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003891 out << value << ".0f";
3892 return true;
3893 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003894 [&](const core::type::F16*) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003895 out << "float16_t(" << value << ".0h)";
3896 return true;
3897 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003898 [&](const core::type::I32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003899 out << value;
3900 return true;
3901 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003902 [&](const core::type::U32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003903 out << value << "u";
3904 return true;
3905 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003906 [&](const core::type::Vector* vec) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003907 if (!EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kReadWrite,
Ben Clayton1b90f932023-02-18 12:37:34 +00003908 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003909 return false;
3910 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00003911 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003912 for (uint32_t i = 0; i < vec->Width(); i++) {
3913 if (i != 0) {
3914 out << ", ";
3915 }
3916 if (!EmitValue(out, vec->type(), value)) {
3917 return false;
3918 }
3919 }
3920 return true;
3921 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003922 [&](const core::type::Matrix* mat) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003923 if (!EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kReadWrite,
Ben Clayton1b90f932023-02-18 12:37:34 +00003924 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003925 return false;
3926 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00003927 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003928 for (uint32_t i = 0; i < (mat->rows() * mat->columns()); i++) {
3929 if (i != 0) {
3930 out << ", ";
3931 }
3932 if (!EmitValue(out, mat->type(), value)) {
3933 return false;
3934 }
3935 }
3936 return true;
3937 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003938 [&](const core::type::Struct*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003939 out << "(";
3940 TINT_DEFER(out << ")" << value);
Ben Claytoncd52f382023-08-07 13:11:08 +00003941 return EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kUndefined,
3942 "");
dan sinclair41e4d9a2022-05-01 14:40:55 +00003943 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003944 [&](const core::type::Array*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003945 out << "(";
3946 TINT_DEFER(out << ")" << value);
Ben Claytoncd52f382023-08-07 13:11:08 +00003947 return EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kUndefined,
3948 "");
Ben Claytond6082c52023-10-26 16:02:01 +00003949 }, //
3950 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003951}
3952
dan sinclaircedcdf32023-08-10 02:39:48 +00003953bool ASTPrinter::EmitZeroValue(StringStream& out, const core::type::Type* type) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003954 return EmitValue(out, type, 0);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003955}
3956
dan sinclair0bfeaf92023-07-26 17:39:51 +00003957bool ASTPrinter::EmitLoop(const ast::LoopStatement* stmt) {
Ben Claytona0aabaf2023-06-22 23:53:09 +00003958 auto emit_continuing = [this, stmt] {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003959 if (stmt->continuing && !stmt->continuing->Empty()) {
3960 if (!EmitBlock(stmt->continuing)) {
3961 return false;
3962 }
3963 }
3964 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003965 };
3966
3967 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
dan sinclair67a18932023-06-26 19:54:49 +00003968 Line() << "while (true) {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003969 {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003970 ScopedIndent si(this);
3971 if (!EmitStatements(stmt->body->statements)) {
3972 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003973 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003974 if (!emit_continuing_()) {
3975 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003976 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003977 }
dan sinclair67a18932023-06-26 19:54:49 +00003978 Line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003979
dan sinclair41e4d9a2022-05-01 14:40:55 +00003980 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003981}
3982
dan sinclair0bfeaf92023-07-26 17:39:51 +00003983bool ASTPrinter::EmitForLoop(const ast::ForLoopStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003984 // Nest a for loop with a new block. In HLSL the initializer scope is not
3985 // nested by the for-loop, so we may get variable redefinitions.
dan sinclair67a18932023-06-26 19:54:49 +00003986 Line() << "{";
3987 IncrementIndent();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003988 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00003989 DecrementIndent();
3990 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003991 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003992
dan sinclair41e4d9a2022-05-01 14:40:55 +00003993 TextBuffer init_buf;
3994 if (auto* init = stmt->initializer) {
3995 TINT_SCOPED_ASSIGNMENT(current_buffer_, &init_buf);
3996 if (!EmitStatement(init)) {
3997 return false;
3998 }
3999 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004000
dan sinclair41e4d9a2022-05-01 14:40:55 +00004001 TextBuffer cond_pre;
dan sinclairbae54e72023-07-28 15:01:54 +00004002 StringStream cond_buf;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004003 if (auto* cond = stmt->condition) {
4004 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
4005 if (!EmitExpression(cond_buf, cond)) {
4006 return false;
4007 }
4008 }
4009
4010 TextBuffer cont_buf;
4011 if (auto* cont = stmt->continuing) {
4012 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cont_buf);
4013 if (!EmitStatement(cont)) {
4014 return false;
4015 }
4016 }
4017
4018 // If the for-loop has a multi-statement conditional and / or continuing, then
4019 // we cannot emit this as a regular for-loop in HLSL. Instead we need to
4020 // generate a `while(true)` loop.
4021 bool emit_as_loop = cond_pre.lines.size() > 0 || cont_buf.lines.size() > 1;
4022
4023 // If the for-loop has multi-statement initializer, or is going to be emitted
4024 // as a `while(true)` loop, then declare the initializer statement(s) before
4025 // the loop.
4026 if (init_buf.lines.size() > 1 || (stmt->initializer && emit_as_loop)) {
4027 current_buffer_->Append(init_buf);
4028 init_buf.lines.clear(); // Don't emit the initializer again in the 'for'
4029 }
4030
4031 if (emit_as_loop) {
Ben Claytona0aabaf2023-06-22 23:53:09 +00004032 auto emit_continuing = [&] {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004033 current_buffer_->Append(cont_buf);
4034 return true;
4035 };
4036
4037 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
dan sinclair67a18932023-06-26 19:54:49 +00004038 Line() << "while (true) {";
4039 IncrementIndent();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004040 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00004041 DecrementIndent();
4042 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004043 });
4044
4045 if (stmt->condition) {
4046 current_buffer_->Append(cond_pre);
dan sinclair67a18932023-06-26 19:54:49 +00004047 Line() << "if (!(" << cond_buf.str() << ")) { break; }";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004048 }
4049
4050 if (!EmitStatements(stmt->body->statements)) {
4051 return false;
4052 }
4053
4054 if (!emit_continuing_()) {
4055 return false;
4056 }
4057 } else {
4058 // For-loop can be generated.
4059 {
dan sinclair67a18932023-06-26 19:54:49 +00004060 auto out = Line();
Antonio Maiorano06844a52022-09-29 16:53:58 +00004061 out << "for";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004062 {
4063 ScopedParen sp(out);
4064
4065 if (!init_buf.lines.empty()) {
4066 out << init_buf.lines[0].content << " ";
4067 } else {
4068 out << "; ";
4069 }
4070
4071 out << cond_buf.str() << "; ";
4072
4073 if (!cont_buf.lines.empty()) {
dan sinclairbae54e72023-07-28 15:01:54 +00004074 out << tint::TrimSuffix(cont_buf.lines[0].content, ";");
dan sinclair41e4d9a2022-05-01 14:40:55 +00004075 }
4076 }
4077 out << " {";
4078 }
4079 {
4080 auto emit_continuing = [] { return true; };
4081 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
4082 if (!EmitStatementsWithIndent(stmt->body->statements)) {
4083 return false;
4084 }
4085 }
dan sinclair67a18932023-06-26 19:54:49 +00004086 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004087 }
4088
4089 return true;
4090}
4091
dan sinclair0bfeaf92023-07-26 17:39:51 +00004092bool ASTPrinter::EmitWhile(const ast::WhileStatement* stmt) {
dan sinclair49d1a2d2022-06-16 12:01:27 +00004093 TextBuffer cond_pre;
dan sinclairbae54e72023-07-28 15:01:54 +00004094 StringStream cond_buf;
dan sinclair49d1a2d2022-06-16 12:01:27 +00004095 {
4096 auto* cond = stmt->condition;
4097 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
4098 if (!EmitExpression(cond_buf, cond)) {
4099 return false;
4100 }
4101 }
4102
Ben Claytona0aabaf2023-06-22 23:53:09 +00004103 auto emit_continuing = [&] { return true; };
dan sinclair4b88dbc2022-06-16 15:27:38 +00004104 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
4105
dan sinclair49d1a2d2022-06-16 12:01:27 +00004106 // If the while has a multi-statement conditional, then we cannot emit this
4107 // as a regular while in HLSL. Instead we need to generate a `while(true)` loop.
4108 bool emit_as_loop = cond_pre.lines.size() > 0;
4109 if (emit_as_loop) {
dan sinclair67a18932023-06-26 19:54:49 +00004110 Line() << "while (true) {";
4111 IncrementIndent();
dan sinclair49d1a2d2022-06-16 12:01:27 +00004112 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00004113 DecrementIndent();
4114 Line() << "}";
dan sinclair49d1a2d2022-06-16 12:01:27 +00004115 });
4116
4117 current_buffer_->Append(cond_pre);
dan sinclair67a18932023-06-26 19:54:49 +00004118 Line() << "if (!(" << cond_buf.str() << ")) { break; }";
dan sinclair49d1a2d2022-06-16 12:01:27 +00004119 if (!EmitStatements(stmt->body->statements)) {
4120 return false;
4121 }
4122 } else {
4123 // While can be generated.
4124 {
dan sinclair67a18932023-06-26 19:54:49 +00004125 auto out = Line();
Antonio Maiorano06844a52022-09-29 16:53:58 +00004126 out << "while";
dan sinclair49d1a2d2022-06-16 12:01:27 +00004127 {
4128 ScopedParen sp(out);
4129 out << cond_buf.str();
4130 }
4131 out << " {";
4132 }
4133 if (!EmitStatementsWithIndent(stmt->body->statements)) {
4134 return false;
4135 }
dan sinclair67a18932023-06-26 19:54:49 +00004136 Line() << "}";
dan sinclair49d1a2d2022-06-16 12:01:27 +00004137 }
4138
4139 return true;
4140}
4141
dan sinclairbae54e72023-07-28 15:01:54 +00004142bool ASTPrinter::EmitMemberAccessor(StringStream& out, const ast::MemberAccessorExpression* expr) {
Ben Claytonad315652023-02-05 12:36:50 +00004143 if (!EmitExpression(out, expr->object)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004144 return false;
4145 }
4146 out << ".";
4147
Ben Clayton2f9a9882022-12-17 02:20:04 +00004148 auto* sem = builder_.Sem().Get(expr)->UnwrapLoad();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004149
Ben Clayton10fae7a2022-11-14 15:29:29 +00004150 return Switch(
4151 sem,
4152 [&](const sem::Swizzle*) {
4153 // Swizzles output the name directly
dan sinclaird026e132023-04-18 19:38:25 +00004154 out << expr->member->symbol.Name();
Ben Clayton10fae7a2022-11-14 15:29:29 +00004155 return true;
4156 },
4157 [&](const sem::StructMemberAccess* member_access) {
dan sinclaird026e132023-04-18 19:38:25 +00004158 out << member_access->Member()->Name().Name();
Ben Clayton10fae7a2022-11-14 15:29:29 +00004159 return true;
Ben Claytond6082c52023-10-26 16:02:01 +00004160 }, //
4161 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004162}
4163
dan sinclair0bfeaf92023-07-26 17:39:51 +00004164bool ASTPrinter::EmitReturn(const ast::ReturnStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004165 if (stmt->value) {
dan sinclair67a18932023-06-26 19:54:49 +00004166 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004167 out << "return ";
4168 if (!EmitExpression(out, stmt->value)) {
4169 return false;
4170 }
4171 out << ";";
4172 } else {
dan sinclair67a18932023-06-26 19:54:49 +00004173 Line() << "return;";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004174 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004175 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004176}
4177
dan sinclair0bfeaf92023-07-26 17:39:51 +00004178bool ASTPrinter::EmitStatement(const ast::Statement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004179 return Switch(
4180 stmt,
4181 [&](const ast::AssignmentStatement* a) { //
4182 return EmitAssign(a);
4183 },
4184 [&](const ast::BlockStatement* b) { //
4185 return EmitBlock(b);
4186 },
4187 [&](const ast::BreakStatement* b) { //
4188 return EmitBreak(b);
4189 },
dan sinclairb8b0c212022-10-20 22:45:50 +00004190 [&](const ast::BreakIfStatement* b) { //
4191 return EmitBreakIf(b);
4192 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004193 [&](const ast::CallStatement* c) { //
dan sinclair67a18932023-06-26 19:54:49 +00004194 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004195 if (!EmitCall(out, c->expr)) {
4196 return false;
4197 }
4198 out << ";";
4199 return true;
4200 },
4201 [&](const ast::ContinueStatement* c) { //
4202 return EmitContinue(c);
4203 },
4204 [&](const ast::DiscardStatement* d) { //
4205 return EmitDiscard(d);
4206 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004207 [&](const ast::IfStatement* i) { //
4208 return EmitIf(i);
4209 },
4210 [&](const ast::LoopStatement* l) { //
4211 return EmitLoop(l);
4212 },
4213 [&](const ast::ForLoopStatement* l) { //
4214 return EmitForLoop(l);
4215 },
dan sinclair49d1a2d2022-06-16 12:01:27 +00004216 [&](const ast::WhileStatement* l) { //
4217 return EmitWhile(l);
4218 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004219 [&](const ast::ReturnStatement* r) { //
4220 return EmitReturn(r);
4221 },
4222 [&](const ast::SwitchStatement* s) { //
4223 return EmitSwitch(s);
4224 },
4225 [&](const ast::VariableDeclStatement* v) { //
Ben Claytondcdf66e2022-06-17 12:48:51 +00004226 return Switch(
4227 v->variable, //
4228 [&](const ast::Var* var) { return EmitVar(var); },
4229 [&](const ast::Let* let) { return EmitLet(let); },
Ben Clayton19576e92022-06-28 12:44:16 +00004230 [&](const ast::Const*) {
4231 return true; // Constants are embedded at their use
Ben Claytond6082c52023-10-26 16:02:01 +00004232 }, //
4233 TINT_ICE_ON_NO_MATCH);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004234 },
Ben Claytonc98d57d2023-01-24 14:59:43 +00004235 [&](const ast::ConstAssert*) {
Ben Claytonb4744ac2022-08-03 07:01:08 +00004236 return true; // Not emitted
Ben Claytond6082c52023-10-26 16:02:01 +00004237 }, //
4238 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004239}
4240
dan sinclair0bfeaf92023-07-26 17:39:51 +00004241bool ASTPrinter::EmitDefaultOnlySwitch(const ast::SwitchStatement* stmt) {
Ben Claytonf848af22023-07-28 16:37:32 +00004242 TINT_ASSERT(stmt->body.Length() == 1 && stmt->body[0]->ContainsDefault());
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004243
dan sinclair41e4d9a2022-05-01 14:40:55 +00004244 // FXC fails to compile a switch with just a default case, ignoring the
4245 // default case body. We work around this here by emitting the default case
4246 // without the switch.
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004247
Antonio Maioranoeab1f622023-02-01 15:46:34 +00004248 // Emit the switch condition as-is if it has side-effects (e.g.
4249 // function call). Note that we can ignore the result of the expression (if any).
Ben Clayton0b4a2f12023-02-05 22:59:40 +00004250 if (auto* sem_cond = builder_.Sem().GetVal(stmt->condition); sem_cond->HasSideEffects()) {
dan sinclair67a18932023-06-26 19:54:49 +00004251 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004252 if (!EmitExpression(out, stmt->condition)) {
4253 return false;
4254 }
4255 out << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004256 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004257
dan sinclair41e4d9a2022-05-01 14:40:55 +00004258 // Emit "do { <default case body> } while(false);". We use a 'do' loop so
4259 // that break statements work as expected, and make it 'while (false)' in
4260 // case there isn't a break statement.
dan sinclair67a18932023-06-26 19:54:49 +00004261 Line() << "do {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004262 {
4263 ScopedIndent si(this);
4264 if (!EmitStatements(stmt->body[0]->body->statements)) {
4265 return false;
4266 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004267 }
dan sinclair67a18932023-06-26 19:54:49 +00004268 Line() << "} while (false);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004269 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004270}
4271
dan sinclair0bfeaf92023-07-26 17:39:51 +00004272bool ASTPrinter::EmitSwitch(const ast::SwitchStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004273 // BUG(crbug.com/tint/1188): work around default-only switches
dan sinclairf148f082022-10-19 15:55:02 +00004274 if (stmt->body.Length() == 1 && stmt->body[0]->selectors.Length() == 1 &&
4275 stmt->body[0]->ContainsDefault()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004276 return EmitDefaultOnlySwitch(stmt);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004277 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004278
dan sinclair41e4d9a2022-05-01 14:40:55 +00004279 { // switch(expr) {
dan sinclair67a18932023-06-26 19:54:49 +00004280 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004281 out << "switch(";
4282 if (!EmitExpression(out, stmt->condition)) {
4283 return false;
4284 }
4285 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004286 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004287
dan sinclair41e4d9a2022-05-01 14:40:55 +00004288 {
4289 ScopedIndent si(this);
Ben Clayton783b1692022-08-02 17:03:35 +00004290 for (size_t i = 0; i < stmt->body.Length(); i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004291 if (!EmitCase(stmt, i)) {
4292 return false;
4293 }
4294 }
4295 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004296
dan sinclair67a18932023-06-26 19:54:49 +00004297 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004298
4299 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004300}
4301
dan sinclairbae54e72023-07-28 15:01:54 +00004302bool ASTPrinter::EmitType(StringStream& out,
dan sinclaircedcdf32023-08-10 02:39:48 +00004303 const core::type::Type* type,
Ben Claytoncd52f382023-08-07 13:11:08 +00004304 core::AddressSpace address_space,
4305 core::Access access,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004306 const std::string& name,
4307 bool* name_printed /* = nullptr */) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004308 if (name_printed) {
4309 *name_printed = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004310 }
dan sinclairff7cf212022-10-03 14:05:23 +00004311 switch (address_space) {
Ben Claytoncd52f382023-08-07 13:11:08 +00004312 case core::AddressSpace::kStorage:
4313 if (access != core::Access::kRead) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004314 out << "RW";
4315 }
4316 out << "ByteAddressBuffer";
4317 return true;
Ben Claytoncd52f382023-08-07 13:11:08 +00004318 case core::AddressSpace::kUniform: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004319 auto array_length = (type->Size() + 15) / 16;
4320 out << "uint4 " << name << "[" << array_length << "]";
4321 if (name_printed) {
4322 *name_printed = true;
4323 }
4324 return true;
4325 }
4326 default:
4327 break;
4328 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004329
dan sinclair41e4d9a2022-05-01 14:40:55 +00004330 return Switch(
4331 type,
dan sinclaircedcdf32023-08-10 02:39:48 +00004332 [&](const core::type::Array* ary) {
4333 const core::type::Type* base_type = ary;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004334 std::vector<uint32_t> sizes;
dan sinclaircedcdf32023-08-10 02:39:48 +00004335 while (auto* arr = base_type->As<core::type::Array>()) {
4336 if (TINT_UNLIKELY(arr->Count()->Is<core::type::RuntimeArrayCount>())) {
Ben Claytonf848af22023-07-28 16:37:32 +00004337 TINT_ICE()
dan sinclair78f80672022-09-22 22:28:21 +00004338 << "runtime arrays may only exist in storage buffers, which should have "
Ben Clayton3a68ab42022-06-24 08:30:28 +00004339 "been transformed into a ByteAddressBuffer";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004340 return false;
4341 }
dan sinclair78f80672022-09-22 22:28:21 +00004342 const auto count = arr->ConstantCount();
4343 if (!count) {
Ben Claytonc27315a2024-02-26 20:24:06 +00004344 diagnostics_.AddError(diag::System::Writer, Source{})
4345 << core::type::Array::kErrExpectedConstantCount;
dan sinclair78f80672022-09-22 22:28:21 +00004346 return false;
4347 }
4348
4349 sizes.push_back(count.value());
dan sinclair41e4d9a2022-05-01 14:40:55 +00004350 base_type = arr->ElemType();
4351 }
dan sinclairff7cf212022-10-03 14:05:23 +00004352 if (!EmitType(out, base_type, address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004353 return false;
4354 }
4355 if (!name.empty()) {
4356 out << " " << name;
4357 if (name_printed) {
4358 *name_printed = true;
4359 }
4360 }
4361 for (uint32_t size : sizes) {
4362 out << "[" << size << "]";
4363 }
4364 return true;
4365 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004366 [&](const core::type::Bool*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004367 out << "bool";
4368 return true;
4369 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004370 [&](const core::type::F32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004371 out << "float";
4372 return true;
4373 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004374 [&](const core::type::F16*) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004375 out << "float16_t";
4376 return true;
Zhaoming Jiang62bfd312022-05-13 12:01:11 +00004377 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004378 [&](const core::type::I32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004379 out << "int";
4380 return true;
4381 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004382 [&](const core::type::Matrix* mat) {
4383 if (mat->type()->Is<core::type::F16>()) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004384 // Use matrix<type, N, M> for f16 matrix
4385 out << "matrix<";
dan sinclairff7cf212022-10-03 14:05:23 +00004386 if (!EmitType(out, mat->type(), address_space, access, "")) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004387 return false;
4388 }
4389 out << ", " << mat->columns() << ", " << mat->rows() << ">";
4390 return true;
4391 }
dan sinclairff7cf212022-10-03 14:05:23 +00004392 if (!EmitType(out, mat->type(), address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004393 return false;
4394 }
4395 // Note: HLSL's matrices are declared as <type>NxM, where N is the
4396 // number of rows and M is the number of columns. Despite HLSL's
4397 // matrices being column-major by default, the index operator and
dan sinclair6e77b472022-10-20 13:38:28 +00004398 // initializers actually operate on row-vectors, where as WGSL operates
dan sinclair41e4d9a2022-05-01 14:40:55 +00004399 // on column vectors. To simplify everything we use the transpose of the
4400 // matrices. See:
4401 // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-per-component-math#matrix-ordering
4402 out << mat->columns() << "x" << mat->rows();
4403 return true;
4404 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004405 [&](const core::type::Pointer*) {
Ben Claytonf848af22023-07-28 16:37:32 +00004406 TINT_ICE() << "Attempting to emit pointer type. These should have "
4407 "been removed with the SimplifyPointers transform";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004408 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004409 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004410 [&](const core::type::Sampler* sampler) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004411 out << "Sampler";
4412 if (sampler->IsComparison()) {
4413 out << "Comparison";
4414 }
4415 out << "State";
4416 return true;
4417 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004418 [&](const core::type::Struct* str) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004419 out << StructName(str);
4420 return true;
4421 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004422 [&](const core::type::Texture* tex) {
4423 if (TINT_UNLIKELY(tex->Is<core::type::ExternalTexture>())) {
Ben Claytonf848af22023-07-28 16:37:32 +00004424 TINT_ICE() << "Multiplanar external texture transform was not run.";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004425 return false;
4426 }
Brandon Jones6661b282022-02-25 20:14:52 +00004427
dan sinclaircedcdf32023-08-10 02:39:48 +00004428 auto* storage = tex->As<core::type::StorageTexture>();
4429 auto* ms = tex->As<core::type::MultisampledTexture>();
4430 auto* depth_ms = tex->As<core::type::DepthMultisampledTexture>();
4431 auto* sampled = tex->As<core::type::SampledTexture>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004432
Ben Claytoncd52f382023-08-07 13:11:08 +00004433 if (storage && storage->access() != core::Access::kRead) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004434 out << "RW";
4435 }
4436 out << "Texture";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004437
dan sinclair41e4d9a2022-05-01 14:40:55 +00004438 switch (tex->dim()) {
dan sinclaircedcdf32023-08-10 02:39:48 +00004439 case core::type::TextureDimension::k1d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004440 out << "1D";
4441 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004442 case core::type::TextureDimension::k2d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004443 out << ((ms || depth_ms) ? "2DMS" : "2D");
4444 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004445 case core::type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004446 out << ((ms || depth_ms) ? "2DMSArray" : "2DArray");
4447 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004448 case core::type::TextureDimension::k3d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004449 out << "3D";
4450 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004451 case core::type::TextureDimension::kCube:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004452 out << "Cube";
4453 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004454 case core::type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004455 out << "CubeArray";
4456 break;
4457 default:
Ben Claytonf848af22023-07-28 16:37:32 +00004458 TINT_UNREACHABLE() << "unexpected TextureDimension " << tex->dim();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004459 return false;
4460 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004461
dan sinclair41e4d9a2022-05-01 14:40:55 +00004462 if (storage) {
4463 auto* component = image_format_to_rwtexture_type(storage->texel_format());
Ben Clayton884f9522023-01-12 22:52:57 +00004464 if (TINT_UNLIKELY(!component)) {
Ben Claytonf848af22023-07-28 16:37:32 +00004465 TINT_ICE() << "Unsupported StorageTexture TexelFormat: "
4466 << static_cast<int>(storage->texel_format());
dan sinclair41e4d9a2022-05-01 14:40:55 +00004467 return false;
4468 }
4469 out << "<" << component << ">";
4470 } else if (depth_ms) {
4471 out << "<float4>";
4472 } else if (sampled || ms) {
4473 auto* subtype = sampled ? sampled->type() : ms->type();
4474 out << "<";
dan sinclaircedcdf32023-08-10 02:39:48 +00004475 if (subtype->Is<core::type::F32>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004476 out << "float4";
dan sinclaircedcdf32023-08-10 02:39:48 +00004477 } else if (subtype->Is<core::type::I32>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004478 out << "int4";
dan sinclaircedcdf32023-08-10 02:39:48 +00004479 } else if (TINT_LIKELY(subtype->Is<core::type::U32>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004480 out << "uint4";
4481 } else {
Ben Claytonf848af22023-07-28 16:37:32 +00004482 TINT_ICE() << "Unsupported multisampled texture type";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004483 return false;
4484 }
4485 out << ">";
4486 }
4487 return true;
4488 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004489 [&](const core::type::U32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004490 out << "uint";
4491 return true;
4492 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004493 [&](const core::type::Vector* vec) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004494 auto width = vec->Width();
dan sinclaircedcdf32023-08-10 02:39:48 +00004495 if (vec->type()->Is<core::type::F32>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004496 out << "float" << width;
dan sinclaircedcdf32023-08-10 02:39:48 +00004497 } else if (vec->type()->Is<core::type::I32>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004498 out << "int" << width;
dan sinclaircedcdf32023-08-10 02:39:48 +00004499 } else if (vec->type()->Is<core::type::U32>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004500 out << "uint" << width;
dan sinclaircedcdf32023-08-10 02:39:48 +00004501 } else if (vec->type()->Is<core::type::Bool>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004502 out << "bool" << width;
4503 } else {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004504 // For example, use "vector<float16_t, N>" for f16 vector.
dan sinclair41e4d9a2022-05-01 14:40:55 +00004505 out << "vector<";
dan sinclairff7cf212022-10-03 14:05:23 +00004506 if (!EmitType(out, vec->type(), address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004507 return false;
4508 }
4509 out << ", " << width << ">";
4510 }
4511 return true;
4512 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004513 [&](const core::type::Atomic* atomic) {
dan sinclairff7cf212022-10-03 14:05:23 +00004514 return EmitType(out, atomic->Type(), address_space, access, name);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004515 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004516 [&](const core::type::Void*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004517 out << "void";
4518 return true;
Ben Claytond6082c52023-10-26 16:02:01 +00004519 }, //
4520 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004521}
4522
dan sinclairbae54e72023-07-28 15:01:54 +00004523bool ASTPrinter::EmitTypeAndName(StringStream& out,
dan sinclaircedcdf32023-08-10 02:39:48 +00004524 const core::type::Type* type,
Ben Claytoncd52f382023-08-07 13:11:08 +00004525 core::AddressSpace address_space,
4526 core::Access access,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004527 const std::string& name) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004528 bool name_printed = false;
dan sinclairff7cf212022-10-03 14:05:23 +00004529 if (!EmitType(out, type, address_space, access, name, &name_printed)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004530 return false;
4531 }
4532 if (!name.empty() && !name_printed) {
4533 out << " " << name;
4534 }
4535 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004536}
4537
dan sinclaircedcdf32023-08-10 02:39:48 +00004538bool ASTPrinter::EmitStructType(TextBuffer* b, const core::type::Struct* str) {
Ben Clayton329dfd72022-11-23 00:05:05 +00004539 auto it = emitted_structs_.emplace(str);
4540 if (!it.second) {
4541 return true;
4542 }
4543
dan sinclair67a18932023-06-26 19:54:49 +00004544 Line(b) << "struct " << StructName(str) << " {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004545 {
4546 ScopedIndent si(b);
4547 for (auto* mem : str->Members()) {
dan sinclaird026e132023-04-18 19:38:25 +00004548 auto mem_name = mem->Name().Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004549 auto* ty = mem->Type();
dan sinclair67a18932023-06-26 19:54:49 +00004550 auto out = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004551 std::string pre, post;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004552
Ben Clayton576ba1c2023-04-27 17:58:25 +00004553 auto& attributes = mem->Attributes();
Ben Claytonf0b4dbb2023-02-21 21:05:28 +00004554
Ben Clayton576ba1c2023-04-27 17:58:25 +00004555 if (auto location = attributes.location) {
4556 auto& pipeline_stage_uses = str->PipelineStageUses();
Ben Clayton8be957f2024-01-29 16:00:36 +00004557 if (TINT_UNLIKELY(pipeline_stage_uses.Count() != 1)) {
Ben Claytonf848af22023-07-28 16:37:32 +00004558 TINT_ICE() << "invalid entry point IO struct uses";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004559 }
Ben Clayton8be957f2024-01-29 16:00:36 +00004560 if (pipeline_stage_uses.Contains(core::type::PipelineStageUsage::kVertexInput)) {
Ben Clayton576ba1c2023-04-27 17:58:25 +00004561 post += " : TEXCOORD" + std::to_string(location.value());
Ben Clayton8be957f2024-01-29 16:00:36 +00004562 } else if (pipeline_stage_uses.Contains(
dan sinclaircedcdf32023-08-10 02:39:48 +00004563 core::type::PipelineStageUsage::kVertexOutput)) {
Ben Clayton576ba1c2023-04-27 17:58:25 +00004564 post += " : TEXCOORD" + std::to_string(location.value());
Ben Clayton8be957f2024-01-29 16:00:36 +00004565 } else if (pipeline_stage_uses.Contains(
dan sinclaircedcdf32023-08-10 02:39:48 +00004566 core::type::PipelineStageUsage::kFragmentInput)) {
Ben Clayton576ba1c2023-04-27 17:58:25 +00004567 post += " : TEXCOORD" + std::to_string(location.value());
Ben Clayton8be957f2024-01-29 16:00:36 +00004568 } else if (TINT_LIKELY(pipeline_stage_uses.Contains(
dan sinclaircedcdf32023-08-10 02:39:48 +00004569 core::type::PipelineStageUsage::kFragmentOutput))) {
Corentin Wallez0880e062024-01-30 16:16:45 +00004570 if (auto blend_src = attributes.blend_src) {
4571 post +=
4572 " : SV_Target" + std::to_string(location.value() + blend_src.value());
Brandon Jones07500d32023-07-21 22:07:03 +00004573 } else {
4574 post += " : SV_Target" + std::to_string(location.value());
4575 }
4576
Ben Clayton576ba1c2023-04-27 17:58:25 +00004577 } else {
Ben Claytonf848af22023-07-28 16:37:32 +00004578 TINT_ICE() << "invalid use of location attribute";
Ben Clayton576ba1c2023-04-27 17:58:25 +00004579 }
4580 }
4581 if (auto builtin = attributes.builtin) {
4582 auto name = builtin_to_attribute(builtin.value());
4583 if (name.empty()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00004584 diagnostics_.AddError(diag::System::Writer, Source{}) << "unsupported builtin";
Ben Clayton576ba1c2023-04-27 17:58:25 +00004585 return false;
4586 }
4587 post += " : " + name;
4588 }
4589 if (auto interpolation = attributes.interpolation) {
4590 auto mod = interpolation_to_modifiers(interpolation->type, interpolation->sampling);
4591 if (mod.empty()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00004592 diagnostics_.AddError(diag::System::Writer, Source{})
4593 << "unsupported interpolation";
Ben Clayton576ba1c2023-04-27 17:58:25 +00004594 return false;
4595 }
4596 pre += mod;
4597 }
4598 if (attributes.invariant) {
4599 // Note: `precise` is not exactly the same as `invariant`, but is
4600 // stricter and therefore provides the necessary guarantees.
4601 // See discussion here: https://github.com/gpuweb/gpuweb/issues/893
4602 pre += "precise ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004603 }
4604
dan sinclair41e4d9a2022-05-01 14:40:55 +00004605 out << pre;
Ben Claytoncd52f382023-08-07 13:11:08 +00004606 if (!EmitTypeAndName(out, ty, core::AddressSpace::kUndefined, core::Access::kReadWrite,
4607 mem_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004608 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004609 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004610 out << post << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004611 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004612 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004613
dan sinclair67a18932023-06-26 19:54:49 +00004614 Line(b) << "};";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004615 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004616}
4617
dan sinclairbae54e72023-07-28 15:01:54 +00004618bool ASTPrinter::EmitUnaryOp(StringStream& out, const ast::UnaryOpExpression* expr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004619 switch (expr->op) {
Ben Clayton67b461b2023-08-08 07:58:19 +00004620 case core::UnaryOp::kIndirection:
4621 case core::UnaryOp::kAddressOf:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004622 return EmitExpression(out, expr->expr);
Ben Clayton67b461b2023-08-08 07:58:19 +00004623 case core::UnaryOp::kComplement:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004624 out << "~";
4625 break;
Ben Clayton67b461b2023-08-08 07:58:19 +00004626 case core::UnaryOp::kNot:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004627 out << "!";
4628 break;
Ben Clayton67b461b2023-08-08 07:58:19 +00004629 case core::UnaryOp::kNegation:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004630 out << "-";
4631 break;
4632 }
4633 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004634
dan sinclair41e4d9a2022-05-01 14:40:55 +00004635 if (!EmitExpression(out, expr->expr)) {
4636 return false;
4637 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004638
dan sinclair41e4d9a2022-05-01 14:40:55 +00004639 out << ")";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004640
dan sinclair41e4d9a2022-05-01 14:40:55 +00004641 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004642}
4643
dan sinclair0bfeaf92023-07-26 17:39:51 +00004644bool ASTPrinter::EmitVar(const ast::Var* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004645 auto* sem = builder_.Sem().Get(var);
4646 auto* type = sem->Type()->UnwrapRef();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004647
dan sinclair67a18932023-06-26 19:54:49 +00004648 auto out = Line();
dan sinclaird026e132023-04-18 19:38:25 +00004649 if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(), var->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004650 return false;
4651 }
4652
4653 out << " = ";
4654
dan sinclair6e77b472022-10-20 13:38:28 +00004655 if (var->initializer) {
4656 if (!EmitExpression(out, var->initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004657 return false;
4658 }
4659 } else {
4660 if (!EmitZeroValue(out, type)) {
4661 return false;
4662 }
4663 }
4664 out << ";";
4665
4666 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004667}
4668
dan sinclair0bfeaf92023-07-26 17:39:51 +00004669bool ASTPrinter::EmitLet(const ast::Let* let) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00004670 auto* sem = builder_.Sem().Get(let);
4671 auto* type = sem->Type()->UnwrapRef();
4672
dan sinclair67a18932023-06-26 19:54:49 +00004673 auto out = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00004674 if (!EmitTypeAndName(out, type, core::AddressSpace::kUndefined, core::Access::kUndefined,
dan sinclaird026e132023-04-18 19:38:25 +00004675 let->name->symbol.Name())) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004676 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004677 }
Ben Claytondcdf66e2022-06-17 12:48:51 +00004678 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00004679 if (!EmitExpression(out, let->initializer)) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00004680 return false;
4681 }
4682 out << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004683
Ben Claytondcdf66e2022-06-17 12:48:51 +00004684 return true;
4685}
4686
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004687template <typename F>
dan sinclairbae54e72023-07-28 15:01:54 +00004688bool ASTPrinter::CallBuiltinHelper(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004689 const ast::CallExpression* call,
Ben Claytond9766dc2023-09-21 12:41:20 +00004690 const sem::BuiltinFn* builtin,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004691 F&& build) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004692 // Generate the helper function if it hasn't been created already
Ben Clayton7a27d6c2024-01-31 19:44:28 +00004693 auto fn = tint::GetOrAdd(builtins_, builtin, [&]() -> std::string {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004694 TextBuffer b;
4695 TINT_DEFER(helpers_.Append(b));
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004696
Ben Claytondfc815c2023-09-25 15:38:43 +00004697 auto fn_name = UniqueIdentifier(std::string("tint_") + wgsl::str(builtin->Fn()));
dan sinclair41e4d9a2022-05-01 14:40:55 +00004698 std::vector<std::string> parameter_names;
4699 {
dan sinclair67a18932023-06-26 19:54:49 +00004700 auto decl = Line(&b);
Ben Claytoncd52f382023-08-07 13:11:08 +00004701 if (!EmitTypeAndName(decl, builtin->ReturnType(), core::AddressSpace::kUndefined,
4702 core::Access::kUndefined, fn_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004703 return "";
4704 }
4705 {
4706 ScopedParen sp(decl);
4707 for (auto* param : builtin->Parameters()) {
4708 if (!parameter_names.empty()) {
4709 decl << ", ";
4710 }
4711 auto param_name = "param_" + std::to_string(parameter_names.size());
4712 const auto* ty = param->Type();
dan sinclaircedcdf32023-08-10 02:39:48 +00004713 if (auto* ptr = ty->As<core::type::Pointer>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004714 decl << "inout ";
4715 ty = ptr->StoreType();
4716 }
Ben Claytoncd52f382023-08-07 13:11:08 +00004717 if (!EmitTypeAndName(decl, ty, core::AddressSpace::kUndefined,
4718 core::Access::kUndefined, param_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004719 return "";
4720 }
4721 parameter_names.emplace_back(std::move(param_name));
4722 }
4723 }
4724 decl << " {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004725 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004726 {
4727 ScopedIndent si(&b);
4728 if (!build(&b, parameter_names)) {
4729 return "";
4730 }
4731 }
dan sinclair67a18932023-06-26 19:54:49 +00004732 Line(&b) << "}";
4733 Line(&b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004734 return fn_name;
4735 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004736
dan sinclair41e4d9a2022-05-01 14:40:55 +00004737 if (fn.empty()) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004738 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004739 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004740
4741 // Call the helper
4742 out << fn;
4743 {
dan sinclairb2ba57b2023-02-28 15:14:09 +00004744 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004745 bool first = true;
4746 for (auto* arg : call->args) {
4747 if (!first) {
4748 out << ", ";
4749 }
4750 first = false;
4751 if (!EmitExpression(out, arg)) {
4752 return false;
4753 }
4754 }
4755 }
4756 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004757}
4758
dan sinclaircedcdf32023-08-10 02:39:48 +00004759std::string ASTPrinter::StructName(const core::type::Struct* s) {
Ben Clayton34c8e572023-08-01 00:37:35 +00004760 auto name = s->Name().Name();
4761 if (HasPrefix(name, "__")) {
Ben Clayton7a27d6c2024-01-31 19:44:28 +00004762 name = tint::GetOrAdd(builtin_struct_names_, s,
4763 [&] { return UniqueIdentifier(name.substr(2)); });
Ben Clayton34c8e572023-08-01 00:37:35 +00004764 }
4765 return name;
4766}
4767
dan sinclair0bfeaf92023-07-26 17:39:51 +00004768std::string ASTPrinter::UniqueIdentifier(const std::string& prefix /* = "" */) {
Ben Clayton5ed099a2023-07-25 18:52:03 +00004769 return builder_.Symbols().New(prefix).Name();
4770}
4771
dan sinclair0bfeaf92023-07-26 17:39:51 +00004772} // namespace tint::hlsl::writer