blob: e2b3b5975dd56efc70cbee596d217dfd793c9657 [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
Ben Clayton87523662024-02-28 19:29:04 +000036#include "src/tint/api/common/binding_point.h"
dan sinclair352f8c82023-07-21 00:40:07 +000037#include "src/tint/lang/core/constant/splat.h"
38#include "src/tint/lang/core/constant/value.h"
dan sinclairce6dffe2023-08-14 21:01:40 +000039#include "src/tint/lang/core/fluent_types.h"
dan sinclair352f8c82023-07-21 00:40:07 +000040#include "src/tint/lang/core/type/array.h"
41#include "src/tint/lang/core/type/atomic.h"
42#include "src/tint/lang/core/type/depth_multisampled_texture.h"
dan sinclair352f8c82023-07-21 00:40:07 +000043#include "src/tint/lang/core/type/multisampled_texture.h"
44#include "src/tint/lang/core/type/sampled_texture.h"
45#include "src/tint/lang/core/type/storage_texture.h"
46#include "src/tint/lang/core/type/texture_dimension.h"
Ben Clayton7a5f54ea2023-09-06 16:58:22 +000047#include "src/tint/lang/hlsl/writer/ast_raise/calculate_array_length.h"
48#include "src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access.h"
49#include "src/tint/lang/hlsl/writer/ast_raise/localize_struct_array_assignment.h"
50#include "src/tint/lang/hlsl/writer/ast_raise/num_workgroups_from_uniform.h"
Jiawei Shaoa87d0632023-11-16 00:21:08 +000051#include "src/tint/lang/hlsl/writer/ast_raise/pixel_local.h"
Ben Clayton7a5f54ea2023-09-06 16:58:22 +000052#include "src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.h"
Antonio Maiorano26a41b92024-02-05 21:36:47 +000053#include "src/tint/lang/hlsl/writer/common/option_helpers.h"
dan sinclair99181d82023-07-20 01:14:15 +000054#include "src/tint/lang/wgsl/ast/call_statement.h"
dan sinclair99181d82023-07-20 01:14:15 +000055#include "src/tint/lang/wgsl/ast/internal_attribute.h"
56#include "src/tint/lang/wgsl/ast/interpolate_attribute.h"
57#include "src/tint/lang/wgsl/ast/transform/add_empty_entry_point.h"
58#include "src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h"
59#include "src/tint/lang/wgsl/ast/transform/binding_remapper.h"
60#include "src/tint/lang/wgsl/ast/transform/builtin_polyfill.h"
dan sinclair99181d82023-07-20 01:14:15 +000061#include "src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h"
dan sinclair99181d82023-07-20 01:14:15 +000062#include "src/tint/lang/wgsl/ast/transform/demote_to_helper.h"
63#include "src/tint/lang/wgsl/ast/transform/direct_variable_access.h"
64#include "src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h"
65#include "src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h"
dan sinclairf3b93c12024-03-20 13:38:35 +000066#include "src/tint/lang/wgsl/ast/transform/fold_constants.h"
Ben Clayton7494e832023-07-25 15:30:31 +000067#include "src/tint/lang/wgsl/ast/transform/manager.h"
dan sinclair99181d82023-07-20 01:14:15 +000068#include "src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h"
dan sinclair99181d82023-07-20 01:14:15 +000069#include "src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h"
70#include "src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h"
dan sinclair997bc012024-02-22 02:08:41 +000071#include "src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.h"
dan sinclair99181d82023-07-20 01:14:15 +000072#include "src/tint/lang/wgsl/ast/transform/remove_phonies.h"
73#include "src/tint/lang/wgsl/ast/transform/robustness.h"
74#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.h"
dan sinclair99181d82023-07-20 01:14:15 +000075#include "src/tint/lang/wgsl/ast/transform/unshadow.h"
76#include "src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h"
77#include "src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h"
78#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
dan sinclaira4c17352023-07-20 09:21:10 +000079#include "src/tint/lang/wgsl/helpers/append_vector.h"
80#include "src/tint/lang/wgsl/helpers/check_supported_extensions.h"
dan sinclaird3b13692023-07-20 01:14:15 +000081#include "src/tint/lang/wgsl/sem/block_statement.h"
82#include "src/tint/lang/wgsl/sem/call.h"
83#include "src/tint/lang/wgsl/sem/function.h"
84#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
85#include "src/tint/lang/wgsl/sem/module.h"
86#include "src/tint/lang/wgsl/sem/statement.h"
87#include "src/tint/lang/wgsl/sem/struct.h"
88#include "src/tint/lang/wgsl/sem/switch_statement.h"
89#include "src/tint/lang/wgsl/sem/value_constructor.h"
90#include "src/tint/lang/wgsl/sem/value_conversion.h"
91#include "src/tint/lang/wgsl/sem/variable.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000092#include "src/tint/utils/containers/map.h"
Ben Claytonf848af22023-07-28 16:37:32 +000093#include "src/tint/utils/ice/ice.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000094#include "src/tint/utils/macros/compiler.h"
95#include "src/tint/utils/macros/defer.h"
96#include "src/tint/utils/macros/scoped_assignment.h"
97#include "src/tint/utils/rtti/switch.h"
Ben Clayton16be11d2023-08-01 00:37:35 +000098#include "src/tint/utils/strconv/float_to_string.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000099#include "src/tint/utils/text/string.h"
100#include "src/tint/utils/text/string_stream.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000101
dan sinclairce6dffe2023-08-14 21:01:40 +0000102using namespace tint::core::number_suffixes; // NOLINT
103using namespace tint::core::fluent_types; // NOLINT
Ben Clayton0ce9ab02022-05-05 20:23:40 +0000104
dan sinclair0bfeaf92023-07-26 17:39:51 +0000105namespace tint::hlsl::writer {
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000106namespace {
107
108const char kTempNamePrefix[] = "tint_tmp";
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000109
Ben Clayton448c01b2024-02-28 00:23:17 +0000110const char* ImageFormatToRWtextureType(core::TexelFormat image_format) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000111 switch (image_format) {
Ben Clayton448c01b2024-02-28 00:23:17 +0000112 case core::TexelFormat::kR8Unorm:
Ben Claytoncd52f382023-08-07 13:11:08 +0000113 case core::TexelFormat::kBgra8Unorm:
114 case core::TexelFormat::kRgba8Unorm:
115 case core::TexelFormat::kRgba8Snorm:
116 case core::TexelFormat::kRgba16Float:
117 case core::TexelFormat::kR32Float:
118 case core::TexelFormat::kRg32Float:
119 case core::TexelFormat::kRgba32Float:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000120 return "float4";
Ben Claytoncd52f382023-08-07 13:11:08 +0000121 case core::TexelFormat::kRgba8Uint:
122 case core::TexelFormat::kRgba16Uint:
123 case core::TexelFormat::kR32Uint:
124 case core::TexelFormat::kRg32Uint:
125 case core::TexelFormat::kRgba32Uint:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000126 return "uint4";
Ben Claytoncd52f382023-08-07 13:11:08 +0000127 case core::TexelFormat::kRgba8Sint:
128 case core::TexelFormat::kRgba16Sint:
129 case core::TexelFormat::kR32Sint:
130 case core::TexelFormat::kRg32Sint:
131 case core::TexelFormat::kRgba32Sint:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000132 return "int4";
133 default:
134 return nullptr;
135 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000136}
137
dan sinclairbae54e72023-07-28 15:01:54 +0000138void PrintF32(StringStream& out, float value) {
Ben Claytone9f8b092022-06-01 13:14:39 +0000139 if (std::isinf(value)) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000140 out << "0.0f " << (value >= 0 ? "/* inf */" : "/* -inf */");
Ben Claytone9f8b092022-06-01 13:14:39 +0000141 } else if (std::isnan(value)) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000142 out << "0.0f /* nan */";
Ben Claytone9f8b092022-06-01 13:14:39 +0000143 } else {
dan sinclair37e9d112023-11-20 13:18:28 +0000144 out << tint::strconv::FloatToString(value) << "f";
Ben Claytone9f8b092022-06-01 13:14:39 +0000145 }
146}
147
dan sinclairbae54e72023-07-28 15:01:54 +0000148void PrintF16(StringStream& out, float value) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000149 if (std::isinf(value)) {
150 out << "0.0h " << (value >= 0 ? "/* inf */" : "/* -inf */");
151 } else if (std::isnan(value)) {
152 out << "0.0h /* nan */";
Zhaoming Jianga5988a32022-07-11 15:43:38 +0000153 } else {
dan sinclair37e9d112023-11-20 13:18:28 +0000154 out << tint::strconv::FloatToString(value) << "h";
Zhaoming Jianga5988a32022-07-11 15:43:38 +0000155 }
156}
157
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000158// Helper for writing " : register(RX, spaceY)", where R is the register, X is
159// the binding point binding value, and Y is the binding point group value.
160struct RegisterAndSpace {
Ben Clayton10252582023-07-25 20:53:25 +0000161 RegisterAndSpace(char r, BindingPoint bp) : reg(r), binding_point(bp) {}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000162
dan sinclair41e4d9a2022-05-01 14:40:55 +0000163 const char reg;
Ben Clayton10252582023-07-25 20:53:25 +0000164 BindingPoint const binding_point;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000165};
166
dan sinclairbae54e72023-07-28 15:01:54 +0000167StringStream& operator<<(StringStream& s, const RegisterAndSpace& rs) {
Peng Huangc00ff7f2023-03-31 17:55:19 +0000168 s << " : register(" << rs.reg << rs.binding_point.binding;
169 // Omit the space if it's 0, as it's the default.
170 // SM 5.0 doesn't support spaces, so we don't emit them if group is 0 for better compatibility.
171 if (rs.binding_point.group == 0) {
172 s << ")";
173 } else {
174 s << ", space" << rs.binding_point.group << ")";
175 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000176 return s;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000177}
178
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000179} // namespace
180
181SanitizedResult::SanitizedResult() = default;
182SanitizedResult::~SanitizedResult() = default;
183SanitizedResult::SanitizedResult(SanitizedResult&&) = default;
184
Ben Clayton5ed5cc42023-09-22 10:31:04 +0000185SanitizedResult Sanitize(const Program& in, const Options& options) {
Ben Clayton7494e832023-07-25 15:30:31 +0000186 ast::transform::Manager manager;
187 ast::transform::DataMap data;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000188
dan sinclairf3b93c12024-03-20 13:38:35 +0000189 manager.Add<ast::transform::FoldConstants>();
190
James Priceb4acbb82023-05-11 21:27:16 +0000191 manager.Add<ast::transform::DisableUniformityAnalysis>();
James Price791b4352022-05-11 13:50:33 +0000192
Ben Clayton46ee6392022-11-09 22:04:11 +0000193 // ExpandCompoundAssignment must come before BuiltinPolyfill
James Priceb4acbb82023-05-11 21:27:16 +0000194 manager.Add<ast::transform::ExpandCompoundAssignment>();
Ben Clayton46ee6392022-11-09 22:04:11 +0000195
James Priceb4acbb82023-05-11 21:27:16 +0000196 manager.Add<ast::transform::Unshadow>(); // Must come before DirectVariableAccess
Ben Clayton03de0e82023-03-02 20:48:48 +0000197
Ben Clayton03de0e82023-03-02 20:48:48 +0000198 // LocalizeStructArrayAssignment must come after:
199 // * SimplifyPointers, because it assumes assignment to arrays in structs are
200 // done directly, not indirectly.
201 // TODO(crbug.com/tint/1340): See if we can get rid of the duplicate
202 // SimplifyPointers transform. Can't do it right now because
203 // LocalizeStructArrayAssignment introduces pointers.
James Priceb4acbb82023-05-11 21:27:16 +0000204 manager.Add<ast::transform::SimplifyPointers>();
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000205 manager.Add<LocalizeStructArrayAssignment>();
Ben Clayton03de0e82023-03-02 20:48:48 +0000206
James Priceb4acbb82023-05-11 21:27:16 +0000207 manager.Add<ast::transform::PromoteSideEffectsToDecl>();
Ben Clayton03de0e82023-03-02 20:48:48 +0000208
209 if (!options.disable_robustness) {
Ben Clayton8525ff22023-03-06 21:05:01 +0000210 // Robustness must come after PromoteSideEffectsToDecl
211 // Robustness must come before BuiltinPolyfill and CanonicalizeEntryPointIO
James Priceb4acbb82023-05-11 21:27:16 +0000212 manager.Add<ast::transform::Robustness>();
Jiawei Shao455e4b82023-06-10 00:32:22 +0000213
214 ast::transform::Robustness::Config config = {};
Ben Clayton10252582023-07-25 20:53:25 +0000215 config.bindings_ignored = std::unordered_set<BindingPoint>(
Antonio Maiorano26a41b92024-02-05 21:36:47 +0000216 options.bindings.ignored_by_robustness_transform.cbegin(),
217 options.bindings.ignored_by_robustness_transform.cend());
Jiawei Shao2e119632023-06-21 01:43:25 +0000218
219 // Direct3D guarantees to return zero for any resource that is accessed out of bounds, and
220 // according to the description of the assembly store_uav_typed, out of bounds addressing
221 // means nothing gets written to memory.
222 config.texture_action = ast::transform::Robustness::Action::kIgnore;
223
Jiawei Shao455e4b82023-06-10 00:32:22 +0000224 data.Add<ast::transform::Robustness::Config>(config);
Ben Clayton03de0e82023-03-02 20:48:48 +0000225 }
226
dan sinclaircd5a5e92024-06-06 02:09:13 +0000227 tint::transform::multiplanar::BindingsMap multiplanar_map{};
Antonio Maiorano26a41b92024-02-05 21:36:47 +0000228 RemapperData remapper_data{};
229 ArrayLengthFromUniformOptions array_length_from_uniform_options{};
dan sinclaircd5a5e92024-06-06 02:09:13 +0000230 PopulateBindingRelatedOptions(options, remapper_data, multiplanar_map,
Antonio Maiorano26a41b92024-02-05 21:36:47 +0000231 array_length_from_uniform_options);
Ben Clayton03de0e82023-03-02 20:48:48 +0000232
James Priceb4acbb82023-05-11 21:27:16 +0000233 manager.Add<ast::transform::BindingRemapper>();
dan sinclair15215502023-09-26 11:31:47 +0000234 // D3D11 and 12 registers like `t3` and `c3` have the same bindingOffset number in
235 // the remapping but should not be considered a collision because they have
236 // different types.
James Priceb4acbb82023-05-11 21:27:16 +0000237 data.Add<ast::transform::BindingRemapper::Remappings>(
Antonio Maiorano26a41b92024-02-05 21:36:47 +0000238 remapper_data, options.bindings.access_controls, /* allow_collisions */ true);
239
240 // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
241 // MultiplanarExternalTexture must come after BindingRemapper
dan sinclaircd5a5e92024-06-06 02:09:13 +0000242 data.Add<ast::transform::MultiplanarExternalTexture::NewBindingPoints>(multiplanar_map,
243 /* may_collide */ true);
Antonio Maiorano26a41b92024-02-05 21:36:47 +0000244 manager.Add<ast::transform::MultiplanarExternalTexture>();
dan sinclair39d40652023-03-15 13:41:46 +0000245
dan sinclair41e4d9a2022-05-01 14:40:55 +0000246 { // Builtin polyfills
James Priceb4acbb82023-05-11 21:27:16 +0000247 ast::transform::BuiltinPolyfill::Builtins polyfills;
248 polyfills.acosh = ast::transform::BuiltinPolyfill::Level::kFull;
dan sinclaird23f2962022-06-28 15:27:44 +0000249 polyfills.asinh = true;
James Priceb4acbb82023-05-11 21:27:16 +0000250 polyfills.atanh = ast::transform::BuiltinPolyfill::Level::kFull;
Ben Clayton02f04d92022-11-03 19:15:17 +0000251 polyfills.bitshift_modulo = true;
Ben Clayton6dbb4632022-10-31 17:54:49 +0000252 polyfills.clamp_int = true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000253 // TODO(crbug.com/tint/1449): Some of these can map to HLSL's `firstbitlow`
254 // and `firstbithigh`.
Ben Claytoncc3f8512023-03-09 21:26:12 +0000255 polyfills.conv_f32_to_iu32 = true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000256 polyfills.count_leading_zeros = true;
257 polyfills.count_trailing_zeros = true;
James Priceb4acbb82023-05-11 21:27:16 +0000258 polyfills.extract_bits = ast::transform::BuiltinPolyfill::Level::kFull;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000259 polyfills.first_leading_bit = true;
260 polyfills.first_trailing_bit = true;
James Price6fd43a02024-04-18 11:42:27 +0000261 polyfills.fwidth_fine = true;
James Priceb4acbb82023-05-11 21:27:16 +0000262 polyfills.insert_bits = ast::transform::BuiltinPolyfill::Level::kFull;
Jiawei Shao825f6262024-01-08 10:05:27 +0000263 polyfills.int_div_mod = !options.disable_polyfill_integer_div_mod;
Antonio Maioranoee665a42023-02-14 16:12:59 +0000264 polyfills.precise_float_mod = true;
Zhaoming Jiang04529be2023-02-27 02:59:50 +0000265 polyfills.reflect_vec2_f32 = options.polyfill_reflect_vec2_f32;
Ben Claytonc4ebf2c2022-09-22 22:59:16 +0000266 polyfills.texture_sample_base_clamp_to_edge_2d_f32 = true;
James Price128980f2023-01-06 02:25:06 +0000267 polyfills.workgroup_uniform_load = true;
Jiawei Shao7c0a8012023-11-29 00:25:56 +0000268 polyfills.dot_4x8_packed = options.polyfill_dot_4x8_packed;
Jiawei Shaodd817232024-01-12 08:10:46 +0000269 polyfills.pack_unpack_4x8 = options.polyfill_pack_unpack_4x8;
270 // Currently Pack4xU8Clamp() must be polyfilled because on latest DXC pack_clamp_u8()
271 // receives an int32_t4 as its input.
272 // See https://github.com/microsoft/DirectXShaderCompiler/issues/5091 for more details.
273 polyfills.pack_4xu8_clamp = true;
James Priceb4acbb82023-05-11 21:27:16 +0000274 data.Add<ast::transform::BuiltinPolyfill::Config>(polyfills);
275 manager.Add<ast::transform::BuiltinPolyfill>(); // Must come before DirectVariableAccess
dan sinclair41e4d9a2022-05-01 14:40:55 +0000276 }
Ben Clayton27aa57c2022-02-22 23:13:39 +0000277
James Priceb4acbb82023-05-11 21:27:16 +0000278 manager.Add<ast::transform::DirectVariableAccess>();
Antonio Maioranofa00fe92023-05-03 15:30:54 +0000279
dan sinclair41e4d9a2022-05-01 14:40:55 +0000280 if (!options.disable_workgroup_init) {
281 // ZeroInitWorkgroupMemory must come before CanonicalizeEntryPointIO as
282 // ZeroInitWorkgroupMemory may inject new builtin parameters.
James Priceb4acbb82023-05-11 21:27:16 +0000283 manager.Add<ast::transform::ZeroInitWorkgroupMemory>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000284 }
Ben Clayton03de0e82023-03-02 20:48:48 +0000285
Jiawei Shaoa87d0632023-11-16 00:21:08 +0000286 {
287 PixelLocal::Config cfg;
dan sinclair260c1f02024-06-06 15:31:12 +0000288 for (auto it : options.pixel_local.attachments) {
Jiawei Shaoa87d0632023-11-16 00:21:08 +0000289 cfg.pls_member_to_rov_reg.Add(it.first, it.second);
290 }
dan sinclair260c1f02024-06-06 15:31:12 +0000291 for (auto it : options.pixel_local.attachment_formats) {
Jiawei Shaoa87d0632023-11-16 00:21:08 +0000292 core::TexelFormat format = core::TexelFormat::kUndefined;
293 switch (it.second) {
294 case PixelLocalOptions::TexelFormat::kR32Sint:
295 format = core::TexelFormat::kR32Sint;
296 break;
297 case PixelLocalOptions::TexelFormat::kR32Uint:
298 format = core::TexelFormat::kR32Uint;
299 break;
300 case PixelLocalOptions::TexelFormat::kR32Float:
301 format = core::TexelFormat::kR32Float;
302 break;
303 default:
304 TINT_ICE() << "missing texel format for pixel local storage attachment";
Jiawei Shaoa87d0632023-11-16 00:21:08 +0000305 }
306 cfg.pls_member_to_rov_format.Add(it.first, format);
307 }
dan sinclair260c1f02024-06-06 15:31:12 +0000308 cfg.rov_group_index = options.pixel_local.group_index;
Jiawei Shaoa87d0632023-11-16 00:21:08 +0000309 data.Add<PixelLocal::Config>(cfg);
310 manager.Add<PixelLocal>();
311 }
312
Ben Clayton03de0e82023-03-02 20:48:48 +0000313 // CanonicalizeEntryPointIO must come after Robustness
James Priceb4acbb82023-05-11 21:27:16 +0000314 manager.Add<ast::transform::CanonicalizeEntryPointIO>();
shrekshaof9c66332022-11-22 21:36:27 +0000315
shrekshao8f0607a2023-04-18 01:34:41 +0000316 if (options.truncate_interstage_variables) {
shrekshaof9c66332022-11-22 21:36:27 +0000317 // When interstage_locations is empty, it means there's no user-defined interstage variables
shrekshao8f0607a2023-04-18 01:34:41 +0000318 // being used in the next stage. Still, HLSL compiler register mismatch could happen, if
319 // there's builtin inputs used in the next stage. So we still run
320 // TruncateInterstageVariables transform.
shrekshaof9c66332022-11-22 21:36:27 +0000321
322 // TruncateInterstageVariables itself will skip when interstage_locations matches exactly
323 // with the current stage output.
324
325 // Build the config for internal TruncateInterstageVariables transform.
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000326 TruncateInterstageVariables::Config truncate_interstage_variables_cfg;
shrekshaof9c66332022-11-22 21:36:27 +0000327 truncate_interstage_variables_cfg.interstage_locations =
328 std::move(options.interstage_locations);
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000329 manager.Add<TruncateInterstageVariables>();
330 data.Add<TruncateInterstageVariables::Config>(std::move(truncate_interstage_variables_cfg));
shrekshaof9c66332022-11-22 21:36:27 +0000331 }
332
dan sinclair41e4d9a2022-05-01 14:40:55 +0000333 // NumWorkgroupsFromUniform must come after CanonicalizeEntryPointIO, as it
334 // assumes that num_workgroups builtins only appear as struct members and are
335 // only accessed directly via member accessors.
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000336 manager.Add<NumWorkgroupsFromUniform>();
James Priceb4acbb82023-05-11 21:27:16 +0000337 manager.Add<ast::transform::VectorizeScalarMatrixInitializers>();
338 manager.Add<ast::transform::SimplifyPointers>();
339 manager.Add<ast::transform::RemovePhonies>();
James Price744d0eb2022-11-09 19:58:59 +0000340
341 // DemoteToHelper must come after CanonicalizeEntryPointIO, PromoteSideEffectsToDecl, and
342 // ExpandCompoundAssignment.
343 // TODO(crbug.com/tint/1752): This is only necessary when FXC is being used.
James Priceb4acbb82023-05-11 21:27:16 +0000344 manager.Add<ast::transform::DemoteToHelper>();
James Price744d0eb2022-11-09 19:58:59 +0000345
Ben Clayton559e5a22023-04-17 14:24:58 +0000346 // ArrayLengthFromUniform must come after SimplifyPointers as it assumes that the form of the
347 // array length argument is &var.array.
James Priceb4acbb82023-05-11 21:27:16 +0000348 manager.Add<ast::transform::ArrayLengthFromUniform>();
Ben Clayton87523662024-02-28 19:29:04 +0000349 // Build the config for the internal ArrayLengthFromUniform transform.
350 ast::transform::ArrayLengthFromUniform::Config array_length_from_uniform_cfg(
351 BindingPoint{array_length_from_uniform_options.ubo_binding.group,
352 array_length_from_uniform_options.ubo_binding.binding});
353 array_length_from_uniform_cfg.bindpoint_to_size_index =
354 std::move(array_length_from_uniform_options.bindpoint_to_size_index);
James Priceb4acbb82023-05-11 21:27:16 +0000355 data.Add<ast::transform::ArrayLengthFromUniform::Config>(
356 std::move(array_length_from_uniform_cfg));
dan sinclair41e4d9a2022-05-01 14:40:55 +0000357 // DecomposeMemoryAccess must come after:
Ben Clayton559e5a22023-04-17 14:24:58 +0000358 // * SimplifyPointers, as we cannot take the address of calls to
359 // DecomposeMemoryAccess::Intrinsic and we need to fold away the address-of and dereferences
360 // of `*(&(intrinsic_load()))` expressions.
dan sinclair41e4d9a2022-05-01 14:40:55 +0000361 // * RemovePhonies, as phonies can be assigned a pointer to a
362 // non-constructible buffer, or dynamic array, which DMA cannot cope with.
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000363 manager.Add<DecomposeMemoryAccess>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000364 // CalculateArrayLength must come after DecomposeMemoryAccess, as
365 // DecomposeMemoryAccess special-cases the arrayLength() intrinsic, which
366 // will be transformed by CalculateArrayLength
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000367 manager.Add<CalculateArrayLength>();
James Priceb4acbb82023-05-11 21:27:16 +0000368 manager.Add<ast::transform::PromoteInitializersToLet>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000369
dan sinclair997bc012024-02-22 02:08:41 +0000370 manager.Add<ast::transform::RemoveContinueInSwitch>();
Antonio Maioranob3497102022-03-31 15:02:25 +0000371
James Priceb4acbb82023-05-11 21:27:16 +0000372 manager.Add<ast::transform::AddEmptyEntryPoint>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000373
James Priceb4acbb82023-05-11 21:27:16 +0000374 data.Add<ast::transform::CanonicalizeEntryPointIO::Config>(
375 ast::transform::CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000376 data.Add<NumWorkgroupsFromUniform::Config>(options.root_constant_binding_point);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000377
dan sinclair41e4d9a2022-05-01 14:40:55 +0000378 SanitizedResult result;
Ben Clayton7494e832023-07-25 15:30:31 +0000379 ast::transform::DataMap outputs;
James Price0e6534e2023-05-17 01:21:45 +0000380 result.program = manager.Run(in, data, outputs);
381 if (auto* res = outputs.Get<ast::transform::ArrayLengthFromUniform::Result>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000382 result.used_array_length_from_uniform_indices = std::move(res->used_size_indices);
383 }
384 return result;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000385}
386
Ben Clayton5ed5cc42023-09-22 10:31:04 +0000387ASTPrinter::ASTPrinter(const Program& program) : builder_(ProgramBuilder::Wrap(program)) {}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000388
dan sinclair0bfeaf92023-07-26 17:39:51 +0000389ASTPrinter::~ASTPrinter() = default;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000390
dan sinclair0bfeaf92023-07-26 17:39:51 +0000391bool ASTPrinter::Generate() {
Ben Clayton2550b492023-10-11 10:41:12 +0000392 if (!tint::wgsl::CheckSupportedExtensions(
dan sinclair0bfeaf92023-07-26 17:39:51 +0000393 "HLSL", builder_.AST(), diagnostics_,
dan sinclairbae54e72023-07-28 15:01:54 +0000394 Vector{
Ben Clayton11653892023-09-19 19:15:59 +0000395 wgsl::Extension::kChromiumDisableUniformityAnalysis,
Ben Clayton448c01b2024-02-28 00:23:17 +0000396 wgsl::Extension::kChromiumExperimentalPixelLocal,
Ben Clayton11653892023-09-19 19:15:59 +0000397 wgsl::Extension::kChromiumExperimentalPushConstant,
Ben Clayton11653892023-09-19 19:15:59 +0000398 wgsl::Extension::kChromiumExperimentalSubgroups,
Ben Clayton448c01b2024-02-28 00:23:17 +0000399 wgsl::Extension::kChromiumInternalGraphite,
400 wgsl::Extension::kF16,
Jiawei Shaocc43f152024-05-29 00:44:43 +0000401 wgsl::Extension::kDualSourceBlending,
James Priceaa88fa02024-06-26 11:25:37 +0000402 wgsl::Extension::kSubgroups,
403 wgsl::Extension::kSubgroupsF16,
dan sinclair0bfeaf92023-07-26 17:39:51 +0000404 })) {
Ben Clayton1a567782022-10-14 13:38:27 +0000405 return false;
406 }
407
dan sinclairbae54e72023-07-28 15:01:54 +0000408 const tint::TypeInfo* last_kind = nullptr;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000409 size_t last_padding_line = 0;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000410
dan sinclair41e4d9a2022-05-01 14:40:55 +0000411 auto* mod = builder_.Sem().Module();
412 for (auto* decl : mod->DependencyOrderedDeclarations()) {
James Price631aaa32023-11-02 15:03:02 +0000413 if (decl->IsAnyOf<ast::Alias, ast::DiagnosticDirective, ast::Enable, ast::Requires,
414 ast::ConstAssert>()) {
Ben Claytonb4744ac2022-08-03 07:01:08 +0000415 continue; // These are not emitted.
James Price791b4352022-05-11 13:50:33 +0000416 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000417
dan sinclair41e4d9a2022-05-01 14:40:55 +0000418 // Emit a new line between declarations if the type of declaration has
419 // changed, or we're about to emit a function
420 auto* kind = &decl->TypeInfo();
421 if (current_buffer_->lines.size() != last_padding_line) {
422 if (last_kind && (last_kind != kind || decl->Is<ast::Function>())) {
dan sinclair67a18932023-06-26 19:54:49 +0000423 Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000424 last_padding_line = current_buffer_->lines.size();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000425 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000426 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000427 last_kind = kind;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000428
Ben Clayton2d501c52024-01-17 01:27:37 +0000429 global_insertion_point_ = current_buffer_->lines.size();
430
dan sinclair41e4d9a2022-05-01 14:40:55 +0000431 bool ok = Switch(
432 decl,
433 [&](const ast::Variable* global) { //
434 return EmitGlobalVariable(global);
435 },
436 [&](const ast::Struct* str) {
437 auto* ty = builder_.Sem().Get(str);
dan sinclairff7cf212022-10-03 14:05:23 +0000438 auto address_space_uses = ty->AddressSpaceUsage();
Ben Clayton8be957f2024-01-29 16:00:36 +0000439 if (address_space_uses.Count() !=
440 ((address_space_uses.Contains(core::AddressSpace::kStorage) ? 1u : 0u) +
441 (address_space_uses.Contains(core::AddressSpace::kUniform) ? 1u : 0u))) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000442 // The structure is used as something other than a storage buffer or
443 // uniform buffer, so it needs to be emitted.
444 // Storage buffer are read and written to via a ByteAddressBuffer
445 // instead of true structure.
446 // Structures used as uniform buffer are read from an array of
447 // vectors instead of true structure.
448 return EmitStructType(current_buffer_, ty);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000449 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000450 return true;
451 },
452 [&](const ast::Function* func) {
453 if (func->IsEntryPoint()) {
454 return EmitEntryPointFunction(func);
455 }
456 return EmitFunction(func);
Ben Claytond6082c52023-10-26 16:02:01 +0000457 }, //
458 TINT_ICE_ON_NO_MATCH);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000459
460 if (!ok) {
461 return false;
462 }
463 }
464
465 if (!helpers_.lines.empty()) {
466 current_buffer_->Insert(helpers_, 0, 0);
467 }
468
469 return true;
470}
471
dan sinclair0bfeaf92023-07-26 17:39:51 +0000472bool ASTPrinter::EmitDynamicVectorAssignment(const ast::AssignmentStatement* stmt,
dan sinclaircedcdf32023-08-10 02:39:48 +0000473 const core::type::Vector* vec) {
Ben Clayton7a27d6c2024-01-31 19:44:28 +0000474 auto name = tint::GetOrAdd(dynamic_vector_write_, vec, [&]() -> std::string {
Ben Clayton80144d22024-01-26 23:45:49 +0000475 std::string fn = UniqueIdentifier("set_vector_element");
dan sinclair41e4d9a2022-05-01 14:40:55 +0000476 {
dan sinclair67a18932023-06-26 19:54:49 +0000477 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000478 out << "void " << fn << "(inout ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000479 if (!EmitTypeAndName(out, vec, core::AddressSpace::kUndefined, core::Access::kUndefined,
480 "vec")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000481 return "";
482 }
483 out << ", int idx, ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000484 if (!EmitTypeAndName(out, vec->type(), core::AddressSpace::kUndefined,
485 core::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000486 return "";
487 }
488 out << ") {";
489 }
490 {
491 ScopedIndent si(&helpers_);
dan sinclair67a18932023-06-26 19:54:49 +0000492 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000493 switch (vec->Width()) {
494 case 2:
495 out << "vec = (idx.xx == int2(0, 1)) ? val.xx : vec;";
496 break;
497 case 3:
498 out << "vec = (idx.xxx == int3(0, 1, 2)) ? val.xxx : vec;";
499 break;
500 case 4:
501 out << "vec = (idx.xxxx == int4(0, 1, 2, 3)) ? val.xxxx : vec;";
502 break;
503 default:
Ben Claytonf848af22023-07-28 16:37:32 +0000504 TINT_UNREACHABLE() << "invalid vector size " << vec->Width();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000505 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000506 }
dan sinclair67a18932023-06-26 19:54:49 +0000507 Line(&helpers_) << "}";
508 Line(&helpers_);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000509 return fn;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000510 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000511
dan sinclair41e4d9a2022-05-01 14:40:55 +0000512 if (name.empty()) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000513 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000514 }
515
dan sinclair41e4d9a2022-05-01 14:40:55 +0000516 auto* ast_access_expr = stmt->lhs->As<ast::IndexAccessorExpression>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000517
dan sinclair67a18932023-06-26 19:54:49 +0000518 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000519 out << name << "(";
520 if (!EmitExpression(out, ast_access_expr->object)) {
521 return false;
522 }
523 out << ", ";
524 if (!EmitExpression(out, ast_access_expr->index)) {
525 return false;
526 }
527 out << ", ";
528 if (!EmitExpression(out, stmt->rhs)) {
529 return false;
530 }
531 out << ");";
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000532
dan sinclair41e4d9a2022-05-01 14:40:55 +0000533 return true;
534}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000535
dan sinclair0bfeaf92023-07-26 17:39:51 +0000536bool ASTPrinter::EmitDynamicMatrixVectorAssignment(const ast::AssignmentStatement* stmt,
dan sinclaircedcdf32023-08-10 02:39:48 +0000537 const core::type::Matrix* mat) {
Ben Clayton7a27d6c2024-01-31 19:44:28 +0000538 auto name = tint::GetOrAdd(dynamic_matrix_vector_write_, mat, [&]() -> std::string {
Ben Clayton80144d22024-01-26 23:45:49 +0000539 std::string fn = UniqueIdentifier("set_matrix_column");
dan sinclair41e4d9a2022-05-01 14:40:55 +0000540 {
dan sinclair67a18932023-06-26 19:54:49 +0000541 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000542 out << "void " << fn << "(inout ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000543 if (!EmitTypeAndName(out, mat, core::AddressSpace::kUndefined, core::Access::kUndefined,
544 "mat")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000545 return "";
546 }
547 out << ", int col, ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000548 if (!EmitTypeAndName(out, mat->ColumnType(), core::AddressSpace::kUndefined,
549 core::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000550 return "";
551 }
552 out << ") {";
553 }
554 {
555 ScopedIndent si(&helpers_);
dan sinclair67a18932023-06-26 19:54:49 +0000556 Line(&helpers_) << "switch (col) {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000557 {
558 ScopedIndent si2(&helpers_);
559 for (uint32_t i = 0; i < mat->columns(); ++i) {
dan sinclair67a18932023-06-26 19:54:49 +0000560 Line(&helpers_) << "case " << i << ": mat[" << i << "] = val; break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000561 }
562 }
dan sinclair67a18932023-06-26 19:54:49 +0000563 Line(&helpers_) << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000564 }
dan sinclair67a18932023-06-26 19:54:49 +0000565 Line(&helpers_) << "}";
566 Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000567 return fn;
568 });
569
570 if (name.empty()) {
571 return false;
572 }
573
574 auto* ast_access_expr = stmt->lhs->As<ast::IndexAccessorExpression>();
575
dan sinclair67a18932023-06-26 19:54:49 +0000576 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000577 out << name << "(";
578 if (!EmitExpression(out, ast_access_expr->object)) {
579 return false;
580 }
581 out << ", ";
582 if (!EmitExpression(out, ast_access_expr->index)) {
583 return false;
584 }
585 out << ", ";
586 if (!EmitExpression(out, stmt->rhs)) {
587 return false;
588 }
589 out << ");";
590
591 return true;
592}
593
dan sinclair0bfeaf92023-07-26 17:39:51 +0000594bool ASTPrinter::EmitDynamicMatrixScalarAssignment(const ast::AssignmentStatement* stmt,
dan sinclaircedcdf32023-08-10 02:39:48 +0000595 const core::type::Matrix* mat) {
Antonio Maioranof031ca22023-02-02 22:16:42 +0000596 auto* lhs_row_access = stmt->lhs->As<ast::IndexAccessorExpression>();
597 auto* lhs_col_access = lhs_row_access->object->As<ast::IndexAccessorExpression>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000598
Ben Clayton7a27d6c2024-01-31 19:44:28 +0000599 auto name = tint::GetOrAdd(dynamic_matrix_scalar_write_, mat, [&]() -> std::string {
Ben Clayton80144d22024-01-26 23:45:49 +0000600 std::string fn = UniqueIdentifier("set_matrix_scalar");
dan sinclair41e4d9a2022-05-01 14:40:55 +0000601 {
dan sinclair67a18932023-06-26 19:54:49 +0000602 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000603 out << "void " << fn << "(inout ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000604 if (!EmitTypeAndName(out, mat, core::AddressSpace::kUndefined, core::Access::kUndefined,
605 "mat")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000606 return "";
607 }
608 out << ", int col, int row, ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000609 if (!EmitTypeAndName(out, mat->type(), core::AddressSpace::kUndefined,
610 core::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000611 return "";
612 }
613 out << ") {";
614 }
615 {
616 ScopedIndent si(&helpers_);
dan sinclair67a18932023-06-26 19:54:49 +0000617 Line(&helpers_) << "switch (col) {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000618 {
619 ScopedIndent si2(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000620 for (uint32_t i = 0; i < mat->columns(); ++i) {
dan sinclair67a18932023-06-26 19:54:49 +0000621 Line(&helpers_) << "case " << i << ":";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000622 {
623 auto vec_name = "mat[" + std::to_string(i) + "]";
624 ScopedIndent si3(&helpers_);
625 {
dan sinclair67a18932023-06-26 19:54:49 +0000626 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000627 switch (mat->rows()) {
628 case 2:
629 out << vec_name
630 << " = (row.xx == int2(0, 1)) ? val.xx : " << vec_name
631 << ";";
632 break;
633 case 3:
634 out << vec_name
635 << " = (row.xxx == int3(0, 1, 2)) ? val.xxx : " << vec_name
636 << ";";
637 break;
638 case 4:
639 out << vec_name
640 << " = (row.xxxx == int4(0, 1, 2, 3)) ? val.xxxx : "
641 << vec_name << ";";
642 break;
Antonio Maioranof031ca22023-02-02 22:16:42 +0000643 default: {
644 auto* vec = TypeOf(lhs_row_access->object)
Antonio Maiorano08d92792024-01-11 20:51:50 +0000645 ->UnwrapPtrOrRef()
dan sinclaircedcdf32023-08-10 02:39:48 +0000646 ->As<core::type::Vector>();
Ben Claytonf848af22023-07-28 16:37:32 +0000647 TINT_UNREACHABLE() << "invalid vector size " << vec->Width();
Antonio Maioranof031ca22023-02-02 22:16:42 +0000648 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000649 }
650 }
dan sinclair67a18932023-06-26 19:54:49 +0000651 Line(&helpers_) << "break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000652 }
653 }
654 }
dan sinclair67a18932023-06-26 19:54:49 +0000655 Line(&helpers_) << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000656 }
dan sinclair67a18932023-06-26 19:54:49 +0000657 Line(&helpers_) << "}";
658 Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000659 return fn;
660 });
661
662 if (name.empty()) {
663 return false;
664 }
665
dan sinclair67a18932023-06-26 19:54:49 +0000666 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000667 out << name << "(";
Antonio Maioranof031ca22023-02-02 22:16:42 +0000668 if (!EmitExpression(out, lhs_col_access->object)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000669 return false;
670 }
671 out << ", ";
672 if (!EmitExpression(out, lhs_col_access->index)) {
673 return false;
674 }
675 out << ", ";
676 if (!EmitExpression(out, lhs_row_access->index)) {
677 return false;
678 }
679 out << ", ";
680 if (!EmitExpression(out, stmt->rhs)) {
681 return false;
682 }
683 out << ");";
684
685 return true;
686}
687
dan sinclairbae54e72023-07-28 15:01:54 +0000688bool ASTPrinter::EmitIndexAccessor(StringStream& out, const ast::IndexAccessorExpression* expr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000689 if (!EmitExpression(out, expr->object)) {
690 return false;
691 }
692 out << "[";
693
694 if (!EmitExpression(out, expr->index)) {
695 return false;
696 }
697 out << "]";
698
699 return true;
700}
701
Ben Clayton52e6a0f2024-02-21 08:11:33 +0000702bool ASTPrinter::EmitBitcastCall(StringStream& out, const ast::CallExpression* call) {
703 auto* arg = call->args[0];
704 auto* src_type = TypeOf(arg)->UnwrapRef();
705 auto* dst_type = TypeOf(call);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000706
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000707 auto* src_el_type = src_type->DeepestElement();
708 auto* dst_el_type = dst_type->DeepestElement();
709
710 if (!dst_el_type->is_integer_scalar() && !dst_el_type->is_float_scalar()) {
Ben Clayton415bd732024-05-02 14:36:02 +0000711 diagnostics_.AddError(Source{})
Ben Claytonc27315a2024-02-26 20:24:06 +0000712 << "Unable to do bitcast to type " << dst_el_type->FriendlyName();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000713 return false;
714 }
715
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000716 // Handle identity bitcast.
717 if (src_type == dst_type) {
Ben Clayton52e6a0f2024-02-21 08:11:33 +0000718 return EmitExpression(out, arg);
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000719 }
720
721 // Handle the f16 types using polyfill functions
dan sinclaircedcdf32023-08-10 02:39:48 +0000722 if (src_el_type->Is<core::type::F16>() || dst_el_type->Is<core::type::F16>()) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000723 auto f16_bitcast_polyfill = [&]() {
dan sinclaircedcdf32023-08-10 02:39:48 +0000724 if (src_el_type->Is<core::type::F16>()) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000725 // Source type must be vec2<f16> or vec4<f16>, since type f16 and vec3<f16> can only
726 // have identity bitcast.
dan sinclaircedcdf32023-08-10 02:39:48 +0000727 auto* src_vec = src_type->As<core::type::Vector>();
Ben Claytonf848af22023-07-28 16:37:32 +0000728 TINT_ASSERT(src_vec);
729 TINT_ASSERT(((src_vec->Width() == 2u) || (src_vec->Width() == 4u)));
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000730
731 // Bitcast f16 types to others by converting the given f16 value to f32 and call
732 // f32tof16 to get the bits. This should be safe, because the convertion is precise
733 // for finite and infinite f16 value as they are exactly representable by f32, and
734 // WGSL spec allow any result if f16 value is NaN.
Ben Clayton7a27d6c2024-01-31 19:44:28 +0000735 return tint::GetOrAdd(
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000736 bitcast_funcs_, BinaryType{{src_type, dst_type}}, [&]() -> std::string {
737 TextBuffer b;
738 TINT_DEFER(helpers_.Append(b));
739
740 auto fn_name = UniqueIdentifier(std::string("tint_bitcast_from_f16"));
741 {
742 auto decl = Line(&b);
Ben Claytoncd52f382023-08-07 13:11:08 +0000743 if (!EmitTypeAndName(decl, dst_type, core::AddressSpace::kUndefined,
744 core::Access::kUndefined, fn_name)) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000745 return "";
746 }
747 {
748 ScopedParen sp(decl);
Ben Claytoncd52f382023-08-07 13:11:08 +0000749 if (!EmitTypeAndName(decl, src_type, core::AddressSpace::kUndefined,
750 core::Access::kUndefined, "src")) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000751 return "";
752 }
753 }
754 decl << " {";
755 }
756 {
757 ScopedIndent si(&b);
758 {
759 Line(&b) << "uint" << src_vec->Width() << " r = f32tof16(float"
760 << src_vec->Width() << "(src));";
761
762 {
763 auto s = Line(&b);
764 s << "return as";
Ben Claytoncd52f382023-08-07 13:11:08 +0000765 if (!EmitType(s, dst_el_type, core::AddressSpace::kUndefined,
766 core::Access::kReadWrite, "")) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000767 return "";
768 }
769 s << "(";
770 switch (src_vec->Width()) {
771 case 2: {
772 s << "uint((r.x & 0xffff) | ((r.y & 0xffff) << 16))";
773 break;
774 }
775 case 4: {
776 s << "uint2((r.x & 0xffff) | ((r.y & 0xffff) << 16), "
777 "(r.z & 0xffff) | ((r.w & 0xffff) << 16))";
778 break;
779 }
780 }
781 s << ");";
782 }
783 }
784 }
785 Line(&b) << "}";
786 Line(&b);
787 return fn_name;
788 });
789 } else {
790 // Destination type must be vec2<f16> or vec4<f16>.
dan sinclaircedcdf32023-08-10 02:39:48 +0000791 auto* dst_vec = dst_type->As<core::type::Vector>();
Ben Claytonf848af22023-07-28 16:37:32 +0000792 TINT_ASSERT((dst_vec && ((dst_vec->Width() == 2u) || (dst_vec->Width() == 4u)) &&
dan sinclaircedcdf32023-08-10 02:39:48 +0000793 dst_el_type->Is<core::type::F16>()));
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000794 // Source type must be f32/i32/u32 or vec2<f32/i32/u32>.
dan sinclaircedcdf32023-08-10 02:39:48 +0000795 auto* src_vec = src_type->As<core::type::Vector>();
796 TINT_ASSERT(
797 (src_type->IsAnyOf<core::type::I32, core::type::U32, core::type::F32>() ||
798 (src_vec && src_vec->Width() == 2u &&
799 src_el_type->IsAnyOf<core::type::I32, core::type::U32, core::type::F32>())));
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000800 std::string src_type_suffix = (src_vec ? "2" : "");
801
802 // Bitcast other types to f16 types by reinterpreting their bits as f16 using
803 // f16tof32, and convert the result f32 to f16. This should be safe, because the
804 // convertion is precise for finite and infinite f16 result value as they are
805 // exactly representable by f32, and WGSL spec allow any result if f16 result value
806 // would be NaN.
Ben Clayton7a27d6c2024-01-31 19:44:28 +0000807 return tint::GetOrAdd(
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000808 bitcast_funcs_, BinaryType{{src_type, dst_type}}, [&]() -> std::string {
809 TextBuffer b;
810 TINT_DEFER(helpers_.Append(b));
811
812 auto fn_name = UniqueIdentifier(std::string("tint_bitcast_to_f16"));
813 {
814 auto decl = Line(&b);
Ben Claytoncd52f382023-08-07 13:11:08 +0000815 if (!EmitTypeAndName(decl, dst_type, core::AddressSpace::kUndefined,
816 core::Access::kUndefined, fn_name)) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000817 return "";
818 }
819 {
820 ScopedParen sp(decl);
Ben Claytoncd52f382023-08-07 13:11:08 +0000821 if (!EmitTypeAndName(decl, src_type, core::AddressSpace::kUndefined,
822 core::Access::kUndefined, "src")) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000823 return "";
824 }
825 }
826 decl << " {";
827 }
828 {
829 ScopedIndent si(&b);
830 {
831 // Convert the source to uint for f16tof32.
832 Line(&b) << "uint" << src_type_suffix << " v = asuint(src);";
833 // Reinterprete the low 16 bits and high 16 bits
834 Line(&b) << "float" << src_type_suffix
835 << " t_low = f16tof32(v & 0xffff);";
836 Line(&b) << "float" << src_type_suffix
837 << " t_high = f16tof32((v >> 16) & 0xffff);";
838 // Construct the result f16 vector
839 {
840 auto s = Line(&b);
841 s << "return ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000842 if (!EmitType(s, dst_type, core::AddressSpace::kUndefined,
843 core::Access::kReadWrite, "")) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000844 return "";
845 }
846 s << "(";
847 switch (dst_vec->Width()) {
848 case 2: {
849 s << "t_low.x, t_high.x";
850 break;
851 }
852 case 4: {
853 s << "t_low.x, t_high.x, t_low.y, t_high.y";
854 break;
855 }
856 }
857 s << ");";
858 }
859 }
860 }
861 Line(&b) << "}";
862 Line(&b);
863 return fn_name;
864 });
865 }
866 };
867
868 // Get or create the polyfill
869 auto fn = f16_bitcast_polyfill();
870 if (fn.empty()) {
871 return false;
872 }
873 // Call the polyfill
874 out << fn;
875 {
876 ScopedParen sp(out);
Ben Clayton52e6a0f2024-02-21 08:11:33 +0000877 if (!EmitExpression(out, arg)) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000878 return false;
879 }
880 }
881
882 return true;
883 }
884
885 // Otherwise, bitcasting between non-f16 types.
dan sinclaircedcdf32023-08-10 02:39:48 +0000886 TINT_ASSERT((!src_el_type->Is<core::type::F16>() && !dst_el_type->Is<core::type::F16>()));
dan sinclair41e4d9a2022-05-01 14:40:55 +0000887 out << "as";
Ben Claytoncd52f382023-08-07 13:11:08 +0000888 if (!EmitType(out, dst_el_type, core::AddressSpace::kUndefined, core::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000889 return false;
890 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000891 out << "(";
Ben Clayton52e6a0f2024-02-21 08:11:33 +0000892 if (!EmitExpression(out, arg)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000893 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000894 }
895 out << ")";
896 return true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000897}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000898
dan sinclair0bfeaf92023-07-26 17:39:51 +0000899bool ASTPrinter::EmitAssign(const ast::AssignmentStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000900 if (auto* lhs_access = stmt->lhs->As<ast::IndexAccessorExpression>()) {
Antonio Maiorano08d92792024-01-11 20:51:50 +0000901 auto validate_obj_not_pointer = [&](const core::type::Type* object_ty) {
902 if (TINT_UNLIKELY(object_ty->Is<core::type::Pointer>())) {
903 TINT_ICE() << "lhs of index accessor should not be a pointer. These should have "
904 "been removed by transforms such as SimplifyPointers, "
905 "DecomposeMemoryAccess, and DirectVariableAccess";
Antonio Maiorano08d92792024-01-11 20:51:50 +0000906 }
907 return true;
908 };
909
dan sinclair41e4d9a2022-05-01 14:40:55 +0000910 // BUG(crbug.com/tint/1333): work around assignment of scalar to matrices
911 // with at least one dynamic index
912 if (auto* lhs_sub_access = lhs_access->object->As<ast::IndexAccessorExpression>()) {
Antonio Maiorano08d92792024-01-11 20:51:50 +0000913 const auto* lhs_sub_access_type = TypeOf(lhs_sub_access->object);
914 if (!validate_obj_not_pointer(lhs_sub_access_type)) {
915 return false;
916 }
917 if (auto* mat = lhs_sub_access_type->UnwrapRef()->As<core::type::Matrix>()) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +0000918 auto* rhs_row_idx_sem = builder_.Sem().GetVal(lhs_access->index);
919 auto* rhs_col_idx_sem = builder_.Sem().GetVal(lhs_sub_access->index);
Antonio Maioranof031ca22023-02-02 22:16:42 +0000920 if (!rhs_row_idx_sem->ConstantValue() || !rhs_col_idx_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000921 return EmitDynamicMatrixScalarAssignment(stmt, mat);
922 }
923 }
924 }
925 // BUG(crbug.com/tint/1333): work around assignment of vector to matrices
926 // with dynamic indices
Antonio Maiorano08d92792024-01-11 20:51:50 +0000927 const auto* lhs_access_type = TypeOf(lhs_access->object);
928 if (!validate_obj_not_pointer(lhs_access_type)) {
929 return false;
930 }
931 if (auto* mat = lhs_access_type->UnwrapRef()->As<core::type::Matrix>()) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +0000932 auto* lhs_index_sem = builder_.Sem().GetVal(lhs_access->index);
Ben Claytonaa037ac2022-06-29 19:07:30 +0000933 if (!lhs_index_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000934 return EmitDynamicMatrixVectorAssignment(stmt, mat);
935 }
936 }
937 // BUG(crbug.com/tint/534): work around assignment to vectors with dynamic
938 // indices
Antonio Maiorano08d92792024-01-11 20:51:50 +0000939 if (auto* vec = lhs_access_type->UnwrapRef()->As<core::type::Vector>()) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +0000940 auto* rhs_sem = builder_.Sem().GetVal(lhs_access->index);
Ben Claytonaa037ac2022-06-29 19:07:30 +0000941 if (!rhs_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000942 return EmitDynamicVectorAssignment(stmt, vec);
943 }
944 }
945 }
946
dan sinclair67a18932023-06-26 19:54:49 +0000947 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000948 if (!EmitExpression(out, stmt->lhs)) {
949 return false;
950 }
951 out << " = ";
952 if (!EmitExpression(out, stmt->rhs)) {
953 return false;
954 }
955 out << ";";
956 return true;
957}
958
dan sinclairbae54e72023-07-28 15:01:54 +0000959bool ASTPrinter::EmitBinary(StringStream& out, const ast::BinaryExpression* expr) {
Ben Claytonedc51ab2023-08-08 07:58:19 +0000960 if (expr->op == core::BinaryOp::kLogicalAnd || expr->op == core::BinaryOp::kLogicalOr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000961 auto name = UniqueIdentifier(kTempNamePrefix);
962
963 {
dan sinclair67a18932023-06-26 19:54:49 +0000964 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000965 pre << "bool " << name << " = ";
966 if (!EmitExpression(pre, expr->lhs)) {
967 return false;
968 }
969 pre << ";";
970 }
971
Ben Claytonedc51ab2023-08-08 07:58:19 +0000972 if (expr->op == core::BinaryOp::kLogicalOr) {
dan sinclair67a18932023-06-26 19:54:49 +0000973 Line() << "if (!" << name << ") {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000974 } else {
dan sinclair67a18932023-06-26 19:54:49 +0000975 Line() << "if (" << name << ") {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000976 }
977
978 {
979 ScopedIndent si(this);
dan sinclair67a18932023-06-26 19:54:49 +0000980 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000981 pre << name << " = ";
982 if (!EmitExpression(pre, expr->rhs)) {
983 return false;
984 }
985 pre << ";";
986 }
987
dan sinclair67a18932023-06-26 19:54:49 +0000988 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000989
990 out << "(" << name << ")";
991 return true;
992 }
993
994 auto* lhs_type = TypeOf(expr->lhs)->UnwrapRef();
995 auto* rhs_type = TypeOf(expr->rhs)->UnwrapRef();
996 // Multiplying by a matrix requires the use of `mul` in order to get the
997 // type of multiply we desire.
Ben Claytonedc51ab2023-08-08 07:58:19 +0000998 if (expr->op == core::BinaryOp::kMultiply &&
dan sinclaircedcdf32023-08-10 02:39:48 +0000999 ((lhs_type->Is<core::type::Vector>() && rhs_type->Is<core::type::Matrix>()) ||
1000 (lhs_type->Is<core::type::Matrix>() && rhs_type->Is<core::type::Vector>()) ||
1001 (lhs_type->Is<core::type::Matrix>() && rhs_type->Is<core::type::Matrix>()))) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001002 // Matrices are transposed, so swap LHS and RHS.
1003 out << "mul(";
1004 if (!EmitExpression(out, expr->rhs)) {
1005 return false;
1006 }
1007 out << ", ";
1008 if (!EmitExpression(out, expr->lhs)) {
1009 return false;
1010 }
1011 out << ")";
1012
1013 return true;
1014 }
1015
dan sinclairb2ba57b2023-02-28 15:14:09 +00001016 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001017
1018 if (!EmitExpression(out, expr->lhs)) {
1019 return false;
1020 }
1021 out << " ";
1022
1023 switch (expr->op) {
Ben Claytonedc51ab2023-08-08 07:58:19 +00001024 case core::BinaryOp::kAnd:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001025 out << "&";
1026 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001027 case core::BinaryOp::kOr:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001028 out << "|";
1029 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001030 case core::BinaryOp::kXor:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001031 out << "^";
1032 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001033 case core::BinaryOp::kLogicalAnd:
1034 case core::BinaryOp::kLogicalOr: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001035 // These are both handled above.
Ben Claytonf848af22023-07-28 16:37:32 +00001036 TINT_UNREACHABLE();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001037 }
Ben Claytonedc51ab2023-08-08 07:58:19 +00001038 case core::BinaryOp::kEqual:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001039 out << "==";
1040 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001041 case core::BinaryOp::kNotEqual:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001042 out << "!=";
1043 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001044 case core::BinaryOp::kLessThan:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001045 out << "<";
1046 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001047 case core::BinaryOp::kGreaterThan:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001048 out << ">";
1049 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001050 case core::BinaryOp::kLessThanEqual:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001051 out << "<=";
1052 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001053 case core::BinaryOp::kGreaterThanEqual:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001054 out << ">=";
1055 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001056 case core::BinaryOp::kShiftLeft:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001057 out << "<<";
1058 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001059 case core::BinaryOp::kShiftRight:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001060 // TODO(dsinclair): MSL is based on C++14, and >> in C++14 has
1061 // implementation-defined behaviour for negative LHS. We may have to
1062 // generate extra code to implement WGSL-specified behaviour for negative
1063 // LHS.
1064 out << R"(>>)";
1065 break;
1066
Ben Claytonedc51ab2023-08-08 07:58:19 +00001067 case core::BinaryOp::kAdd:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001068 out << "+";
1069 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001070 case core::BinaryOp::kSubtract:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001071 out << "-";
1072 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001073 case core::BinaryOp::kMultiply:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001074 out << "*";
1075 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001076 case core::BinaryOp::kDivide:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001077 out << "/";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001078 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001079 case core::BinaryOp::kModulo:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001080 out << "%";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001081 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001082 }
1083 out << " ";
1084
1085 if (!EmitExpression(out, expr->rhs)) {
1086 return false;
1087 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001088
1089 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001090}
1091
dan sinclairbae54e72023-07-28 15:01:54 +00001092bool ASTPrinter::EmitStatements(VectorRef<const ast::Statement*> stmts) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001093 for (auto* s : stmts) {
1094 if (!EmitStatement(s)) {
1095 return false;
1096 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001097 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001098 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001099}
1100
dan sinclairbae54e72023-07-28 15:01:54 +00001101bool ASTPrinter::EmitStatementsWithIndent(VectorRef<const ast::Statement*> stmts) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001102 ScopedIndent si(this);
1103 return EmitStatements(stmts);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001104}
1105
dan sinclair0bfeaf92023-07-26 17:39:51 +00001106bool ASTPrinter::EmitBlock(const ast::BlockStatement* stmt) {
dan sinclair67a18932023-06-26 19:54:49 +00001107 Line() << "{";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001108 if (!EmitStatementsWithIndent(stmt->statements)) {
1109 return false;
1110 }
dan sinclair67a18932023-06-26 19:54:49 +00001111 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001112 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001113}
1114
dan sinclair0bfeaf92023-07-26 17:39:51 +00001115bool ASTPrinter::EmitBreak(const ast::BreakStatement*) {
dan sinclair67a18932023-06-26 19:54:49 +00001116 Line() << "break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001117 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001118}
1119
dan sinclair0bfeaf92023-07-26 17:39:51 +00001120bool ASTPrinter::EmitBreakIf(const ast::BreakIfStatement* b) {
dan sinclair67a18932023-06-26 19:54:49 +00001121 auto out = Line();
dan sinclairb8b0c212022-10-20 22:45:50 +00001122 out << "if (";
1123 if (!EmitExpression(out, b->condition)) {
1124 return false;
1125 }
1126 out << ") { break; }";
1127 return true;
1128}
1129
dan sinclairbae54e72023-07-28 15:01:54 +00001130bool ASTPrinter::EmitCall(StringStream& out, const ast::CallExpression* expr) {
Ben Claytone9f8b092022-06-01 13:14:39 +00001131 auto* call = builder_.Sem().Get<sem::Call>(expr);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001132 auto* target = call->Target();
1133 return Switch(
Ben Clayton54a104e2023-02-22 20:04:40 +00001134 target, //
1135 [&](const sem::Function* func) { return EmitFunctionCall(out, call, func); },
Ben Claytond9766dc2023-09-21 12:41:20 +00001136 [&](const sem::BuiltinFn* builtin) { return EmitBuiltinCall(out, call, builtin); },
Ben Clayton54a104e2023-02-22 20:04:40 +00001137 [&](const sem::ValueConversion* conv) { return EmitValueConversion(out, call, conv); },
1138 [&](const sem::ValueConstructor* ctor) { return EmitValueConstructor(out, call, ctor); },
Ben Claytond6082c52023-10-26 16:02:01 +00001139 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001140}
1141
dan sinclairbae54e72023-07-28 15:01:54 +00001142bool ASTPrinter::EmitFunctionCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001143 const sem::Call* call,
1144 const sem::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001145 auto* expr = call->Declaration();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001146
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001147 if (ast::HasAttribute<CalculateArrayLength::BufferSizeIntrinsic>(
dan sinclair41e4d9a2022-05-01 14:40:55 +00001148 func->Declaration()->attributes)) {
1149 // Special function generated by the CalculateArrayLength transform for
1150 // calling X.GetDimensions(Y)
1151 if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
1152 return false;
1153 }
1154 out << ".GetDimensions(";
1155 if (!EmitExpression(out, call->Arguments()[1]->Declaration())) {
1156 return false;
1157 }
1158 out << ")";
1159 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001160 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001161
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001162 if (auto* intrinsic =
1163 ast::GetAttribute<DecomposeMemoryAccess::Intrinsic>(func->Declaration()->attributes)) {
dan sinclairff7cf212022-10-03 14:05:23 +00001164 switch (intrinsic->address_space) {
Ben Claytoncd52f382023-08-07 13:11:08 +00001165 case core::AddressSpace::kUniform:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001166 return EmitUniformBufferAccess(out, expr, intrinsic);
Ben Claytoncd52f382023-08-07 13:11:08 +00001167 case core::AddressSpace::kStorage:
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001168 if (!intrinsic->IsAtomic()) {
1169 return EmitStorageBufferAccess(out, expr, intrinsic);
1170 }
1171 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001172 default:
Ben Claytonf848af22023-07-28 16:37:32 +00001173 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic address space:"
1174 << intrinsic->address_space;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001175 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001176 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001177
James Pricefe24b4a2023-08-08 01:40:43 +00001178 if (auto* wave_intrinsic =
1179 ast::GetAttribute<ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic>(
1180 func->Declaration()->attributes)) {
1181 switch (wave_intrinsic->op) {
1182 case ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic::Op::kWaveGetLaneCount:
1183 out << "WaveGetLaneCount()";
1184 return true;
1185 case ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic::Op::kWaveGetLaneIndex:
1186 out << "WaveGetLaneIndex()";
1187 return true;
1188 }
1189 }
1190
dan sinclaird026e132023-04-18 19:38:25 +00001191 out << func->Declaration()->name->symbol.Name() << "(";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001192
1193 bool first = true;
1194 for (auto* arg : call->Arguments()) {
1195 if (!first) {
1196 out << ", ";
1197 }
1198 first = false;
1199
1200 if (!EmitExpression(out, arg->Declaration())) {
1201 return false;
1202 }
1203 }
1204
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001205 out << ")";
1206 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001207}
1208
dan sinclairbae54e72023-07-28 15:01:54 +00001209bool ASTPrinter::EmitBuiltinCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001210 const sem::Call* call,
Ben Claytond9766dc2023-09-21 12:41:20 +00001211 const sem::BuiltinFn* builtin) {
1212 const auto type = builtin->Fn();
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001213
dan sinclair41e4d9a2022-05-01 14:40:55 +00001214 auto* expr = call->Declaration();
1215 if (builtin->IsTexture()) {
1216 return EmitTextureCall(out, call, builtin);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001217 }
Ben Clayton52e6a0f2024-02-21 08:11:33 +00001218 if (type == wgsl::BuiltinFn::kBitcast) {
1219 return EmitBitcastCall(out, expr);
1220 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001221 if (type == wgsl::BuiltinFn::kSelect) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001222 return EmitSelectCall(out, expr);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001223 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001224 if (type == wgsl::BuiltinFn::kModf) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001225 return EmitModfCall(out, expr, builtin);
1226 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001227 if (type == wgsl::BuiltinFn::kFrexp) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001228 return EmitFrexpCall(out, expr, builtin);
1229 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001230 if (type == wgsl::BuiltinFn::kDegrees) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001231 return EmitDegreesCall(out, expr, builtin);
1232 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001233 if (type == wgsl::BuiltinFn::kRadians) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001234 return EmitRadiansCall(out, expr, builtin);
1235 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001236 if (type == wgsl::BuiltinFn::kSign) {
dan sinclair70927862023-01-11 13:18:29 +00001237 return EmitSignCall(out, call, builtin);
1238 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001239 if (type == wgsl::BuiltinFn::kQuantizeToF16) {
Ben Clayton2bea9052022-11-02 00:09:50 +00001240 return EmitQuantizeToF16Call(out, expr, builtin);
1241 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001242 if (type == wgsl::BuiltinFn::kTrunc) {
Antonio Maiorano5cf943e2023-03-27 18:55:25 +00001243 return EmitTruncCall(out, expr, builtin);
1244 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001245 if (builtin->IsDataPacking()) {
1246 return EmitDataPackingCall(out, expr, builtin);
1247 }
1248 if (builtin->IsDataUnpacking()) {
1249 return EmitDataUnpackingCall(out, expr, builtin);
1250 }
1251 if (builtin->IsBarrier()) {
1252 return EmitBarrierCall(out, builtin);
1253 }
1254 if (builtin->IsAtomic()) {
1255 return EmitWorkgroupAtomicCall(out, expr, builtin);
1256 }
Jiawei Shao53c0fa92023-12-08 01:24:00 +00001257 if (builtin->IsPacked4x8IntegerDotProductBuiltin()) {
1258 return EmitPacked4x8IntegerDotProductBuiltinCall(out, expr, builtin);
Jiawei Shaoab975702022-05-13 00:09:56 +00001259 }
Natalie Chouinarddbefda92024-08-13 19:10:21 +00001260 if (type == wgsl::BuiltinFn::kSubgroupShuffleXor ||
1261 type == wgsl::BuiltinFn::kSubgroupShuffleUp ||
1262 type == wgsl::BuiltinFn::kSubgroupShuffleDown) {
1263 return EmitSubgroupShuffleBuiltinCall(out, expr, builtin);
1264 }
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001265
dan sinclair41e4d9a2022-05-01 14:40:55 +00001266 auto name = generate_builtin_name(builtin);
1267 if (name.empty()) {
1268 return false;
1269 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001270
Natalie Chouinarde9e450e2024-08-08 23:16:40 +00001271 // Handle single argument builtins that only accept and return uint (not int overload).
1272 // For count bits and reverse bits, we need to explicitly cast the return value (we also cast
1273 // the arg for good measure). See crbug.com/tint/1550.
1274 // For the following subgroup builtins, the lack of support may be a bug in DXC. See
1275 // github.com/microsoft/DirectXShaderCompiler/issues/6850.
1276 if (type == wgsl::BuiltinFn::kCountOneBits || type == wgsl::BuiltinFn::kReverseBits ||
1277 type == wgsl::BuiltinFn::kSubgroupAnd || type == wgsl::BuiltinFn::kSubgroupOr ||
1278 type == wgsl::BuiltinFn::kSubgroupXor) {
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001279 auto* arg = call->Arguments()[0];
Natalie Chouinarde9e450e2024-08-08 23:16:40 +00001280 auto* argType = arg->Type()->UnwrapRef();
1281 if (argType->is_signed_integer_scalar_or_vector()) {
1282 // Bitcast of literal int vectors fails in DXC so extract arg to a var. See
1283 // github.com/microsoft/DirectXShaderCompiler/issues/6851.
1284 if (argType->is_signed_integer_vector() &&
1285 arg->Stage() == core::EvaluationStage::kConstant) {
1286 auto varName = UniqueIdentifier(kTempNamePrefix);
1287 auto pre = Line();
1288 if (!EmitTypeAndName(pre, argType, core::AddressSpace::kUndefined,
1289 core::Access::kUndefined, varName)) {
1290 return false;
1291 }
1292 pre << " = ";
1293 if (!EmitExpression(pre, arg->Declaration())) {
1294 return false;
1295 }
1296 pre << ";";
1297 out << "asint(" << name << "(asuint(" << varName << ")))";
1298 return true;
1299 }
1300
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001301 out << "asint(" << name << "(asuint(";
1302 if (!EmitExpression(out, arg->Declaration())) {
1303 return false;
1304 }
1305 out << ")))";
1306 return true;
1307 }
1308 }
1309
dan sinclair41e4d9a2022-05-01 14:40:55 +00001310 out << name << "(";
1311
1312 bool first = true;
1313 for (auto* arg : call->Arguments()) {
1314 if (!first) {
1315 out << ", ";
1316 }
1317 first = false;
1318
1319 if (!EmitExpression(out, arg->Declaration())) {
1320 return false;
1321 }
1322 }
1323
1324 out << ")";
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001325
dan sinclair41e4d9a2022-05-01 14:40:55 +00001326 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001327}
1328
dan sinclairbae54e72023-07-28 15:01:54 +00001329bool ASTPrinter::EmitValueConversion(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001330 const sem::Call* call,
1331 const sem::ValueConversion* conv) {
Ben Claytoncd52f382023-08-07 13:11:08 +00001332 if (!EmitType(out, conv->Target(), core::AddressSpace::kUndefined, core::Access::kReadWrite,
1333 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001334 return false;
1335 }
1336 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001337
dan sinclair41e4d9a2022-05-01 14:40:55 +00001338 if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
1339 return false;
1340 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001341
dan sinclair41e4d9a2022-05-01 14:40:55 +00001342 out << ")";
1343 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001344}
1345
dan sinclairbae54e72023-07-28 15:01:54 +00001346bool ASTPrinter::EmitValueConstructor(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001347 const sem::Call* call,
1348 const sem::ValueConstructor* ctor) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001349 auto* type = call->Type();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001350
Ben Clayton54a104e2023-02-22 20:04:40 +00001351 // If the value constructor arguments are empty then we need to construct with the zero value
1352 // for all components.
Ben Clayton958a4642022-07-26 07:55:24 +00001353 if (call->Arguments().IsEmpty()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001354 return EmitZeroValue(out, type);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001355 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001356
dan sinclair6e77b472022-10-20 13:38:28 +00001357 // Single parameter matrix initializers must be identity initializer.
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001358 // It could also be conversions between f16 and f32 matrix when f16 is properly supported.
dan sinclaircedcdf32023-08-10 02:39:48 +00001359 if (type->Is<core::type::Matrix>() && call->Arguments().Length() == 1) {
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001360 if (!ctor->Parameters()[0]->Type()->UnwrapRef()->is_float_matrix()) {
Ben Claytonf848af22023-07-28 16:37:32 +00001361 TINT_UNREACHABLE()
dan sinclair6e77b472022-10-20 13:38:28 +00001362 << "found a single-parameter matrix initializer that is not identity initializer";
Ben Clayton3b5edf12022-05-16 21:14:11 +00001363 }
1364 }
1365
dan sinclaircedcdf32023-08-10 02:39:48 +00001366 bool brackets = type->IsAnyOf<core::type::Array, core::type::Struct>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001367
dan sinclair41e4d9a2022-05-01 14:40:55 +00001368 // For single-value vector initializers, swizzle the scalar to the right
1369 // vector dimension using .x
dan sinclaircedcdf32023-08-10 02:39:48 +00001370 const bool is_single_value_vector_init =
1371 type->is_scalar_vector() && call->Arguments().Length() == 1 &&
1372 ctor->Parameters()[0]->Type()->Is<core::type::Scalar>();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001373
Ben Clayton6c098ba2022-07-14 20:46:39 +00001374 if (brackets) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001375 out << "{";
1376 } else {
Ben Claytoncd52f382023-08-07 13:11:08 +00001377 if (!EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001378 return false;
1379 }
1380 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001381 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001382
dan sinclair41e4d9a2022-05-01 14:40:55 +00001383 if (is_single_value_vector_init) {
1384 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001385 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001386
dan sinclair41e4d9a2022-05-01 14:40:55 +00001387 bool first = true;
1388 for (auto* e : call->Arguments()) {
1389 if (!first) {
1390 out << ", ";
1391 }
1392 first = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001393
dan sinclair41e4d9a2022-05-01 14:40:55 +00001394 if (!EmitExpression(out, e->Declaration())) {
1395 return false;
1396 }
1397 }
1398
1399 if (is_single_value_vector_init) {
dan sinclaircedcdf32023-08-10 02:39:48 +00001400 out << ")." << std::string(type->As<core::type::Vector>()->Width(), 'x');
dan sinclair41e4d9a2022-05-01 14:40:55 +00001401 }
1402
1403 out << (brackets ? "}" : ")");
1404 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001405}
1406
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001407bool ASTPrinter::EmitUniformBufferAccess(StringStream& out,
1408 const ast::CallExpression* expr,
1409 const DecomposeMemoryAccess::Intrinsic* intrinsic) {
dan sinclaird026e132023-04-18 19:38:25 +00001410 auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001411 auto* const offset = expr->args[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001412
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001413 // offset in bytes
1414 uint32_t scalar_offset_bytes = 0;
1415 // offset in uint (4 bytes)
1416 uint32_t scalar_offset_index = 0;
1417 // expression to calculate offset in bytes
1418 std::string scalar_offset_bytes_expr;
1419 // expression to calculate offset in uint, by dividing scalar_offset_bytes_expr by 4
1420 std::string scalar_offset_index_expr;
1421 // expression to calculate offset in uint, independently
1422 std::string scalar_offset_index_unified_expr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001423
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001424 // If true, use scalar_offset_index, otherwise use scalar_offset_index_expr
dan sinclair41e4d9a2022-05-01 14:40:55 +00001425 bool scalar_offset_constant = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001426
Ben Clayton1a1b5272023-02-24 17:16:55 +00001427 if (auto* val = builder_.Sem().GetVal(offset)->ConstantValue()) {
dan sinclaircedcdf32023-08-10 02:39:48 +00001428 TINT_ASSERT(val->Type()->Is<core::type::U32>());
dan sinclairb53b8cf2022-12-15 16:25:31 +00001429 scalar_offset_bytes = static_cast<uint32_t>(val->ValueAs<AInt>());
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001430 scalar_offset_index = scalar_offset_bytes / 4; // bytes -> scalar index
dan sinclair41e4d9a2022-05-01 14:40:55 +00001431 scalar_offset_constant = true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001432 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001433
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001434 // If true, scalar_offset_bytes or scalar_offset_bytes_expr should be used, otherwise only use
1435 // scalar_offset_index or scalar_offset_index_unified_expr. Currently only loading f16 scalar
1436 // require using offset in bytes.
1437 const bool need_offset_in_bytes =
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001438 intrinsic->type == DecomposeMemoryAccess::Intrinsic::DataType::kF16;
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001439
dan sinclair41e4d9a2022-05-01 14:40:55 +00001440 if (!scalar_offset_constant) {
1441 // UBO offset not compile-time known.
1442 // Calculate the scalar offset into a temporary.
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001443 if (need_offset_in_bytes) {
1444 scalar_offset_bytes_expr = UniqueIdentifier("scalar_offset_bytes");
1445 scalar_offset_index_expr = UniqueIdentifier("scalar_offset_index");
1446 {
dan sinclair67a18932023-06-26 19:54:49 +00001447 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001448 pre << "const uint " << scalar_offset_bytes_expr << " = (";
Ben Clayton1a1b5272023-02-24 17:16:55 +00001449 if (!EmitExpression(pre, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001450 return false;
1451 }
1452 pre << ");";
1453 }
dan sinclair67a18932023-06-26 19:54:49 +00001454 Line() << "const uint " << scalar_offset_index_expr << " = " << scalar_offset_bytes_expr
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001455 << " / 4;";
1456 } else {
1457 scalar_offset_index_unified_expr = UniqueIdentifier("scalar_offset");
dan sinclair67a18932023-06-26 19:54:49 +00001458 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001459 pre << "const uint " << scalar_offset_index_unified_expr << " = (";
Ben Clayton1a1b5272023-02-24 17:16:55 +00001460 if (!EmitExpression(pre, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001461 return false;
1462 }
1463 pre << ") / 4;";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001464 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001465 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001466
Austin Enga0e96b52023-02-18 00:39:01 +00001467 const char swizzle[] = {'x', 'y', 'z', 'w'};
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001468
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001469 using Op = DecomposeMemoryAccess::Intrinsic::Op;
1470 using DataType = DecomposeMemoryAccess::Intrinsic::DataType;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001471 switch (intrinsic->op) {
1472 case Op::kLoad: {
1473 auto cast = [&](const char* to, auto&& load) {
1474 out << to << "(";
1475 auto result = load();
1476 out << ")";
1477 return result;
1478 };
dan sinclairbae54e72023-07-28 15:01:54 +00001479 auto load_u32_to = [&](StringStream& target) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001480 target << buffer;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001481 if (scalar_offset_constant) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001482 target << "[" << (scalar_offset_index / 4) << "]."
1483 << swizzle[scalar_offset_index & 3];
dan sinclair41e4d9a2022-05-01 14:40:55 +00001484 } else {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001485 target << "[" << scalar_offset_index_unified_expr << " / 4]["
1486 << scalar_offset_index_unified_expr << " % 4]";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001487 }
1488 return true;
1489 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001490 auto load_u32 = [&] { return load_u32_to(out); };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001491 // Has a minimum alignment of 8 bytes, so is either .xy or .zw
dan sinclairbae54e72023-07-28 15:01:54 +00001492 auto load_vec2_u32_to = [&](StringStream& target) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001493 if (scalar_offset_constant) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001494 target << buffer << "[" << (scalar_offset_index / 4) << "]"
1495 << ((scalar_offset_index & 2) == 0 ? ".xy" : ".zw");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001496 } else {
1497 std::string ubo_load = UniqueIdentifier("ubo_load");
1498 {
dan sinclair67a18932023-06-26 19:54:49 +00001499 auto pre = Line();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001500 pre << "uint4 " << ubo_load << " = " << buffer << "["
1501 << scalar_offset_index_unified_expr << " / 4];";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001502 }
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001503 target << "((" << scalar_offset_index_unified_expr << " & 2) ? " << ubo_load
1504 << ".zw : " << ubo_load << ".xy)";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001505 }
1506 return true;
1507 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001508 auto load_vec2_u32 = [&] { return load_vec2_u32_to(out); };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001509 // vec4 has a minimum alignment of 16 bytes, easiest case
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001510 auto load_vec4_u32 = [&] {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001511 out << buffer;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001512 if (scalar_offset_constant) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001513 out << "[" << (scalar_offset_index / 4) << "]";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001514 } else {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001515 out << "[" << scalar_offset_index_unified_expr << " / 4]";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001516 }
1517 return true;
1518 };
1519 // vec3 has a minimum alignment of 16 bytes, so is just a .xyz swizzle
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001520 auto load_vec3_u32 = [&] {
1521 if (!load_vec4_u32()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001522 return false;
1523 }
1524 out << ".xyz";
1525 return true;
1526 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001527 auto load_scalar_f16 = [&] {
1528 // offset bytes = 4k, ((buffer[index].x) & 0xFFFF)
1529 // offset bytes = 4k+2, ((buffer[index].x >> 16) & 0xFFFF)
Ben Clayton1a1b5272023-02-24 17:16:55 +00001530 out << "float16_t(f16tof32(((" << buffer;
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001531 if (scalar_offset_constant) {
1532 out << "[" << (scalar_offset_index / 4) << "]."
1533 << swizzle[scalar_offset_index & 3];
1534 // WGSL spec ensure little endian memory layout.
1535 if (scalar_offset_bytes % 4 == 0) {
1536 out << ") & 0xFFFF)";
1537 } else {
1538 out << " >> 16) & 0xFFFF)";
1539 }
1540 } else {
1541 out << "[" << scalar_offset_index_expr << " / 4][" << scalar_offset_index_expr
1542 << " % 4] >> (" << scalar_offset_bytes_expr
1543 << " % 4 == 0 ? 0 : 16)) & 0xFFFF)";
1544 }
1545 out << "))";
1546 return true;
1547 };
1548 auto load_vec2_f16 = [&] {
1549 // vec2<f16> is aligned to 4 bytes
1550 // Preclude code load the vec2<f16> data as a uint:
1551 // uint ubo_load = buffer[id0][id1];
1552 // Loading code convert it to vec2<f16>:
1553 // vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)),
1554 // float16_t(f16tof32(ubo_load >> 16)))
1555 std::string ubo_load = UniqueIdentifier("ubo_load");
1556 {
dan sinclair67a18932023-06-26 19:54:49 +00001557 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001558 // Load the 4 bytes f16 vector as an uint
1559 pre << "uint " << ubo_load << " = ";
1560 if (!load_u32_to(pre)) {
1561 return false;
1562 }
1563 pre << ";";
1564 }
1565 out << "vector<float16_t, 2>(float16_t(f16tof32(" << ubo_load
1566 << " & 0xFFFF)), float16_t(f16tof32(" << ubo_load << " >> 16)))";
1567 return true;
1568 };
1569 auto load_vec3_f16 = [&] {
1570 // vec3<f16> is aligned to 8 bytes
1571 // Preclude code load the vec3<f16> data as uint2 and convert its elements to
1572 // float16_t:
1573 // uint2 ubo_load = buffer[id0].xy;
1574 // /* The low 8 bits of two uint are the x and z elements of vec3<f16> */
1575 // vector<float16_t> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load &
1576 // 0xFFFF));
1577 // /* The high 8 bits of first uint is the y element of vec3<f16> */
1578 // float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
1579 // Loading code convert it to vec3<f16>:
1580 // vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1])
1581 std::string ubo_load = UniqueIdentifier("ubo_load");
1582 std::string ubo_load_xz = UniqueIdentifier(ubo_load + "_xz");
1583 std::string ubo_load_y = UniqueIdentifier(ubo_load + "_y");
1584 {
dan sinclair67a18932023-06-26 19:54:49 +00001585 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001586 // Load the 8 bytes uint2 with the f16 vector at lower 6 bytes
1587 pre << "uint2 " << ubo_load << " = ";
1588 if (!load_vec2_u32_to(pre)) {
1589 return false;
1590 }
1591 pre << ";";
1592 }
1593 {
dan sinclair67a18932023-06-26 19:54:49 +00001594 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001595 pre << "vector<float16_t, 2> " << ubo_load_xz
1596 << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " & 0xFFFF));";
1597 }
1598 {
dan sinclair67a18932023-06-26 19:54:49 +00001599 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001600 pre << "float16_t " << ubo_load_y << " = f16tof32(" << ubo_load
1601 << "[0] >> 16);";
1602 }
1603 out << "vector<float16_t, 3>(" << ubo_load_xz << "[0], " << ubo_load_y << ", "
1604 << ubo_load_xz << "[1])";
1605 return true;
1606 };
1607 auto load_vec4_f16 = [&] {
1608 // vec4<f16> is aligned to 8 bytes
1609 // Preclude code load the vec4<f16> data as uint2 and convert its elements to
1610 // float16_t:
1611 // uint2 ubo_load = buffer[id0].xy;
1612 // /* The low 8 bits of two uint are the x and z elements of vec4<f16> */
1613 // vector<float16_t> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load &
1614 // 0xFFFF));
1615 // /* The high 8 bits of two uint are the y and w elements of vec4<f16> */
1616 // vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >>
1617 // 16));
1618 // Loading code convert it to vec4<f16>:
1619 // vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1],
1620 // ubo_load_yw[1])
1621 std::string ubo_load = UniqueIdentifier("ubo_load");
1622 std::string ubo_load_xz = UniqueIdentifier(ubo_load + "_xz");
1623 std::string ubo_load_yw = UniqueIdentifier(ubo_load + "_yw");
1624 {
dan sinclair67a18932023-06-26 19:54:49 +00001625 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001626 // Load the 8 bytes f16 vector as an uint2
1627 pre << "uint2 " << ubo_load << " = ";
1628 if (!load_vec2_u32_to(pre)) {
1629 return false;
1630 }
1631 pre << ";";
1632 }
1633 {
dan sinclair67a18932023-06-26 19:54:49 +00001634 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001635 pre << "vector<float16_t, 2> " << ubo_load_xz
1636 << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " & 0xFFFF));";
1637 }
1638 {
dan sinclair67a18932023-06-26 19:54:49 +00001639 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001640 pre << "vector<float16_t, 2> " << ubo_load_yw
1641 << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " >> 16));";
1642 }
1643 out << "vector<float16_t, 4>(" << ubo_load_xz << "[0], " << ubo_load_yw << "[0], "
1644 << ubo_load_xz << "[1], " << ubo_load_yw << "[1])";
1645 return true;
1646 };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001647 switch (intrinsic->type) {
1648 case DataType::kU32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001649 return load_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001650 case DataType::kF32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001651 return cast("asfloat", load_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001652 case DataType::kI32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001653 return cast("asint", load_u32);
1654 case DataType::kF16:
1655 return load_scalar_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001656 case DataType::kVec2U32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001657 return load_vec2_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001658 case DataType::kVec2F32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001659 return cast("asfloat", load_vec2_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001660 case DataType::kVec2I32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001661 return cast("asint", load_vec2_u32);
1662 case DataType::kVec2F16:
1663 return load_vec2_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001664 case DataType::kVec3U32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001665 return load_vec3_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001666 case DataType::kVec3F32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001667 return cast("asfloat", load_vec3_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001668 case DataType::kVec3I32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001669 return cast("asint", load_vec3_u32);
1670 case DataType::kVec3F16:
1671 return load_vec3_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001672 case DataType::kVec4U32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001673 return load_vec4_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001674 case DataType::kVec4F32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001675 return cast("asfloat", load_vec4_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001676 case DataType::kVec4I32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001677 return cast("asint", load_vec4_u32);
1678 case DataType::kVec4F16:
1679 return load_vec4_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001680 }
Ben Claytonf848af22023-07-28 16:37:32 +00001681 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1682 << static_cast<int>(intrinsic->type);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001683 }
1684 default:
1685 break;
1686 }
Ben Claytonf848af22023-07-28 16:37:32 +00001687 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::Op: "
1688 << static_cast<int>(intrinsic->op);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001689}
1690
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001691bool ASTPrinter::EmitStorageBufferAccess(StringStream& out,
1692 const ast::CallExpression* expr,
1693 const DecomposeMemoryAccess::Intrinsic* intrinsic) {
dan sinclaird026e132023-04-18 19:38:25 +00001694 auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001695 auto* const offset = expr->args[0];
Ben Clayton407137a2023-06-28 21:02:32 +00001696 auto* const value = expr->args.Length() > 1 ? expr->args[1] : nullptr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001697
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001698 using Op = DecomposeMemoryAccess::Intrinsic::Op;
1699 using DataType = DecomposeMemoryAccess::Intrinsic::DataType;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001700 switch (intrinsic->op) {
1701 case Op::kLoad: {
1702 auto load = [&](const char* cast, int n) {
1703 if (cast) {
1704 out << cast << "(";
1705 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001706 out << buffer << ".Load";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001707 if (n > 1) {
1708 out << n;
1709 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00001710 ScopedParen sp(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001711 if (!EmitExpression(out, offset)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001712 return false;
1713 }
1714 if (cast) {
1715 out << ")";
1716 }
1717 return true;
1718 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001719 // Templated load used for f16 types, requires SM6.2 or higher and DXC
1720 // Used by loading f16 types, e.g. for f16 type, set type parameter to "float16_t"
1721 // to emit `buffer.Load<float16_t>(offset)`.
1722 auto templated_load = [&](const char* type) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001723 out << buffer << ".Load<" << type << ">"; // templated load
dan sinclairb2ba57b2023-02-28 15:14:09 +00001724 ScopedParen sp(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001725 if (!EmitExpression(out, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001726 return false;
1727 }
1728 return true;
1729 };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001730 switch (intrinsic->type) {
1731 case DataType::kU32:
1732 return load(nullptr, 1);
1733 case DataType::kF32:
1734 return load("asfloat", 1);
1735 case DataType::kI32:
1736 return load("asint", 1);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001737 case DataType::kF16:
1738 return templated_load("float16_t");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001739 case DataType::kVec2U32:
1740 return load(nullptr, 2);
1741 case DataType::kVec2F32:
1742 return load("asfloat", 2);
1743 case DataType::kVec2I32:
1744 return load("asint", 2);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001745 case DataType::kVec2F16:
1746 return templated_load("vector<float16_t, 2> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001747 case DataType::kVec3U32:
1748 return load(nullptr, 3);
1749 case DataType::kVec3F32:
1750 return load("asfloat", 3);
1751 case DataType::kVec3I32:
1752 return load("asint", 3);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001753 case DataType::kVec3F16:
1754 return templated_load("vector<float16_t, 3> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001755 case DataType::kVec4U32:
1756 return load(nullptr, 4);
1757 case DataType::kVec4F32:
1758 return load("asfloat", 4);
1759 case DataType::kVec4I32:
1760 return load("asint", 4);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001761 case DataType::kVec4F16:
1762 return templated_load("vector<float16_t, 4> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001763 }
Ben Claytonf848af22023-07-28 16:37:32 +00001764 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1765 << static_cast<int>(intrinsic->type);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001766 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001767
1768 case Op::kStore: {
1769 auto store = [&](int n) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001770 out << buffer << ".Store";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001771 if (n > 1) {
1772 out << n;
1773 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00001774 ScopedParen sp1(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001775 if (!EmitExpression(out, offset)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001776 return false;
1777 }
1778 out << ", asuint";
dan sinclairb2ba57b2023-02-28 15:14:09 +00001779 ScopedParen sp2(out);
Antonio Maioranod1368d72023-09-05 20:29:56 +00001780 if (!EmitTextureOrStorageBufferCallArgExpression(out, value)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001781 return false;
1782 }
1783 return true;
1784 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001785 // Templated stored used for f16 types, requires SM6.2 or higher and DXC
1786 // Used by storing f16 types, e.g. for f16 type, set type parameter to "float16_t"
1787 // to emit `buffer.Store<float16_t>(offset)`.
1788 auto templated_store = [&](const char* type) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001789 out << buffer << ".Store<" << type << ">"; // templated store
dan sinclairb2ba57b2023-02-28 15:14:09 +00001790 ScopedParen sp1(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001791 if (!EmitExpression(out, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001792 return false;
1793 }
1794 out << ", ";
Ben Clayton1a1b5272023-02-24 17:16:55 +00001795 if (!EmitExpression(out, value)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001796 return false;
1797 }
1798 return true;
1799 };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001800 switch (intrinsic->type) {
1801 case DataType::kU32:
1802 return store(1);
1803 case DataType::kF32:
1804 return store(1);
1805 case DataType::kI32:
1806 return store(1);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001807 case DataType::kF16:
1808 return templated_store("float16_t");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001809 case DataType::kVec2U32:
1810 return store(2);
1811 case DataType::kVec2F32:
1812 return store(2);
1813 case DataType::kVec2I32:
1814 return store(2);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001815 case DataType::kVec2F16:
1816 return templated_store("vector<float16_t, 2> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001817 case DataType::kVec3U32:
1818 return store(3);
1819 case DataType::kVec3F32:
1820 return store(3);
1821 case DataType::kVec3I32:
1822 return store(3);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001823 case DataType::kVec3F16:
1824 return templated_store("vector<float16_t, 3> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001825 case DataType::kVec4U32:
1826 return store(4);
1827 case DataType::kVec4F32:
1828 return store(4);
1829 case DataType::kVec4I32:
1830 return store(4);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001831 case DataType::kVec4F16:
1832 return templated_store("vector<float16_t, 4> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001833 }
Ben Claytonf848af22023-07-28 16:37:32 +00001834 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1835 << static_cast<int>(intrinsic->type);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001836 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001837 default:
Ben Clayton1a1b5272023-02-24 17:16:55 +00001838 // Break out to error case below
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001839 // Note that atomic intrinsics are generated as functions.
1840 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001841 }
1842
Ben Claytonf848af22023-07-28 16:37:32 +00001843 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::Op: "
1844 << static_cast<int>(intrinsic->op);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001845}
1846
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001847bool ASTPrinter::EmitStorageAtomicIntrinsic(const ast::Function* func,
1848 const DecomposeMemoryAccess::Intrinsic* intrinsic) {
1849 using Op = DecomposeMemoryAccess::Intrinsic::Op;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001850
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001851 const sem::Function* sem_func = builder_.Sem().Get(func);
1852 auto* result_ty = sem_func->ReturnType();
dan sinclaird026e132023-04-18 19:38:25 +00001853 const auto name = func->name->symbol.Name();
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001854 auto& buf = *current_buffer_;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001855
dan sinclaird026e132023-04-18 19:38:25 +00001856 auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001857
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001858 auto rmw = [&](const char* hlsl) -> bool {
1859 {
dan sinclair67a18932023-06-26 19:54:49 +00001860 auto fn = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001861 if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
1862 core::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001863 return false;
1864 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001865 fn << "(uint offset, ";
Ben Claytoncd52f382023-08-07 13:11:08 +00001866 if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
1867 core::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001868 return false;
1869 }
1870 fn << ") {";
1871 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001872
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001873 buf.IncrementIndent();
1874 TINT_DEFER({
1875 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001876 Line(&buf) << "}";
1877 Line(&buf);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001878 });
1879
1880 {
dan sinclair67a18932023-06-26 19:54:49 +00001881 auto l = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001882 if (!EmitTypeAndName(l, result_ty, core::AddressSpace::kUndefined,
1883 core::Access::kUndefined, "original_value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001884 return false;
1885 }
1886 l << " = 0;";
1887 }
1888 {
dan sinclair67a18932023-06-26 19:54:49 +00001889 auto l = Line(&buf);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001890 l << buffer << "." << hlsl << "(offset, ";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001891 if (intrinsic->op == Op::kAtomicSub) {
1892 l << "-";
1893 }
1894 l << "value, original_value);";
1895 }
dan sinclair67a18932023-06-26 19:54:49 +00001896 Line(&buf) << "return original_value;";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001897 return true;
1898 };
1899
1900 switch (intrinsic->op) {
1901 case Op::kAtomicAdd:
1902 return rmw("InterlockedAdd");
1903
1904 case Op::kAtomicSub:
1905 // Use add with the operand negated.
1906 return rmw("InterlockedAdd");
1907
1908 case Op::kAtomicMax:
1909 return rmw("InterlockedMax");
1910
1911 case Op::kAtomicMin:
1912 return rmw("InterlockedMin");
1913
1914 case Op::kAtomicAnd:
1915 return rmw("InterlockedAnd");
1916
1917 case Op::kAtomicOr:
1918 return rmw("InterlockedOr");
1919
1920 case Op::kAtomicXor:
1921 return rmw("InterlockedXor");
1922
1923 case Op::kAtomicExchange:
1924 return rmw("InterlockedExchange");
1925
1926 case Op::kAtomicLoad: {
1927 // HLSL does not have an InterlockedLoad, so we emulate it with
1928 // InterlockedOr using 0 as the OR value
dan sinclair41e4d9a2022-05-01 14:40:55 +00001929 {
dan sinclair67a18932023-06-26 19:54:49 +00001930 auto fn = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001931 if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
1932 core::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001933 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001934 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001935 fn << "(uint offset) {";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001936 }
1937
1938 buf.IncrementIndent();
1939 TINT_DEFER({
1940 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001941 Line(&buf) << "}";
1942 Line(&buf);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001943 });
1944
1945 {
dan sinclair67a18932023-06-26 19:54:49 +00001946 auto l = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001947 if (!EmitTypeAndName(l, result_ty, core::AddressSpace::kUndefined,
1948 core::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001949 return false;
1950 }
1951 l << " = 0;";
1952 }
1953
dan sinclair67a18932023-06-26 19:54:49 +00001954 Line(&buf) << buffer << ".InterlockedOr(offset, 0, value);";
1955 Line(&buf) << "return value;";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001956 return true;
1957 }
1958 case Op::kAtomicStore: {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001959 auto* const value_ty = sem_func->Parameters()[1]->Type()->UnwrapRef();
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001960 // HLSL does not have an InterlockedStore, so we emulate it with
1961 // InterlockedExchange and discard the returned value
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001962 {
dan sinclair67a18932023-06-26 19:54:49 +00001963 auto fn = Line(&buf);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001964 fn << "void " << name << "(uint offset, ";
Ben Claytoncd52f382023-08-07 13:11:08 +00001965 if (!EmitTypeAndName(fn, value_ty, core::AddressSpace::kUndefined,
1966 core::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001967 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001968 }
1969 fn << ") {";
1970 }
1971
1972 buf.IncrementIndent();
1973 TINT_DEFER({
1974 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001975 Line(&buf) << "}";
1976 Line(&buf);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001977 });
1978
1979 {
dan sinclair67a18932023-06-26 19:54:49 +00001980 auto l = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001981 if (!EmitTypeAndName(l, value_ty, core::AddressSpace::kUndefined,
1982 core::Access::kUndefined, "ignored")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001983 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001984 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001985 l << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001986 }
dan sinclair67a18932023-06-26 19:54:49 +00001987 Line(&buf) << buffer << ".InterlockedExchange(offset, value, ignored);";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001988 return true;
1989 }
1990 case Op::kAtomicCompareExchangeWeak: {
dan sinclaircedcdf32023-08-10 02:39:48 +00001991 if (!EmitStructType(&helpers_, result_ty->As<core::type::Struct>())) {
Ben Clayton4b8a3d32023-06-13 16:55:57 +00001992 return false;
1993 }
1994
Ben Clayton1a1b5272023-02-24 17:16:55 +00001995 auto* const value_ty = sem_func->Parameters()[1]->Type()->UnwrapRef();
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001996 // NOTE: We don't need to emit the return type struct here as DecomposeMemoryAccess
1997 // already added it to the AST, and it should have already been emitted by now.
dan sinclair41e4d9a2022-05-01 14:40:55 +00001998 {
dan sinclair67a18932023-06-26 19:54:49 +00001999 auto fn = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00002000 if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
2001 core::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002002 return false;
2003 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00002004 fn << "(uint offset, ";
Ben Claytoncd52f382023-08-07 13:11:08 +00002005 if (!EmitTypeAndName(fn, value_ty, core::AddressSpace::kUndefined,
2006 core::Access::kUndefined, "compare")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002007 return false;
2008 }
2009 fn << ", ";
Ben Claytoncd52f382023-08-07 13:11:08 +00002010 if (!EmitTypeAndName(fn, value_ty, core::AddressSpace::kUndefined,
2011 core::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002012 return false;
2013 }
2014 fn << ") {";
2015 }
2016
2017 buf.IncrementIndent();
2018 TINT_DEFER({
2019 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00002020 Line(&buf) << "}";
2021 Line(&buf);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002022 });
2023
2024 { // T result = {0};
dan sinclair67a18932023-06-26 19:54:49 +00002025 auto l = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00002026 if (!EmitTypeAndName(l, result_ty, core::AddressSpace::kUndefined,
2027 core::Access::kUndefined, "result")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002028 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002029 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002030 l << "=";
2031 if (!EmitZeroValue(l, result_ty)) {
2032 return false;
2033 }
2034 l << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002035 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002036
dan sinclair67a18932023-06-26 19:54:49 +00002037 Line(&buf) << buffer
Ben Clayton1a1b5272023-02-24 17:16:55 +00002038 << ".InterlockedCompareExchange(offset, compare, value, result.old_value);";
dan sinclair67a18932023-06-26 19:54:49 +00002039 Line(&buf) << "result.exchanged = result.old_value == compare;";
2040 Line(&buf) << "return result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002041
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002042 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002043 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002044 default:
2045 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002046 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002047
Ben Claytonf848af22023-07-28 16:37:32 +00002048 TINT_UNREACHABLE() << "unsupported atomic DecomposeMemoryAccess::Intrinsic::Op: "
2049 << static_cast<int>(intrinsic->op);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002050}
2051
dan sinclairbae54e72023-07-28 15:01:54 +00002052bool ASTPrinter::EmitWorkgroupAtomicCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002053 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002054 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002055 std::string result = UniqueIdentifier("atomic_result");
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002056
dan sinclaircedcdf32023-08-10 02:39:48 +00002057 if (!builtin->ReturnType()->Is<core::type::Void>()) {
dan sinclair67a18932023-06-26 19:54:49 +00002058 auto pre = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00002059 if (!EmitTypeAndName(pre, builtin->ReturnType(), core::AddressSpace::kUndefined,
2060 core::Access::kUndefined, result)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002061 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002062 }
2063 pre << " = ";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002064 if (!EmitZeroValue(pre, builtin->ReturnType())) {
2065 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002066 }
2067 pre << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002068 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002069
dan sinclair41e4d9a2022-05-01 14:40:55 +00002070 auto call = [&](const char* name) {
dan sinclair67a18932023-06-26 19:54:49 +00002071 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002072 pre << name;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002073
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002074 {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002075 ScopedParen sp(pre);
Ben Clayton783b1692022-08-02 17:03:35 +00002076 for (size_t i = 0; i < expr->args.Length(); i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002077 auto* arg = expr->args[i];
2078 if (i > 0) {
2079 pre << ", ";
2080 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002081 if (!EmitExpression(pre, arg)) {
2082 return false;
2083 }
2084 }
2085
2086 pre << ", " << result;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002087 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002088
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002089 pre << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002090
dan sinclair41e4d9a2022-05-01 14:40:55 +00002091 out << result;
2092 return true;
2093 };
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002094
Ben Claytond9766dc2023-09-21 12:41:20 +00002095 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002096 case wgsl::BuiltinFn::kAtomicLoad: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002097 // HLSL does not have an InterlockedLoad, so we emulate it with
2098 // InterlockedOr using 0 as the OR value
dan sinclair67a18932023-06-26 19:54:49 +00002099 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002100 pre << "InterlockedOr";
2101 {
2102 ScopedParen sp(pre);
2103 if (!EmitExpression(pre, expr->args[0])) {
2104 return false;
2105 }
2106 pre << ", 0, " << result;
2107 }
2108 pre << ";";
2109
2110 out << result;
2111 return true;
2112 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002113 case wgsl::BuiltinFn::kAtomicStore: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002114 // HLSL does not have an InterlockedStore, so we emulate it with
2115 // InterlockedExchange and discard the returned value
2116 { // T result = 0;
dan sinclair67a18932023-06-26 19:54:49 +00002117 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002118 auto* value_ty = builtin->Parameters()[1]->Type()->UnwrapRef();
Ben Claytoncd52f382023-08-07 13:11:08 +00002119 if (!EmitTypeAndName(pre, value_ty, core::AddressSpace::kUndefined,
2120 core::Access::kUndefined, result)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002121 return false;
2122 }
2123 pre << " = ";
2124 if (!EmitZeroValue(pre, value_ty)) {
2125 return false;
2126 }
2127 pre << ";";
2128 }
2129
2130 out << "InterlockedExchange";
2131 {
dan sinclairb2ba57b2023-02-28 15:14:09 +00002132 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002133 if (!EmitExpression(out, expr->args[0])) {
2134 return false;
2135 }
2136 out << ", ";
2137 if (!EmitExpression(out, expr->args[1])) {
2138 return false;
2139 }
2140 out << ", " << result;
2141 }
2142 return true;
2143 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002144 case wgsl::BuiltinFn::kAtomicCompareExchangeWeak: {
dan sinclaircedcdf32023-08-10 02:39:48 +00002145 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002146 return false;
2147 }
2148
dan sinclair41e4d9a2022-05-01 14:40:55 +00002149 auto* dest = expr->args[0];
2150 auto* compare_value = expr->args[1];
2151 auto* value = expr->args[2];
2152
2153 std::string compare = UniqueIdentifier("atomic_compare_value");
2154
2155 { // T compare_value = <compare_value>;
dan sinclair67a18932023-06-26 19:54:49 +00002156 auto pre = Line();
Antonio Maioranof99671b2022-06-23 13:14:54 +00002157 if (!EmitTypeAndName(pre, TypeOf(compare_value)->UnwrapRef(),
Ben Claytoncd52f382023-08-07 13:11:08 +00002158 core::AddressSpace::kUndefined, core::Access::kUndefined,
dan sinclair61c16eb2023-01-21 23:44:38 +00002159 compare)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002160 return false;
2161 }
2162 pre << " = ";
2163 if (!EmitExpression(pre, compare_value)) {
2164 return false;
2165 }
2166 pre << ";";
2167 }
2168
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002169 { // InterlockedCompareExchange(dst, compare, value, result.old_value);
dan sinclair67a18932023-06-26 19:54:49 +00002170 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002171 pre << "InterlockedCompareExchange";
2172 {
2173 ScopedParen sp(pre);
2174 if (!EmitExpression(pre, dest)) {
2175 return false;
2176 }
2177 pre << ", " << compare << ", ";
2178 if (!EmitExpression(pre, value)) {
2179 return false;
2180 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002181 pre << ", " << result << ".old_value";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002182 }
2183 pre << ";";
2184 }
2185
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002186 // result.exchanged = result.old_value == compare;
dan sinclair67a18932023-06-26 19:54:49 +00002187 Line() << result << ".exchanged = " << result << ".old_value == " << compare << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002188
2189 out << result;
2190 return true;
2191 }
2192
Ben Claytondfc815c2023-09-25 15:38:43 +00002193 case wgsl::BuiltinFn::kAtomicAdd:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002194 return call("InterlockedAdd");
2195
Stephen White42491242023-10-18 18:02:20 +00002196 case wgsl::BuiltinFn::kAtomicSub: {
2197 auto pre = Line();
2198 // Sub uses InterlockedAdd with the operand negated.
2199 pre << "InterlockedAdd";
2200 {
2201 ScopedParen sp(pre);
2202 TINT_ASSERT(expr->args.Length() == 2);
2203
2204 if (!EmitExpression(pre, expr->args[0])) {
2205 return false;
2206 }
2207 pre << ", -";
2208 {
2209 ScopedParen argSP(pre);
2210 if (!EmitExpression(pre, expr->args[1])) {
2211 return false;
2212 }
2213 }
2214
2215 pre << ", " << result;
2216 }
2217
2218 pre << ";";
2219
2220 out << result;
2221 }
2222 return true;
2223
Ben Claytondfc815c2023-09-25 15:38:43 +00002224 case wgsl::BuiltinFn::kAtomicMax:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002225 return call("InterlockedMax");
2226
Ben Claytondfc815c2023-09-25 15:38:43 +00002227 case wgsl::BuiltinFn::kAtomicMin:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002228 return call("InterlockedMin");
2229
Ben Claytondfc815c2023-09-25 15:38:43 +00002230 case wgsl::BuiltinFn::kAtomicAnd:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002231 return call("InterlockedAnd");
2232
Ben Claytondfc815c2023-09-25 15:38:43 +00002233 case wgsl::BuiltinFn::kAtomicOr:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002234 return call("InterlockedOr");
2235
Ben Claytondfc815c2023-09-25 15:38:43 +00002236 case wgsl::BuiltinFn::kAtomicXor:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002237 return call("InterlockedXor");
2238
Ben Claytondfc815c2023-09-25 15:38:43 +00002239 case wgsl::BuiltinFn::kAtomicExchange:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002240 return call("InterlockedExchange");
2241
2242 default:
2243 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002244 }
2245
Ben Claytond9766dc2023-09-21 12:41:20 +00002246 TINT_UNREACHABLE() << "unsupported atomic builtin: " << builtin->Fn();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002247}
2248
dan sinclairbae54e72023-07-28 15:01:54 +00002249bool ASTPrinter::EmitSelectCall(StringStream& out, const ast::CallExpression* expr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002250 auto* expr_false = expr->args[0];
2251 auto* expr_true = expr->args[1];
2252 auto* expr_cond = expr->args[2];
dan sinclairb2ba57b2023-02-28 15:14:09 +00002253 ScopedParen paren(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002254 if (!EmitExpression(out, expr_cond)) {
2255 return false;
2256 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002257
dan sinclair41e4d9a2022-05-01 14:40:55 +00002258 out << " ? ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002259
dan sinclair41e4d9a2022-05-01 14:40:55 +00002260 if (!EmitExpression(out, expr_true)) {
2261 return false;
2262 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002263
dan sinclair41e4d9a2022-05-01 14:40:55 +00002264 out << " : ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002265
dan sinclair41e4d9a2022-05-01 14:40:55 +00002266 if (!EmitExpression(out, expr_false)) {
2267 return false;
2268 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002269
dan sinclair41e4d9a2022-05-01 14:40:55 +00002270 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002271}
2272
dan sinclairbae54e72023-07-28 15:01:54 +00002273bool ASTPrinter::EmitModfCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002274 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002275 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002276 return CallBuiltinHelper(
2277 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2278 auto* ty = builtin->Parameters()[0]->Type();
2279 auto in = params[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002280
dan sinclair41e4d9a2022-05-01 14:40:55 +00002281 std::string width;
dan sinclaircedcdf32023-08-10 02:39:48 +00002282 if (auto* vec = ty->As<core::type::Vector>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002283 width = std::to_string(vec->Width());
2284 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002285
dan sinclair41e4d9a2022-05-01 14:40:55 +00002286 // Emit the builtin return type unique to this overload. This does not
2287 // exist in the AST, so it will not be generated in Generate().
dan sinclaircedcdf32023-08-10 02:39:48 +00002288 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002289 return false;
2290 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002291
dan sinclair41e4d9a2022-05-01 14:40:55 +00002292 {
dan sinclair67a18932023-06-26 19:54:49 +00002293 auto l = Line(b);
Ben Claytoncd52f382023-08-07 13:11:08 +00002294 if (!EmitType(l, builtin->ReturnType(), core::AddressSpace::kUndefined,
2295 core::Access::kUndefined, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002296 return false;
2297 }
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00002298 l << " result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002299 }
dan sinclair67a18932023-06-26 19:54:49 +00002300 Line(b) << "result.fract = modf(" << params[0] << ", result.whole);";
2301 Line(b) << "return result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002302 return true;
2303 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002304}
2305
dan sinclairbae54e72023-07-28 15:01:54 +00002306bool ASTPrinter::EmitFrexpCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002307 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002308 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002309 return CallBuiltinHelper(
2310 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2311 auto* ty = builtin->Parameters()[0]->Type();
2312 auto in = params[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002313
dan sinclair41e4d9a2022-05-01 14:40:55 +00002314 std::string width;
dan sinclaircedcdf32023-08-10 02:39:48 +00002315 if (auto* vec = ty->As<core::type::Vector>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002316 width = std::to_string(vec->Width());
2317 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002318
dan sinclair41e4d9a2022-05-01 14:40:55 +00002319 // Emit the builtin return type unique to this overload. This does not
2320 // exist in the AST, so it will not be generated in Generate().
dan sinclaircedcdf32023-08-10 02:39:48 +00002321 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002322 return false;
2323 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002324
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00002325 std::string member_type;
dan sinclaircedcdf32023-08-10 02:39:48 +00002326 if (Is<core::type::F16>(ty->DeepestElement())) {
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00002327 member_type = width.empty() ? "float16_t" : ("vector<float16_t, " + width + ">");
2328 } else {
2329 member_type = "float" + width;
2330 }
2331
dan sinclair67a18932023-06-26 19:54:49 +00002332 Line(b) << member_type << " exp;";
2333 Line(b) << member_type << " fract = sign(" << in << ") * frexp(" << in << ", exp);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002334 {
dan sinclair67a18932023-06-26 19:54:49 +00002335 auto l = Line(b);
Ben Claytoncd52f382023-08-07 13:11:08 +00002336 if (!EmitType(l, builtin->ReturnType(), core::AddressSpace::kUndefined,
2337 core::Access::kUndefined, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002338 return false;
2339 }
Ben Clayton10fae7a2022-11-14 15:29:29 +00002340 l << " result = {fract, int" << width << "(exp)};";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002341 }
dan sinclair67a18932023-06-26 19:54:49 +00002342 Line(b) << "return result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002343 return true;
2344 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002345}
2346
dan sinclairbae54e72023-07-28 15:01:54 +00002347bool ASTPrinter::EmitDegreesCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002348 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002349 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002350 return CallBuiltinHelper(out, expr, builtin,
2351 [&](TextBuffer* b, const std::vector<std::string>& params) {
dan sinclair67a18932023-06-26 19:54:49 +00002352 Line(b) << "return " << params[0] << " * " << std::setprecision(20)
dan sinclair41e4d9a2022-05-01 14:40:55 +00002353 << sem::kRadToDeg << ";";
2354 return true;
2355 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002356}
2357
dan sinclairbae54e72023-07-28 15:01:54 +00002358bool ASTPrinter::EmitRadiansCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002359 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002360 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002361 return CallBuiltinHelper(out, expr, builtin,
2362 [&](TextBuffer* b, const std::vector<std::string>& params) {
dan sinclair67a18932023-06-26 19:54:49 +00002363 Line(b) << "return " << params[0] << " * " << std::setprecision(20)
dan sinclair41e4d9a2022-05-01 14:40:55 +00002364 << sem::kDegToRad << ";";
2365 return true;
2366 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002367}
2368
dan sinclair70927862023-01-11 13:18:29 +00002369// The HLSL `sign` method always returns an `int` result (scalar or vector). In WGSL the result is
2370// expected to be the same type as the argument. This injects a cast to the expected WGSL result
2371// type after the call to `sign`.
Ben Claytond9766dc2023-09-21 12:41:20 +00002372bool ASTPrinter::EmitSignCall(StringStream& out, const sem::Call* call, const sem::BuiltinFn*) {
dan sinclair70927862023-01-11 13:18:29 +00002373 auto* arg = call->Arguments()[0];
Ben Claytoncd52f382023-08-07 13:11:08 +00002374 if (!EmitType(out, arg->Type(), core::AddressSpace::kUndefined, core::Access::kReadWrite, "")) {
dan sinclair70927862023-01-11 13:18:29 +00002375 return false;
2376 }
2377 out << "(sign(";
2378 if (!EmitExpression(out, arg->Declaration())) {
2379 return false;
2380 }
2381 out << "))";
2382 return true;
2383}
2384
dan sinclairbae54e72023-07-28 15:01:54 +00002385bool ASTPrinter::EmitQuantizeToF16Call(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 Maiorano51249b82023-03-31 08:00:33 +00002388 // Cast to f16 and back
Ben Clayton2bea9052022-11-02 00:09:50 +00002389 std::string width;
dan sinclaircedcdf32023-08-10 02:39:48 +00002390 if (auto* vec = builtin->ReturnType()->As<core::type::Vector>()) {
Ben Clayton2bea9052022-11-02 00:09:50 +00002391 width = std::to_string(vec->Width());
2392 }
dan sinclair260c1f02024-06-06 15:31:12 +00002393 out << "f16tof32(f32tof16(";
Ben Clayton2bea9052022-11-02 00:09:50 +00002394 if (!EmitExpression(out, expr->args[0])) {
2395 return false;
2396 }
2397 out << "))";
2398 return true;
2399}
2400
dan sinclairbae54e72023-07-28 15:01:54 +00002401bool ASTPrinter::EmitTruncCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002402 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002403 const sem::BuiltinFn* builtin) {
Antonio Maiorano5cf943e2023-03-27 18:55:25 +00002404 // HLSL's trunc is broken for very large/small float values.
2405 // See crbug.com/tint/1883
2406 return CallBuiltinHelper( //
2407 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2408 // value < 0 ? ceil(value) : floor(value)
dan sinclair67a18932023-06-26 19:54:49 +00002409 Line(b) << "return " << params[0] << " < 0 ? ceil(" << params[0] << ") : floor("
Antonio Maiorano5cf943e2023-03-27 18:55:25 +00002410 << params[0] << ");";
2411 return true;
2412 });
2413}
2414
dan sinclairbae54e72023-07-28 15:01:54 +00002415bool ASTPrinter::EmitDataPackingCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002416 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002417 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002418 return CallBuiltinHelper(
2419 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2420 uint32_t dims = 2;
2421 bool is_signed = false;
2422 uint32_t scale = 65535;
Ben Claytondfc815c2023-09-25 15:38:43 +00002423 if (builtin->Fn() == wgsl::BuiltinFn::kPack4X8Snorm ||
2424 builtin->Fn() == wgsl::BuiltinFn::kPack4X8Unorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002425 dims = 4;
2426 scale = 255;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002427 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002428 if (builtin->Fn() == wgsl::BuiltinFn::kPack4X8Snorm ||
2429 builtin->Fn() == wgsl::BuiltinFn::kPack2X16Snorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002430 is_signed = true;
2431 scale = (scale - 1) / 2;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002432 }
Ben Claytond9766dc2023-09-21 12:41:20 +00002433 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002434 case wgsl::BuiltinFn::kPack4X8Snorm:
2435 case wgsl::BuiltinFn::kPack4X8Unorm:
2436 case wgsl::BuiltinFn::kPack2X16Snorm:
2437 case wgsl::BuiltinFn::kPack2X16Unorm: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002438 {
dan sinclair67a18932023-06-26 19:54:49 +00002439 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002440 l << (is_signed ? "" : "u") << "int" << dims
2441 << " i = " << (is_signed ? "" : "u") << "int" << dims << "(round(clamp("
2442 << params[0] << ", " << (is_signed ? "-1.0" : "0.0") << ", 1.0) * "
2443 << scale << ".0))";
2444 if (is_signed) {
2445 l << " & " << (dims == 4 ? "0xff" : "0xffff");
2446 }
2447 l << ";";
2448 }
2449 {
dan sinclair67a18932023-06-26 19:54:49 +00002450 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002451 l << "return ";
2452 if (is_signed) {
2453 l << "asuint";
2454 }
2455 l << "(i.x | i.y << " << (32 / dims);
2456 if (dims == 4) {
2457 l << " | i.z << 16 | i.w << 24";
2458 }
2459 l << ");";
2460 }
2461 break;
2462 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002463 case wgsl::BuiltinFn::kPack2X16Float: {
dan sinclair67a18932023-06-26 19:54:49 +00002464 Line(b) << "uint2 i = f32tof16(" << params[0] << ");";
2465 Line(b) << "return i.x | (i.y << 16);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002466 break;
2467 }
2468 default:
Ben Claytonc27315a2024-02-26 20:24:06 +00002469 TINT_ICE() << " unhandled data packing builtin";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002470 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002471
dan sinclair41e4d9a2022-05-01 14:40:55 +00002472 return true;
2473 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002474}
2475
dan sinclairbae54e72023-07-28 15:01:54 +00002476bool ASTPrinter::EmitDataUnpackingCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002477 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002478 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002479 return CallBuiltinHelper(
2480 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2481 uint32_t dims = 2;
2482 bool is_signed = false;
2483 uint32_t scale = 65535;
Ben Claytondfc815c2023-09-25 15:38:43 +00002484 if (builtin->Fn() == wgsl::BuiltinFn::kUnpack4X8Snorm ||
2485 builtin->Fn() == wgsl::BuiltinFn::kUnpack4X8Unorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002486 dims = 4;
2487 scale = 255;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002488 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002489 if (builtin->Fn() == wgsl::BuiltinFn::kUnpack4X8Snorm ||
2490 builtin->Fn() == wgsl::BuiltinFn::kUnpack2X16Snorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002491 is_signed = true;
2492 scale = (scale - 1) / 2;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002493 }
Ben Claytond9766dc2023-09-21 12:41:20 +00002494 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002495 case wgsl::BuiltinFn::kUnpack4X8Snorm:
2496 case wgsl::BuiltinFn::kUnpack2X16Snorm: {
dan sinclair67a18932023-06-26 19:54:49 +00002497 Line(b) << "int j = int(" << params[0] << ");";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002498 { // Perform sign extension on the converted values.
dan sinclair67a18932023-06-26 19:54:49 +00002499 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002500 l << "int" << dims << " i = int" << dims << "(";
2501 if (dims == 2) {
2502 l << "j << 16, j) >> 16";
2503 } else {
2504 l << "j << 24, j << 16, j << 8, j) >> 24";
2505 }
2506 l << ";";
2507 }
dan sinclair67a18932023-06-26 19:54:49 +00002508 Line(b) << "return clamp(float" << dims << "(i) / " << scale << ".0, "
dan sinclair41e4d9a2022-05-01 14:40:55 +00002509 << (is_signed ? "-1.0" : "0.0") << ", 1.0);";
2510 break;
2511 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002512 case wgsl::BuiltinFn::kUnpack4X8Unorm:
2513 case wgsl::BuiltinFn::kUnpack2X16Unorm: {
dan sinclair67a18932023-06-26 19:54:49 +00002514 Line(b) << "uint j = " << params[0] << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002515 {
dan sinclair67a18932023-06-26 19:54:49 +00002516 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002517 l << "uint" << dims << " i = uint" << dims << "(";
2518 l << "j & " << (dims == 2 ? "0xffff" : "0xff") << ", ";
2519 if (dims == 4) {
2520 l << "(j >> " << (32 / dims) << ") & 0xff, (j >> 16) & 0xff, j >> 24";
2521 } else {
2522 l << "j >> " << (32 / dims);
2523 }
2524 l << ");";
2525 }
dan sinclair67a18932023-06-26 19:54:49 +00002526 Line(b) << "return float" << dims << "(i) / " << scale << ".0;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002527 break;
2528 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002529 case wgsl::BuiltinFn::kUnpack2X16Float:
dan sinclair67a18932023-06-26 19:54:49 +00002530 Line(b) << "uint i = " << params[0] << ";";
2531 Line(b) << "return f16tof32(uint2(i & 0xffff, i >> 16));";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002532 break;
2533 default:
Ben Claytonc27315a2024-02-26 20:24:06 +00002534 TINT_ICE() << "unhandled data packing builtin";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002535 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002536
dan sinclair41e4d9a2022-05-01 14:40:55 +00002537 return true;
2538 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002539}
2540
Jiawei Shao53c0fa92023-12-08 01:24:00 +00002541bool ASTPrinter::EmitPacked4x8IntegerDotProductBuiltinCall(StringStream& out,
2542 const ast::CallExpression* expr,
2543 const sem::BuiltinFn* builtin) {
Jiawei Shaodd817232024-01-12 08:10:46 +00002544 switch (builtin->Fn()) {
2545 case wgsl::BuiltinFn::kDot4I8Packed:
2546 case wgsl::BuiltinFn::kDot4U8Packed:
2547 break;
2548 case wgsl::BuiltinFn::kPack4XI8: {
2549 out << "uint(pack_s8(";
2550 if (!EmitExpression(out, expr->args[0])) {
2551 return false;
2552 }
2553 out << "))";
2554 return true;
2555 }
2556 case wgsl::BuiltinFn::kPack4XU8: {
2557 out << "uint(pack_u8(";
2558 if (!EmitExpression(out, expr->args[0])) {
2559 return false;
2560 }
2561 out << "))";
2562 return true;
2563 }
2564 case wgsl::BuiltinFn::kPack4XI8Clamp: {
2565 out << "uint(pack_clamp_s8(";
2566 if (!EmitExpression(out, expr->args[0])) {
2567 return false;
2568 }
2569 out << "))";
2570 return true;
2571 }
2572 case wgsl::BuiltinFn::kUnpack4XI8: {
2573 out << "unpack_s8s32(int8_t4_packed(";
2574 if (!EmitExpression(out, expr->args[0])) {
2575 return false;
2576 }
2577 out << "))";
2578 return true;
2579 }
2580 case wgsl::BuiltinFn::kUnpack4XU8: {
2581 out << "unpack_u8u32(uint8_t4_packed(";
2582 if (!EmitExpression(out, expr->args[0])) {
2583 return false;
2584 }
2585 out << "))";
2586 return true;
2587 }
2588 case wgsl::BuiltinFn::kPack4XU8Clamp:
2589 default:
2590 TINT_UNIMPLEMENTED() << builtin->Fn();
Jiawei Shaodd817232024-01-12 08:10:46 +00002591 }
2592
Ben Claytonc27315a2024-02-26 20:24:06 +00002593 return CallBuiltinHelper(out, expr, builtin,
2594 [&](TextBuffer* b, const std::vector<std::string>& params) {
2595 std::string functionName;
2596 switch (builtin->Fn()) {
2597 case wgsl::BuiltinFn::kDot4I8Packed:
2598 Line(b) << "int accumulator = 0;";
2599 functionName = "dot4add_i8packed";
2600 break;
2601 case wgsl::BuiltinFn::kDot4U8Packed:
2602 Line(b) << "uint accumulator = 0u;";
2603 functionName = "dot4add_u8packed";
2604 break;
2605 default:
2606 TINT_ICE() << "Internal error: unhandled DP4a builtin";
Ben Claytonc27315a2024-02-26 20:24:06 +00002607 }
2608 Line(b) << "return " << functionName << "(" << params[0] << ", "
2609 << params[1] << ", accumulator);";
Jiawei Shaoab975702022-05-13 00:09:56 +00002610
Ben Claytonc27315a2024-02-26 20:24:06 +00002611 return true;
2612 });
Jiawei Shaoab975702022-05-13 00:09:56 +00002613}
2614
Ben Claytond9766dc2023-09-21 12:41:20 +00002615bool ASTPrinter::EmitBarrierCall(StringStream& out, const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002616 // TODO(crbug.com/tint/661): Combine sequential barriers to a single
2617 // instruction.
Ben Claytondfc815c2023-09-25 15:38:43 +00002618 if (builtin->Fn() == wgsl::BuiltinFn::kWorkgroupBarrier) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002619 out << "GroupMemoryBarrierWithGroupSync()";
Ben Claytondfc815c2023-09-25 15:38:43 +00002620 } else if (builtin->Fn() == wgsl::BuiltinFn::kStorageBarrier) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002621 out << "DeviceMemoryBarrierWithGroupSync()";
Ben Claytondfc815c2023-09-25 15:38:43 +00002622 } else if (builtin->Fn() == wgsl::BuiltinFn::kTextureBarrier) {
James Pricea6287df2023-08-11 00:45:54 +00002623 out << "DeviceMemoryBarrierWithGroupSync()";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002624 } else {
Ben Claytondfc815c2023-09-25 15:38:43 +00002625 TINT_UNREACHABLE() << "unexpected barrier builtin type " << builtin->Fn();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002626 }
2627 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002628}
2629
Antonio Maioranod1368d72023-09-05 20:29:56 +00002630bool ASTPrinter::EmitTextureOrStorageBufferCallArgExpression(StringStream& out,
2631 const ast::Expression* expr) {
2632 // TODO(crbug.com/tint/1976): Workaround DXC bug that fails to compile texture/storage function
2633 // calls with signed integer splatted constants. DXC fails to convert the coord arg, for e.g.
2634 // `0.xxx`, from a vector of 64-bit ints to a vector of 32-bit ints to match the texture load
2635 // parameter type. We work around this for now by explicitly casting the splatted constant to
2636 // the right type, for e.g. `int3(0.xxx)`.
2637 bool emitted_cast = false;
2638 if (auto* sem = builder_.Sem().GetVal(expr)) {
2639 if (auto* constant = sem->ConstantValue()) {
2640 if (auto* splat = constant->As<core::constant::Splat>()) {
2641 if (splat->Type()->is_signed_integer_vector()) {
2642 if (!EmitType(out, constant->Type(), core::AddressSpace::kUndefined,
2643 core::Access::kUndefined, "")) {
2644 return false;
2645 }
2646 out << "(";
2647 emitted_cast = true;
2648 }
2649 }
2650 }
2651 }
2652 if (!EmitExpression(out, expr)) {
2653 return false;
2654 }
2655 if (emitted_cast) {
2656 out << ")";
2657 }
2658 return true;
2659}
2660
dan sinclairbae54e72023-07-28 15:01:54 +00002661bool ASTPrinter::EmitTextureCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002662 const sem::Call* call,
Ben Claytond9766dc2023-09-21 12:41:20 +00002663 const sem::BuiltinFn* builtin) {
Ben Clayton015fbe72023-08-09 07:46:44 +00002664 using Usage = core::ParameterUsage;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002665
dan sinclair41e4d9a2022-05-01 14:40:55 +00002666 auto& signature = builtin->Signature();
2667 auto* expr = call->Declaration();
2668 auto arguments = expr->args;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002669
dan sinclair41e4d9a2022-05-01 14:40:55 +00002670 // Returns the argument with the given usage
2671 auto arg = [&](Usage usage) {
2672 int idx = signature.IndexOf(usage);
dan sinclair3a2a2792022-06-29 14:38:15 +00002673 return (idx >= 0) ? arguments[static_cast<size_t>(idx)] : nullptr;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002674 };
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002675
dan sinclair41e4d9a2022-05-01 14:40:55 +00002676 auto* texture = arg(Usage::kTexture);
Ben Clayton884f9522023-01-12 22:52:57 +00002677 if (TINT_UNLIKELY(!texture)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002678 TINT_ICE() << "missing texture argument";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002679 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002680
dan sinclaircedcdf32023-08-10 02:39:48 +00002681 auto* texture_type = TypeOf(texture)->UnwrapRef()->As<core::type::Texture>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002682
Ben Claytond9766dc2023-09-21 12:41:20 +00002683 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002684 case wgsl::BuiltinFn::kTextureDimensions:
2685 case wgsl::BuiltinFn::kTextureNumLayers:
2686 case wgsl::BuiltinFn::kTextureNumLevels:
2687 case wgsl::BuiltinFn::kTextureNumSamples: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002688 // All of these builtins use the GetDimensions() method on the texture
dan sinclaircedcdf32023-08-10 02:39:48 +00002689 bool is_ms = texture_type->IsAnyOf<core::type::MultisampledTexture,
2690 core::type::DepthMultisampledTexture>();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002691 int num_dimensions = 0;
2692 std::string swizzle;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002693
Ben Claytond9766dc2023-09-21 12:41:20 +00002694 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002695 case wgsl::BuiltinFn::kTextureDimensions:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002696 switch (texture_type->dim()) {
dan sinclaircedcdf32023-08-10 02:39:48 +00002697 case core::type::TextureDimension::kNone:
Ben Claytonf848af22023-07-28 16:37:32 +00002698 TINT_ICE() << "texture dimension is kNone";
dan sinclaircedcdf32023-08-10 02:39:48 +00002699 case core::type::TextureDimension::k1d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002700 num_dimensions = 1;
2701 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002702 case core::type::TextureDimension::k2d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002703 num_dimensions = is_ms ? 3 : 2;
2704 swizzle = is_ms ? ".xy" : "";
2705 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002706 case core::type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002707 num_dimensions = is_ms ? 4 : 3;
2708 swizzle = ".xy";
2709 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002710 case core::type::TextureDimension::k3d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002711 num_dimensions = 3;
2712 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002713 case core::type::TextureDimension::kCube:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002714 num_dimensions = 2;
2715 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002716 case core::type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002717 num_dimensions = 3;
2718 swizzle = ".xy";
2719 break;
2720 }
2721 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002722 case wgsl::BuiltinFn::kTextureNumLayers:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002723 switch (texture_type->dim()) {
2724 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002725 TINT_ICE() << "texture dimension is not arrayed";
dan sinclaircedcdf32023-08-10 02:39:48 +00002726 case core::type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002727 num_dimensions = is_ms ? 4 : 3;
2728 swizzle = ".z";
2729 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002730 case core::type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002731 num_dimensions = 3;
2732 swizzle = ".z";
2733 break;
2734 }
2735 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002736 case wgsl::BuiltinFn::kTextureNumLevels:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002737 switch (texture_type->dim()) {
2738 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002739 TINT_ICE() << "texture dimension does not support mips";
dan sinclaircedcdf32023-08-10 02:39:48 +00002740 case core::type::TextureDimension::k1d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002741 num_dimensions = 2;
2742 swizzle = ".y";
2743 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002744 case core::type::TextureDimension::k2d:
2745 case core::type::TextureDimension::kCube:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002746 num_dimensions = 3;
2747 swizzle = ".z";
2748 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002749 case core::type::TextureDimension::k2dArray:
2750 case core::type::TextureDimension::k3d:
2751 case core::type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002752 num_dimensions = 4;
2753 swizzle = ".w";
2754 break;
2755 }
2756 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002757 case wgsl::BuiltinFn::kTextureNumSamples:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002758 switch (texture_type->dim()) {
2759 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002760 TINT_ICE() << "texture dimension does not support multisampling";
dan sinclaircedcdf32023-08-10 02:39:48 +00002761 case core::type::TextureDimension::k2d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002762 num_dimensions = 3;
2763 swizzle = ".z";
2764 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002765 case core::type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002766 num_dimensions = 4;
2767 swizzle = ".w";
2768 break;
2769 }
2770 break;
2771 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002772 TINT_ICE() << "unexpected builtin";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002773 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002774
2775 auto* level_arg = arg(Usage::kLevel);
2776
2777 if (level_arg) {
2778 // `NumberOfLevels` is a non-optional argument if `MipLevel` was passed.
2779 // Increment the number of dimensions for the temporary vector to
2780 // accommodate this.
2781 num_dimensions++;
2782
2783 // If the swizzle was empty, the expression will evaluate to the whole
2784 // vector. As we've grown the vector by one element, we now need to
2785 // swizzle to keep the result expression equivalent.
2786 if (swizzle.empty()) {
2787 static constexpr const char* swizzles[] = {"", ".x", ".xy", ".xyz"};
2788 swizzle = swizzles[num_dimensions - 1];
2789 }
2790 }
2791
Ben Clayton884f9522023-01-12 22:52:57 +00002792 if (TINT_UNLIKELY(num_dimensions > 4)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002793 TINT_ICE() << "Texture query builtin temporary vector has " << num_dimensions
2794 << " dimensions";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002795 }
2796
2797 // Declare a variable to hold the queried texture info
2798 auto dims = UniqueIdentifier(kTempNamePrefix);
2799 if (num_dimensions == 1) {
dan sinclair67a18932023-06-26 19:54:49 +00002800 Line() << "uint " << dims << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002801 } else {
dan sinclair67a18932023-06-26 19:54:49 +00002802 Line() << "uint" << num_dimensions << " " << dims << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002803 }
2804
2805 { // texture.GetDimensions(...)
dan sinclair67a18932023-06-26 19:54:49 +00002806 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002807 if (!EmitExpression(pre, texture)) {
2808 return false;
2809 }
2810 pre << ".GetDimensions(";
2811
2812 if (level_arg) {
2813 if (!EmitExpression(pre, level_arg)) {
2814 return false;
2815 }
2816 pre << ", ";
Ben Claytondfc815c2023-09-25 15:38:43 +00002817 } else if (builtin->Fn() == wgsl::BuiltinFn::kTextureNumLevels) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002818 pre << "0, ";
2819 }
2820
2821 if (num_dimensions == 1) {
2822 pre << dims;
2823 } else {
2824 static constexpr char xyzw[] = {'x', 'y', 'z', 'w'};
Ben Clayton884f9522023-01-12 22:52:57 +00002825 if (TINT_UNLIKELY(num_dimensions < 0 || num_dimensions > 4)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002826 TINT_ICE() << "vector dimensions are " << num_dimensions;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002827 }
2828 for (int i = 0; i < num_dimensions; i++) {
2829 if (i > 0) {
2830 pre << ", ";
2831 }
2832 pre << dims << "." << xyzw[i];
2833 }
2834 }
2835
2836 pre << ");";
2837 }
2838
2839 // The out parameters of the GetDimensions() call is now in temporary
2840 // `dims` variable. This may be packed with other data, so the final
2841 // expression may require a swizzle.
2842 out << dims << swizzle;
2843 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002844 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002845 default:
2846 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002847 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002848
Austin Eng86a617f2022-05-19 20:08:19 +00002849 if (!EmitExpression(out, texture)) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002850 return false;
Austin Eng86a617f2022-05-19 20:08:19 +00002851 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002852
2853 // If pack_level_in_coords is true, then the mip level will be appended as the
2854 // last value of the coordinates argument. If the WGSL builtin overload does
2855 // not have a level parameter and pack_level_in_coords is true, then a zero
2856 // mip level will be inserted.
2857 bool pack_level_in_coords = false;
2858
2859 uint32_t hlsl_ret_width = 4u;
2860
Ben Claytond9766dc2023-09-21 12:41:20 +00002861 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002862 case wgsl::BuiltinFn::kTextureSample:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002863 out << ".Sample(";
2864 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002865 case wgsl::BuiltinFn::kTextureSampleBias:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002866 out << ".SampleBias(";
2867 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002868 case wgsl::BuiltinFn::kTextureSampleLevel:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002869 out << ".SampleLevel(";
2870 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002871 case wgsl::BuiltinFn::kTextureSampleGrad:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002872 out << ".SampleGrad(";
2873 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002874 case wgsl::BuiltinFn::kTextureSampleCompare:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002875 out << ".SampleCmp(";
2876 hlsl_ret_width = 1;
2877 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002878 case wgsl::BuiltinFn::kTextureSampleCompareLevel:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002879 out << ".SampleCmpLevelZero(";
2880 hlsl_ret_width = 1;
2881 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002882 case wgsl::BuiltinFn::kTextureLoad:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002883 out << ".Load(";
Jiawei Shaoa54554a2023-11-13 08:24:12 +00002884 // Multisampled textures and read-write storage textures do not support mip-levels.
2885 if (texture_type->Is<core::type::MultisampledTexture>()) {
2886 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002887 }
Jiawei Shaoa54554a2023-11-13 08:24:12 +00002888 if (auto* storage_texture_type = texture_type->As<core::type::StorageTexture>()) {
2889 if (storage_texture_type->access() == core::Access::kReadWrite) {
2890 break;
2891 }
2892 }
2893 pack_level_in_coords = true;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002894 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002895 case wgsl::BuiltinFn::kTextureGather:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002896 out << ".Gather";
Ben Clayton015fbe72023-08-09 07:46:44 +00002897 if (builtin->Parameters()[0]->Usage() == core::ParameterUsage::kComponent) {
dan sinclair5addefb2022-12-14 20:46:32 +00002898 switch (call->Arguments()[0]->ConstantValue()->ValueAs<AInt>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002899 case 0:
2900 out << "Red";
2901 break;
2902 case 1:
2903 out << "Green";
2904 break;
2905 case 2:
2906 out << "Blue";
2907 break;
2908 case 3:
2909 out << "Alpha";
2910 break;
2911 }
2912 }
2913 out << "(";
2914 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002915 case wgsl::BuiltinFn::kTextureGatherCompare:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002916 out << ".GatherCmp(";
2917 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002918 case wgsl::BuiltinFn::kTextureStore:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002919 out << "[";
2920 break;
2921 default:
Ben Claytonc27315a2024-02-26 20:24:06 +00002922 TINT_ICE() << "Unhandled texture builtin '" << builtin << "'";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002923 }
2924
2925 if (auto* sampler = arg(Usage::kSampler)) {
Austin Eng86a617f2022-05-19 20:08:19 +00002926 if (!EmitExpression(out, sampler)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002927 return false;
Austin Eng86a617f2022-05-19 20:08:19 +00002928 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002929 out << ", ";
2930 }
2931
2932 auto* param_coords = arg(Usage::kCoords);
Ben Clayton884f9522023-01-12 22:52:57 +00002933 if (TINT_UNLIKELY(!param_coords)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002934 TINT_ICE() << "missing coords argument";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002935 }
2936
2937 auto emit_vector_appended_with_i32_zero = [&](const ast::Expression* vector) {
dan sinclaircedcdf32023-08-10 02:39:48 +00002938 auto* i32 = builder_.create<core::type::I32>();
Ben Clayton0ce9ab02022-05-05 20:23:40 +00002939 auto* zero = builder_.Expr(0_i);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002940 auto* stmt = builder_.Sem().Get(vector)->Stmt();
Ben Clayton3fb9a3f2023-02-04 21:20:26 +00002941 builder_.Sem().Add(zero, builder_.create<sem::ValueExpression>(
Ben Clayton36c61552023-08-08 07:58:19 +00002942 zero, i32, core::EvaluationStage::kRuntime, stmt,
Ben Clayton3fb9a3f2023-02-04 21:20:26 +00002943 /* constant_value */ nullptr,
2944 /* has_side_effects */ false));
Ben Clayton2550b492023-10-11 10:41:12 +00002945 auto* packed = tint::wgsl::AppendVector(&builder_, vector, zero);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002946 return EmitExpression(out, packed->Declaration());
2947 };
2948
2949 auto emit_vector_appended_with_level = [&](const ast::Expression* vector) {
2950 if (auto* level = arg(Usage::kLevel)) {
Ben Clayton2550b492023-10-11 10:41:12 +00002951 auto* packed = tint::wgsl::AppendVector(&builder_, vector, level);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002952 return EmitExpression(out, packed->Declaration());
2953 }
2954 return emit_vector_appended_with_i32_zero(vector);
2955 };
2956
2957 if (auto* array_index = arg(Usage::kArrayIndex)) {
2958 // Array index needs to be appended to the coordinates.
Ben Clayton2550b492023-10-11 10:41:12 +00002959 auto* packed = tint::wgsl::AppendVector(&builder_, param_coords, array_index);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002960 if (pack_level_in_coords) {
2961 // Then mip level needs to be appended to the coordinates.
2962 if (!emit_vector_appended_with_level(packed->Declaration())) {
2963 return false;
2964 }
2965 } else {
2966 if (!EmitExpression(out, packed->Declaration())) {
2967 return false;
2968 }
2969 }
2970 } else if (pack_level_in_coords) {
2971 // Mip level needs to be appended to the coordinates.
2972 if (!emit_vector_appended_with_level(param_coords)) {
2973 return false;
2974 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002975 } else if (builtin->Fn() == wgsl::BuiltinFn::kTextureStore) {
Antonio Maioranob0dfccd2023-08-30 14:49:42 +00002976 // param_coords is an index expression, not a function arg
dan sinclair41e4d9a2022-05-01 14:40:55 +00002977 if (!EmitExpression(out, param_coords)) {
2978 return false;
2979 }
Antonio Maioranod1368d72023-09-05 20:29:56 +00002980 } else if (!EmitTextureOrStorageBufferCallArgExpression(out, param_coords)) {
Antonio Maioranob0dfccd2023-08-30 14:49:42 +00002981 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002982 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002983
dan sinclair41e4d9a2022-05-01 14:40:55 +00002984 for (auto usage : {Usage::kDepthRef, Usage::kBias, Usage::kLevel, Usage::kDdx, Usage::kDdy,
2985 Usage::kSampleIndex, Usage::kOffset}) {
2986 if (usage == Usage::kLevel && pack_level_in_coords) {
2987 continue; // mip level already packed in coordinates.
2988 }
2989 if (auto* e = arg(usage)) {
2990 out << ", ";
Antonio Maioranod1368d72023-09-05 20:29:56 +00002991 if (!EmitTextureOrStorageBufferCallArgExpression(out, e)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002992 return false;
2993 }
2994 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002995 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002996
Ben Claytondfc815c2023-09-25 15:38:43 +00002997 if (builtin->Fn() == wgsl::BuiltinFn::kTextureStore) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002998 out << "] = ";
2999 if (!EmitExpression(out, arg(Usage::kValue))) {
3000 return false;
3001 }
3002 } else {
3003 out << ")";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003004
dan sinclair41e4d9a2022-05-01 14:40:55 +00003005 // If the builtin return type does not match the number of elements of the
3006 // HLSL builtin, we need to swizzle the expression to generate the correct
3007 // number of components.
3008 uint32_t wgsl_ret_width = 1;
dan sinclaircedcdf32023-08-10 02:39:48 +00003009 if (auto* vec = builtin->ReturnType()->As<core::type::Vector>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003010 wgsl_ret_width = vec->Width();
3011 }
3012 if (wgsl_ret_width < hlsl_ret_width) {
3013 out << ".";
3014 for (uint32_t i = 0; i < wgsl_ret_width; i++) {
3015 out << "xyz"[i];
3016 }
3017 }
Ben Clayton884f9522023-01-12 22:52:57 +00003018 if (TINT_UNLIKELY(wgsl_ret_width > hlsl_ret_width)) {
Ben Claytonf848af22023-07-28 16:37:32 +00003019 TINT_ICE() << "WGSL return width (" << wgsl_ret_width
3020 << ") is wider than HLSL return width (" << hlsl_ret_width << ") for "
Ben Claytond9766dc2023-09-21 12:41:20 +00003021 << builtin->Fn();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003022 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003023 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003024
dan sinclair41e4d9a2022-05-01 14:40:55 +00003025 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003026}
3027
Natalie Chouinarddbefda92024-08-13 19:10:21 +00003028// The following subgroup builtin functions are translated to HLSL as follows:
3029// +---------------------+----------------------------------------------------------------+
3030// | WGSL | HLSL |
3031// +---------------------+----------------------------------------------------------------+
3032// | subgroupShuffleXor | WaveReadLaneAt with index equal subgroup_invocation_id ^ mask |
3033// | subgroupShuffleUp | WaveReadLaneAt with index equal subgroup_invocation_id - delta |
3034// | subgroupShuffleDown | WaveReadLaneAt with index equal subgroup_invocation_id + delta |
3035// +---------------------+----------------------------------------------------------------+
3036bool ASTPrinter::EmitSubgroupShuffleBuiltinCall(StringStream& out,
3037 const ast::CallExpression* expr,
3038 const sem::BuiltinFn* builtin) {
3039 out << "WaveReadLaneAt(";
3040
3041 if (!EmitExpression(out, expr->args[0])) {
3042 return false;
3043 }
3044
3045 out << ", (WaveGetLaneIndex() ";
3046
3047 switch (builtin->Fn()) {
3048 case wgsl::BuiltinFn::kSubgroupShuffleXor:
3049 out << "^ ";
3050 break;
3051 case wgsl::BuiltinFn::kSubgroupShuffleUp:
3052 out << "- ";
3053 break;
3054 case wgsl::BuiltinFn::kSubgroupShuffleDown:
3055 out << "+ ";
3056 break;
3057 default:
3058 TINT_UNREACHABLE();
3059 }
3060
3061 if (!EmitExpression(out, expr->args[1])) {
3062 return false;
3063 }
3064 out << "))";
3065
3066 return true;
3067}
3068
Ben Claytond9766dc2023-09-21 12:41:20 +00003069std::string ASTPrinter::generate_builtin_name(const sem::BuiltinFn* builtin) {
3070 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00003071 case wgsl::BuiltinFn::kAbs:
3072 case wgsl::BuiltinFn::kAcos:
3073 case wgsl::BuiltinFn::kAll:
3074 case wgsl::BuiltinFn::kAny:
3075 case wgsl::BuiltinFn::kAsin:
3076 case wgsl::BuiltinFn::kAtan:
3077 case wgsl::BuiltinFn::kAtan2:
3078 case wgsl::BuiltinFn::kCeil:
3079 case wgsl::BuiltinFn::kClamp:
3080 case wgsl::BuiltinFn::kCos:
3081 case wgsl::BuiltinFn::kCosh:
3082 case wgsl::BuiltinFn::kCross:
3083 case wgsl::BuiltinFn::kDeterminant:
3084 case wgsl::BuiltinFn::kDistance:
3085 case wgsl::BuiltinFn::kDot:
3086 case wgsl::BuiltinFn::kExp:
3087 case wgsl::BuiltinFn::kExp2:
3088 case wgsl::BuiltinFn::kFloor:
3089 case wgsl::BuiltinFn::kFrexp:
3090 case wgsl::BuiltinFn::kLdexp:
3091 case wgsl::BuiltinFn::kLength:
3092 case wgsl::BuiltinFn::kLog:
3093 case wgsl::BuiltinFn::kLog2:
3094 case wgsl::BuiltinFn::kMax:
3095 case wgsl::BuiltinFn::kMin:
3096 case wgsl::BuiltinFn::kModf:
3097 case wgsl::BuiltinFn::kNormalize:
3098 case wgsl::BuiltinFn::kPow:
3099 case wgsl::BuiltinFn::kReflect:
3100 case wgsl::BuiltinFn::kRefract:
3101 case wgsl::BuiltinFn::kRound:
3102 case wgsl::BuiltinFn::kSaturate:
3103 case wgsl::BuiltinFn::kSin:
3104 case wgsl::BuiltinFn::kSinh:
3105 case wgsl::BuiltinFn::kSqrt:
3106 case wgsl::BuiltinFn::kStep:
3107 case wgsl::BuiltinFn::kTan:
3108 case wgsl::BuiltinFn::kTanh:
3109 case wgsl::BuiltinFn::kTranspose:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003110 return builtin->str();
Ben Claytondfc815c2023-09-25 15:38:43 +00003111 case wgsl::BuiltinFn::kCountOneBits: // uint
dan sinclair41e4d9a2022-05-01 14:40:55 +00003112 return "countbits";
Ben Claytondfc815c2023-09-25 15:38:43 +00003113 case wgsl::BuiltinFn::kDpdx:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003114 return "ddx";
Ben Claytondfc815c2023-09-25 15:38:43 +00003115 case wgsl::BuiltinFn::kDpdxCoarse:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003116 return "ddx_coarse";
Ben Claytondfc815c2023-09-25 15:38:43 +00003117 case wgsl::BuiltinFn::kDpdxFine:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003118 return "ddx_fine";
Ben Claytondfc815c2023-09-25 15:38:43 +00003119 case wgsl::BuiltinFn::kDpdy:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003120 return "ddy";
Ben Claytondfc815c2023-09-25 15:38:43 +00003121 case wgsl::BuiltinFn::kDpdyCoarse:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003122 return "ddy_coarse";
Ben Claytondfc815c2023-09-25 15:38:43 +00003123 case wgsl::BuiltinFn::kDpdyFine:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003124 return "ddy_fine";
Ben Claytondfc815c2023-09-25 15:38:43 +00003125 case wgsl::BuiltinFn::kFaceForward:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003126 return "faceforward";
Ben Claytondfc815c2023-09-25 15:38:43 +00003127 case wgsl::BuiltinFn::kFract:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003128 return "frac";
Ben Claytondfc815c2023-09-25 15:38:43 +00003129 case wgsl::BuiltinFn::kFma:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003130 return "mad";
Ben Claytondfc815c2023-09-25 15:38:43 +00003131 case wgsl::BuiltinFn::kFwidth:
3132 case wgsl::BuiltinFn::kFwidthCoarse:
3133 case wgsl::BuiltinFn::kFwidthFine:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003134 return "fwidth";
Ben Claytondfc815c2023-09-25 15:38:43 +00003135 case wgsl::BuiltinFn::kInverseSqrt:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003136 return "rsqrt";
Ben Claytondfc815c2023-09-25 15:38:43 +00003137 case wgsl::BuiltinFn::kMix:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003138 return "lerp";
Ben Claytondfc815c2023-09-25 15:38:43 +00003139 case wgsl::BuiltinFn::kReverseBits: // uint
dan sinclair41e4d9a2022-05-01 14:40:55 +00003140 return "reversebits";
Ben Claytondfc815c2023-09-25 15:38:43 +00003141 case wgsl::BuiltinFn::kSmoothstep:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003142 return "smoothstep";
James Priceb2b6ade2024-07-18 18:06:30 +00003143 case wgsl::BuiltinFn::kSubgroupBallot:
3144 return "WaveActiveBallot";
Natalie Chouinardf15cc072024-08-09 20:50:15 +00003145 case wgsl::BuiltinFn::kSubgroupElect:
3146 return "WaveIsFirstLane";
Ben Claytondfc815c2023-09-25 15:38:43 +00003147 case wgsl::BuiltinFn::kSubgroupBroadcast:
David Netoeea786f2023-09-21 05:32:53 +00003148 return "WaveReadLaneAt";
Natalie Chouinardf15cc072024-08-09 20:50:15 +00003149 case wgsl::BuiltinFn::kSubgroupBroadcastFirst:
3150 return "WaveReadLaneFirst";
Natalie Chouinardb8622992024-08-12 15:44:48 +00003151 case wgsl::BuiltinFn::kSubgroupShuffle:
3152 return "WaveReadLaneAt";
Natalie Chouinard06e23ab2024-08-07 18:07:27 +00003153 case wgsl::BuiltinFn::kSubgroupAdd:
3154 return "WaveActiveSum";
3155 case wgsl::BuiltinFn::kSubgroupExclusiveAdd:
3156 return "WavePrefixSum";
3157 case wgsl::BuiltinFn::kSubgroupMul:
3158 return "WaveActiveProduct";
3159 case wgsl::BuiltinFn::kSubgroupExclusiveMul:
3160 return "WavePrefixProduct";
Natalie Chouinarde9e450e2024-08-08 23:16:40 +00003161 case wgsl::BuiltinFn::kSubgroupAnd:
3162 return "WaveActiveBitAnd";
3163 case wgsl::BuiltinFn::kSubgroupOr:
3164 return "WaveActiveBitOr";
3165 case wgsl::BuiltinFn::kSubgroupXor:
3166 return "WaveActiveBitXor";
Natalie Chouinard7be306a2024-08-09 00:48:25 +00003167 case wgsl::BuiltinFn::kSubgroupMin:
3168 return "WaveActiveMin";
3169 case wgsl::BuiltinFn::kSubgroupMax:
3170 return "WaveActiveMax";
Natalie Chouinard95624692024-08-09 19:03:38 +00003171 case wgsl::BuiltinFn::kSubgroupAll:
3172 return "WaveActiveAllTrue";
3173 case wgsl::BuiltinFn::kSubgroupAny:
3174 return "WaveActiveAnyTrue";
Natalie Chouinardb7114a62024-08-14 16:10:59 +00003175 case wgsl::BuiltinFn::kQuadBroadcast:
3176 return "QuadReadLaneAt";
Natalie Chouinarde31fa9d2024-08-14 20:52:07 +00003177 case wgsl::BuiltinFn::kQuadSwapX:
3178 return "QuadReadAcrossX";
3179 case wgsl::BuiltinFn::kQuadSwapY:
3180 return "QuadReadAcrossY";
3181 case wgsl::BuiltinFn::kQuadSwapDiagonal:
3182 return "QuadReadAcrossDiagonal";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003183 default:
Ben Clayton415bd732024-05-02 14:36:02 +00003184 diagnostics_.AddError(Source{}) << "Unknown builtin method: " << builtin->str();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003185 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003186
dan sinclair41e4d9a2022-05-01 14:40:55 +00003187 return "";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003188}
3189
dan sinclair0bfeaf92023-07-26 17:39:51 +00003190bool ASTPrinter::EmitCase(const ast::SwitchStatement* s, size_t case_idx) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003191 auto* stmt = s->body[case_idx];
dan sinclairf148f082022-10-19 15:55:02 +00003192 auto* sem = builder_.Sem().Get<sem::CaseStatement>(stmt);
3193 for (auto* selector : sem->Selectors()) {
dan sinclair67a18932023-06-26 19:54:49 +00003194 auto out = Line();
dan sinclairf148f082022-10-19 15:55:02 +00003195 if (selector->IsDefault()) {
3196 out << "default";
3197 } else {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003198 out << "case ";
Ben Clayton329dfd72022-11-23 00:05:05 +00003199 if (!EmitConstant(out, selector->Value(), /* is_variable_initializer */ false)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003200 return false;
3201 }
dan sinclairf148f082022-10-19 15:55:02 +00003202 }
3203 out << ":";
3204 if (selector == sem->Selectors().back()) {
3205 out << " {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003206 }
3207 }
3208
dan sinclair67a18932023-06-26 19:54:49 +00003209 IncrementIndent();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003210 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00003211 DecrementIndent();
3212 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003213 });
3214
3215 // Emit the case statement
3216 if (!EmitStatements(stmt->body->statements)) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003217 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003218 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003219
dan sinclairbae54e72023-07-28 15:01:54 +00003220 if (!tint::IsAnyOf<ast::BreakStatement>(stmt->body->Last())) {
dan sinclair67a18932023-06-26 19:54:49 +00003221 Line() << "break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003222 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003223
dan sinclair41e4d9a2022-05-01 14:40:55 +00003224 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003225}
3226
dan sinclair0bfeaf92023-07-26 17:39:51 +00003227bool ASTPrinter::EmitContinue(const ast::ContinueStatement*) {
dan sinclair4b88dbc2022-06-16 15:27:38 +00003228 if (!emit_continuing_ || !emit_continuing_()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003229 return false;
3230 }
dan sinclair67a18932023-06-26 19:54:49 +00003231 Line() << "continue;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003232 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003233}
3234
dan sinclair0bfeaf92023-07-26 17:39:51 +00003235bool ASTPrinter::EmitDiscard(const ast::DiscardStatement*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003236 // TODO(dsinclair): Verify this is correct when the discard semantics are
3237 // defined for WGSL (https://github.com/gpuweb/gpuweb/issues/361)
dan sinclair67a18932023-06-26 19:54:49 +00003238 Line() << "discard;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003239 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003240}
3241
dan sinclairbae54e72023-07-28 15:01:54 +00003242bool ASTPrinter::EmitExpression(StringStream& out, const ast::Expression* expr) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +00003243 if (auto* sem = builder_.Sem().GetVal(expr)) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003244 if (auto* constant = sem->ConstantValue()) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003245 bool is_variable_initializer = false;
3246 if (auto* stmt = sem->Stmt()) {
3247 if (auto* decl = As<ast::VariableDeclStatement>(stmt->Declaration())) {
3248 is_variable_initializer = decl->variable->initializer == expr;
3249 }
3250 }
3251 return EmitConstant(out, constant, is_variable_initializer);
Ben Claytone9f8b092022-06-01 13:14:39 +00003252 }
3253 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003254 return Switch(
Ben Claytonb90b6bf2022-08-23 16:23:05 +00003255 expr, //
3256 [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(out, a); },
3257 [&](const ast::BinaryExpression* b) { return EmitBinary(out, b); },
Ben Claytonb90b6bf2022-08-23 16:23:05 +00003258 [&](const ast::CallExpression* c) { return EmitCall(out, c); },
3259 [&](const ast::IdentifierExpression* i) { return EmitIdentifier(out, i); },
3260 [&](const ast::LiteralExpression* l) { return EmitLiteral(out, l); },
3261 [&](const ast::MemberAccessorExpression* m) { return EmitMemberAccessor(out, m); },
Ben Claytond6082c52023-10-26 16:02:01 +00003262 [&](const ast::UnaryOpExpression* u) { return EmitUnaryOp(out, u); }, //
3263 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003264}
3265
dan sinclairbae54e72023-07-28 15:01:54 +00003266bool ASTPrinter::EmitIdentifier(StringStream& out, const ast::IdentifierExpression* expr) {
dan sinclaird026e132023-04-18 19:38:25 +00003267 out << expr->identifier->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003268 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003269}
3270
dan sinclair0bfeaf92023-07-26 17:39:51 +00003271bool ASTPrinter::EmitIf(const ast::IfStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003272 {
dan sinclair67a18932023-06-26 19:54:49 +00003273 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003274 out << "if (";
3275 if (!EmitExpression(out, stmt->condition)) {
3276 return false;
3277 }
3278 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003279 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003280
dan sinclair41e4d9a2022-05-01 14:40:55 +00003281 if (!EmitStatementsWithIndent(stmt->body->statements)) {
James Price26ebe5e2022-04-29 00:14:53 +00003282 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003283 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003284
dan sinclair41e4d9a2022-05-01 14:40:55 +00003285 if (stmt->else_statement) {
dan sinclair67a18932023-06-26 19:54:49 +00003286 Line() << "} else {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003287 if (auto* block = stmt->else_statement->As<ast::BlockStatement>()) {
3288 if (!EmitStatementsWithIndent(block->statements)) {
3289 return false;
3290 }
3291 } else {
dan sinclairbae54e72023-07-28 15:01:54 +00003292 if (!EmitStatementsWithIndent(Vector{stmt->else_statement})) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003293 return false;
3294 }
3295 }
3296 }
dan sinclair67a18932023-06-26 19:54:49 +00003297 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003298
3299 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003300}
3301
dan sinclair0bfeaf92023-07-26 17:39:51 +00003302bool ASTPrinter::EmitFunction(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003303 auto* sem = builder_.Sem().Get(func);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003304
Antonio Maiorano08f4b552022-05-31 13:20:28 +00003305 // Emit storage atomic helpers
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00003306 if (auto* intrinsic = ast::GetAttribute<DecomposeMemoryAccess::Intrinsic>(func->attributes)) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003307 if (intrinsic->address_space == core::AddressSpace::kStorage && intrinsic->IsAtomic()) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00003308 if (!EmitStorageAtomicIntrinsic(func, intrinsic)) {
3309 return false;
3310 }
3311 }
3312 return true;
3313 }
3314
dan sinclair41e4d9a2022-05-01 14:40:55 +00003315 if (ast::HasAttribute<ast::InternalAttribute>(func->attributes)) {
3316 // An internal function. Do not emit.
3317 return true;
3318 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003319
dan sinclair41e4d9a2022-05-01 14:40:55 +00003320 {
dan sinclair67a18932023-06-26 19:54:49 +00003321 auto out = Line();
dan sinclaird026e132023-04-18 19:38:25 +00003322 auto name = func->name->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003323 // If the function returns an array, then we need to declare a typedef for
3324 // this.
dan sinclaircedcdf32023-08-10 02:39:48 +00003325 if (sem->ReturnType()->Is<core::type::Array>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003326 auto typedef_name = UniqueIdentifier(name + "_ret");
dan sinclair67a18932023-06-26 19:54:49 +00003327 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003328 pre << "typedef ";
Ben Claytoncd52f382023-08-07 13:11:08 +00003329 if (!EmitTypeAndName(pre, sem->ReturnType(), core::AddressSpace::kUndefined,
3330 core::Access::kReadWrite, typedef_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003331 return false;
3332 }
3333 pre << ";";
3334 out << typedef_name;
3335 } else {
Ben Claytoncd52f382023-08-07 13:11:08 +00003336 if (!EmitType(out, sem->ReturnType(), core::AddressSpace::kUndefined,
3337 core::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003338 return false;
3339 }
3340 }
3341
3342 out << " " << name << "(";
3343
3344 bool first = true;
3345
3346 for (auto* v : sem->Parameters()) {
3347 if (!first) {
3348 out << ", ";
3349 }
3350 first = false;
3351
3352 auto const* type = v->Type();
Ben Claytoncd52f382023-08-07 13:11:08 +00003353 auto address_space = core::AddressSpace::kUndefined;
3354 auto access = core::Access::kUndefined;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003355
dan sinclaircedcdf32023-08-10 02:39:48 +00003356 if (auto* ptr = type->As<core::type::Pointer>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003357 type = ptr->StoreType();
dan sinclairff7cf212022-10-03 14:05:23 +00003358 switch (ptr->AddressSpace()) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003359 case core::AddressSpace::kStorage:
3360 case core::AddressSpace::kUniform:
Ben Clayton2032d032022-06-15 19:32:37 +00003361 // Not allowed by WGSL, but is used by certain transforms (e.g. DMA) to pass
3362 // storage buffers and uniform buffers down into transform-generated
3363 // functions. In this situation we want to generate the parameter without an
dan sinclairff7cf212022-10-03 14:05:23 +00003364 // 'inout', using the address space and access from the pointer.
3365 address_space = ptr->AddressSpace();
Ben Clayton2032d032022-06-15 19:32:37 +00003366 access = ptr->Access();
3367 break;
3368 default:
3369 // Transform regular WGSL pointer parameters in to `inout` parameters.
3370 out << "inout ";
3371 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003372 }
3373
Ben Clayton1b90f932023-02-18 12:37:34 +00003374 // Note: WGSL only allows for AddressSpace::kUndefined on parameters, however
dan sinclair41e4d9a2022-05-01 14:40:55 +00003375 // the sanitizer transforms generates load / store functions for storage
3376 // or uniform buffers. These functions have a buffer parameter with
dan sinclairff7cf212022-10-03 14:05:23 +00003377 // AddressSpace::kStorage or AddressSpace::kUniform. This is required to
dan sinclair41e4d9a2022-05-01 14:40:55 +00003378 // correctly translate the parameter to a [RW]ByteAddressBuffer for
3379 // storage buffers and a uint4[N] for uniform buffers.
dan sinclairff7cf212022-10-03 14:05:23 +00003380 if (!EmitTypeAndName(out, type, address_space, access,
dan sinclaird026e132023-04-18 19:38:25 +00003381 v->Declaration()->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003382 return false;
3383 }
3384 }
3385 out << ") {";
3386 }
3387
dan sinclaircedcdf32023-08-10 02:39:48 +00003388 if (sem->DiscardStatement() && !sem->ReturnType()->Is<core::type::Void>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003389 // BUG(crbug.com/tint/1081): work around non-void functions with discard
3390 // failing compilation sometimes
3391 if (!EmitFunctionBodyWithDiscard(func)) {
3392 return false;
3393 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003394 } else {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003395 if (!EmitStatementsWithIndent(func->body->statements)) {
3396 return false;
3397 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003398 }
3399
dan sinclair67a18932023-06-26 19:54:49 +00003400 Line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003401
dan sinclair41e4d9a2022-05-01 14:40:55 +00003402 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003403}
3404
dan sinclair0bfeaf92023-07-26 17:39:51 +00003405bool ASTPrinter::EmitFunctionBodyWithDiscard(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003406 // FXC sometimes fails to compile functions that discard with 'Not all control
3407 // paths return a value'. We work around this by wrapping the function body
3408 // within an "if (true) { <body> } return <default return type obj>;" so that
3409 // there is always an (unused) return statement.
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003410
dan sinclair41e4d9a2022-05-01 14:40:55 +00003411 auto* sem = builder_.Sem().Get(func);
dan sinclaircedcdf32023-08-10 02:39:48 +00003412 TINT_ASSERT(sem->DiscardStatement() && !sem->ReturnType()->Is<core::type::Void>());
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003413
dan sinclair41e4d9a2022-05-01 14:40:55 +00003414 ScopedIndent si(this);
dan sinclair67a18932023-06-26 19:54:49 +00003415 Line() << "if (true) {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003416
dan sinclair41e4d9a2022-05-01 14:40:55 +00003417 if (!EmitStatementsWithIndent(func->body->statements)) {
3418 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003419 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003420
dan sinclair67a18932023-06-26 19:54:49 +00003421 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003422
3423 // Return an unused result that matches the type of the return value
dan sinclaird026e132023-04-18 19:38:25 +00003424 auto name = builder_.Symbols().New("unused").Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003425 {
dan sinclair67a18932023-06-26 19:54:49 +00003426 auto out = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00003427 if (!EmitTypeAndName(out, sem->ReturnType(), core::AddressSpace::kUndefined,
3428 core::Access::kReadWrite, name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003429 return false;
3430 }
3431 out << ";";
3432 }
dan sinclair67a18932023-06-26 19:54:49 +00003433 Line() << "return " << name << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003434
3435 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003436}
3437
dan sinclair0bfeaf92023-07-26 17:39:51 +00003438bool ASTPrinter::EmitGlobalVariable(const ast::Variable* global) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00003439 return Switch(
3440 global, //
3441 [&](const ast::Var* var) {
3442 auto* sem = builder_.Sem().Get(global);
dan sinclairff7cf212022-10-03 14:05:23 +00003443 switch (sem->AddressSpace()) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003444 case core::AddressSpace::kUniform:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003445 return EmitUniformVariable(var, sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003446 case core::AddressSpace::kStorage:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003447 return EmitStorageVariable(var, sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003448 case core::AddressSpace::kHandle:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003449 return EmitHandleVariable(var, sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003450 case core::AddressSpace::kPrivate:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003451 return EmitPrivateVariable(sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003452 case core::AddressSpace::kWorkgroup:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003453 return EmitWorkgroupVariable(sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003454 case core::AddressSpace::kPushConstant:
Ben Clayton415bd732024-05-02 14:36:02 +00003455 diagnostics_.AddError(Source{})
Ben Claytonc27315a2024-02-26 20:24:06 +00003456 << "unhandled address space " << sem->AddressSpace();
dan sinclair4abf28e2022-08-02 15:55:35 +00003457 return false;
dan sinclair8dbd4d02022-07-27 18:54:05 +00003458 default: {
Ben Claytonf848af22023-07-28 16:37:32 +00003459 TINT_ICE() << "unhandled address space " << sem->AddressSpace();
dan sinclair8dbd4d02022-07-27 18:54:05 +00003460 }
Ben Claytondcdf66e2022-06-17 12:48:51 +00003461 }
3462 },
dan sinclairf6a94042022-09-09 16:16:19 +00003463 [&](const ast::Override*) {
3464 // Override is removed with SubstituteOverride
Ben Clayton415bd732024-05-02 14:36:02 +00003465 diagnostics_.AddError(Source{})
Ben Claytonc27315a2024-02-26 20:24:06 +00003466 << "override-expressions should have been removed with the SubstituteOverride "
3467 "transform";
dan sinclairf6a94042022-09-09 16:16:19 +00003468 return false;
3469 },
Ben Clayton19576e92022-06-28 12:44:16 +00003470 [&](const ast::Const*) {
3471 return true; // Constants are embedded at their use
Ben Claytond6082c52023-10-26 16:02:01 +00003472 }, //
3473 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003474}
3475
dan sinclair0bfeaf92023-07-26 17:39:51 +00003476bool ASTPrinter::EmitUniformVariable(const ast::Var* var, const sem::Variable* sem) {
Ben Clayton65e38242023-11-09 19:37:44 +00003477 auto binding_point = *sem->As<sem::GlobalVariable>()->Attributes().binding_point;
Ben Claytondcdf66e2022-06-17 12:48:51 +00003478 auto* type = sem->Type()->UnwrapRef();
dan sinclaird026e132023-04-18 19:38:25 +00003479 auto name = var->name->symbol.Name();
dan sinclair67a18932023-06-26 19:54:49 +00003480 Line() << "cbuffer cbuffer_" << name << RegisterAndSpace('b', binding_point) << " {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003481
dan sinclair41e4d9a2022-05-01 14:40:55 +00003482 {
3483 ScopedIndent si(this);
dan sinclair67a18932023-06-26 19:54:49 +00003484 auto out = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00003485 if (!EmitTypeAndName(out, type, core::AddressSpace::kUniform, sem->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003486 return false;
3487 }
3488 out << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003489 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003490
dan sinclair67a18932023-06-26 19:54:49 +00003491 Line() << "};";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003492
dan sinclair41e4d9a2022-05-01 14:40:55 +00003493 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003494}
3495
dan sinclair0bfeaf92023-07-26 17:39:51 +00003496bool ASTPrinter::EmitStorageVariable(const ast::Var* var, const sem::Variable* sem) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00003497 auto* type = sem->Type()->UnwrapRef();
dan sinclair67a18932023-06-26 19:54:49 +00003498 auto out = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00003499 if (!EmitTypeAndName(out, type, core::AddressSpace::kStorage, sem->Access(),
dan sinclaird026e132023-04-18 19:38:25 +00003500 var->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003501 return false;
3502 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003503
dan sinclairacdf6e12022-08-24 15:47:25 +00003504 auto* global_sem = sem->As<sem::GlobalVariable>();
Ben Claytoncd52f382023-08-07 13:11:08 +00003505 out << RegisterAndSpace(sem->Access() == core::Access::kRead ? 't' : 'u',
Ben Clayton65e38242023-11-09 19:37:44 +00003506 *global_sem->Attributes().binding_point)
dan sinclair41e4d9a2022-05-01 14:40:55 +00003507 << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003508
dan sinclair41e4d9a2022-05-01 14:40:55 +00003509 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003510}
3511
dan sinclair0bfeaf92023-07-26 17:39:51 +00003512bool ASTPrinter::EmitHandleVariable(const ast::Var* var, const sem::Variable* sem) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00003513 auto* unwrapped_type = sem->Type()->UnwrapRef();
dan sinclair67a18932023-06-26 19:54:49 +00003514 auto out = Line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003515
dan sinclaird026e132023-04-18 19:38:25 +00003516 auto name = var->name->symbol.Name();
Ben Claytondcdf66e2022-06-17 12:48:51 +00003517 auto* type = sem->Type()->UnwrapRef();
Jiawei Shaoa87d0632023-11-16 00:21:08 +00003518 if (ast::HasAttribute<PixelLocal::RasterizerOrderedView>(var->attributes)) {
3519 TINT_ASSERT(!type->Is<core::type::MultisampledTexture>());
3520 auto* storage = type->As<core::type::StorageTexture>();
3521 if (!storage) {
3522 TINT_ICE() << "Rasterizer Ordered View type isn't storage texture";
Jiawei Shaoa87d0632023-11-16 00:21:08 +00003523 }
3524 out << "RasterizerOrderedTexture2D";
Ben Clayton448c01b2024-02-28 00:23:17 +00003525 auto* component = ImageFormatToRWtextureType(storage->texel_format());
Jiawei Shaoa87d0632023-11-16 00:21:08 +00003526 if (TINT_UNLIKELY(!component)) {
3527 TINT_ICE() << "Unsupported StorageTexture TexelFormat: "
3528 << static_cast<int>(storage->texel_format());
Jiawei Shaoa87d0632023-11-16 00:21:08 +00003529 }
3530 out << "<" << component << "> " << name;
3531 } else if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003532 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003533 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003534
dan sinclair41e4d9a2022-05-01 14:40:55 +00003535 const char* register_space = nullptr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003536
dan sinclaircedcdf32023-08-10 02:39:48 +00003537 if (unwrapped_type->Is<core::type::Texture>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003538 register_space = "t";
James Price88c231b2023-08-15 12:47:28 +00003539 if (auto* st = unwrapped_type->As<core::type::StorageTexture>();
3540 st && st->access() != core::Access::kRead) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003541 register_space = "u";
3542 }
dan sinclaircedcdf32023-08-10 02:39:48 +00003543 } else if (unwrapped_type->Is<core::type::Sampler>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003544 register_space = "s";
3545 }
3546
3547 if (register_space) {
Ben Clayton65e38242023-11-09 19:37:44 +00003548 auto bp = sem->As<sem::GlobalVariable>()->Attributes().binding_point;
Ben Clayton5f4847c2023-04-19 13:24:27 +00003549 out << " : register(" << register_space << bp->binding;
Peng Huangc00ff7f2023-03-31 17:55:19 +00003550 // Omit the space if it's 0, as it's the default.
3551 // SM 5.0 doesn't support spaces, so we don't emit them if group is 0 for better
3552 // compatibility.
Ben Clayton5f4847c2023-04-19 13:24:27 +00003553 if (bp->group == 0) {
Peng Huangc00ff7f2023-03-31 17:55:19 +00003554 out << ")";
3555 } else {
Ben Clayton5f4847c2023-04-19 13:24:27 +00003556 out << ", space" << bp->group << ")";
Peng Huangc00ff7f2023-03-31 17:55:19 +00003557 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003558 }
3559
3560 out << ";";
3561 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003562}
3563
dan sinclair0bfeaf92023-07-26 17:39:51 +00003564bool ASTPrinter::EmitPrivateVariable(const sem::Variable* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003565 auto* decl = var->Declaration();
dan sinclair67a18932023-06-26 19:54:49 +00003566 auto out = Line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003567
dan sinclair41e4d9a2022-05-01 14:40:55 +00003568 out << "static ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003569
dan sinclaird026e132023-04-18 19:38:25 +00003570 auto name = decl->name->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003571 auto* type = var->Type()->UnwrapRef();
dan sinclairff7cf212022-10-03 14:05:23 +00003572 if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003573 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003574 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003575
dan sinclair41e4d9a2022-05-01 14:40:55 +00003576 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00003577 if (auto* initializer = decl->initializer) {
3578 if (!EmitExpression(out, initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003579 return false;
3580 }
3581 } else {
3582 if (!EmitZeroValue(out, var->Type()->UnwrapRef())) {
3583 return false;
3584 }
3585 }
3586
3587 out << ";";
3588 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003589}
3590
dan sinclair0bfeaf92023-07-26 17:39:51 +00003591bool ASTPrinter::EmitWorkgroupVariable(const sem::Variable* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003592 auto* decl = var->Declaration();
dan sinclair67a18932023-06-26 19:54:49 +00003593 auto out = Line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003594
dan sinclair41e4d9a2022-05-01 14:40:55 +00003595 out << "groupshared ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003596
dan sinclaird026e132023-04-18 19:38:25 +00003597 auto name = decl->name->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003598 auto* type = var->Type()->UnwrapRef();
dan sinclairff7cf212022-10-03 14:05:23 +00003599 if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003600 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003601 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003602
dan sinclair6e77b472022-10-20 13:38:28 +00003603 if (auto* initializer = decl->initializer) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003604 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00003605 if (!EmitExpression(out, initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003606 return false;
3607 }
3608 }
3609
3610 out << ";";
3611 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003612}
3613
Ben Claytoncd52f382023-08-07 13:11:08 +00003614std::string ASTPrinter::builtin_to_attribute(core::BuiltinValue builtin) const {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003615 switch (builtin) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003616 case core::BuiltinValue::kPosition:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003617 return "SV_Position";
Ben Claytoncd52f382023-08-07 13:11:08 +00003618 case core::BuiltinValue::kVertexIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003619 return "SV_VertexID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003620 case core::BuiltinValue::kInstanceIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003621 return "SV_InstanceID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003622 case core::BuiltinValue::kFrontFacing:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003623 return "SV_IsFrontFace";
Ben Claytoncd52f382023-08-07 13:11:08 +00003624 case core::BuiltinValue::kFragDepth:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003625 return "SV_Depth";
Ben Claytoncd52f382023-08-07 13:11:08 +00003626 case core::BuiltinValue::kLocalInvocationId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003627 return "SV_GroupThreadID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003628 case core::BuiltinValue::kLocalInvocationIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003629 return "SV_GroupIndex";
Ben Claytoncd52f382023-08-07 13:11:08 +00003630 case core::BuiltinValue::kGlobalInvocationId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003631 return "SV_DispatchThreadID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003632 case core::BuiltinValue::kWorkgroupId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003633 return "SV_GroupID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003634 case core::BuiltinValue::kSampleIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003635 return "SV_SampleIndex";
Ben Claytoncd52f382023-08-07 13:11:08 +00003636 case core::BuiltinValue::kSampleMask:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003637 return "SV_Coverage";
3638 default:
3639 break;
3640 }
3641 return "";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003642}
3643
Ben Claytoncd52f382023-08-07 13:11:08 +00003644std::string ASTPrinter::interpolation_to_modifiers(core::InterpolationType type,
3645 core::InterpolationSampling sampling) const {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003646 std::string modifiers;
3647 switch (type) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003648 case core::InterpolationType::kPerspective:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003649 modifiers += "linear ";
3650 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003651 case core::InterpolationType::kLinear:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003652 modifiers += "noperspective ";
3653 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003654 case core::InterpolationType::kFlat:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003655 modifiers += "nointerpolation ";
3656 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003657 case core::InterpolationType::kUndefined:
Ben Claytonf9ed9d32022-10-11 19:49:17 +00003658 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003659 }
3660 switch (sampling) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003661 case core::InterpolationSampling::kCentroid:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003662 modifiers += "centroid ";
3663 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003664 case core::InterpolationSampling::kSample:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003665 modifiers += "sample ";
3666 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003667 case core::InterpolationSampling::kCenter:
Gregg Tavaresd0151512024-07-11 23:12:44 +00003668 case core::InterpolationSampling::kFirst:
3669 case core::InterpolationSampling::kEither:
Ben Claytoncd52f382023-08-07 13:11:08 +00003670 case core::InterpolationSampling::kUndefined:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003671 break;
3672 }
3673 return modifiers;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003674}
3675
dan sinclair0bfeaf92023-07-26 17:39:51 +00003676bool ASTPrinter::EmitEntryPointFunction(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003677 auto* func_sem = builder_.Sem().Get(func);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003678
dan sinclair41e4d9a2022-05-01 14:40:55 +00003679 {
dan sinclair67a18932023-06-26 19:54:49 +00003680 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003681 if (func->PipelineStage() == ast::PipelineStage::kCompute) {
3682 // Emit the workgroup_size attribute.
3683 auto wgsize = func_sem->WorkgroupSize();
3684 out << "[numthreads(";
dan sinclair3a2a2792022-06-29 14:38:15 +00003685 for (size_t i = 0; i < 3; i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003686 if (i > 0) {
3687 out << ", ";
3688 }
Ben Clayton490d9882022-09-21 21:05:45 +00003689 if (!wgsize[i].has_value()) {
Ben Clayton415bd732024-05-02 14:36:02 +00003690 diagnostics_.AddError(Source{})
Ben Claytonc27315a2024-02-26 20:24:06 +00003691 << "override-expressions should have been removed with the "
3692 "SubstituteOverride transform";
Ben Clayton490d9882022-09-21 21:05:45 +00003693 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003694 }
Ben Clayton490d9882022-09-21 21:05:45 +00003695 out << std::to_string(wgsize[i].value());
dan sinclair41e4d9a2022-05-01 14:40:55 +00003696 }
dan sinclair8700be52024-05-31 18:07:26 +00003697 out << ")]\n";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003698 }
3699
Ben Claytoncd52f382023-08-07 13:11:08 +00003700 if (!EmitTypeAndName(out, func_sem->ReturnType(), core::AddressSpace::kUndefined,
3701 core::Access::kUndefined, func->name->symbol.Name())) {
Ben Clayton19068572023-02-07 21:28:09 +00003702 return false;
3703 }
3704 out << "(";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003705
3706 bool first = true;
3707
3708 // Emit entry point parameters.
3709 for (auto* var : func->params) {
3710 auto* sem = builder_.Sem().Get(var);
3711 auto* type = sem->Type();
dan sinclaircedcdf32023-08-10 02:39:48 +00003712 if (TINT_UNLIKELY(!type->Is<core::type::Struct>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003713 // ICE likely indicates that the CanonicalizeEntryPointIO transform was
3714 // not run, or a builtin parameter was added after it was run.
Ben Claytonf848af22023-07-28 16:37:32 +00003715 TINT_ICE() << "Unsupported non-struct entry point parameter";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003716 }
3717
3718 if (!first) {
3719 out << ", ";
3720 }
3721 first = false;
3722
dan sinclairff7cf212022-10-03 14:05:23 +00003723 if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(),
dan sinclaird026e132023-04-18 19:38:25 +00003724 var->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003725 return false;
3726 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003727 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003728
3729 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003730 }
3731
dan sinclair41e4d9a2022-05-01 14:40:55 +00003732 {
3733 ScopedIndent si(this);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003734
dan sinclair41e4d9a2022-05-01 14:40:55 +00003735 if (!EmitStatements(func->body->statements)) {
3736 return false;
3737 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003738
dan sinclair41e4d9a2022-05-01 14:40:55 +00003739 if (!Is<ast::ReturnStatement>(func->body->Last())) {
dan sinclair637a2fe2023-07-24 21:11:41 +00003740 ast::ReturnStatement ret(GenerationID(), ast::NodeID{}, Source{});
dan sinclair41e4d9a2022-05-01 14:40:55 +00003741 if (!EmitStatement(&ret)) {
3742 return false;
3743 }
3744 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003745 }
3746
dan sinclair67a18932023-06-26 19:54:49 +00003747 Line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003748
dan sinclair41e4d9a2022-05-01 14:40:55 +00003749 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003750}
3751
dan sinclairbae54e72023-07-28 15:01:54 +00003752bool ASTPrinter::EmitConstant(StringStream& out,
dan sinclair464b3b82023-08-09 14:14:28 +00003753 const core::constant::Value* constant,
dan sinclair0bfeaf92023-07-26 17:39:51 +00003754 bool is_variable_initializer) {
Ben Clayton50414802022-06-24 08:06:19 +00003755 return Switch(
Ben Claytonaa037ac2022-06-29 19:07:30 +00003756 constant->Type(), //
dan sinclaircedcdf32023-08-10 02:39:48 +00003757 [&](const core::type::Bool*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003758 out << (constant->ValueAs<AInt>() ? "true" : "false");
Ben Claytone9f8b092022-06-01 13:14:39 +00003759 return true;
Ben Clayton50414802022-06-24 08:06:19 +00003760 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003761 [&](const core::type::F32*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003762 PrintF32(out, constant->ValueAs<f32>());
Ben Clayton50414802022-06-24 08:06:19 +00003763 return true;
3764 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003765 [&](const core::type::F16*) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003766 // emit a f16 scalar with explicit float16_t type declaration.
3767 out << "float16_t(";
dan sinclair5addefb2022-12-14 20:46:32 +00003768 PrintF16(out, constant->ValueAs<f16>());
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003769 out << ")";
Antonio Maiorano679cf4f2022-09-03 21:43:01 +00003770 return true;
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003771 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003772 [&](const core::type::I32*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003773 out << constant->ValueAs<AInt>();
Ben Clayton50414802022-06-24 08:06:19 +00003774 return true;
3775 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003776 [&](const core::type::U32*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003777 out << constant->ValueAs<AInt>() << "u";
Ben Clayton50414802022-06-24 08:06:19 +00003778 return true;
3779 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003780 [&](const core::type::Vector* v) {
dan sinclair464b3b82023-08-09 14:14:28 +00003781 if (auto* splat = constant->As<core::constant::Splat>()) {
Ben Clayton50414802022-06-24 08:06:19 +00003782 {
dan sinclairb2ba57b2023-02-28 15:14:09 +00003783 ScopedParen sp(out);
James Price7bca4d72023-03-13 19:05:16 +00003784 if (!EmitConstant(out, splat->el, is_variable_initializer)) {
Ben Clayton50414802022-06-24 08:06:19 +00003785 return false;
3786 }
3787 }
3788 out << ".";
Ben Claytonaa037ac2022-06-29 19:07:30 +00003789 for (size_t i = 0; i < v->Width(); i++) {
Ben Clayton50414802022-06-24 08:06:19 +00003790 out << "x";
3791 }
3792 return true;
3793 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003794
Ben Claytoncd52f382023-08-07 13:11:08 +00003795 if (!EmitType(out, v, core::AddressSpace::kUndefined, core::Access::kUndefined, "")) {
Ben Clayton50414802022-06-24 08:06:19 +00003796 return false;
3797 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003798
dan sinclairb2ba57b2023-02-28 15:14:09 +00003799 ScopedParen sp(out);
Ben Claytone9f8b092022-06-01 13:14:39 +00003800
Ben Claytonaa037ac2022-06-29 19:07:30 +00003801 for (size_t i = 0; i < v->Width(); i++) {
3802 if (i > 0) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003803 out << ", ";
3804 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003805 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003806 return false;
3807 }
3808 }
3809 return true;
Ben Clayton50414802022-06-24 08:06:19 +00003810 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003811 [&](const core::type::Matrix* m) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003812 if (!EmitType(out, m, core::AddressSpace::kUndefined, core::Access::kUndefined, "")) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003813 return false;
3814 }
Ben Clayton50414802022-06-24 08:06:19 +00003815
dan sinclairb2ba57b2023-02-28 15:14:09 +00003816 ScopedParen sp(out);
Ben Clayton50414802022-06-24 08:06:19 +00003817
Ben Claytonaa037ac2022-06-29 19:07:30 +00003818 for (size_t i = 0; i < m->columns(); i++) {
3819 if (i > 0) {
Ben Clayton50414802022-06-24 08:06:19 +00003820 out << ", ";
3821 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003822 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Clayton50414802022-06-24 08:06:19 +00003823 return false;
3824 }
3825 }
3826 return true;
3827 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003828 [&](const core::type::Array* a) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003829 if (constant->AllZero()) {
Ben Clayton19576e92022-06-28 12:44:16 +00003830 out << "(";
Ben Claytoncd52f382023-08-07 13:11:08 +00003831 if (!EmitType(out, a, core::AddressSpace::kUndefined, core::Access::kUndefined,
3832 "")) {
Ben Clayton19576e92022-06-28 12:44:16 +00003833 return false;
3834 }
3835 out << ")0";
3836 return true;
3837 }
3838
3839 out << "{";
3840 TINT_DEFER(out << "}");
3841
dan sinclair78f80672022-09-22 22:28:21 +00003842 auto count = a->ConstantCount();
3843 if (!count) {
Ben Clayton415bd732024-05-02 14:36:02 +00003844 diagnostics_.AddError(Source{}) << core::type::Array::kErrExpectedConstantCount;
dan sinclair78f80672022-09-22 22:28:21 +00003845 return false;
3846 }
3847
3848 for (size_t i = 0; i < count; i++) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003849 if (i > 0) {
Ben Clayton19576e92022-06-28 12:44:16 +00003850 out << ", ";
3851 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003852 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Clayton19576e92022-06-28 12:44:16 +00003853 return false;
3854 }
3855 }
3856
3857 return true;
3858 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003859 [&](const core::type::Struct* s) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003860 if (!EmitStructType(&helpers_, s)) {
3861 return false;
3862 }
3863
Ben Clayton6c098ba2022-07-14 20:46:39 +00003864 if (constant->AllZero()) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003865 out << "(" << StructName(s) << ")0";
Ben Clayton6c098ba2022-07-14 20:46:39 +00003866 return true;
3867 }
3868
dan sinclairbae54e72023-07-28 15:01:54 +00003869 auto emit_member_values = [&](StringStream& o) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003870 o << "{";
dan sinclairad9cd0a2022-12-06 20:01:54 +00003871 for (size_t i = 0; i < s->Members().Length(); i++) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003872 if (i > 0) {
3873 o << ", ";
3874 }
3875 if (!EmitConstant(o, constant->Index(i), is_variable_initializer)) {
3876 return false;
3877 }
Ben Clayton6c098ba2022-07-14 20:46:39 +00003878 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003879 o << "}";
3880 return true;
3881 };
3882
3883 if (is_variable_initializer) {
3884 if (!emit_member_values(out)) {
Ben Clayton6c098ba2022-07-14 20:46:39 +00003885 return false;
3886 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003887 } else {
3888 // HLSL requires structure initializers to be assigned directly to a variable.
Ben Clayton2d501c52024-01-17 01:27:37 +00003889 // For these constants use 'static const' at global-scope. 'const' at global scope
3890 // creates a variable who's initializer is ignored, and the value is expected to be
3891 // provided in a cbuffer. 'static const' is a true value-embedded-in-the-shader-code
3892 // constant. We also emit these for function-local constant expressions for
3893 // consistency and to ensure that these are not computed at execution time.
Ben Clayton329dfd72022-11-23 00:05:05 +00003894 auto name = UniqueIdentifier("c");
3895 {
Ben Clayton2d501c52024-01-17 01:27:37 +00003896 StringStream decl;
3897 decl << "static const " << StructName(s) << " " << name << " = ";
Ben Clayton329dfd72022-11-23 00:05:05 +00003898 if (!emit_member_values(decl)) {
3899 return false;
3900 }
3901 decl << ";";
Ben Clayton2d501c52024-01-17 01:27:37 +00003902 current_buffer_->Insert(decl.str(), global_insertion_point_++, 0);
Ben Clayton329dfd72022-11-23 00:05:05 +00003903 }
3904 out << name;
Ben Clayton6c098ba2022-07-14 20:46:39 +00003905 }
3906
3907 return true;
Ben Claytond6082c52023-10-26 16:02:01 +00003908 }, //
3909 TINT_ICE_ON_NO_MATCH);
Ben Claytone9f8b092022-06-01 13:14:39 +00003910}
3911
dan sinclairbae54e72023-07-28 15:01:54 +00003912bool ASTPrinter::EmitLiteral(StringStream& out, const ast::LiteralExpression* lit) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003913 return Switch(
3914 lit,
3915 [&](const ast::BoolLiteralExpression* l) {
3916 out << (l->value ? "true" : "false");
3917 return true;
3918 },
Ben Clayton3ad927c2022-05-25 23:12:14 +00003919 [&](const ast::FloatLiteralExpression* l) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003920 if (l->suffix == ast::FloatLiteralExpression::Suffix::kH) {
3921 // Emit f16 literal with explicit float16_t type declaration.
3922 out << "float16_t(";
Antonio Maiorano679cf4f2022-09-03 21:43:01 +00003923 PrintF16(out, static_cast<float>(l->value));
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003924 out << ")";
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003925 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003926 PrintF32(out, static_cast<float>(l->value));
dan sinclair41e4d9a2022-05-01 14:40:55 +00003927 return true;
3928 },
Ben Clayton8822e292022-05-04 22:18:49 +00003929 [&](const ast::IntLiteralExpression* i) {
3930 out << i->value;
3931 switch (i->suffix) {
3932 case ast::IntLiteralExpression::Suffix::kNone:
3933 case ast::IntLiteralExpression::Suffix::kI:
3934 return true;
3935 case ast::IntLiteralExpression::Suffix::kU:
3936 out << "u";
3937 return true;
3938 }
Ben Clayton415bd732024-05-02 14:36:02 +00003939 diagnostics_.AddError(Source{}) << "unknown integer literal suffix type";
Ben Clayton8822e292022-05-04 22:18:49 +00003940 return false;
Ben Claytond6082c52023-10-26 16:02:01 +00003941 }, //
3942 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003943}
3944
dan sinclaircedcdf32023-08-10 02:39:48 +00003945bool ASTPrinter::EmitValue(StringStream& out, const core::type::Type* type, int value) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003946 return Switch(
3947 type,
dan sinclaircedcdf32023-08-10 02:39:48 +00003948 [&](const core::type::Bool*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003949 out << (value == 0 ? "false" : "true");
3950 return true;
3951 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003952 [&](const core::type::F32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003953 out << value << ".0f";
3954 return true;
3955 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003956 [&](const core::type::F16*) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003957 out << "float16_t(" << value << ".0h)";
3958 return true;
3959 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003960 [&](const core::type::I32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003961 out << value;
3962 return true;
3963 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003964 [&](const core::type::U32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003965 out << value << "u";
3966 return true;
3967 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003968 [&](const core::type::Vector* vec) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003969 if (!EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kReadWrite,
Ben Clayton1b90f932023-02-18 12:37:34 +00003970 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003971 return false;
3972 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00003973 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003974 for (uint32_t i = 0; i < vec->Width(); i++) {
3975 if (i != 0) {
3976 out << ", ";
3977 }
3978 if (!EmitValue(out, vec->type(), value)) {
3979 return false;
3980 }
3981 }
3982 return true;
3983 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003984 [&](const core::type::Matrix* mat) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003985 if (!EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kReadWrite,
Ben Clayton1b90f932023-02-18 12:37:34 +00003986 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003987 return false;
3988 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00003989 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003990 for (uint32_t i = 0; i < (mat->rows() * mat->columns()); i++) {
3991 if (i != 0) {
3992 out << ", ";
3993 }
3994 if (!EmitValue(out, mat->type(), value)) {
3995 return false;
3996 }
3997 }
3998 return true;
3999 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004000 [&](const core::type::Struct*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004001 out << "(";
4002 TINT_DEFER(out << ")" << value);
Ben Claytoncd52f382023-08-07 13:11:08 +00004003 return EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kUndefined,
4004 "");
dan sinclair41e4d9a2022-05-01 14:40:55 +00004005 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004006 [&](const core::type::Array*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004007 out << "(";
4008 TINT_DEFER(out << ")" << value);
Ben Claytoncd52f382023-08-07 13:11:08 +00004009 return EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kUndefined,
4010 "");
Ben Claytond6082c52023-10-26 16:02:01 +00004011 }, //
4012 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004013}
4014
dan sinclaircedcdf32023-08-10 02:39:48 +00004015bool ASTPrinter::EmitZeroValue(StringStream& out, const core::type::Type* type) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004016 return EmitValue(out, type, 0);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004017}
4018
dan sinclair0bfeaf92023-07-26 17:39:51 +00004019bool ASTPrinter::EmitLoop(const ast::LoopStatement* stmt) {
Ben Claytona0aabaf2023-06-22 23:53:09 +00004020 auto emit_continuing = [this, stmt] {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004021 if (stmt->continuing && !stmt->continuing->Empty()) {
4022 if (!EmitBlock(stmt->continuing)) {
4023 return false;
4024 }
4025 }
4026 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004027 };
4028
4029 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
dan sinclair67a18932023-06-26 19:54:49 +00004030 Line() << "while (true) {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004031 {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004032 ScopedIndent si(this);
4033 if (!EmitStatements(stmt->body->statements)) {
4034 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004035 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004036 if (!emit_continuing_()) {
4037 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004038 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004039 }
dan sinclair67a18932023-06-26 19:54:49 +00004040 Line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004041
dan sinclair41e4d9a2022-05-01 14:40:55 +00004042 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004043}
4044
dan sinclair0bfeaf92023-07-26 17:39:51 +00004045bool ASTPrinter::EmitForLoop(const ast::ForLoopStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004046 // Nest a for loop with a new block. In HLSL the initializer scope is not
4047 // nested by the for-loop, so we may get variable redefinitions.
dan sinclair67a18932023-06-26 19:54:49 +00004048 Line() << "{";
4049 IncrementIndent();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004050 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00004051 DecrementIndent();
4052 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004053 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004054
dan sinclair41e4d9a2022-05-01 14:40:55 +00004055 TextBuffer init_buf;
4056 if (auto* init = stmt->initializer) {
4057 TINT_SCOPED_ASSIGNMENT(current_buffer_, &init_buf);
4058 if (!EmitStatement(init)) {
4059 return false;
4060 }
4061 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004062
dan sinclair41e4d9a2022-05-01 14:40:55 +00004063 TextBuffer cond_pre;
dan sinclairbae54e72023-07-28 15:01:54 +00004064 StringStream cond_buf;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004065 if (auto* cond = stmt->condition) {
4066 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
4067 if (!EmitExpression(cond_buf, cond)) {
4068 return false;
4069 }
4070 }
4071
4072 TextBuffer cont_buf;
4073 if (auto* cont = stmt->continuing) {
4074 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cont_buf);
4075 if (!EmitStatement(cont)) {
4076 return false;
4077 }
4078 }
4079
4080 // If the for-loop has a multi-statement conditional and / or continuing, then
4081 // we cannot emit this as a regular for-loop in HLSL. Instead we need to
4082 // generate a `while(true)` loop.
4083 bool emit_as_loop = cond_pre.lines.size() > 0 || cont_buf.lines.size() > 1;
4084
4085 // If the for-loop has multi-statement initializer, or is going to be emitted
4086 // as a `while(true)` loop, then declare the initializer statement(s) before
4087 // the loop.
4088 if (init_buf.lines.size() > 1 || (stmt->initializer && emit_as_loop)) {
4089 current_buffer_->Append(init_buf);
4090 init_buf.lines.clear(); // Don't emit the initializer again in the 'for'
4091 }
4092
4093 if (emit_as_loop) {
Ben Claytona0aabaf2023-06-22 23:53:09 +00004094 auto emit_continuing = [&] {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004095 current_buffer_->Append(cont_buf);
4096 return true;
4097 };
4098
4099 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
dan sinclair67a18932023-06-26 19:54:49 +00004100 Line() << "while (true) {";
4101 IncrementIndent();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004102 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00004103 DecrementIndent();
4104 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004105 });
4106
4107 if (stmt->condition) {
4108 current_buffer_->Append(cond_pre);
dan sinclair67a18932023-06-26 19:54:49 +00004109 Line() << "if (!(" << cond_buf.str() << ")) { break; }";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004110 }
4111
4112 if (!EmitStatements(stmt->body->statements)) {
4113 return false;
4114 }
4115
4116 if (!emit_continuing_()) {
4117 return false;
4118 }
4119 } else {
4120 // For-loop can be generated.
4121 {
dan sinclair67a18932023-06-26 19:54:49 +00004122 auto out = Line();
Antonio Maiorano06844a52022-09-29 16:53:58 +00004123 out << "for";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004124 {
4125 ScopedParen sp(out);
4126
4127 if (!init_buf.lines.empty()) {
4128 out << init_buf.lines[0].content << " ";
4129 } else {
4130 out << "; ";
4131 }
4132
4133 out << cond_buf.str() << "; ";
4134
4135 if (!cont_buf.lines.empty()) {
dan sinclairbae54e72023-07-28 15:01:54 +00004136 out << tint::TrimSuffix(cont_buf.lines[0].content, ";");
dan sinclair41e4d9a2022-05-01 14:40:55 +00004137 }
4138 }
4139 out << " {";
4140 }
4141 {
4142 auto emit_continuing = [] { return true; };
4143 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
4144 if (!EmitStatementsWithIndent(stmt->body->statements)) {
4145 return false;
4146 }
4147 }
dan sinclair67a18932023-06-26 19:54:49 +00004148 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004149 }
4150
4151 return true;
4152}
4153
dan sinclair0bfeaf92023-07-26 17:39:51 +00004154bool ASTPrinter::EmitWhile(const ast::WhileStatement* stmt) {
dan sinclair49d1a2d2022-06-16 12:01:27 +00004155 TextBuffer cond_pre;
dan sinclairbae54e72023-07-28 15:01:54 +00004156 StringStream cond_buf;
dan sinclair49d1a2d2022-06-16 12:01:27 +00004157 {
4158 auto* cond = stmt->condition;
4159 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
4160 if (!EmitExpression(cond_buf, cond)) {
4161 return false;
4162 }
4163 }
4164
Ben Claytona0aabaf2023-06-22 23:53:09 +00004165 auto emit_continuing = [&] { return true; };
dan sinclair4b88dbc2022-06-16 15:27:38 +00004166 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
4167
dan sinclair49d1a2d2022-06-16 12:01:27 +00004168 // If the while has a multi-statement conditional, then we cannot emit this
4169 // as a regular while in HLSL. Instead we need to generate a `while(true)` loop.
4170 bool emit_as_loop = cond_pre.lines.size() > 0;
4171 if (emit_as_loop) {
dan sinclair67a18932023-06-26 19:54:49 +00004172 Line() << "while (true) {";
4173 IncrementIndent();
dan sinclair49d1a2d2022-06-16 12:01:27 +00004174 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00004175 DecrementIndent();
4176 Line() << "}";
dan sinclair49d1a2d2022-06-16 12:01:27 +00004177 });
4178
4179 current_buffer_->Append(cond_pre);
dan sinclair67a18932023-06-26 19:54:49 +00004180 Line() << "if (!(" << cond_buf.str() << ")) { break; }";
dan sinclair49d1a2d2022-06-16 12:01:27 +00004181 if (!EmitStatements(stmt->body->statements)) {
4182 return false;
4183 }
4184 } else {
4185 // While can be generated.
4186 {
dan sinclair67a18932023-06-26 19:54:49 +00004187 auto out = Line();
Antonio Maiorano06844a52022-09-29 16:53:58 +00004188 out << "while";
dan sinclair49d1a2d2022-06-16 12:01:27 +00004189 {
4190 ScopedParen sp(out);
4191 out << cond_buf.str();
4192 }
4193 out << " {";
4194 }
4195 if (!EmitStatementsWithIndent(stmt->body->statements)) {
4196 return false;
4197 }
dan sinclair67a18932023-06-26 19:54:49 +00004198 Line() << "}";
dan sinclair49d1a2d2022-06-16 12:01:27 +00004199 }
4200
4201 return true;
4202}
4203
dan sinclairbae54e72023-07-28 15:01:54 +00004204bool ASTPrinter::EmitMemberAccessor(StringStream& out, const ast::MemberAccessorExpression* expr) {
Ben Claytonad315652023-02-05 12:36:50 +00004205 if (!EmitExpression(out, expr->object)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004206 return false;
4207 }
4208 out << ".";
4209
Ben Clayton2f9a9882022-12-17 02:20:04 +00004210 auto* sem = builder_.Sem().Get(expr)->UnwrapLoad();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004211
Ben Clayton10fae7a2022-11-14 15:29:29 +00004212 return Switch(
4213 sem,
4214 [&](const sem::Swizzle*) {
4215 // Swizzles output the name directly
dan sinclaird026e132023-04-18 19:38:25 +00004216 out << expr->member->symbol.Name();
Ben Clayton10fae7a2022-11-14 15:29:29 +00004217 return true;
4218 },
4219 [&](const sem::StructMemberAccess* member_access) {
dan sinclaird026e132023-04-18 19:38:25 +00004220 out << member_access->Member()->Name().Name();
Ben Clayton10fae7a2022-11-14 15:29:29 +00004221 return true;
Ben Claytond6082c52023-10-26 16:02:01 +00004222 }, //
4223 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004224}
4225
dan sinclair0bfeaf92023-07-26 17:39:51 +00004226bool ASTPrinter::EmitReturn(const ast::ReturnStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004227 if (stmt->value) {
dan sinclair67a18932023-06-26 19:54:49 +00004228 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004229 out << "return ";
4230 if (!EmitExpression(out, stmt->value)) {
4231 return false;
4232 }
4233 out << ";";
4234 } else {
dan sinclair67a18932023-06-26 19:54:49 +00004235 Line() << "return;";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004236 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004237 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004238}
4239
dan sinclair0bfeaf92023-07-26 17:39:51 +00004240bool ASTPrinter::EmitStatement(const ast::Statement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004241 return Switch(
4242 stmt,
4243 [&](const ast::AssignmentStatement* a) { //
4244 return EmitAssign(a);
4245 },
4246 [&](const ast::BlockStatement* b) { //
4247 return EmitBlock(b);
4248 },
4249 [&](const ast::BreakStatement* b) { //
4250 return EmitBreak(b);
4251 },
dan sinclairb8b0c212022-10-20 22:45:50 +00004252 [&](const ast::BreakIfStatement* b) { //
4253 return EmitBreakIf(b);
4254 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004255 [&](const ast::CallStatement* c) { //
dan sinclair67a18932023-06-26 19:54:49 +00004256 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004257 if (!EmitCall(out, c->expr)) {
4258 return false;
4259 }
4260 out << ";";
4261 return true;
4262 },
4263 [&](const ast::ContinueStatement* c) { //
4264 return EmitContinue(c);
4265 },
4266 [&](const ast::DiscardStatement* d) { //
4267 return EmitDiscard(d);
4268 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004269 [&](const ast::IfStatement* i) { //
4270 return EmitIf(i);
4271 },
4272 [&](const ast::LoopStatement* l) { //
4273 return EmitLoop(l);
4274 },
4275 [&](const ast::ForLoopStatement* l) { //
4276 return EmitForLoop(l);
4277 },
dan sinclair49d1a2d2022-06-16 12:01:27 +00004278 [&](const ast::WhileStatement* l) { //
4279 return EmitWhile(l);
4280 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004281 [&](const ast::ReturnStatement* r) { //
4282 return EmitReturn(r);
4283 },
4284 [&](const ast::SwitchStatement* s) { //
4285 return EmitSwitch(s);
4286 },
4287 [&](const ast::VariableDeclStatement* v) { //
Ben Claytondcdf66e2022-06-17 12:48:51 +00004288 return Switch(
4289 v->variable, //
4290 [&](const ast::Var* var) { return EmitVar(var); },
4291 [&](const ast::Let* let) { return EmitLet(let); },
Ben Clayton19576e92022-06-28 12:44:16 +00004292 [&](const ast::Const*) {
4293 return true; // Constants are embedded at their use
Ben Claytond6082c52023-10-26 16:02:01 +00004294 }, //
4295 TINT_ICE_ON_NO_MATCH);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004296 },
Ben Claytonc98d57d2023-01-24 14:59:43 +00004297 [&](const ast::ConstAssert*) {
Ben Claytonb4744ac2022-08-03 07:01:08 +00004298 return true; // Not emitted
Ben Claytond6082c52023-10-26 16:02:01 +00004299 }, //
4300 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004301}
4302
dan sinclair0bfeaf92023-07-26 17:39:51 +00004303bool ASTPrinter::EmitDefaultOnlySwitch(const ast::SwitchStatement* stmt) {
Ben Claytonf848af22023-07-28 16:37:32 +00004304 TINT_ASSERT(stmt->body.Length() == 1 && stmt->body[0]->ContainsDefault());
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004305
dan sinclair41e4d9a2022-05-01 14:40:55 +00004306 // FXC fails to compile a switch with just a default case, ignoring the
4307 // default case body. We work around this here by emitting the default case
4308 // without the switch.
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004309
Antonio Maioranoeab1f622023-02-01 15:46:34 +00004310 // Emit the switch condition as-is if it has side-effects (e.g.
4311 // function call). Note that we can ignore the result of the expression (if any).
Ben Clayton0b4a2f12023-02-05 22:59:40 +00004312 if (auto* sem_cond = builder_.Sem().GetVal(stmt->condition); sem_cond->HasSideEffects()) {
dan sinclair67a18932023-06-26 19:54:49 +00004313 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004314 if (!EmitExpression(out, stmt->condition)) {
4315 return false;
4316 }
4317 out << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004318 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004319
dan sinclair41e4d9a2022-05-01 14:40:55 +00004320 // Emit "do { <default case body> } while(false);". We use a 'do' loop so
4321 // that break statements work as expected, and make it 'while (false)' in
4322 // case there isn't a break statement.
dan sinclair67a18932023-06-26 19:54:49 +00004323 Line() << "do {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004324 {
4325 ScopedIndent si(this);
4326 if (!EmitStatements(stmt->body[0]->body->statements)) {
4327 return false;
4328 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004329 }
dan sinclair67a18932023-06-26 19:54:49 +00004330 Line() << "} while (false);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004331 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004332}
4333
dan sinclair0bfeaf92023-07-26 17:39:51 +00004334bool ASTPrinter::EmitSwitch(const ast::SwitchStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004335 // BUG(crbug.com/tint/1188): work around default-only switches
dan sinclairf148f082022-10-19 15:55:02 +00004336 if (stmt->body.Length() == 1 && stmt->body[0]->selectors.Length() == 1 &&
4337 stmt->body[0]->ContainsDefault()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004338 return EmitDefaultOnlySwitch(stmt);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004339 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004340
dan sinclair41e4d9a2022-05-01 14:40:55 +00004341 { // switch(expr) {
dan sinclair67a18932023-06-26 19:54:49 +00004342 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004343 out << "switch(";
4344 if (!EmitExpression(out, stmt->condition)) {
4345 return false;
4346 }
4347 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004348 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004349
dan sinclair41e4d9a2022-05-01 14:40:55 +00004350 {
4351 ScopedIndent si(this);
Ben Clayton783b1692022-08-02 17:03:35 +00004352 for (size_t i = 0; i < stmt->body.Length(); i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004353 if (!EmitCase(stmt, i)) {
4354 return false;
4355 }
4356 }
4357 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004358
dan sinclair67a18932023-06-26 19:54:49 +00004359 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004360
4361 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004362}
4363
dan sinclairbae54e72023-07-28 15:01:54 +00004364bool ASTPrinter::EmitType(StringStream& out,
dan sinclaircedcdf32023-08-10 02:39:48 +00004365 const core::type::Type* type,
Ben Claytoncd52f382023-08-07 13:11:08 +00004366 core::AddressSpace address_space,
4367 core::Access access,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004368 const std::string& name,
4369 bool* name_printed /* = nullptr */) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004370 if (name_printed) {
4371 *name_printed = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004372 }
dan sinclairff7cf212022-10-03 14:05:23 +00004373 switch (address_space) {
Ben Claytoncd52f382023-08-07 13:11:08 +00004374 case core::AddressSpace::kStorage:
4375 if (access != core::Access::kRead) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004376 out << "RW";
4377 }
4378 out << "ByteAddressBuffer";
4379 return true;
Ben Claytoncd52f382023-08-07 13:11:08 +00004380 case core::AddressSpace::kUniform: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004381 auto array_length = (type->Size() + 15) / 16;
4382 out << "uint4 " << name << "[" << array_length << "]";
4383 if (name_printed) {
4384 *name_printed = true;
4385 }
4386 return true;
4387 }
4388 default:
4389 break;
4390 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004391
dan sinclair41e4d9a2022-05-01 14:40:55 +00004392 return Switch(
4393 type,
dan sinclaircedcdf32023-08-10 02:39:48 +00004394 [&](const core::type::Array* ary) {
4395 const core::type::Type* base_type = ary;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004396 std::vector<uint32_t> sizes;
dan sinclaircedcdf32023-08-10 02:39:48 +00004397 while (auto* arr = base_type->As<core::type::Array>()) {
4398 if (TINT_UNLIKELY(arr->Count()->Is<core::type::RuntimeArrayCount>())) {
Ben Claytonf848af22023-07-28 16:37:32 +00004399 TINT_ICE()
dan sinclair78f80672022-09-22 22:28:21 +00004400 << "runtime arrays may only exist in storage buffers, which should have "
Ben Clayton3a68ab42022-06-24 08:30:28 +00004401 "been transformed into a ByteAddressBuffer";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004402 }
dan sinclair78f80672022-09-22 22:28:21 +00004403 const auto count = arr->ConstantCount();
4404 if (!count) {
Ben Clayton415bd732024-05-02 14:36:02 +00004405 diagnostics_.AddError(Source{}) << core::type::Array::kErrExpectedConstantCount;
dan sinclair78f80672022-09-22 22:28:21 +00004406 return false;
4407 }
4408
4409 sizes.push_back(count.value());
dan sinclair41e4d9a2022-05-01 14:40:55 +00004410 base_type = arr->ElemType();
4411 }
dan sinclairff7cf212022-10-03 14:05:23 +00004412 if (!EmitType(out, base_type, address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004413 return false;
4414 }
4415 if (!name.empty()) {
4416 out << " " << name;
4417 if (name_printed) {
4418 *name_printed = true;
4419 }
4420 }
4421 for (uint32_t size : sizes) {
4422 out << "[" << size << "]";
4423 }
4424 return true;
4425 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004426 [&](const core::type::Bool*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004427 out << "bool";
4428 return true;
4429 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004430 [&](const core::type::F32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004431 out << "float";
4432 return true;
4433 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004434 [&](const core::type::F16*) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004435 out << "float16_t";
4436 return true;
Zhaoming Jiang62bfd312022-05-13 12:01:11 +00004437 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004438 [&](const core::type::I32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004439 out << "int";
4440 return true;
4441 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004442 [&](const core::type::Matrix* mat) {
4443 if (mat->type()->Is<core::type::F16>()) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004444 // Use matrix<type, N, M> for f16 matrix
4445 out << "matrix<";
dan sinclairff7cf212022-10-03 14:05:23 +00004446 if (!EmitType(out, mat->type(), address_space, access, "")) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004447 return false;
4448 }
4449 out << ", " << mat->columns() << ", " << mat->rows() << ">";
4450 return true;
4451 }
dan sinclairff7cf212022-10-03 14:05:23 +00004452 if (!EmitType(out, mat->type(), address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004453 return false;
4454 }
4455 // Note: HLSL's matrices are declared as <type>NxM, where N is the
4456 // number of rows and M is the number of columns. Despite HLSL's
4457 // matrices being column-major by default, the index operator and
dan sinclair6e77b472022-10-20 13:38:28 +00004458 // initializers actually operate on row-vectors, where as WGSL operates
dan sinclair41e4d9a2022-05-01 14:40:55 +00004459 // on column vectors. To simplify everything we use the transpose of the
4460 // matrices. See:
4461 // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-per-component-math#matrix-ordering
4462 out << mat->columns() << "x" << mat->rows();
4463 return true;
4464 },
Ben Clayton7cdaffe2024-05-08 16:24:07 +00004465 [&](const core::type::Pointer*) -> bool {
4466 TINT_ICE() << "Attempting to emit pointer type. These should have been removed with "
4467 "the SimplifyPointers transform";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004468 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004469 [&](const core::type::Sampler* sampler) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004470 out << "Sampler";
4471 if (sampler->IsComparison()) {
4472 out << "Comparison";
4473 }
4474 out << "State";
4475 return true;
4476 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004477 [&](const core::type::Struct* str) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004478 out << StructName(str);
4479 return true;
4480 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004481 [&](const core::type::Texture* tex) {
4482 if (TINT_UNLIKELY(tex->Is<core::type::ExternalTexture>())) {
Ben Claytonf848af22023-07-28 16:37:32 +00004483 TINT_ICE() << "Multiplanar external texture transform was not run.";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004484 }
Brandon Jones6661b282022-02-25 20:14:52 +00004485
dan sinclaircedcdf32023-08-10 02:39:48 +00004486 auto* storage = tex->As<core::type::StorageTexture>();
4487 auto* ms = tex->As<core::type::MultisampledTexture>();
4488 auto* depth_ms = tex->As<core::type::DepthMultisampledTexture>();
4489 auto* sampled = tex->As<core::type::SampledTexture>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004490
Ben Claytoncd52f382023-08-07 13:11:08 +00004491 if (storage && storage->access() != core::Access::kRead) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004492 out << "RW";
4493 }
4494 out << "Texture";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004495
dan sinclair41e4d9a2022-05-01 14:40:55 +00004496 switch (tex->dim()) {
dan sinclaircedcdf32023-08-10 02:39:48 +00004497 case core::type::TextureDimension::k1d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004498 out << "1D";
4499 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004500 case core::type::TextureDimension::k2d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004501 out << ((ms || depth_ms) ? "2DMS" : "2D");
4502 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004503 case core::type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004504 out << ((ms || depth_ms) ? "2DMSArray" : "2DArray");
4505 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004506 case core::type::TextureDimension::k3d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004507 out << "3D";
4508 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004509 case core::type::TextureDimension::kCube:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004510 out << "Cube";
4511 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004512 case core::type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004513 out << "CubeArray";
4514 break;
4515 default:
Ben Claytonf848af22023-07-28 16:37:32 +00004516 TINT_UNREACHABLE() << "unexpected TextureDimension " << tex->dim();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004517 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004518
dan sinclair41e4d9a2022-05-01 14:40:55 +00004519 if (storage) {
Ben Clayton448c01b2024-02-28 00:23:17 +00004520 auto* component = ImageFormatToRWtextureType(storage->texel_format());
Ben Clayton884f9522023-01-12 22:52:57 +00004521 if (TINT_UNLIKELY(!component)) {
Ben Claytonf848af22023-07-28 16:37:32 +00004522 TINT_ICE() << "Unsupported StorageTexture TexelFormat: "
4523 << static_cast<int>(storage->texel_format());
dan sinclair41e4d9a2022-05-01 14:40:55 +00004524 }
4525 out << "<" << component << ">";
4526 } else if (depth_ms) {
4527 out << "<float4>";
4528 } else if (sampled || ms) {
4529 auto* subtype = sampled ? sampled->type() : ms->type();
4530 out << "<";
dan sinclaircedcdf32023-08-10 02:39:48 +00004531 if (subtype->Is<core::type::F32>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004532 out << "float4";
dan sinclaircedcdf32023-08-10 02:39:48 +00004533 } else if (subtype->Is<core::type::I32>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004534 out << "int4";
dan sinclaircedcdf32023-08-10 02:39:48 +00004535 } else if (TINT_LIKELY(subtype->Is<core::type::U32>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004536 out << "uint4";
4537 } else {
Ben Claytonf848af22023-07-28 16:37:32 +00004538 TINT_ICE() << "Unsupported multisampled texture type";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004539 }
4540 out << ">";
4541 }
4542 return true;
4543 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004544 [&](const core::type::U32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004545 out << "uint";
4546 return true;
4547 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004548 [&](const core::type::Vector* vec) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004549 auto width = vec->Width();
dan sinclaircedcdf32023-08-10 02:39:48 +00004550 if (vec->type()->Is<core::type::F32>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004551 out << "float" << width;
dan sinclaircedcdf32023-08-10 02:39:48 +00004552 } else if (vec->type()->Is<core::type::I32>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004553 out << "int" << width;
dan sinclaircedcdf32023-08-10 02:39:48 +00004554 } else if (vec->type()->Is<core::type::U32>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004555 out << "uint" << width;
dan sinclaircedcdf32023-08-10 02:39:48 +00004556 } else if (vec->type()->Is<core::type::Bool>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004557 out << "bool" << width;
4558 } else {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004559 // For example, use "vector<float16_t, N>" for f16 vector.
dan sinclair41e4d9a2022-05-01 14:40:55 +00004560 out << "vector<";
dan sinclairff7cf212022-10-03 14:05:23 +00004561 if (!EmitType(out, vec->type(), address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004562 return false;
4563 }
4564 out << ", " << width << ">";
4565 }
4566 return true;
4567 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004568 [&](const core::type::Atomic* atomic) {
dan sinclairff7cf212022-10-03 14:05:23 +00004569 return EmitType(out, atomic->Type(), address_space, access, name);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004570 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004571 [&](const core::type::Void*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004572 out << "void";
4573 return true;
Ben Claytond6082c52023-10-26 16:02:01 +00004574 }, //
4575 TINT_ICE_ON_NO_MATCH);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004576}
4577
dan sinclairbae54e72023-07-28 15:01:54 +00004578bool ASTPrinter::EmitTypeAndName(StringStream& out,
dan sinclaircedcdf32023-08-10 02:39:48 +00004579 const core::type::Type* type,
Ben Claytoncd52f382023-08-07 13:11:08 +00004580 core::AddressSpace address_space,
4581 core::Access access,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004582 const std::string& name) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004583 bool name_printed = false;
dan sinclairff7cf212022-10-03 14:05:23 +00004584 if (!EmitType(out, type, address_space, access, name, &name_printed)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004585 return false;
4586 }
4587 if (!name.empty() && !name_printed) {
4588 out << " " << name;
4589 }
4590 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004591}
4592
dan sinclaircedcdf32023-08-10 02:39:48 +00004593bool ASTPrinter::EmitStructType(TextBuffer* b, const core::type::Struct* str) {
Ben Clayton329dfd72022-11-23 00:05:05 +00004594 auto it = emitted_structs_.emplace(str);
4595 if (!it.second) {
4596 return true;
4597 }
4598
dan sinclair67a18932023-06-26 19:54:49 +00004599 Line(b) << "struct " << StructName(str) << " {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004600 {
4601 ScopedIndent si(b);
4602 for (auto* mem : str->Members()) {
dan sinclaird026e132023-04-18 19:38:25 +00004603 auto mem_name = mem->Name().Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004604 auto* ty = mem->Type();
dan sinclair67a18932023-06-26 19:54:49 +00004605 auto out = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004606 std::string pre, post;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004607
Ben Clayton576ba1c2023-04-27 17:58:25 +00004608 auto& attributes = mem->Attributes();
Ben Claytonf0b4dbb2023-02-21 21:05:28 +00004609
Ben Clayton576ba1c2023-04-27 17:58:25 +00004610 if (auto location = attributes.location) {
4611 auto& pipeline_stage_uses = str->PipelineStageUses();
Ben Clayton8be957f2024-01-29 16:00:36 +00004612 if (TINT_UNLIKELY(pipeline_stage_uses.Count() != 1)) {
Ben Claytonf848af22023-07-28 16:37:32 +00004613 TINT_ICE() << "invalid entry point IO struct uses";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004614 }
Ben Clayton8be957f2024-01-29 16:00:36 +00004615 if (pipeline_stage_uses.Contains(core::type::PipelineStageUsage::kVertexInput)) {
Ben Clayton576ba1c2023-04-27 17:58:25 +00004616 post += " : TEXCOORD" + std::to_string(location.value());
Ben Clayton8be957f2024-01-29 16:00:36 +00004617 } else if (pipeline_stage_uses.Contains(
dan sinclaircedcdf32023-08-10 02:39:48 +00004618 core::type::PipelineStageUsage::kVertexOutput)) {
Ben Clayton576ba1c2023-04-27 17:58:25 +00004619 post += " : TEXCOORD" + std::to_string(location.value());
Ben Clayton8be957f2024-01-29 16:00:36 +00004620 } else if (pipeline_stage_uses.Contains(
dan sinclaircedcdf32023-08-10 02:39:48 +00004621 core::type::PipelineStageUsage::kFragmentInput)) {
Ben Clayton576ba1c2023-04-27 17:58:25 +00004622 post += " : TEXCOORD" + std::to_string(location.value());
Ben Clayton8be957f2024-01-29 16:00:36 +00004623 } else if (TINT_LIKELY(pipeline_stage_uses.Contains(
dan sinclaircedcdf32023-08-10 02:39:48 +00004624 core::type::PipelineStageUsage::kFragmentOutput))) {
Corentin Wallez0880e062024-01-30 16:16:45 +00004625 if (auto blend_src = attributes.blend_src) {
4626 post +=
4627 " : SV_Target" + std::to_string(location.value() + blend_src.value());
Brandon Jones07500d32023-07-21 22:07:03 +00004628 } else {
4629 post += " : SV_Target" + std::to_string(location.value());
4630 }
4631
Ben Clayton576ba1c2023-04-27 17:58:25 +00004632 } else {
Ben Claytonf848af22023-07-28 16:37:32 +00004633 TINT_ICE() << "invalid use of location attribute";
Ben Clayton576ba1c2023-04-27 17:58:25 +00004634 }
4635 }
4636 if (auto builtin = attributes.builtin) {
4637 auto name = builtin_to_attribute(builtin.value());
4638 if (name.empty()) {
Ben Clayton415bd732024-05-02 14:36:02 +00004639 diagnostics_.AddError(Source{}) << "unsupported builtin";
Ben Clayton576ba1c2023-04-27 17:58:25 +00004640 return false;
4641 }
4642 post += " : " + name;
4643 }
4644 if (auto interpolation = attributes.interpolation) {
4645 auto mod = interpolation_to_modifiers(interpolation->type, interpolation->sampling);
4646 if (mod.empty()) {
Ben Clayton415bd732024-05-02 14:36:02 +00004647 diagnostics_.AddError(Source{}) << "unsupported interpolation";
Ben Clayton576ba1c2023-04-27 17:58:25 +00004648 return false;
4649 }
4650 pre += mod;
4651 }
4652 if (attributes.invariant) {
4653 // Note: `precise` is not exactly the same as `invariant`, but is
4654 // stricter and therefore provides the necessary guarantees.
4655 // See discussion here: https://github.com/gpuweb/gpuweb/issues/893
4656 pre += "precise ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004657 }
4658
dan sinclair41e4d9a2022-05-01 14:40:55 +00004659 out << pre;
Ben Claytoncd52f382023-08-07 13:11:08 +00004660 if (!EmitTypeAndName(out, ty, core::AddressSpace::kUndefined, core::Access::kReadWrite,
4661 mem_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004662 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004663 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004664 out << post << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004665 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004666 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004667
dan sinclair67a18932023-06-26 19:54:49 +00004668 Line(b) << "};";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004669 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004670}
4671
dan sinclairbae54e72023-07-28 15:01:54 +00004672bool ASTPrinter::EmitUnaryOp(StringStream& out, const ast::UnaryOpExpression* expr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004673 switch (expr->op) {
Ben Clayton67b461b2023-08-08 07:58:19 +00004674 case core::UnaryOp::kIndirection:
4675 case core::UnaryOp::kAddressOf:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004676 return EmitExpression(out, expr->expr);
Ben Clayton67b461b2023-08-08 07:58:19 +00004677 case core::UnaryOp::kComplement:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004678 out << "~";
4679 break;
Ben Clayton67b461b2023-08-08 07:58:19 +00004680 case core::UnaryOp::kNot:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004681 out << "!";
4682 break;
Ben Clayton67b461b2023-08-08 07:58:19 +00004683 case core::UnaryOp::kNegation:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004684 out << "-";
4685 break;
4686 }
4687 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004688
dan sinclair41e4d9a2022-05-01 14:40:55 +00004689 if (!EmitExpression(out, expr->expr)) {
4690 return false;
4691 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004692
dan sinclair41e4d9a2022-05-01 14:40:55 +00004693 out << ")";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004694
dan sinclair41e4d9a2022-05-01 14:40:55 +00004695 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004696}
4697
dan sinclair0bfeaf92023-07-26 17:39:51 +00004698bool ASTPrinter::EmitVar(const ast::Var* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004699 auto* sem = builder_.Sem().Get(var);
4700 auto* type = sem->Type()->UnwrapRef();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004701
dan sinclair67a18932023-06-26 19:54:49 +00004702 auto out = Line();
dan sinclaird026e132023-04-18 19:38:25 +00004703 if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(), var->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004704 return false;
4705 }
4706
4707 out << " = ";
4708
dan sinclair6e77b472022-10-20 13:38:28 +00004709 if (var->initializer) {
4710 if (!EmitExpression(out, var->initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004711 return false;
4712 }
4713 } else {
4714 if (!EmitZeroValue(out, type)) {
4715 return false;
4716 }
4717 }
4718 out << ";";
4719
4720 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004721}
4722
dan sinclair0bfeaf92023-07-26 17:39:51 +00004723bool ASTPrinter::EmitLet(const ast::Let* let) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00004724 auto* sem = builder_.Sem().Get(let);
4725 auto* type = sem->Type()->UnwrapRef();
4726
dan sinclair67a18932023-06-26 19:54:49 +00004727 auto out = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00004728 if (!EmitTypeAndName(out, type, core::AddressSpace::kUndefined, core::Access::kUndefined,
dan sinclaird026e132023-04-18 19:38:25 +00004729 let->name->symbol.Name())) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004730 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004731 }
Ben Claytondcdf66e2022-06-17 12:48:51 +00004732 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00004733 if (!EmitExpression(out, let->initializer)) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00004734 return false;
4735 }
4736 out << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004737
Ben Claytondcdf66e2022-06-17 12:48:51 +00004738 return true;
4739}
4740
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004741template <typename F>
dan sinclairbae54e72023-07-28 15:01:54 +00004742bool ASTPrinter::CallBuiltinHelper(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004743 const ast::CallExpression* call,
Ben Claytond9766dc2023-09-21 12:41:20 +00004744 const sem::BuiltinFn* builtin,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004745 F&& build) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004746 // Generate the helper function if it hasn't been created already
Ben Clayton7a27d6c2024-01-31 19:44:28 +00004747 auto fn = tint::GetOrAdd(builtins_, builtin, [&]() -> std::string {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004748 TextBuffer b;
4749 TINT_DEFER(helpers_.Append(b));
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004750
Ben Claytondfc815c2023-09-25 15:38:43 +00004751 auto fn_name = UniqueIdentifier(std::string("tint_") + wgsl::str(builtin->Fn()));
dan sinclair41e4d9a2022-05-01 14:40:55 +00004752 std::vector<std::string> parameter_names;
4753 {
dan sinclair67a18932023-06-26 19:54:49 +00004754 auto decl = Line(&b);
Ben Claytoncd52f382023-08-07 13:11:08 +00004755 if (!EmitTypeAndName(decl, builtin->ReturnType(), core::AddressSpace::kUndefined,
4756 core::Access::kUndefined, fn_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004757 return "";
4758 }
4759 {
4760 ScopedParen sp(decl);
4761 for (auto* param : builtin->Parameters()) {
4762 if (!parameter_names.empty()) {
4763 decl << ", ";
4764 }
4765 auto param_name = "param_" + std::to_string(parameter_names.size());
4766 const auto* ty = param->Type();
dan sinclaircedcdf32023-08-10 02:39:48 +00004767 if (auto* ptr = ty->As<core::type::Pointer>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004768 decl << "inout ";
4769 ty = ptr->StoreType();
4770 }
Ben Claytoncd52f382023-08-07 13:11:08 +00004771 if (!EmitTypeAndName(decl, ty, core::AddressSpace::kUndefined,
4772 core::Access::kUndefined, param_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004773 return "";
4774 }
4775 parameter_names.emplace_back(std::move(param_name));
4776 }
4777 }
4778 decl << " {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004779 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004780 {
4781 ScopedIndent si(&b);
4782 if (!build(&b, parameter_names)) {
4783 return "";
4784 }
4785 }
dan sinclair67a18932023-06-26 19:54:49 +00004786 Line(&b) << "}";
4787 Line(&b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004788 return fn_name;
4789 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004790
dan sinclair41e4d9a2022-05-01 14:40:55 +00004791 if (fn.empty()) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004792 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004793 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004794
4795 // Call the helper
4796 out << fn;
4797 {
dan sinclairb2ba57b2023-02-28 15:14:09 +00004798 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004799 bool first = true;
4800 for (auto* arg : call->args) {
4801 if (!first) {
4802 out << ", ";
4803 }
4804 first = false;
4805 if (!EmitExpression(out, arg)) {
4806 return false;
4807 }
4808 }
4809 }
4810 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004811}
4812
dan sinclaircedcdf32023-08-10 02:39:48 +00004813std::string ASTPrinter::StructName(const core::type::Struct* s) {
Ben Clayton34c8e572023-08-01 00:37:35 +00004814 auto name = s->Name().Name();
4815 if (HasPrefix(name, "__")) {
Ben Clayton7a27d6c2024-01-31 19:44:28 +00004816 name = tint::GetOrAdd(builtin_struct_names_, s,
4817 [&] { return UniqueIdentifier(name.substr(2)); });
Ben Clayton34c8e572023-08-01 00:37:35 +00004818 }
4819 return name;
4820}
4821
dan sinclair0bfeaf92023-07-26 17:39:51 +00004822std::string ASTPrinter::UniqueIdentifier(const std::string& prefix /* = "" */) {
Ben Clayton5ed099a2023-07-25 18:52:03 +00004823 return builder_.Symbols().New(prefix).Name();
4824}
4825
dan sinclair0bfeaf92023-07-26 17:39:51 +00004826} // namespace tint::hlsl::writer